From 68fb01c20684f60b1fbb065190cdd7b3242ab4f1 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 3 Mar 2013 20:45:58 -0800 Subject: [PATCH 001/509] initial commit for interpolation --- scripts/mk_project.py | 3 +- src/interp/foci2.h | 56 ++++ src/interp/iz3base.cpp | 228 ++++++++++++++ src/interp/iz3base.h | 107 +++++++ src/interp/iz3foci.cpp | 327 +++++++++++++++++++++ src/interp/iz3foci.h | 15 + src/interp/iz3hash.h | 151 ++++++++++ src/interp/iz3mgr.cpp | 352 ++++++++++++++++++++++ src/interp/iz3mgr.h | 397 +++++++++++++++++++++++++ src/interp/iz3profiling.h | 20 ++ src/interp/iz3proof.cpp | 604 ++++++++++++++++++++++++++++++++++++++ src/interp/iz3proof.h | 255 ++++++++++++++++ src/interp/iz3scopes.cpp | 302 +++++++++++++++++++ src/interp/iz3scopes.h | 167 +++++++++++ src/interp/iz3secondary.h | 22 ++ 15 files changed, 3005 insertions(+), 1 deletion(-) create mode 100755 src/interp/foci2.h create mode 100755 src/interp/iz3base.cpp create mode 100755 src/interp/iz3base.h create mode 100755 src/interp/iz3foci.cpp create mode 100755 src/interp/iz3foci.h create mode 100755 src/interp/iz3hash.h create mode 100644 src/interp/iz3mgr.cpp create mode 100644 src/interp/iz3mgr.h create mode 100755 src/interp/iz3profiling.h create mode 100755 src/interp/iz3proof.cpp create mode 100755 src/interp/iz3proof.h create mode 100755 src/interp/iz3scopes.cpp create mode 100755 src/interp/iz3scopes.h create mode 100755 src/interp/iz3secondary.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 6ea794040..e4c788cea 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -59,8 +59,9 @@ def init_project_def(): add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') + add_lib('interp', ['solver']) API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h'] - add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure'], + add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure', 'interp'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) add_exe('shell', ['api', 'sat', 'extra_cmds'], exe_name='z3') add_exe('test', ['api', 'fuzzing'], exe_name='test-z3', install=False) diff --git a/src/interp/foci2.h b/src/interp/foci2.h new file mode 100755 index 000000000..3f32b8118 --- /dev/null +++ b/src/interp/foci2.h @@ -0,0 +1,56 @@ +#ifndef FOCI2_H +#define FOCI2_H + +#include +#include + +#ifdef WIN32 +#define FOCI2_EXPORT __declspec(dllexport) +#else +#define FOCI2_EXPORT __attribute__ ((visibility ("default"))) +#endif + +class foci2 { + public: + virtual ~foci2(){} + + typedef int ast; + typedef int symb; + + /** Built-in operators */ + enum ops { + And = 0, Or, Not, Iff, Ite, Equal, Plus, Times, Floor, Leq, Div, Bool, Int, Array, Tsym, Fsym, Forall, Exists, Distinct, LastOp + }; + + virtual symb mk_func(const std::string &s) = 0; + virtual symb mk_pred(const std::string &s) = 0; + virtual ast mk_op(ops op, const std::vector args) = 0; + virtual ast mk_op(ops op, ast) = 0; + virtual ast mk_op(ops op, ast, ast) = 0; + virtual ast mk_op(ops op, ast, ast, ast) = 0; + virtual ast mk_int(const std::string &) = 0; + virtual ast mk_rat(const std::string &) = 0; + virtual ast mk_true() = 0; + virtual ast mk_false() = 0; + virtual ast mk_app(symb,const std::vector args) = 0; + + virtual bool get_func(ast, symb &) = 0; + virtual bool get_pred(ast, symb &) = 0; + virtual bool get_op(ast, ops &) = 0; + virtual bool get_true(ast id) = 0; + virtual bool get_false(ast id) = 0; + virtual bool get_int(ast id, std::string &res) = 0; + virtual bool get_rat(ast id, std::string &res) = 0; + virtual const std::string &get_symb(symb) = 0; + + virtual int get_num_args(ast) = 0; + virtual ast get_arg(ast, int) = 0; + + virtual void show_ast(ast) = 0; + + virtual bool interpolate(const std::vector &frames, std::vector &itps, std::vector parents) = 0; + + FOCI2_EXPORT static foci2 *create(const std::string &); +}; + +#endif diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp new file mode 100755 index 000000000..44006f7f2 --- /dev/null +++ b/src/interp/iz3base.cpp @@ -0,0 +1,228 @@ +/* Copyright 2011 Microsoft Research. */ + +#include "iz3base.h" +#include +#include +#include +#include + + +#ifndef WIN32 +using namespace stl_ext; +#endif + + +iz3base::range &iz3base::ast_range(ast t){ + return ast_ranges_hash[t].rng; +} + +iz3base::range &iz3base::sym_range(symb d){ + return sym_range_hash[d]; +} + +void iz3base::add_frame_range(int frame, ast t){ + range &rng = ast_range(t); + if(!in_range(frame,rng)){ + range_add(frame,rng); + for(int i = 0, n = num_args(t); i < n; ++i) + add_frame_range(frame,arg(t,i)); + if(op(t) == Uninterpreted) + range_add(frame,sym_range(sym(t))); + } +} + +#if 1 +iz3base::range &iz3base::ast_scope(ast t){ + ranges &rngs = ast_ranges_hash[t]; + range &rng = rngs.scp; + if(!rngs.scope_computed){ // not computed yet + rng = range_full(); + for(int i = 0, n = num_args(t); i < n; ++i) + rng = range_glb(rng,ast_scope(arg(t,i))); + if(op(t) == Uninterpreted) + if(parents.empty() || num_args(t) == 0) // in tree mode, all function syms are global + rng = range_glb(rng,sym_range(sym(t))); + rngs.scope_computed = true; + } + return rng; +} +#else +iz3base::range &iz3base::ast_scope(ast t){ + ranges &rngs = ast_ranges_hash[t]; + if(rngs.scope_computed) return rngs.scp; + range rng = range_full(); + for(int i = 0, n = num_args(t); i < n; ++i) + rng = range_glb(rng,ast_scope(arg(t,i))); + if(op(t) == Uninterpreted) + if(parents.empty() || num_args(t) == 0) // in tree mode, all function syms are global + rng = range_glb(rng,sym_range(sym(t))); + rngs = ast_ranges_hash[t]; + rngs.scope_computed = true; + rngs.scp = rng; + return rngs.scp; +} +#endif + +void iz3base::print(const std::string &filename){ + ast t = make(And,cnsts); + assert(0 && "not implemented"); + // Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"iZ3","QFLIA","unsat","",0,0,t); + // std::ofstream f(filename.c_str()); + // f << smt; +} + + +void iz3base::gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo){ + if(memo.find(n) == memo.end()){ + memo.insert(n); + if(op(n) == And){ + int nargs = num_args(n); + for(int i = 0; i < nargs; i++) + gather_conjuncts_rec(arg(n,i),conjuncts,memo); + } + else + conjuncts.push_back(n); + } +} + +void iz3base::gather_conjuncts(ast n, std::vector &conjuncts){ + hash_set memo; + gather_conjuncts_rec(n,conjuncts,memo); +} + +bool iz3base::is_literal(ast n){ + if(is_not(n))n = arg(n,0); + if(is_true(n) || is_false(n)) return false; + if(op(n) == And) return false; + return true; +} + +iz3base::ast iz3base::simplify_and(std::vector &conjuncts){ + hash_set memo; + for(unsigned i = 0; i < conjuncts.size(); i++){ + if(is_false(conjuncts[i])) + return conjuncts[i]; + if(is_true(conjuncts[i]) || memo.find(conjuncts[i]) != memo.end()){ + std::swap(conjuncts[i],conjuncts.back()); + conjuncts.pop_back(); + } + else if(memo.find(mk_not(conjuncts[i])) != memo.end()) + return mk_false(); // contradiction! + else + memo.insert(conjuncts[i]); + } + if(conjuncts.empty())return mk_true(); + return make(And,conjuncts); +} + +iz3base::ast iz3base::simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth){ + if(is_not(n))return mk_not(simplify_with_lit_rec(mk_not(n),lit,memo,depth)); + if(n == lit) return mk_true(); + ast not_lit = mk_not(lit); + if(n == not_lit) return mk_false(); + if(op(n) != And || depth <= 0) return n; + std::pair foo(n,(ast)0); + std::pair::iterator,bool> bar = memo.insert(foo); + ast &res = bar.first->second; + if(!bar.second) return res; + int nargs = num_args(n); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = simplify_with_lit_rec(arg(n,i),lit,memo,depth-1); + res = simplify_and(args); + return res; +} + +iz3base::ast iz3base::simplify_with_lit(ast n, ast lit){ + hash_map memo; + return simplify_with_lit_rec(n,lit,memo,1); +} + +iz3base::ast iz3base::simplify(ast n){ + if(is_not(n)) return mk_not(simplify(mk_not(n))); + std::pair memo_obj(n,(ast)0); + std::pair::iterator,bool> memo = simplify_memo.insert(memo_obj); + ast &res = memo.first->second; + if(!memo.second) return res; + switch(op(n)){ + case And: { + std::vector conjuncts; + gather_conjuncts(n,conjuncts); + for(unsigned i = 0; i < conjuncts.size(); i++) + conjuncts[i] = simplify(conjuncts[i]); +#if 0 + for(unsigned i = 0; i < conjuncts.size(); i++) + if(is_literal(conjuncts[i])) + for(unsigned j = 0; j < conjuncts.size(); j++) + if(j != i) + conjuncts[j] = simplify_with_lit(conjuncts[j],conjuncts[i]); +#endif + res = simplify_and(conjuncts); + } + break; + case Equal: { + ast x = arg(n,0); + ast y = arg(n,1); + if(ast_id(x) > ast_id(y)) + std::swap(x,y); + res = make(Equal,x,y); + break; + } + default: + res = n; + } + return res; +} + +void iz3base::initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &theory){ + cnsts = _parts; + for(unsigned i = 0; i < cnsts.size(); i++) + add_frame_range(i, cnsts[i]); + for(unsigned i = 0; i < theory.size(); i++){ + add_frame_range(SHRT_MIN, theory[i]); + add_frame_range(SHRT_MAX, theory[i]); + } +} + +void iz3base::check_interp(const std::vector &itps, std::vector &theory){ +#if 0 + Z3_config config = Z3_mk_config(); + Z3_context vctx = Z3_mk_context(config); + int frames = cnsts.size(); + std::vector foocnsts(cnsts); + for(unsigned i = 0; i < frames; i++) + foocnsts[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),cnsts[i]); + Z3_write_interpolation_problem(ctx,frames,&foocnsts[0],0, "temp_lemma.smt", theory.size(), &theory[0]); + int vframes,*vparents; + Z3_ast *vcnsts; + const char *verror; + bool ok = Z3_read_interpolation_problem(vctx,&vframes,&vcnsts,0,"temp_lemma.smt",&verror); + assert(ok); + std::vector vvcnsts(vframes); + std::copy(vcnsts,vcnsts+vframes,vvcnsts.begin()); + std::vector vitps(itps.size()); + for(unsigned i = 0; i < itps.size(); i++) + vitps[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),itps[i]); + Z3_write_interpolation_problem(ctx,itps.size(),&vitps[0],0,"temp_interp.smt"); + int iframes,*iparents; + Z3_ast *icnsts; + const char *ierror; + ok = Z3_read_interpolation_problem(vctx,&iframes,&icnsts,0,"temp_interp.smt",&ierror); + assert(ok); + const char *error = 0; + bool iok = Z3_check_interpolant(vctx, frames, &vvcnsts[0], parents.size() ? &parents[0] : 0, icnsts, &error); + assert(iok); +#endif +} + +bool iz3base::is_sat(ast f){ + assert(0 && "iz3base::is_sat() not implemented"); +#if 0 + Z3_push(ctx); + Z3_assert_cnstr(ctx,f); + Z3_lbool res = Z3_check(ctx); + Z3_pop(ctx,1); + return res != Z3_L_FALSE; +#endif +} + diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h new file mode 100755 index 000000000..383a89ea4 --- /dev/null +++ b/src/interp/iz3base.h @@ -0,0 +1,107 @@ +/* Copyright 2011 Microsoft Research. */ + +#ifndef IZ3BASE_H +#define IZ3BASE_H + +#include "iz3mgr.h" +#include "iz3scopes.h" + +/* Base class for interpolators. Includes an AST manager and a scoping + object as bases. */ + +class iz3base : public iz3mgr, public scopes { + + public: + + /** Get the range in which an expression occurs. This is the + smallest subtree containing all occurrences of the + expression. */ + range &ast_range(ast); + + /** Get the scope of an expression. This is the set of tree nodes in + which all of the expression's symbols are in scope. */ + range &ast_scope(ast); + + /** Get the range of a symbol. This is the smallest subtree containing + all occurrences of the symbol. */ + range &sym_range(symb); + + /** Is an expression local (in scope in some frame)? */ + + bool is_local(ast node){ + return !range_is_empty(ast_scope(node)); + } + + /** Simplify an expression */ + + ast simplify(ast); + + /** Constructor */ + + iz3base(scoped_ptr &_m_manager, + const std::vector &_cnsts, + const std::vector &_parents, + const std::vector &_theory) + : iz3mgr(_m_manager), scopes(_parents) { + initialize(_cnsts,_parents,_theory); + weak = false; + } + + /* Set our options */ + void set_option(const std::string &name, const std::string &value){ + if(name == "weak" && value == "1") weak = true; + } + + /* Are we doing weak interpolants? */ + bool weak_mode(){return weak;} + + /** Print interpolation problem to an SMTLIB format file */ + void print(const std::string &filename); + + /** Check correctness of a solutino to this problem. */ + void check_interp(const std::vector &itps, std::vector &theory); + + /** For convenience -- is this formula SAT? */ + bool is_sat(ast); + + /** Interpolator for clauses, to be implemented */ + virtual void interpolate_clause(std::vector &lits, std::vector &itps){ + throw "no interpolator"; + } + + private: + + struct ranges { + range rng; + range scp; + bool scope_computed; + ranges(){scope_computed = false;} + }; + + stl_ext::hash_map sym_range_hash; + stl_ext::hash_map ast_ranges_hash; + stl_ext::hash_map simplify_memo; + + + void add_frame_range(int frame, ast t); + + void initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory); + + std::vector cnsts; + + bool is_literal(ast n); + void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); + void gather_conjuncts(ast n, std::vector &conjuncts); + ast simplify_and(std::vector &conjuncts); + ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); + ast simplify_with_lit(ast n, ast lit); + + bool weak; + +}; + + + + + +#endif diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp new file mode 100755 index 000000000..741391a8d --- /dev/null +++ b/src/interp/iz3foci.cpp @@ -0,0 +1,327 @@ +/* Copyright 2011 Microsoft Research. */ + +#include +#include +#include + +#include "iz3hash.h" +#include "foci2.h" +#include "iz3foci.h" + +#ifndef WIN32 +using namespace stl_ext; +#endif + +class iz3foci_impl : public iz3secondary { + + int frames; + int *parents; + foci2 *foci; + foci2::symb select_op; + foci2::symb store_op; + foci2::symb mod_op; + +public: + iz3foci_impl(iz3mgr *mgr, int _frames, int *_parents) : iz3secondary(*mgr) { + frames = _frames; + parents = _parents; + foci = 0; + } + + typedef hash_map AstToNode; + AstToNode ast_to_node; // maps Z3 ast's to foci expressions + + typedef hash_map NodeToAst; + NodeToAst node_to_ast; // maps Z3 ast's to foci expressions + + typedef hash_map FuncDeclToSymbol; + FuncDeclToSymbol func_decl_to_symbol; // maps Z3 func decls to symbols + + typedef hash_map SymbolToFuncDecl; + SymbolToFuncDecl symbol_to_func_decl; // maps symbols to Z3 func decls + + int from_symb(symb func){ + std::string name = string_of_symbol(func); + bool is_bool = is_bool_type(get_range_type(func)); + foci2::symb f; + if(is_bool) + f = foci->mk_pred(name); + else + f = foci->mk_func(name); + symbol_to_func_decl[f] = func; + func_decl_to_symbol[func] = f; + return f; + } + + // create a symbol corresponding to a DeBruijn index of a particular type + // the type has to be encoded into the name because the same index can + // occur with different types + foci2::symb make_deBruijn_symbol(int index, int type){ + std::ostringstream s; + s << "#" << index << "#" << type; + return foci->mk_func(s.str()); + } + + int from_Z3_ast(ast t){ + std::pair foo(t,0); + std::pair bar = ast_to_node.insert(foo); + int &res = bar.first->second; + if(!bar.second) return res; + int nargs = num_args(t); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = from_Z3_ast(arg(t,i)); + + switch(op(t)){ + case True: + res = foci->mk_true(); break; + case False: + res = foci->mk_false(); break; + case And: + res = foci->mk_op(foci2::And,args); break; + case Or: + res = foci->mk_op(foci2::Or,args); break; + case Not: + res = foci->mk_op(foci2::Not,args[0]); break; + case Iff: + res = foci->mk_op(foci2::Iff,args); break; + case OP_OEQ: // bit of a mystery, this one... + if(args[0] == args[1]) res = foci->mk_true(); + else res = foci->mk_op(foci2::Iff,args); + break; + case Ite: + if(is_bool_type(get_type(t))) + res = foci->mk_op(foci2::And,foci->mk_op(foci2::Or,foci->mk_op(foci2::Not,args[0]),args[1]),foci->mk_op(foci2::Or,args[0],args[2])); + else + res = foci->mk_op(foci2::Ite,args); + break; + case Equal: + res = foci->mk_op(foci2::Equal,args); break; + case Implies: + args[0] = foci->mk_op(foci2::Not,args[0]); res = foci->mk_op(foci2::Or,args); break; + case Xor: + res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Iff,args)); break; + case Leq: + res = foci->mk_op(foci2::Leq,args); break; + case Geq: + std::swap(args[0],args[1]); res = foci->mk_op(foci2::Leq,args); break; + case Gt: + res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Leq,args)); break; + case Lt: + std::swap(args[0],args[1]); res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Leq,args)); break; + case Plus: + res = foci->mk_op(foci2::Plus,args); break; + case Sub: + args[1] = foci->mk_op(foci2::Times,foci->mk_int("-1"),args[1]); res = foci->mk_op(foci2::Plus,args); break; + case Uminus: + res = foci->mk_op(foci2::Times,foci->mk_int("-1"),args[0]); break; + case Times: + res = foci->mk_op(foci2::Times,args); break; + case Idiv: + res = foci->mk_op(foci2::Div,args); break; + case Mod: + res = foci->mk_app(mod_op,args); break; + case Select: + res = foci->mk_app(select_op,args); break; + case Store: + res = foci->mk_app(store_op,args); break; + case Distinct: + res = foci->mk_op(foci2::Distinct,args); break; + case Uninterpreted: { + symb func = sym(t); + FuncDeclToSymbol::iterator it = func_decl_to_symbol.find(func); + foci2::symb f = (it == func_decl_to_symbol.end()) ? from_symb(func) : it->second; + if(foci->get_symb(f).substr(0,3) == "lbl" && args.size()==1) // HACK to handle Z3 labels + res = args[0]; + else if(foci->get_symb(f).substr(0,3) == "lbl" && args.size()==0) // HACK to handle Z3 labels + res = foci->mk_true(); + else res = foci->mk_app(f,args); + break; + } + case Numeral: { + std::string s = string_of_numeral(t); + res = foci->mk_int(s); + } + case Forall: + case Exists: { + bool is_forall = op(t) == Forall; + foci2::ops qop = is_forall ? foci2::Forall : foci2::Exists; + int bvs = get_quantifier_num_bound(t); + std::vector foci_bvs(bvs); + for(int i = 0; i < bvs; i++){ + std::string name = get_quantifier_bound_name(t,i); + //Z3_string name = Z3_get_symbol_string(ctx,sym); + // type ty = get_quantifier_bound_type(t,i); + foci2::symb f = foci->mk_func(name); + foci2::ast v = foci->mk_app(f,std::vector()); + foci_bvs[i] = v; + } + foci2::ast body = from_Z3_ast(get_quantifier_body(t)); + foci_bvs.push_back(body); + res = foci->mk_op(qop,foci_bvs); + node_to_ast[res] = t; // desperate + } + case Variable: { // a deBruijn index + int index = get_variable_index_value(t); + type ty = get_type(t); + foci2::symb symbol = make_deBruijn_symbol(index,(int)(ty)); + res = foci->mk_app(symbol,std::vector()); + } + default: + { + std::cerr << "iZ3: unsupported Z3 operator in expression\n "; + print_expr(std::cerr,t); + std::cerr << "\n"; + assert(0 && "iZ3: unsupported Z3 operator"); + } + } + return res; + } + + // convert an expr to Z3 ast + ast to_Z3_ast(foci2::ast i){ + std::pair foo(i,(ast)0); + std::pair bar = node_to_ast.insert(foo); + if(!bar.second) return bar.first->second; + ast &res = bar.first->second; + + if(i < 0){ + res = mk_not(to_Z3_ast(-i)); + return res; + } + + // get the arguments + unsigned n = foci->get_num_args(i); + std::vector args(n); + for(unsigned j = 0; j < n; j++) + args[j] = to_Z3_ast(foci->get_arg(i,j)); + + // handle operators + foci2::ops o; + foci2::symb f; + std::string nval; + if(foci->get_true(i)) + res = mk_true(); + else if(foci->get_false(i)) + res = mk_false(); + else if(foci->get_op(i,o)){ + switch(o){ + case foci2::And: + res = make(And,args); break; + case foci2::Or: + res = make(Or,args); break; + case foci2::Not: + res = mk_not(args[0]); break; + case foci2::Iff: + res = make(Iff,args[0],args[1]); break; + case foci2::Ite: + res = make(Ite,args[0],args[1],args[2]); break; + case foci2::Equal: + res = make(Equal,args[0],args[1]); break; + case foci2::Plus: + res = make(Plus,args); break; + case foci2::Times: + res = make(Times,args); break; + case foci2::Div: + res = make(Div,args[0],args[1]); break; + case foci2::Leq: + res = make(Leq,args[0],args[1]); break; + case foci2::Distinct: + res = make(Distinct,args); + break; + case foci2::Tsym: + res = mk_true(); + break; + case foci2::Fsym: + res = mk_false(); + break; + case foci2::Forall: + case foci2::Exists: + { + int nargs = n; + std::vector bounds(nargs-1); + for(int i = 0; i < nargs-1; i++) + bounds[i] = args[i]; + opr oz = o == foci2::Forall ? Forall : Exists; + res = make_quant(oz,bounds,args[nargs-1]); + } + break; + default: + assert("unknown built-in op"); + } + } + else if(foci->get_int(i,nval)){ + res = make_int(nval); + } + else if(foci->get_func(i,f)){ + if(f == select_op){ + assert(n == 2); + res = make(Select,args[0],args[1]); + } + else if(f == store_op){ + assert(n == 3); + res = make(Store,args[0],args[1],args[2]); + } + else if(f == mod_op){ + assert(n == 2); + res = make(Mod,args[0],args[1]); + } + else { + std::pair foo(f,(symb)0); + std::pair bar = symbol_to_func_decl.insert(foo); + symb &func_decl = bar.first->second; + if(bar.second){ + std::cout << "unknown function symbol:\n"; + foci->show_ast(i); + assert(0); + } + res = make(func_decl,args); + } + } + else { + std::cerr << "iZ3: unknown FOCI expression kind\n"; + assert(0 && "iZ3: unknown FOCI expression kind"); + } + return res; + } + + int interpolate(const std::vector &cnsts, std::vector &itps){ + assert((int)cnsts.size() == frames); + foci = foci2::create("lia"); + if(!foci){ + std::cerr << "iZ3: cannot find foci lia solver.\n"; + assert(0); + } + select_op = foci->mk_func("select"); + store_op = foci->mk_func("store"); + mod_op = foci->mk_func("mod"); + std::vector foci_cnsts(frames), foci_itps(frames-1), foci_parents; + if(parents) + foci_parents.resize(frames); + for(int i = 0; i < frames; i++){ + foci_cnsts[i] = from_Z3_ast(cnsts[i]); + if(parents) + foci_parents[i] = parents[i]; + } + int res = foci->interpolate(foci_cnsts, foci_itps, foci_parents); + if(res == 0){ + assert((int)foci_itps.size() == frames-1); + itps.resize(frames-1); + for(int i = 0; i < frames-1; i++){ + // foci->show_ast(foci_itps[i]); + itps[i] = to_Z3_ast(foci_itps[i]); + } + } + ast_to_node.clear(); + node_to_ast.clear(); + func_decl_to_symbol.clear(); + symbol_to_func_decl.clear(); + delete foci; + return res; + } + +}; + +iz3secondary *iz3foci::create(iz3mgr *mgr, int num, int *parents){ + return new iz3foci_impl(mgr,num,parents); +} diff --git a/src/interp/iz3foci.h b/src/interp/iz3foci.h new file mode 100755 index 000000000..4b7040b98 --- /dev/null +++ b/src/interp/iz3foci.h @@ -0,0 +1,15 @@ +/* Copyright 2011 Microsoft Research. */ + +#ifndef IZ3FOCI_H +#define IZ3FOCI_H + +#include "iz3secondary.h" + +/** Secondary prover based on Cadence FOCI. */ + +class iz3foci { + public: + static iz3secondary *create(iz3mgr *mgr, int num, int *parents); +}; + +#endif diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h new file mode 100755 index 000000000..abbe5b5fb --- /dev/null +++ b/src/interp/iz3hash.h @@ -0,0 +1,151 @@ + +// pull in the headers for has_map and hash_set +// these live in non-standard places + +#ifndef IZ3_HASH_H +#define IZ3_HASH_H + +//#define USE_UNORDERED_MAP +#ifdef USE_UNORDERED_MAP + +#define stl_ext std +#define hash_space std +#include +#include +#define hash_map unordered_map +#define hash_set unordered_set + +#else + +#if __GNUC__ >= 3 +#undef __DEPRECATED +#define stl_ext __gnu_cxx +#define hash_space stl_ext +#include +#include +#else +#ifdef WIN32 +#define stl_ext stdext +#define hash_space std +#include +#include +#else +#define stl_ext std +#define hash_space std +#include +#include +#endif +#endif + +#endif + +#include + +// stupid STL doesn't include hash function for class string + +#ifndef WIN32 + +namespace stl_ext { + template <> + class hash { + stl_ext::hash H; + public: + size_t operator()(const std::string &s) const { + return H(s.c_str()); + } + }; +} + +#endif + +namespace hash_space { + template <> + class hash > { + public: + size_t operator()(const std::pair &p) const { + return p.first + p.second; + } + }; +} + +#ifdef WIN32 +template <> inline +size_t stdext::hash_value >(const std::pair& p) +{ // hash _Keyval to size_t value one-to-one + return p.first + p.second; +} +#endif + +namespace hash_space { + template + class hash > { + public: + size_t operator()(const std::pair &p) const { + return (size_t)p.first + (size_t)p.second; + } + }; +} + +#if 0 +template inline +size_t stdext::hash_value >(const std::pair& p) +{ // hash _Keyval to size_t value one-to-one + return (size_t)p.first + (size_t)p.second; +} +#endif + +#ifdef WIN32 + +namespace std { + template <> + class less > { + public: + bool operator()(const pair &x, const pair &y) const { + return x.first < y.first || x.first == y.first && x.second < y.second; + } + }; + +} + +namespace std { + template + class less > { + public: + bool operator()(const pair &x, const pair &y) const { + return (size_t)x.first < (size_t)y.first || (size_t)x.first == (size_t)y.first && (size_t)x.second < (size_t)y.second; + } + }; + +} + +#endif + + +#ifndef WIN32 + +namespace stl_ext { + template + class hash { + public: + size_t operator()(const T *p) const { + return (size_t) p; + } + }; +} + +#endif + +#ifdef WIN32 + + + + +template +class hash_map : public stl_ext::hash_map > > {}; + +template +class hash_set : public stl_ext::hash_set > > {}; + +#endif + +#endif diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp new file mode 100644 index 000000000..b79fa0f6b --- /dev/null +++ b/src/interp/iz3mgr.cpp @@ -0,0 +1,352 @@ +/* Copyright 2011 Microsoft Research. */ + + +#include "iz3mgr.h" + +#include +#include +#include +#include + +#include "expr_abstract.h" + + +#ifndef WIN32 +using namespace stl_ext; +#endif + + +std::ostream &operator <<(std::ostream &s, const iz3mgr::ast &a){ + return s; +} + + +iz3mgr::ast iz3mgr::make_var(const std::string &name, type ty){ + symbol s = symbol(name.c_str()); + return m().mk_const(m().mk_const_decl(s, ty)); +} + +iz3mgr::ast iz3mgr::make(opr op, int n, raw_ast **args){ + return m().mk_app(m().get_basic_family_id(), op, 0, 0, n, (expr **)args); +} + +iz3mgr::ast iz3mgr::make(opr op, const std::vector &args){ + static std::vector a(10); + if(a.size() < args.size()) + a.resize(args.size()); + for(unsigned i = 0; i < args.size(); i++) + a[i] = args[i].raw(); + return make(op,args.size(), args.size() ? &a[0] : 0); +} + +iz3mgr::ast iz3mgr::make(opr op){ + return make(op,0,0); +} + +iz3mgr::ast iz3mgr::make(opr op, ast &arg0){ + raw_ast *a = arg0.raw(); + return make(op,1,&a); +} + +iz3mgr::ast iz3mgr::make(opr op, ast &arg0, ast &arg1){ + raw_ast *args[2]; + args[0] = arg0.raw(); + args[1] = arg1.raw(); + return make(op,2,args); +} + +iz3mgr::ast iz3mgr::make(opr op, ast &arg0, ast &arg1, ast &arg2){ + raw_ast *args[3]; + args[0] = arg0.raw(); + args[1] = arg1.raw(); + args[2] = arg2.raw(); + return make(op,3,args); +} + +iz3mgr::ast iz3mgr::make(symb sym, int n, raw_ast **args){ + return m().mk_app(sym, n, (expr **) args); +} + +iz3mgr::ast iz3mgr::make(symb sym, const std::vector &args){ + static std::vector a(10); + if(a.size() < args.size()) + a.resize(args.size()); + for(unsigned i = 0; i < args.size(); i++) + a[i] = args[i].raw(); + return make(sym,args.size(), args.size() ? &a[0] : 0); +} + +iz3mgr::ast iz3mgr::make(symb sym){ + return make(sym,0,0); +} + +iz3mgr::ast iz3mgr::make(symb sym, ast &arg0){ + raw_ast *a = arg0.raw(); + return make(sym,1,&a); +} + +iz3mgr::ast iz3mgr::make(symb sym, ast &arg0, ast &arg1){ + raw_ast *args[2]; + args[0] = arg0.raw(); + args[1] = arg1.raw(); + return make(sym,2,args); +} + +iz3mgr::ast iz3mgr::make(symb sym, ast &arg0, ast &arg1, ast &arg2){ + raw_ast *args[3]; + args[0] = arg0.raw(); + args[1] = arg1.raw(); + args[2] = arg2.raw(); + return make(sym,3,args); +} + +iz3mgr::ast iz3mgr::make_quant(opr op, const std::vector &bvs, ast &body){ + if(bvs.size() == 0) return body; + std::vector foo(bvs.size()); + + + std::vector names; + std::vector types; + std::vector bound_asts; + unsigned num_bound = bvs.size(); + + for (unsigned i = 0; i < num_bound; ++i) { + app* a = to_app(bvs[i].raw()); + symbol s(to_app(a)->get_decl()->get_name()); + names.push_back(s); + types.push_back(m().get_sort(a)); + bound_asts.push_back(a); + } + expr_ref abs_body(m()); + expr_abstract(m(), 0, num_bound, &bound_asts[0], to_expr(body.raw()), abs_body); + expr_ref result(m()); + result = m().mk_quantifier( + op == Forall, + names.size(), &types[0], &names[0], abs_body.get(), + 0, + symbol(), + symbol(), + 0, 0, + 0, 0 + ); + return result.get(); +} + +iz3mgr::ast iz3mgr::clone(ast &t, const std::vector &_args){ + if(_args.size() == 0) + return t; + + ast_manager& m = *m_manager.get(); + expr* a = to_expr(t.raw()); + static std::vector rargs(10); + if(rargs.size() < _args.size()) + rargs.resize(_args.size()); + for(unsigned i = 0; i < _args.size(); i++) + rargs[i] = _args[i].raw(); + expr* const* args = (expr **)&rargs[0]; + switch(a->get_kind()) { + case AST_APP: { + app* e = to_app(a); + if (e->get_num_args() != _args.size()) { + assert(0); + } + else { + a = m.mk_app(e->get_decl(), _args.size(), args); + } + break; + } + case AST_QUANTIFIER: { + if (_args.size() != 1) { + assert(0); + } + else { + a = m.update_quantifier(to_quantifier(a), args[0]); + } + break; + } + default: + break; + } + return a; +} + + +void iz3mgr::show(ast t){ + std::cout << mk_pp(t.raw(), m()) << std::endl; +} + +void iz3mgr::print_expr(std::ostream &s, const ast &e){ + s << mk_pp(e.raw(), m()); +} + + +void iz3mgr::print_clause(std::ostream &s, std::vector &cls){ + s << "("; + for(unsigned i = 0; i < cls.size(); i++){ + if(i > 0) s << ","; + print_expr(s,cls[i]); + } + s << ")"; +} + +void iz3mgr::show_clause(std::vector &cls){ + print_clause(std::cout,cls); + std::cout << std::endl; +} + +void iz3mgr::print_lit(ast lit){ + ast abslit = is_not(lit) ? arg(lit,0) : lit; + int f = op(abslit); + if(f == And || f == Or || f == Iff){ + if(is_not(lit)) std::cout << "~"; + std::cout << "[" << abslit << "]"; + } + else + std::cout << lit; +} + + +static int pretty_cols = 79; +static int pretty_indent_chars = 2; + +static int pretty_find_delim(const std::string &s, int pos){ + int level = 0; + int end = s.size(); + for(; pos < end; pos++){ + int ch = s[pos]; + if(ch == '(')level++; + if(ch == ')')level--; + if(level < 0 || (level == 0 && ch == ','))break; + } + return pos; +} + +static void pretty_newline(std::ostream &f, int indent){ + f << std::endl; + for(int i = 0; i < indent; i++) + f << " "; +} + +void iz3mgr::pretty_print(std::ostream &f, const std::string &s){ + int cur_indent = 0; + int indent = 0; + int col = 0; + int pos = 0; + while(pos < (int)s.size()){ + int delim = pretty_find_delim(s,pos); + if(s[pos] != ')' && s[pos] != ',' && cur_indent > indent){ + pretty_newline(f,indent); + cur_indent = indent; + col = indent; + continue; + } + if (col + delim - pos > pretty_cols) { + if (col > indent) { + pretty_newline(f,indent); + cur_indent = indent; + col = indent; + continue; + } + unsigned paren = s.find('(',pos); + if(paren != std::string::npos){ + int chars = paren - pos + 1; + f << s.substr(pos,chars); + indent += pretty_indent_chars; + if(col) pretty_newline(f,indent); + cur_indent = indent; + pos += chars; + col = indent; + continue; + } + } + int chars = delim - pos + 1; + f << s.substr(pos,chars); + pos += chars; + col += chars; + if(s[delim] == ')') + indent -= pretty_indent_chars; + } +} + + +iz3mgr::opr iz3mgr::op(ast &t){ + ast_kind dk = t.raw()->get_kind(); + switch(dk){ + case AST_APP: { + expr * e = to_expr(t.raw()); + if (m().is_unique_value(e)) + return Numeral; + func_decl *d = to_app(t.raw())->get_decl(); + if (null_family_id == d->get_family_id()) + return Uninterpreted; + // return (opr)d->get_decl_kind(); + if (m_basic_fid == d->get_family_id()) { + switch(d->get_decl_kind()) { + case OP_TRUE: return True; + case OP_FALSE: return False; + case OP_EQ: return Equal; + case OP_DISTINCT: return Distinct; + case OP_ITE: return Ite; + case OP_AND: return And; + case OP_OR: return Or; + case OP_IFF: return Iff; + case OP_XOR: return Xor; + case OP_NOT: return Not; + case OP_IMPLIES: return Implies; + case OP_OEQ: return Oeq; + default: + return Other; + } + } + if (m_arith_fid == d->get_family_id()) { + switch(d->get_decl_kind()) { + case OP_LE: return Leq; + case OP_GE: return Geq; + case OP_LT: return Lt; + case OP_GT: return Gt; + case OP_ADD: return Plus; + case OP_SUB: return Sub; + case OP_UMINUS: return Uminus; + case OP_MUL: return Times; + case OP_DIV: return Div; + case OP_IDIV: return Idiv; + case OP_REM: return Rem; + case OP_MOD: return Mod; + case OP_POWER: return Power; + case OP_TO_REAL: return ToReal; + case OP_TO_INT: return ToInt; + case OP_IS_INT: return IsInt; + default: + return Other; + } + } + if (m_array_fid == d->get_family_id()) { + switch(d->get_decl_kind()) { + case OP_STORE: return Store; + case OP_SELECT: return Select; + case OP_CONST_ARRAY: return ConstArray; + case OP_ARRAY_DEFAULT: return ArrayDefault; + case OP_ARRAY_MAP: return ArrayMap; + case OP_SET_UNION: return SetUnion; + case OP_SET_INTERSECT: return SetIntersect; + case OP_SET_DIFFERENCE: return SetDifference; + case OP_SET_COMPLEMENT: return SetComplement; + case OP_SET_SUBSET: return SetSubSet; + case OP_AS_ARRAY: return AsArray; + default: + return Other; + } + } + + return Other; + } + + + case AST_QUANTIFIER: + return to_quantifier(t.raw())->is_forall() ? Forall : Exists; + case AST_VAR: + return Variable; + default:; + } + return Other; +} diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h new file mode 100644 index 000000000..211b9a45a --- /dev/null +++ b/src/interp/iz3mgr.h @@ -0,0 +1,397 @@ +/* Copyright 2011 Microsoft Research. */ + +#ifndef IZ3MGR_H +#define IZ3MGR_H + + +#include +#include "iz3hash.h" + +#include"well_sorted.h" +#include"arith_decl_plugin.h" +#include"bv_decl_plugin.h" +#include"datatype_decl_plugin.h" +#include"array_decl_plugin.h" +#include"ast_translation.h" +#include"ast_pp.h" +#include"ast_ll_pp.h" +#include"ast_smt_pp.h" +#include"ast_smt2_pp.h" +#include"th_rewriter.h" +#include"var_subst.h" +#include"expr_substitution.h" +#include"pp.h" +#include"scoped_ctrl_c.h" +#include"cancel_eh.h" +#include"scoped_timer.h" +//# include"pp_params.hpp" + +/* A wrapper around an ast manager, providing convenience methods. */ + +/** Shorthands for some built-in operators. */ + + + +// rename this to keep it accessible, as we use ast for something else +typedef ast raw_ast; + +/** Wrapper around an ast pointer */ +class ast_r { + raw_ast *_ast; + public: + raw_ast * const &raw() const {return _ast;} + ast_r(raw_ast *a){_ast = a;} + + ast_r(){_ast = 0;} + bool eq(const ast_r &other) const { + return _ast == other._ast; + } + friend bool operator==(const ast_r &x, const ast_r&y){ + return x.eq(y); + } +}; + + +// to make ast_r hashable +namespace stl_ext { + template <> + class hash { + public: + size_t operator()(const ast_r &s) const { + return s.raw()->get_id(); + } + }; +} + +// to make ast_r usable in ordered collections +namespace std { + template <> + class less { + public: + size_t operator()(const ast_r &s, const ast_r &t) const { + return s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + + +/** Wrapper around an AST manager, providing convenience methods. */ + +class iz3mgr { + + public: + typedef ast_r ast; + // typedef decl_kind opr; + typedef func_decl *symb; + typedef sort *type; + + enum opr { + True, + False, + And, + Or, + Not, + Iff, + Ite, + Equal, + Implies, + Distinct, + Xor, + Oeq, + Leq, + Geq, + Lt, + Gt, + Plus, + Sub, + Uminus, + Times, + Div, + Idiv, + Rem, + Mod, + Power, + ToReal, + ToInt, + IsInt, + Select, + Store, + ConstArray, + ArrayDefault, + ArrayMap, + SetUnion, + SetIntersect, + SetDifference, + SetComplement, + SetSubSet, + AsArray, + Numeral, + Forall, + Exists, + Variable, + Uninterpreted, + Other + }; + + opr op(ast &t); + + unsigned ast_id(const ast &x) + { + return to_expr(x.raw())->get_id(); + } + + /** Overloads for constructing ast. */ + + ast make_var(const std::string &name, type ty); + ast make(opr op, const std::vector &args); + ast make(opr op); + ast make(opr op, ast &arg0); + ast make(opr op, ast &arg0, ast &arg1); + ast make(opr op, ast &arg0, ast &arg1, ast &arg2); + ast make(symb sym, const std::vector &args); + ast make(symb sym); + ast make(symb sym, ast &arg0); + ast make(symb sym, ast &arg0, ast &arg1); + ast make(symb sym, ast &arg0, ast &arg1, ast &arg2); + ast make_quant(opr op, const std::vector &bvs, ast &body); + ast clone(ast &t, const std::vector &args); + + ast_manager &m() {return *m_manager.get();} + + + /** Methods for destructing ast. */ + + + int num_args(ast t){ + ast_kind dk = t.raw()->get_kind(); + switch(dk){ + case AST_APP: + return to_app(t.raw())->get_num_args(); + case AST_QUANTIFIER: + return 1; + case AST_VAR: + return 0; + default:; + } + assert(0); + } + + ast arg(ast t, int i){ + ast_kind dk = t.raw()->get_kind(); + switch(dk){ + case AST_APP: + return to_app(t.raw())->get_arg(i); + case AST_QUANTIFIER: + return to_quantifier(t.raw())->get_expr(); + default:; + } + assert(0); + return ast((raw_ast *)0); + } + + symb sym(ast t){ + return to_app(t.raw())->get_decl(); + } + + std::string string_of_symbol(symb s){ + symbol _s = s->get_name(); + if (_s.is_numerical()) { + std::ostringstream buffer; + buffer << _s.get_num(); + return buffer.str(); + } + else { + return _s.bare_str(); + } + } + + type get_type(ast t){ + return m().get_sort(to_expr(t.raw())); + } + + std::string string_of_numeral(const ast& t){ + rational r; + expr* e = to_expr(t.raw()); + assert(e); + if (m_arith_util.is_numeral(e, r)) + return r.to_string(); + assert(0); + return "NaN"; + } + + int get_quantifier_num_bound(const ast &t) { + return to_quantifier(t.raw())->get_num_decls(); + } + + std::string get_quantifier_bound_name(const ast &t, unsigned i) { + return to_quantifier(t.raw())->get_decl_names()[i].bare_str(); + } + + type get_quantifier_bound_type(const ast &t, unsigned i) { + return to_quantifier(t.raw())->get_decl_sort(i); + } + + ast get_quantifier_body(const ast &t) { + return to_quantifier(t.raw())->get_expr(); + } + + unsigned get_variable_index_value(const ast &t) { + var* va = to_var(t.raw()); + return va->get_idx(); + } + + bool is_bool_type(type t){ + family_id fid = to_sort(t)->get_family_id(); + decl_kind k = to_sort(t)->get_decl_kind(); + return fid == m().get_basic_family_id() && k == BOOL_SORT; + } + + type get_range_type(symb s){ + return to_func_decl(s)->get_range(); + } + + bool is_true(ast t){ + return op(t) == True; + } + + bool is_false(ast t){ + return op(t) == False; + } + + bool is_iff(ast t){ + return op(t) == Iff; + } + + bool is_or(ast t){ + return op(t) == Or; + } + + bool is_not(ast t){ + return op(t) == Not; + } + + // Some constructors that simplify things + + ast mk_not(ast x){ + opr o = op(x); + if(o == True) return make(False); + if(o == False) return make(True); + if(o == Not) return arg(x,0); + return make(Not,x); + } + + ast mk_and(ast x, ast y){ + opr ox = op(x); + opr oy = op(y); + if(ox == True) return y; + if(oy == True) return x; + if(ox == False) return x; + if(oy == False) return y; + if(x == y) return x; + return make(And,x,y); + } + + ast mk_or(ast x, ast y){ + opr ox = op(x); + opr oy = op(y); + if(ox == False) return y; + if(oy == False) return x; + if(ox == True) return x; + if(oy == True) return y; + if(x == y) return x; + return make(Or,x,y); + } + + ast mk_equal(ast x, ast y){ + if(x == y) return make(True); + opr ox = op(x); + opr oy = op(y); + if(ox == True) return y; + if(oy == True) return x; + if(ox == False) return mk_not(y); + if(oy == False) return mk_not(x); + if(ox == False && oy == True) return make(False); + if(oy == False && ox == True) return make(False); + return make(Equal,x,y); + } + + ast z3_ite(ast x, ast y, ast z){ + opr ox = op(x); + opr oy = op(y); + opr oz = op(z); + if(ox == True) return y; + if(ox == False) return z; + if(y == z) return y; + if(oy == True && oz == False) return x; + if(oz == True && oy == False) return mk_not(x); + return make(Ite,x,y,z); + } + + ast make_int(const std::string &s) { + sort *r = m().mk_sort(m_arith_fid, INT_SORT); + return m_arith_util.mk_numeral(rational(s.c_str()),r); + } + + + ast mk_false() { return make(False); } + + ast mk_true() { return make(True); } + + /** For debugging */ + void show(ast); + + /** Constructor */ + + void print_lit(ast lit); + + void print_expr(std::ostream &s, const ast &e); + + void print_clause(std::ostream &s, std::vector &cls); + + void show_clause(std::vector &cls); + + static void pretty_print(std::ostream &f, const std::string &s); + + iz3mgr(scoped_ptr &_m_manager) + : m_manager(_m_manager), + m_arith_util(*_m_manager) + { + m_basic_fid = m().get_basic_family_id(); + m_arith_fid = m().mk_family_id("arith"); + m_bv_fid = m().mk_family_id("bv"); + m_array_fid = m().mk_family_id("array"); + m_dt_fid = m().mk_family_id("datatype"); + m_datalog_fid = m().mk_family_id("datalog_relation"); + } + + iz3mgr(const iz3mgr& other) + : m_manager(other.m_manager), + m_arith_util((const arith_util&)*other.m_manager) + { + m_basic_fid = m().get_basic_family_id(); + m_arith_fid = m().mk_family_id("arith"); + m_bv_fid = m().mk_family_id("bv"); + m_array_fid = m().mk_family_id("array"); + m_dt_fid = m().mk_family_id("datatype"); + m_datalog_fid = m().mk_family_id("datalog_relation"); + } + + protected: + scoped_ptr m_manager; + + private: + ast make(opr op, int n, raw_ast **args); + + ast make(symb sym, int n, raw_ast **args); + + family_id m_basic_fid; + family_id m_array_fid; + family_id m_arith_fid; + family_id m_bv_fid; + family_id m_dt_fid; + family_id m_datalog_fid; + arith_util m_arith_util; +}; + +#endif + diff --git a/src/interp/iz3profiling.h b/src/interp/iz3profiling.h new file mode 100755 index 000000000..fd518b1d9 --- /dev/null +++ b/src/interp/iz3profiling.h @@ -0,0 +1,20 @@ +/* Copyright 2011 Microsoft Research. */ + +#ifndef IZ3PROFILING_H +#define IZ3PROFILING_H + +#include + +namespace profiling { + /** Start a timer with given name */ + void timer_start(const char *); + /** Stop a timer with given name */ + void timer_stop(const char *); + /** Print out timings */ + void print(std::ostream &s); + /** Show the current time. */ + void show_time(); +} + +#endif + diff --git a/src/interp/iz3proof.cpp b/src/interp/iz3proof.cpp new file mode 100755 index 000000000..4c79a4ae6 --- /dev/null +++ b/src/interp/iz3proof.cpp @@ -0,0 +1,604 @@ +/* Copyright 2011 Microsoft Research. */ + + +#include "iz3proof.h" +#include "iz3profiling.h" + +#include +#include +#include +#include + +// #define FACTOR_INTERPS +// #define CHECK_PROOFS + + +void iz3proof::resolve(ast pivot, std::vector &cls1, const std::vector &cls2){ +#ifdef CHECK_PROOFS + std::vector orig_cls1 = cls1; +#endif + ast neg_pivot = pv->mk_not(pivot); + bool found_pivot1 = false, found_pivot2 = false; + for(unsigned i = 0; i < cls1.size(); i++){ + if(cls1[i] == neg_pivot){ + cls1[i] = cls1.back(); + cls1.pop_back(); + found_pivot1 = true; + break; + } + } + { + std::set memo; + memo.insert(cls1.begin(),cls1.end()); + for(unsigned j = 0; j < cls2.size(); j++){ + if(cls2[j] == pivot) + found_pivot2 = true; + else + if(memo.find(cls2[j]) == memo.end()) + cls1.push_back(cls2[j]); + } + } + if(found_pivot1 && found_pivot2) + return; + +#ifdef CHECK_PROOFS + std::cerr << "resolution anomaly: " << nodes.size()-1 << "\n"; +#if 0 + std::cerr << "pivot: "; {pv->print_lit(pivot); std::cout << "\n";} + std::cerr << "left clause:\n"; + for(unsigned i = 0; i < orig_cls1.size(); i++) + {pv->print_lit(orig_cls1[i]); std::cout << "\n";} + std::cerr << "right clause:\n"; + for(unsigned i = 0; i < cls2.size(); i++) + {pv->print_lit(cls2[i]); std::cout << "\n";} + throw proof_error(); +#endif +#endif +} + +iz3proof::node iz3proof::make_resolution(ast pivot, node premise1, node premise2) +{ + if(nodes[premise1].rl == Hypothesis) return premise2; // resolve with hyp is noop + if(nodes[premise2].rl == Hypothesis) return premise1; + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Resolution; + n.aux = pivot; + n.premises.resize(2); + n.premises[0] = (premise1); + n.premises[1] = (premise2); +#ifdef CHECK_PROOFS + n.conclusion = nodes[premise1].conclusion; + resolve(pivot,n.conclusion,nodes[premise2].conclusion); + n.frame = 1; +#else + n.frame = 0; // compute conclusion lazily +#endif + return res; +} + +iz3proof::node iz3proof::resolve_lemmas(ast pivot, node premise1, node premise2) +{ + std::vector lits(nodes[premise1].conclusion), itp; // no interpolant + resolve(pivot,lits,nodes[premise2].conclusion); + return make_lemma(lits,itp); +} + + +iz3proof::node iz3proof::make_assumption(int frame, const std::vector &assumption){ +#if 0 + std::cout << "assumption: \n"; + for(unsigned i = 0; i < assumption.size(); i++) + pv->show(assumption[i]); + std::cout << "\n"; +#endif + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Assumption; + n.conclusion.resize(1); + n.conclusion = assumption; + n.frame = frame; + return res; +} + +iz3proof::node iz3proof::make_hypothesis(ast hypothesis){ + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Hypothesis; + n.conclusion.resize(2); + n.conclusion[0] = hypothesis; + n.conclusion[1] = pv->mk_not(hypothesis); + return res; +} + +iz3proof::node iz3proof::make_theory(const std::vector &conclusion, std::vector premises){ + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Theory; + n.conclusion = conclusion; + n.premises = premises; + return res; +} + +iz3proof::node iz3proof::make_axiom(const std::vector &conclusion){ + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Axiom; + n.conclusion = conclusion; + return res; +} + +iz3proof::node iz3proof::make_contra(node prem, const std::vector &conclusion){ + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Contra; + n.conclusion = conclusion; +#ifdef CHECK_PROOFS + //if(!(conclusion == nodes[prem].conclusion)){ + //std::cerr << "internal error: proof error\n"; + //assert(0 && "proof error"); + //} +#endif + n.premises.push_back(prem); + return res; +} + + +iz3proof::node iz3proof::make_lemma(const std::vector &conclusion, const std::vector &interpolation){ + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Lemma; + n.conclusion = conclusion; + n.frame = interps.size(); + interps.push_back(interpolation); + return res; +} + +/** Make a Reflexivity node. This rule produces |- x = x */ + +iz3proof::node iz3proof::make_reflexivity(ast con){ + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Reflexivity; + n.conclusion.push_back(con); + return res; +} + +/** Make a Symmetry node. This takes a derivation of |- x = y and + produces | y = x */ + +iz3proof::node iz3proof::make_symmetry(ast con, node prem){ + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Reflexivity; + n.conclusion.push_back(con); + n.premises.push_back(prem); + return res; +} + +/** Make a transitivity node. This takes derivations of |- x = y + and |- y = z produces | x = z */ + +iz3proof::node iz3proof::make_transitivity(ast con, node prem1, node prem2){ + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Transitivity; + n.conclusion.push_back(con); + n.premises.push_back(prem1); + n.premises.push_back(prem2); + return res; +} + + +/** Make a congruence node. This takes derivations of |- x_i = y_i + and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ + +iz3proof::node iz3proof::make_congruence(ast con, const std::vector &prems){ + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = Congruence; + n.conclusion.push_back(con); + n.premises = prems; + return res; +} + + +/** Make an equality contradicition node. This takes |- x = y + and |- !(x = y) and produces false. */ + +iz3proof::node iz3proof::make_eqcontra(node prem1, node prem2){ + node res = make_node(); + node_struct &n = nodes[res]; + n.rl = EqContra; + n.premises.push_back(prem1); + n.premises.push_back(prem2); + return res; +} + +iz3proof::node iz3proof::copy_rec(stl_ext::hash_map &memo, iz3proof &src, node n){ + stl_ext::hash_map::iterator it = memo.find(n); + if(it != memo.end()) return (*it).second; + node_struct &ns = src.nodes[n]; + std::vector prems(ns.premises.size()); + for(unsigned i = 0; i < prems.size(); i++) + prems[i] = copy_rec(memo,src,ns.premises[i]); + nodes.push_back(ns); + nodes.back().premises.swap(prems); + if(ns.rl == Lemma){ + nodes.back().frame = interps.size(); + interps.push_back(src.interps[ns.frame]); + } + int res = nodes.size()-1; + memo[n] = res; + return res; +} + +iz3proof::node iz3proof::copy(iz3proof &src, node n){ + stl_ext::hash_map memo; + return copy_rec(memo, src, n); +} + +bool iz3proof::pred_in_A(ast id){ + return weak + ? pv->ranges_intersect(pv->ast_range(id),rng) : + pv->range_contained(pv->ast_range(id),rng); +} + +bool iz3proof::term_in_B(ast id){ + prover::range r = pv->ast_scope(id); + if(weak) { + if(pv->range_min(r) == SHRT_MIN) + return !pv->range_contained(r,rng); + else + return !pv->ranges_intersect(r,rng); + } + else + return !pv->range_contained(r,rng); +} + +bool iz3proof::frame_in_A(int frame){ + return pv->in_range(frame,rng); +} + +bool iz3proof::lit_in_B(ast lit){ + return + b_lits.find(lit) != b_lits.end() + || b_lits.find(pv->mk_not(lit)) != b_lits.end(); +} + +iz3proof::ast iz3proof::my_or(ast x, ast y){ + return pv->mk_not(pv->mk_and(pv->mk_not(x),pv->mk_not(y))); +} + +iz3proof::ast iz3proof::get_A_lits(std::vector &cls){ + ast foo = pv->mk_false(); + for(unsigned i = 0; i < cls.size(); i++){ + ast lit = cls[i]; + if(b_lits.find(pv->mk_not(lit)) == b_lits.end()){ + if(pv->range_max(pv->ast_scope(lit)) == pv->range_min(pv->ast_scope(lit))){ + std::cout << "bad lit: " << pv->range_max(rng) << " : " << pv->range_max(pv->ast_scope(lit)) << " : " << (pv->ast_id(lit)) << " : "; + pv->show(lit); + } + foo = my_or(foo,lit); + } + } + return foo; +} + +iz3proof::ast iz3proof::get_B_lits(std::vector &cls){ + ast foo = pv->mk_false(); + for(unsigned i = 0; i < cls.size(); i++){ + ast lit = cls[i]; + if(b_lits.find(pv->mk_not(lit)) != b_lits.end()) + foo = my_or(foo,lit); + } + return foo; +} + +void iz3proof::set_of_B_lits(std::vector &cls, std::set &res){ + for(unsigned i = 0; i < cls.size(); i++){ + ast lit = cls[i]; + if(b_lits.find(pv->mk_not(lit)) != b_lits.end()) + res.insert(lit); + } +} + +void iz3proof::set_of_A_lits(std::vector &cls, std::set &res){ + for(unsigned i = 0; i < cls.size(); i++){ + ast lit = cls[i]; + if(b_lits.find(pv->mk_not(lit)) == b_lits.end()) + res.insert(lit); + } +} + +void iz3proof::find_B_lits(){ + b_lits.clear(); + for(unsigned i = 0; i < nodes.size(); i++){ + node_struct &n = nodes[i]; + std::vector &cls = n.conclusion; + if(n.rl == Assumption){ + if(weak) goto lemma; + if(!frame_in_A(n.frame)) + for(unsigned j = 0; j < cls.size(); j++) + b_lits.insert(cls[j]); + } + else if(n.rl == Lemma) { + lemma: + for(unsigned j = 0; j < cls.size(); j++) + if(term_in_B(cls[j])) + b_lits.insert(cls[j]); + } + } +} + +iz3proof::ast iz3proof::disj_of_set(std::set &s){ + ast res = pv->mk_false(); + for(std::set::iterator it = s.begin(), en = s.end(); it != en; ++it) + res = my_or(*it,res); + return res; +} + +void iz3proof::mk_and_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs){ +#ifdef FACTOR_INTERPS + std::set &d1 = disjs[p1]; + std::set &d2 = disjs[p2]; + if(!weak){ + if(pv->is_true(itps[p1])){ + itps[i] = itps[p2]; + disjs[i] = disjs[p2]; + } + else if(pv->is_true(itps[p2])){ + itps[i] = itps[p1]; + disjs[i] = disjs[p1]; + } + else { + std::set p1only,p2only; + std::insert_iterator > p1o(p1only,p1only.begin()); + std::insert_iterator > p2o(p2only,p2only.begin()); + std::insert_iterator > dio(disjs[i],disjs[i].begin()); + std::set_difference(d1.begin(),d1.end(),d2.begin(),d2.end(),p1o); + std::set_difference(d2.begin(),d2.end(),d1.begin(),d1.end(),p2o); + std::set_intersection(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); + ast p1i = my_or(itps[p1],disj_of_set(p1only)); + ast p2i = my_or(itps[p2],disj_of_set(p2only)); + itps[i] = pv->mk_and(p1i,p2i); + } + } + else { + itps[i] = pv->mk_and(itps[p1],itps[p2]); + std::insert_iterator > dio(disjs[i],disjs[i].begin()); + std::set_union(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); + } +#endif +} + +void iz3proof::mk_or_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs){ +#ifdef FACTOR_INTERPS + std::set &d1 = disjs[p1]; + std::set &d2 = disjs[p2]; + if(weak){ + if(pv->is_false(itps[p1])){ + itps[i] = itps[p2]; + disjs[i] = disjs[p2]; + } + else if(pv->is_false(itps[p2])){ + itps[i] = itps[p1]; + disjs[i] = disjs[p1]; + } + else { + std::set p1only,p2only; + std::insert_iterator > p1o(p1only,p1only.begin()); + std::insert_iterator > p2o(p2only,p2only.begin()); + std::insert_iterator > dio(disjs[i],disjs[i].begin()); + std::set_difference(d1.begin(),d1.end(),d2.begin(),d2.end(),p1o); + std::set_difference(d2.begin(),d2.end(),d1.begin(),d1.end(),p2o); + std::set_intersection(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); + ast p1i = pv->mk_and(itps[p1],pv->mk_not(disj_of_set(p1only))); + ast p2i = pv->mk_and(itps[p2],pv->mk_not(disj_of_set(p2only))); + itps[i] = my_or(p1i,p2i); + } + } + else { + itps[i] = my_or(itps[p1],itps[p2]); + std::insert_iterator > dio(disjs[i],disjs[i].begin()); + std::set_union(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); + } +#endif +} + +void iz3proof::interpolate_lemma(node_struct &n){ + if(interps[n.frame].size()) + return; // already computed + pv->interpolate_clause(n.conclusion,interps[n.frame]); +} + + iz3proof::ast iz3proof::interpolate(const prover::range &_rng, bool _weak +#ifdef CHECK_PROOFS + , ast assump + , std::vector *parents +#endif +){ + // std::cout << "proof size: " << nodes.size() << "\n"; + rng = _rng; + weak = _weak; +#ifdef CHECK_PROOFS + if(nodes[nodes.size()-1].conclusion.size() != 0) + std::cerr << "internal error: proof conclusion is not empty clause\n"; + if(!child_interps.size()){ + child_interps.resize(nodes.size()); + for(unsigned j = 0; j < nodes.size(); j++) + child_interps[j] = pv->mk_true(); + } +#endif + std::vector itps(nodes.size()); +#ifdef FACTOR_INTERPS + std::vector > disjs(nodes.size()); +#endif + profiling::timer_start("Blits"); + find_B_lits(); + profiling::timer_stop("Blits"); + profiling::timer_start("interp_proof"); + // strengthen(); + for(unsigned i = 0; i < nodes.size(); i++){ + node_struct &n = nodes[i]; + ast &q = itps[i]; + switch(n.rl){ + case Assumption: { + + if(frame_in_A(n.frame)){ + /* HypC-A */ + if(!weak) +#ifdef FACTOR_INTERPS + { + q = pv->mk_false(); + set_of_B_lits(n.conclusion,disjs[i]); + } +#else + q = get_B_lits(n.conclusion); +#endif + else + q = pv->mk_false(); + } + else { + /* HypEq-B */ + if(!weak) + q = pv->mk_true(); + else +#ifdef FACTOR_INTERPS + { + q = pv->mk_true(); + set_of_A_lits(n.conclusion,disjs[i]); + } +#else + q = pv->mk_not(get_A_lits(n.conclusion)); +#endif + } + break; + } + case Resolution: { + ast p = n.aux; + p = pv->is_not(p) ? pv->mk_not(p) : p; // should be positive, but just in case + if(lit_in_B(p)) +#ifdef FACTOR_INTERPS + mk_and_factor(n.premises[0],n.premises[1],i,itps,disjs); +#else + q = pv->mk_and(itps[n.premises[0]],itps[n.premises[1]]); +#endif + else +#ifdef FACTOR_INTERPS + mk_or_factor(n.premises[0],n.premises[1],i,itps,disjs); +#else + q = my_or(itps[n.premises[0]],itps[n.premises[1]]); +#endif + break; + } + case Lemma: { + interpolate_lemma(n); // make sure lemma interpolants have been computed + q = interps[n.frame][pv->range_max(rng)]; + break; + } + case Contra: { + q = itps[n.premises[0]]; +#ifdef FACTOR_INTERPS + disjs[i] = disjs[n.premises[0]]; +#endif + break; + } + default: + assert(0 && "rule not allowed in interpolated proof"); + } +#ifdef CHECK_PROOFS + int this_frame = pv->range_max(rng); + if(0 && this_frame == 39) { + std::vector alits; + ast s = pv->mk_true(); + for(unsigned j = 0; j < n.conclusion.size(); j++) + if(pred_in_A(n.conclusion[j])){ + int scpmax = pv->range_max(pv->ast_scope(n.conclusion[j])); + if(scpmax == this_frame) + s = pv->mk_and(s,pv->mk_not(n.conclusion[j])); + } + ast ci = child_interps[i]; + s = pv->mk_and(pv->mk_and(s,pv->mk_and(assump,pv->mk_not(q))),ci); + if(pv->is_sat(s)){ + std::cout << "interpolation invariant violated at step " << i << "\n"; + assert(0 && "interpolation invariant violated"); + } + } + if((*parents)[this_frame] == 39) + child_interps[i] = pv->mk_and(child_interps[i],q); +#endif + } + ast &bar = itps[nodes.size()-1]; +#ifdef FACTOR_INTERPS + if(!weak) + bar = my_or(bar,disj_of_set(disjs[nodes.size()-1])); + else + bar = pv->mk_and(bar,pv->mk_not(disj_of_set(disjs[nodes.size()-1]))); +#endif + profiling::timer_stop("interp_proof"); + profiling::timer_start("simplifying"); + bar = pv->simplify(bar); + profiling::timer_stop("simplifying"); + return bar; +} + + +void iz3proof::print(std::ostream &s, int id){ + node_struct &n = nodes[id]; + switch(n.rl){ + case Assumption: + s << "Assumption("; + pv->print_clause(s,n.conclusion); + s << ")"; + break; + case Hypothesis: + s << "Hyp("; pv->print_expr(s,n.conclusion[0]); s << ")"; break; + case Reflexivity: + s << "Refl("; pv->print_expr(s,n.conclusion[0]); s << ")"; break; + case Symmetry: + s << "Symm("; print(s,n.premises[0]); s << ")"; break; + case Transitivity: + s << "Trans("; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break; + case Congruence: + s << "Cong("; pv->print_expr(s,n.conclusion[0]); + for(unsigned i = 0; i < n.premises.size(); i++){ + s << ","; + print(s,n.premises[i]); + } + s << ")"; break; + case EqContra: + s << "EqContra("; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break; + case Resolution: + s << "Res("; + pv->print_expr(s,n.aux); s << ","; + print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; + break; + case Lemma: + s << "Lemma("; + pv->print_clause(s,n.conclusion); + for(unsigned i = 0; i < n.premises.size(); i++){ + s << ","; + print(s,n.premises[i]); + } + s << ")"; + break; + case Contra: + s << "Contra("; + print(s,n.premises[0]); + s << ")"; + break; + default:; + } +} + + +void iz3proof::show(int id){ + std::ostringstream ss; + print(ss,id); + iz3base::pretty_print(std::cout,ss.str()); + // std::cout << ss.str(); + std::cout << "\n"; +} + + diff --git a/src/interp/iz3proof.h b/src/interp/iz3proof.h new file mode 100755 index 000000000..ea1973f63 --- /dev/null +++ b/src/interp/iz3proof.h @@ -0,0 +1,255 @@ +/* Copyright 2011 Microsoft Research. */ + +#ifndef IZ3PROOF_H +#define IZ3PROOF_H + +#include + +#include "iz3base.h" +#include "iz3secondary.h" + +// #define CHECK_PROOFS + +/** This class defines a simple proof system. + + A proof is a dag consisting of "nodes". The children of each node + are its "premises". Each node has a "conclusion" that is a clause, + represented as a vector of literals. + + The literals are represented by abstract syntax trees. Operations + on these, including computation of scopes are provided by iz3base. + + A proof can be interpolated, provided it is restricted to the + rules Resolution, Assumption, Contra and Lemma, and that all + clauses are strict (i.e., each literal in each clause is local). + + */ + +class iz3proof { + public: + /** The type of proof nodes (nodes in the derivation tree). */ + typedef int node; + + /** Enumeration of proof rules. */ + enum rule {Resolution,Assumption,Hypothesis,Theory,Axiom,Contra,Lemma,Reflexivity,Symmetry,Transitivity,Congruence,EqContra}; + + /** Interface to prover. */ + typedef iz3base prover; + + /** Ast type. */ + typedef prover::ast ast; + + /** Object thrown in case of a proof error. */ + struct proof_error {}; + + /* Null proof node */ + static const node null = -1; + + /** Make a resolution node with given pivot liter and premises. + The conclusion of premise1 should contain the negation of the + pivot literal, while the conclusion of premise2 should containe the + pivot literal. + */ + node make_resolution(ast pivot, node premise1, node premise2); + + /** Make an assumption node. The given clause is assumed in the given frame. */ + node make_assumption(int frame, const std::vector &assumption); + + /** Make a hypothesis node. If phi is the hypothesis, this is + effectively phi |- phi. */ + node make_hypothesis(ast hypothesis); + + /** Make a theory node. This can be any inference valid in the theory. */ + node make_theory(const std::vector &conclusion, std::vector premises); + + /** Make an axiom node. The conclusion must be an instance of an axiom. */ + node make_axiom(const std::vector &conclusion); + + /** Make a Contra node. This rule takes a derivation of the form + Gamma |- False and produces |- \/~Gamma. */ + + node make_contra(node prem, const std::vector &conclusion); + + /** Make a lemma node. A lemma node must have an interpolation. */ + node make_lemma(const std::vector &conclusion, const std::vector &interpolation); + + /** Make a Reflexivity node. This rule produces |- x = x */ + + node make_reflexivity(ast con); + + /** Make a Symmetry node. This takes a derivation of |- x = y and + produces | y = x */ + + node make_symmetry(ast con, node prem); + + /** Make a transitivity node. This takes derivations of |- x = y + and |- y = z produces | x = z */ + + node make_transitivity(ast con, node prem1, node prem2); + + /** Make a congruence node. This takes derivations of |- x_i = y_i + and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ + + node make_congruence(ast con, const std::vector &prems); + + /** Make an equality contradicition node. This takes |- x = y + and |- !(x = y) and produces false. */ + + node make_eqcontra(node prem1, node prem2); + + /** Get the rule of a node in a proof. */ + rule get_rule(node n){ + return nodes[n].rl; + } + + /** Get the pivot of a resolution node. */ + ast get_pivot(node n){ + return nodes[n].aux; + } + + /** Get the frame of an assumption node. */ + int get_frame(node n){ + return nodes[n].frame; + } + + /** Get the number of literals of the conclusion of a node. */ + int get_num_conclusion_lits(node n){ + return get_conclusion(n).size(); + } + + /** Get the nth literal of the conclusion of a node. */ + ast get_nth_conclusion_lit(node n, int i){ + return get_conclusion(n)[i]; + } + + /** Get the conclusion of a node. */ + void get_conclusion(node n, std::vector &result){ + result = get_conclusion(n); + } + + /** Get the number of premises of a node. */ + int get_num_premises(node n){ + return nodes[n].premises.size(); + } + + /** Get the nth premise of a node. */ + int get_nth_premise(node n, int i){ + return nodes[n].premises[i]; + } + + /** Get all the premises of a node. */ + void get_premises(node n, std::vector &result){ + result = nodes[n].premises; + } + + /** Create a new proof node, replacing the premises of an old + one. */ + + node clone(node n, std::vector &premises){ + if(premises == nodes[n].premises) + return n; + nodes.push_back(nodes[n]); + nodes.back().premises = premises; + return nodes.size()-1; + } + + /** Copy a proof node from src */ + node copy(iz3proof &src, node n); + + /** Resolve two lemmas on a given literal. */ + + node resolve_lemmas(ast pivot, node left, node right); + + /** Swap two proofs. */ + void swap(iz3proof &other){ + std::swap(pv,other.pv); + nodes.swap(other.nodes); + interps.swap(other.interps); + } + + /** Compute an interpolant for a proof, where the "A" side is defined by + the given range of frames. Parameter "weak", when true, uses different + interpolation system that resutls in generally weaker interpolants. + */ + ast interpolate(const prover::range &_rng, bool weak = false +#ifdef CHECK_PROOFS + , Z3_ast assump = (Z3_ast)0, std::vector *parents = 0 + +#endif + ); + + /** print proof node to a stream */ + + void print(std::ostream &s, node n); + + /** show proof node on stdout */ + void show(node n); + + /** Construct a proof, with a given prover. */ + iz3proof(prover *p){ + pv = p; + } + + /** Default constructor */ + iz3proof(){pv = 0;} + + + protected: + + struct node_struct { + rule rl; + ast aux; + int frame; + std::vector conclusion; + std::vector premises; + }; + + std::vector nodes; + std::vector > interps; // interpolations of lemmas + prover *pv; + + node make_node(){ + nodes.push_back(node_struct()); + return nodes.size()-1; + } + + void resolve(ast pivot, std::vector &cls1, const std::vector &cls2); + + node copy_rec(stl_ext::hash_map &memo, iz3proof &src, node n); + + void interpolate_lemma(node_struct &n); + + // lazily compute the result of resolution + // the node member "frame" indicates result is computed + const std::vector &get_conclusion(node x){ + node_struct &n = nodes[x]; + if(n.rl == Resolution && !n.frame){ + n.conclusion = get_conclusion(n.premises[0]); + resolve(n.aux,n.conclusion,get_conclusion(n.premises[1])); + n.frame = 1; + } + return n.conclusion; + } + + prover::range rng; + bool weak; + stl_ext::hash_set b_lits; + ast my_or(ast x, ast y); +#ifdef CHECK_PROOFS + std::vector child_interps; +#endif + bool pred_in_A(ast id); + bool term_in_B(ast id); + bool frame_in_A(int frame); + bool lit_in_B(ast lit); + ast get_A_lits(std::vector &cls); + ast get_B_lits(std::vector &cls); + void find_B_lits(); + ast disj_of_set(std::set &s); + void mk_or_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs); + void mk_and_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs); + void set_of_B_lits(std::vector &cls, std::set &res); + void set_of_A_lits(std::vector &cls, std::set &res); +}; + +#endif diff --git a/src/interp/iz3scopes.cpp b/src/interp/iz3scopes.cpp new file mode 100755 index 000000000..65c6f6d88 --- /dev/null +++ b/src/interp/iz3scopes.cpp @@ -0,0 +1,302 @@ +/* Copyright 2011 Microsoft Research. */ + +#include + +#include "iz3scopes.h" + + +/** computes the least common ancestor of two nodes in the tree, or SHRT_MAX if none */ +int scopes::tree_lca(int n1, int n2){ + if(!tree_mode()) + return std::max(n1,n2); + if(n1 == SHRT_MIN) return n2; + if(n2 == SHRT_MIN) return n1; + if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; + while(n1 != n2){ + if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; + assert(n1 >= 0 && n2 >= 0 && n1 < parents.size() && n2 < parents.size()); + if(n1 < n2) n1 = parents[n1]; + else n2 = parents[n2]; + } + return n1; +} + +/** computes the greatest common descendant two nodes in the tree, or SHRT_MIN if none */ +int scopes::tree_gcd(int n1, int n2){ + if(!tree_mode()) + return std::min(n1,n2); + int foo = tree_lca(n1,n2); + if(foo == n1) return n2; + if(foo == n2) return n1; + return SHRT_MIN; +} + +#ifndef FULL_TREE + +/** test whether a tree node is contained in a range */ +bool scopes::in_range(int n, const range &rng){ + return tree_lca(rng.lo,n) == n && tree_gcd(rng.hi,n) == n; +} + +/** test whether two ranges of tree nodes intersect */ +bool scopes::ranges_intersect(const range &rng1, const range &rng2){ + return tree_lca(rng1.lo,rng2.hi) == rng2.hi && tree_lca(rng1.hi,rng2.lo) == rng1.hi; +} + + +bool scopes::range_contained(const range &rng1, const range &rng2){ + return tree_lca(rng2.lo,rng1.lo) == rng1.lo + && tree_lca(rng1.hi,rng2.hi) == rng2.hi; +} + +scopes::range scopes::range_lub(const range &rng1, const range &rng2){ + range res; + res.lo = tree_gcd(rng1.lo,rng2.lo); + res.hi = tree_lca(rng1.hi,rng2.hi); + return res; +} + +scopes::range scopes::range_glb(const range &rng1, const range &rng2){ + range res; + res.lo = tree_lca(rng1.lo,rng2.lo); + res.hi = tree_gcd(rng1.hi,rng2.hi); + return res; +} + +#else + + + namespace std { + template <> + class hash { + public: + size_t operator()(const scopes::range_lo &p) const { + return p.lo + (size_t)p.next; + } + }; + } + + template <> inline + size_t stdext::hash_value(const scopes::range_lo& p) + { + std::hash h; + return h(p); + } + + namespace std { + template <> + class less { + public: + bool operator()(const scopes::range_lo &x, const scopes::range_lo &y) const { + return x.lo < y.lo || x.lo == y.lo && (size_t)x.next < (size_t)y.next; + } + }; + } + + + struct range_op { + scopes::range_lo *x, *y; + int hi; + range_op(scopes::range_lo *_x, scopes::range_lo *_y, int _hi){ + x = _x; y = _y; hi = _hi; + } + }; + + namespace std { + template <> + class hash { + public: + size_t operator()(const range_op &p) const { + return (size_t) p.x + (size_t)p.y + p.hi; + } + }; + } + + template <> inline + size_t stdext::hash_value(const range_op& p) + { + std::hash h; + return h(p); + } + + namespace std { + template <> + class less { + public: + bool operator()(const range_op &x, const range_op &y) const { + return (size_t)x.x < (size_t)y.x || x.x == y.x && + ((size_t)x.y < (size_t)y.y || x.y == y.y && x.hi < y.hi); + } + }; + } + + struct range_tables { + hash_map unique; + hash_map lub; + hash_map glb; + }; + + + scopes::range_lo *scopes::find_range_lo(int lo, range_lo *next){ + range_lo foo(lo,next); + std::pair baz(foo,(range_lo *)0); + std::pair::iterator,bool> bar = rt->unique.insert(baz); + if(bar.second) + bar.first->second = new range_lo(lo,next); + return bar.first->second; + //std::pair::iterator,bool> bar = rt->unique.insert(foo); + // const range_lo *baz = &*(bar.first); + // return (range_lo *)baz; // exit const hell + } + + scopes::range_lo *scopes::range_lub_lo(range_lo *rng1, range_lo *rng2){ + if(!rng1) return rng2; + if(!rng2) return rng1; + if(rng1->lo > rng2->lo) + std::swap(rng1,rng2); + std::pair foo(range_op(rng1,rng2,0),(range_lo *)0); + std::pair::iterator,bool> bar = rt->lub.insert(foo); + range_lo *&res = bar.first->second; + if(!bar.second) return res; + if(!(rng1->next && rng1->next->lo <= rng2->lo)){ + for(int lo = rng1->lo; lo <= rng2->lo; lo = parents[lo]) + if(lo == rng2->lo) + {rng2 = rng2->next; break;} + } + range_lo *baz = range_lub_lo(rng1->next,rng2); + res = find_range_lo(rng1->lo,baz); + return res; + } + + + scopes::range_lo *scopes::range_glb_lo(range_lo *rng1, range_lo *rng2, int hi){ + if(!rng1) return rng1; + if(!rng2) return rng2; + if(rng1->lo > rng2->lo) + std::swap(rng1,rng2); + std::pair cand(range_op(rng1,rng2,hi),(range_lo *)0); + std::pair::iterator,bool> bar = rt->glb.insert(cand); + range_lo *&res = bar.first->second; + if(!bar.second) return res; + range_lo *foo; + if(!(rng1->next && rng1->next->lo <= rng2->lo)){ + int lim = hi; + if(rng1->next) lim = std::min(lim,rng1->next->lo); + int a = rng1->lo, b = rng2->lo; + while(a != b && b <= lim){ + a = parents[a]; + if(a > b)std::swap(a,b); + } + if(a == b && b <= lim){ + foo = range_glb_lo(rng1->next,rng2->next,hi); + foo = find_range_lo(b,foo); + } + else + foo = range_glb_lo(rng2,rng1->next,hi); + } + else foo = range_glb_lo(rng1->next,rng2,hi); + res = foo; + return res; + } + + /** computes the lub (smallest containing subtree) of two ranges */ + scopes::range scopes::range_lub(const range &rng1, const range &rng2){ + int hi = tree_lca(rng1.hi,rng2.hi); + if(hi == SHRT_MAX) return range_full(); + range_lo *lo = range_lub_lo(rng1.lo,rng2.lo); + return range(hi,lo); + } + + /** computes the glb (intersection) of two ranges */ + scopes::range scopes::range_glb(const range &rng1, const range &rng2){ + if(rng1.hi == SHRT_MAX) return rng2; + if(rng2.hi == SHRT_MAX) return rng1; + int hi = tree_gcd(rng1.hi,rng2.hi); + range_lo *lo = hi == SHRT_MIN ? 0 : range_glb_lo(rng1.lo,rng2.lo,hi); + if(!lo) hi = SHRT_MIN; + return range(hi,lo); + } + + /** is this range empty? */ + bool scopes::range_is_empty(const range &rng){ + return rng.hi == SHRT_MIN; + } + + /** return an empty range */ + scopes::range scopes::range_empty(){ + return range(SHRT_MIN,0); + } + + /** return a full range */ + scopes::range scopes::range_full(){ + return range(SHRT_MAX,0); + } + + /** return the maximal element of a range */ + int scopes::range_max(const range &rng){ + return rng.hi; + } + + /** return a minimal (not necessarily unique) element of a range */ + int scopes::range_min(const range &rng){ + if(rng.hi == SHRT_MAX) return SHRT_MIN; + return rng.lo ? rng.lo->lo : SHRT_MAX; + } + + + /** return range consisting of downward closure of a point */ + scopes::range scopes::range_downward(int _hi){ + std::vector descendants(parents.size()); + for(int i = descendants.size() - 1; i >= 0 ; i--) + descendants[i] = i == _hi || parents[i] < parents.size() && descendants[parents[i]]; + for(unsigned i = 0; i < descendants.size() - 1; i++) + if(parents[i] < parents.size()) + descendants[parents[i]] = false; + range_lo *foo = 0; + for(int i = descendants.size() - 1; i >= 0; --i) + if(descendants[i]) foo = find_range_lo(i,foo); + return range(_hi,foo); + } + + /** add an element to a range */ + void scopes::range_add(int i, range &n){ + range foo = range(i, find_range_lo(i,0)); + n = range_lub(foo,n); + } + + /** Choose an element of rng1 that is near to rng2 */ + int scopes::range_near(const range &rng1, const range &rng2){ + + int frame; + int thing = tree_lca(rng1.hi,rng2.hi); + if(thing != rng1.hi) return rng1.hi; + range line = range(rng1.hi,find_range_lo(rng2.hi,(range_lo *)0)); + line = range_glb(line,rng1); + return range_min(line); + } + + + /** test whether a tree node is contained in a range */ + bool scopes::in_range(int n, const range &rng){ + range r = range_empty(); + range_add(n,r); + r = range_glb(rng,r); + return !range_is_empty(r); + } + + /** test whether two ranges of tree nodes intersect */ + bool scopes::ranges_intersect(const range &rng1, const range &rng2){ + range r = range_glb(rng1,rng2); + return !range_is_empty(r); + } + + + bool scopes::range_contained(const range &rng1, const range &rng2){ + range r = range_glb(rng1,rng2); + return r.hi == rng1.hi && r.lo == rng1.lo; + } + + +#endif + + diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h new file mode 100755 index 000000000..e7ca387d5 --- /dev/null +++ b/src/interp/iz3scopes.h @@ -0,0 +1,167 @@ +/* Copyright 2011 Microsoft Research. */ + +#ifndef IZ3SOPES_H +#define IZ3SOPES_H + +#include +#include + +class scopes { + + public: + /** Construct from parents vector. */ + scopes(const std::vector &_parents){ + parents = _parents; + } + + /** The parents vector defining the tree structure */ + std::vector parents; + + // #define FULL_TREE +#ifndef FULL_TREE + struct range { + range(){ + lo = SHRT_MAX; + hi = SHRT_MIN; + } + short lo, hi; + }; + + /** computes the lub (smallest containing subtree) of two ranges */ + range range_lub(const range &rng1, const range &rng2); + + /** computes the glb (intersection) of two ranges */ + range range_glb(const range &rng1, const range &rng2); + + /** is this range empty? */ + bool range_is_empty(const range &rng){ + return rng.hi < rng.lo; + } + + /** return an empty range */ + range range_empty(){ + range res; + res.lo = SHRT_MAX; + res.hi = SHRT_MIN; + return res; + } + + /** return an empty range */ + range range_full(){ + range res; + res.lo = SHRT_MIN; + res.hi = SHRT_MAX; + return res; + } + + /** return the maximal element of a range */ + int range_max(const range &rng){ + return rng.hi; + } + + /** return a minimal (not necessarily unique) element of a range */ + int range_min(const range &rng){ + return rng.lo; + } + + /** return range consisting of downward closure of a point */ + range range_downward(int _hi){ + range foo; + foo.lo = SHRT_MIN; + foo.hi = _hi; + return foo; + } + + void range_add(int i, range &n){ + if(i < n.lo) n.lo = i; + if(i > n.hi) n.hi = i; + } + + /** Choose an element of rng1 that is near to rng2 */ + int range_near(const range &rng1, const range &rng2){ + int frame; + int thing = tree_lca(rng1.lo,rng2.hi); + if(thing == rng1.lo) frame = rng1.lo; + else frame = tree_gcd(thing,rng1.hi); + return frame; + } +#else + + struct range_lo { + int lo; + range_lo *next; + range_lo(int _lo, range_lo *_next){ + lo = _lo; + next = _next; + } + }; + + struct range { + int hi; + range_lo *lo; + range(int _hi, range_lo *_lo){ + hi = _hi; + lo = _lo; + } + range(){ + hi = SHRT_MIN; + lo = 0; + } + }; + + range_tables *rt; + + /** computes the lub (smallest containing subtree) of two ranges */ + range range_lub(const range &rng1, const range &rng2); + + /** computes the glb (intersection) of two ranges */ + range range_glb(const range &rng1, const range &rng2); + + /** is this range empty? */ + bool range_is_empty(const range &rng); + + /** return an empty range */ + range range_empty(); + + /** return a full range */ + range range_full(); + + /** return the maximal element of a range */ + int range_max(const range &rng); + + /** return a minimal (not necessarily unique) element of a range */ + int range_min(const range &rng); + + /** return range consisting of downward closure of a point */ + range range_downward(int _hi); + + /** add an element to a range */ + void range_add(int i, range &n); + + /** Choose an element of rng1 that is near to rng2 */ + int range_near(const range &rng1, const range &rng2); + + range_lo *find_range_lo(int lo, range_lo *next); + range_lo *range_lub_lo(range_lo *rng1, range_lo *rng2); + range_lo *range_glb_lo(range_lo *rng1, range_lo *rng2, int lim); + +#endif + + /** test whether a tree node is contained in a range */ + bool in_range(int n, const range &rng); + + /** test whether two ranges of tree nodes intersect */ + bool ranges_intersect(const range &rng1, const range &rng2); + + /** test whether range rng1 contained in range rng2 */ + bool range_contained(const range &rng1, const range &rng2); + + private: + int tree_lca(int n1, int n2); + int tree_gcd(int n1, int n2); + bool tree_mode(){return parents.size() != 0;} + + + +}; +#endif diff --git a/src/interp/iz3secondary.h b/src/interp/iz3secondary.h new file mode 100755 index 000000000..749feaacc --- /dev/null +++ b/src/interp/iz3secondary.h @@ -0,0 +1,22 @@ +/* Copyright 2011 Microsoft Research. */ + +#ifndef IZ3SECONDARY_H +#define IZ3SECONDARY_H + +/** Interface class for secondary provers. */ + +#include "iz3base.h" +#include + +class iz3secondary : public iz3mgr { + public: + virtual int interpolate(const std::vector &frames, std::vector &interpolants) = 0; + virtual ~iz3secondary(){} + + protected: + iz3secondary(const iz3mgr &mgr) : iz3mgr(mgr) {} +}; + + + +#endif From e5f5e008aaa6e090771a08f9cc666eaecb64631a Mon Sep 17 00:00:00 2001 From: Kenneth McMillan Date: Sun, 3 Mar 2013 21:22:50 -0800 Subject: [PATCH 002/509] fixing file heads to match z3 --- src/interp/foci2.h | 19 +++++++++++++++++++ src/interp/iz3base.cpp | 21 ++++++++++++++++++++- src/interp/iz3base.h | 20 +++++++++++++++++++- src/interp/iz3foci.cpp | 19 ++++++++++++++++++- src/interp/iz3foci.h | 19 ++++++++++++++++++- src/interp/iz3hash.h | 18 ++++++++++++++++++ src/interp/iz3mgr.cpp | 19 ++++++++++++++++++- src/interp/iz3mgr.h | 19 ++++++++++++++++++- src/interp/iz3profiling.h | 19 ++++++++++++++++++- src/interp/iz3proof.cpp | 20 +++++++++++++++++++- src/interp/iz3proof.h | 19 ++++++++++++++++++- src/interp/iz3scopes.cpp | 19 ++++++++++++++++++- src/interp/iz3scopes.h | 20 +++++++++++++++++++- src/interp/iz3secondary.h | 20 +++++++++++++++++++- 14 files changed, 259 insertions(+), 12 deletions(-) diff --git a/src/interp/foci2.h b/src/interp/foci2.h index 3f32b8118..261dd05dc 100755 --- a/src/interp/foci2.h +++ b/src/interp/foci2.h @@ -1,3 +1,22 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + foci2.h + +Abstract: + + An interface class for foci2. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + #ifndef FOCI2_H #define FOCI2_H diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 44006f7f2..d3b469462 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -1,4 +1,23 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3base.cpp + +Abstract: + + Base class for interpolators. Includes an AST manager and a scoping + object as bases. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + #include "iz3base.h" #include diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index 383a89ea4..4209f3c67 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -1,4 +1,22 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3base.h + +Abstract: + + Base class for interpolators. Includes an AST manager and a scoping + object as bases. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ #ifndef IZ3BASE_H #define IZ3BASE_H diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp index 741391a8d..31d8b56c7 100755 --- a/src/interp/iz3foci.cpp +++ b/src/interp/iz3foci.cpp @@ -1,4 +1,21 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3foci.cpp + +Abstract: + + Implements a secondary solver using foci2. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ #include #include diff --git a/src/interp/iz3foci.h b/src/interp/iz3foci.h index 4b7040b98..86b049f24 100755 --- a/src/interp/iz3foci.h +++ b/src/interp/iz3foci.h @@ -1,4 +1,21 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3foci.h + +Abstract: + + Implements a secondary solver using foci2. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ #ifndef IZ3FOCI_H #define IZ3FOCI_H diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index abbe5b5fb..f6767c037 100755 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -1,3 +1,21 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3hash.h + +Abstract: + + Wrapper for stl hash tables + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ // pull in the headers for has_map and hash_set // these live in non-standard places diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index b79fa0f6b..b1f07f5ce 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -1,4 +1,21 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3mgr.cpp + +Abstract: + + A wrapper around an ast manager, providing convenience methods. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ #include "iz3mgr.h" diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 211b9a45a..32ec25d70 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -1,4 +1,21 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3mgr.h + +Abstract: + + A wrapper around an ast manager, providing convenience methods. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ #ifndef IZ3MGR_H #define IZ3MGR_H diff --git a/src/interp/iz3profiling.h b/src/interp/iz3profiling.h index fd518b1d9..cdb7066e3 100755 --- a/src/interp/iz3profiling.h +++ b/src/interp/iz3profiling.h @@ -1,4 +1,21 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3profiling.h + +Abstract: + + Some routines for measuring performance. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ #ifndef IZ3PROFILING_H #define IZ3PROFILING_H diff --git a/src/interp/iz3proof.cpp b/src/interp/iz3proof.cpp index 4c79a4ae6..968a4b25a 100755 --- a/src/interp/iz3proof.cpp +++ b/src/interp/iz3proof.cpp @@ -1,4 +1,22 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3proof.cpp + +Abstract: + + This class defines a simple interpolating proof system. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + #include "iz3proof.h" diff --git a/src/interp/iz3proof.h b/src/interp/iz3proof.h index ea1973f63..ae08e9d36 100755 --- a/src/interp/iz3proof.h +++ b/src/interp/iz3proof.h @@ -1,4 +1,21 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3proof.h + +Abstract: + + This class defines a simple interpolating proof system. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ #ifndef IZ3PROOF_H #define IZ3PROOF_H diff --git a/src/interp/iz3scopes.cpp b/src/interp/iz3scopes.cpp index 65c6f6d88..90ff3b829 100755 --- a/src/interp/iz3scopes.cpp +++ b/src/interp/iz3scopes.cpp @@ -1,4 +1,21 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3scopes.cpp + +Abstract: + + Calculations with scopes, for both sequence and tree interpolation. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ #include diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h index e7ca387d5..2ba3a999f 100755 --- a/src/interp/iz3scopes.h +++ b/src/interp/iz3scopes.h @@ -1,4 +1,22 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3scopes.h + +Abstract: + + Calculations with scopes, for both sequence and tree interpolation. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + #ifndef IZ3SOPES_H #define IZ3SOPES_H diff --git a/src/interp/iz3secondary.h b/src/interp/iz3secondary.h index 749feaacc..0eb1b9a93 100755 --- a/src/interp/iz3secondary.h +++ b/src/interp/iz3secondary.h @@ -1,4 +1,22 @@ -/* Copyright 2011 Microsoft Research. */ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3secondary + +Abstract: + + Interface for secondary provers. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + #ifndef IZ3SECONDARY_H #define IZ3SECONDARY_H From 9792f6dd33f51bf6d8e4a2cdd81e5b5c75d3ebd4 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 4 Mar 2013 18:41:30 -0800 Subject: [PATCH 003/509] more work on incorporating iz3 --- src/api/api_interp.cpp | 201 ++++ src/api/z3_api.h | 122 ++ src/interp/iz3base.cpp | 9 +- src/interp/iz3base.h | 16 + src/interp/iz3interp.cpp | 256 ++++ src/interp/iz3interp.h | 40 + src/interp/iz3mgr.cpp | 9 +- src/interp/iz3mgr.h | 25 +- src/interp/iz3scopes.cpp | 2 +- src/interp/iz3translate.h | 57 + src/interp/iz3translate_direct.cpp | 1797 ++++++++++++++++++++++++++++ 11 files changed, 2527 insertions(+), 7 deletions(-) create mode 100644 src/api/api_interp.cpp create mode 100755 src/interp/iz3interp.cpp create mode 100644 src/interp/iz3interp.h create mode 100755 src/interp/iz3translate.h create mode 100755 src/interp/iz3translate_direct.cpp diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp new file mode 100644 index 000000000..2768e97e3 --- /dev/null +++ b/src/api/api_interp.cpp @@ -0,0 +1,201 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + api_interp.cpp + +Abstract: + API for interpolation + +Author: + + Ken McMillan + +Revision History: + +--*/ +#include +#include"z3.h" +#include"api_log_macros.h" +#include"api_context.h" +#include"api_tactic.h" +#include"api_solver.h" +#include"api_model.h" +#include"api_stats.h" +#include"api_ast_vector.h" +#include"tactic2solver.h" +#include"scoped_ctrl_c.h" +#include"cancel_eh.h" +#include"scoped_timer.h" +#include"smt_strategic_solver.h" +#include"smt_solver.h" +#include"smt_implied_equalities.h" +#include"iz3interp.h" +#include"iz3profiling.h" + +typedef interpolation_options_struct *Z3_interpolation_options; + +extern "C" { + + Z3_context Z3_mk_interpolation_context(Z3_config cfg){ + if(!cfg) cfg = Z3_mk_config(); + Z3_set_param_value(cfg, "PROOF_MODE", "2"); + Z3_set_param_value(cfg, "MODEL", "true"); + Z3_set_param_value(cfg, "PRE_SIMPLIFIER","false"); + Z3_set_param_value(cfg, "SIMPLIFY_CLAUSES","false"); + + Z3_context ctx = Z3_mk_context(cfg); + Z3_del_config(cfg); + return ctx; + } + + void Z3_interpolate_proof(Z3_context ctx, + Z3_ast proof, + int num, + Z3_ast *cnsts, + int *parents, + Z3_interpolation_options options, + Z3_ast *interps, + int num_theory, + Z3_ast *theory + ){ + + if(num > 1){ // if we have interpolants to compute + + ptr_vector pre_cnsts_vec(num); // get constraints in a vector + for(int i = 0; i < num; i++){ + ast *a = to_ast(cnsts[i]); + pre_cnsts_vec[i] = a; + } + + vector pre_parents_vec; // get parents in a vector + if(parents){ + pre_parents_vec.resize(num); + for(int i = 0; i < num; i++) + pre_parents_vec[i] = parents[i]; + } + + ptr_vector theory_vec; // get background theory in a vector + if(theory){ + theory_vec.resize(num_theory); + for(int i = 0; i < num_theory; i++) + theory_vec[i] = to_ast(theory[i]); + } + + ptr_vector interpolants(num-1); // make space for result + + scoped_ptr _m(&mk_c(ctx)->m()); + iz3interpolate(_m, + to_ast(proof), + pre_cnsts_vec, + pre_parents_vec, + interpolants, + theory_vec, + (interpolation_options) options); + + // copy result back + for(unsigned i = 0; i < interpolants.size(); i++) + interps[i] = of_ast(interpolants[i]); + } + + } + + Z3_lbool Z3_interpolate(Z3_context ctx, + int num, + Z3_ast *cnsts, + int *parents, + Z3_interpolation_options options, + Z3_ast *interps, + Z3_model *model, + Z3_literals *labels, + bool incremental, + int num_theory, + Z3_ast *theory + ){ + + + if(!incremental){ + + profiling::timer_start("Z3 assert"); + + Z3_push(ctx); // so we can rewind later + + for(int i = 0; i < num; i++) + Z3_assert_cnstr(ctx,cnsts[i]); // assert all the constraints + + if(theory){ + for(int i = 0; i < num_theory; i++) + Z3_assert_cnstr(ctx,theory[i]); + } + + profiling::timer_stop("Z3 assert"); + } + + + // Get a proof of unsat + + Z3_ast proof; + Z3_lbool result; + + profiling::timer_start("Z3 solving"); + result = Z3_check_assumptions(ctx, 0, 0, model, &proof, 0, 0); + profiling::timer_stop("Z3 solving"); + + switch (result) { + case Z3_L_FALSE: + + Z3_interpolate_proof(ctx, + proof, + num, + cnsts, + parents, + options, + interps, + num_theory, + theory); + break; + + case Z3_L_UNDEF: + if(labels) + *labels = Z3_get_relevant_labels(ctx); + break; + + case Z3_L_TRUE: + if(labels) + *labels = Z3_get_relevant_labels(ctx); + break; + } + + return result; + + } + + static std::string Z3_profile_string; + + Z3_string Z3_interpolation_profile(Z3_context ctx){ + std::ostringstream f; + profiling::print(f); + Z3_profile_string = f.str(); + return Z3_profile_string.c_str(); + } + + + Z3_interpolation_options + Z3_mk_interpolation_options(){ + return (Z3_interpolation_options) new interpolation_options_struct; + } + + void + Z3_del_interpolation_options(Z3_interpolation_options opts){ + delete opts; + } + + void + Z3_set_interpolation_option(Z3_interpolation_options opts, + Z3_string name, + Z3_string value){ + opts->map[name] = value; + } + +}; diff --git a/src/api/z3_api.h b/src/api/z3_api.h index ea3b05e40..12db470fd 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7660,6 +7660,128 @@ END_MLAPI_EXCLUDE /*@}*/ + /** + @name Interpolation + */ + /*@{*/ + + /** \brief This function generates a Z3 context suitable for generation of + interpolants. Formulas can be generated as abstract syntx trees in + this context using the Z3 C API. + + Interpolants are also generated as AST's in this context. + + If cfg is non-null, it will be used as the base configuration + for the Z3 context. This makes it possible to set Z3 options + to be used during interpolation. This feature should be used + with some caution however, as it may be that certain Z3 options + are incompatible with interpolation. + + def_API('Z3_mk_interpolation_context', CONTEXT, (_in(CONFIG),)) + + */ + + Z3_context Z3_API Z3_mk_interpolation_context(__in Z3_config cfg); + + +/** Constant reprepresenting a root of a formula tree for tree interpolation */ +#define IZ3_ROOT SHRT_MAX + +/** This function uses Z3 to determine satisfiability of a set of + constraints. If UNSAT, an interpolant is returned, based on the + refutation generated by Z3. If SAT, a model is returned. + + If "parents" is non-null, computes a tree interpolant. The tree is + defined by the array "parents". This array maps each formula in + the tree to its parent, where formulas are indicated by their + integer index in "cnsts". The parent of formula n must have index + greater than n. The last formula is the root of the tree. Its + parent entry should be the constant IZ3_ROOT. + + If "parents" is null, computes a sequence interpolant. + + \param ctx The Z3 context. Must be generated by iz3_mk_context + \param num The number of constraints in the sequence + \param cnsts Array of constraints (AST's in context ctx) + \param parents The parents vector defining the tree structure + \param options Interpolation options (may be NULL) + \param interps Array to return interpolants (size at least num-1, may be NULL) + \param model Returns a Z3 model if constraints SAT (may be NULL) + \param labels Returns relevant labels if SAT (may be NULL) + \param incremental + + VERY IMPORTANT: All the Z3 formulas in cnsts must be in Z3 + context ctx. The model and interpolants returned are also + in this context. + + The return code is as in Z3_check_assumptions, that is, + + Z3_L_FALSE = constraints UNSAT (interpolants returned) + Z3_L_TRUE = constraints SAT (model returned) + Z3_L_UNDEF = Z3 produced no result, or interpolation not possible + + Currently, this function supports integer and boolean variables, + as well as arrays over these types, with linear arithmetic, + uninterpreted functions and quantifiers over integers (that is + AUFLIA). Interpolants are produced in AUFLIA. However, some + uses of array operations may cause quantifiers to appear in the + interpolants even when there are no quantifiers in the input formulas. + Although quantifiers may appear in the input formulas, Z3 may give up in + this case, returning Z3_L_UNDEF. + + If "incremental" is true, cnsts must contain exactly the set of + formulas that are currently asserted in the context. If false, + there must be no formulas currently asserted in the context. + Setting "incremental" to true makes it posisble to incrementally + add and remove constraints from the context until the context + becomes UNSAT, at which point an interpolant is computed. Caution + must be used, however. Before popping the context, if you wish to + keep the interolant formulas, you *must* preserve them by using + Z3_persist_ast. Also, if you want to simplify the interpolant + formulas using Z3_simplify, you must first pop all of the + assertions in the context (or use a different context). Otherwise, + the formulas will be simplified *relative* to these constraints, + which is almost certainly not what you want. + + + Current limitations on tree interpolants. In a tree interpolation + problem, each constant (0-ary function symbol) must occur only + along one path from root to leaf. Function symbols (of arity > 0) + are considered to have global scope (i.e., may appear in any + interpolant formula). + + def_API('Z3_interpolate', LBOOL, (_in(CONTEXT),), (_in(INT),), (_in_ptr(AST),), + (_in_ptr(INT),), (_in(PARAMS),), (_in_ptr(AST),), + (_out(MODEL),), (_out(LITERALS),), (_in(BOOL),), (_in(INT),), (_in_ptr(AST,0),)) + + */ + + + Z3_lbool Z3_API Z3_interpolate(Z3_context ctx, + int num, + Z3_ast *cnsts, + int *parents, + Z3_interpolation_options options, + Z3_ast *interps, + Z3_model *model = 0, + Z3_literals *labels = 0, + bool incremental = false, + int num_theory = 0, + Z3_ast *theory = 0); + + /** Return a string summarizing cumulative time used for + interpolation. This string is purely for entertainment purposes + and has no semantics. + + \param ctx The context (currently ignored) + + def_API('Z3_interpolation_profile', STRING, (_in(CONTEXT),)) + */ + + Z3_string Z3_API Z3_interpolation_profile(Z3_context ctx); + + + #endif diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index d3b469462..296688086 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -193,13 +193,14 @@ iz3base::ast iz3base::simplify(ast n){ return res; } -void iz3base::initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &theory){ +void iz3base::initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory){ cnsts = _parts; + theory = _theory; for(unsigned i = 0; i < cnsts.size(); i++) add_frame_range(i, cnsts[i]); - for(unsigned i = 0; i < theory.size(); i++){ - add_frame_range(SHRT_MIN, theory[i]); - add_frame_range(SHRT_MAX, theory[i]); + for(unsigned i = 0; i < _theory.size(); i++){ + add_frame_range(SHRT_MIN, _theory[i]); + add_frame_range(SHRT_MAX, _theory[i]); } } diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index 4209f3c67..8a8ad332a 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -65,6 +65,15 @@ class iz3base : public iz3mgr, public scopes { weak = false; } + iz3base(const iz3mgr& other, + const std::vector &_cnsts, + const std::vector &_parents, + const std::vector &_theory) + : iz3mgr(other), scopes(_parents) { + initialize(_cnsts,_parents,_theory); + weak = false; + } + /* Set our options */ void set_option(const std::string &name, const std::string &value){ if(name == "weak" && value == "1") weak = true; @@ -87,6 +96,12 @@ class iz3base : public iz3mgr, public scopes { throw "no interpolator"; } + ast get_proof_check_assump(range &rng){ + std::vector cs(theory); + cs.push_back(cnsts[rng.hi]); + return make(And,cs); + } + private: struct ranges { @@ -106,6 +121,7 @@ class iz3base : public iz3mgr, public scopes { void initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory); std::vector cnsts; + std::vector theory; bool is_literal(ast n); void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp new file mode 100755 index 000000000..15bdf0c22 --- /dev/null +++ b/src/interp/iz3interp.cpp @@ -0,0 +1,256 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3interp.cpp + +Abstract: + + Interpolation based on proof translation. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +/* Copyright 2011 Microsoft Research. */ +#include +#include +#include +#include +#include +#include +#include + +#include "iz3profiling.h" +#include "iz3translate.h" +#include "iz3foci.h" +#include "iz3proof.h" +#include "iz3hash.h" +#include "iz3interp.h" + + +#ifndef WIN32 +using namespace stl_ext; +#endif + + + +#if 1 + +struct frame_reducer : public iz3mgr { + + int frames; + hash_map frame_map; + std::vector assertions_map; + std::vector orig_parents_copy; + std::vector used_frames; + + + frame_reducer(const iz3mgr &other) + : iz3mgr(other) {} + + void get_proof_assumptions_rec(z3pf proof, hash_set &memo, std::vector &used_frames){ + if(memo.count(proof))return; + memo.insert(proof); + pfrule dk = pr(proof); + if(dk == PR_ASSERTED){ + ast con = conc(proof); + if(frame_map.find(con) != frame_map.end()){ // false for theory facts + int frame = frame_map[con]; + used_frames[frame] = true; + } + } + else { + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + z3pf arg = prem(proof,i); + get_proof_assumptions_rec(arg,memo,used_frames); + } + } + } + + void get_frames(const std::vector &z3_preds, + const std::vector &orig_parents, + std::vector &assertions, + std::vector &parents, + z3pf proof){ + frames = z3_preds.size(); + orig_parents_copy = orig_parents; + for(unsigned i = 0; i < z3_preds.size(); i++) + frame_map[z3_preds[i]] = i; + used_frames.resize(frames); + hash_set memo; + get_proof_assumptions_rec(proof,memo,used_frames); + std::vector assertions_back_map(frames); + + for(unsigned i = 0; i < z3_preds.size(); i++) + if(used_frames[i] || i == z3_preds.size() - 1){ + assertions.push_back(z3_preds[i]); + assertions_map.push_back(i); + assertions_back_map[i] = assertions.size() - 1; + } + + if(orig_parents.size()){ + parents.resize(assertions.size()); + for(unsigned i = 0; i < assertions.size(); i++){ + int p = orig_parents[assertions_map[i]]; + while(p != SHRT_MAX && !used_frames[p]) + p = orig_parents[p]; + parents[i] = p == SHRT_MAX ? p : assertions_back_map[p]; + } + } + + // std::cout << "used frames = " << frames << "\n"; + } + + void fix_interpolants(std::vector &interpolants){ + std::vector unfixed = interpolants; + interpolants.resize(frames - 1); + for(int i = 0; i < frames - 1; i++) + interpolants[i] = mk_true(); + for(unsigned i = 0; i < unfixed.size(); i++) + interpolants[assertions_map[i]] = unfixed[i]; + for(int i = 0; i < frames-2; i++){ + int p = orig_parents_copy.size() == 0 ? i+1 : orig_parents_copy[i]; + if(p < frames - 1 && !used_frames[p]) + interpolants[p] = interpolants[i]; + } + } +}; + +#else +struct frame_reducer { + + + + frame_reducer(context _ctx){ + } + + void get_frames(const std::vector &z3_preds, + const std::vector &orig_parents, + std::vector &assertions, + std::vector &parents, + ast proof){ + assertions = z3_preds; + parents = orig_parents; + } + + void fix_interpolants(std::vector &interpolants){ + } +}; + +#endif + + +#if 0 +static lbool test_secondary(context ctx, + int num, + ast *cnsts, + ast *interps, + int *parents = 0 + ){ + iz3secondary *sp = iz3foci::create(ctx,num,parents); + std::vector frames(num), interpolants(num-1); + std::copy(cnsts,cnsts+num,frames.begin()); + int res = sp->interpolate(frames,interpolants); + if(res == 0) + std::copy(interpolants.begin(),interpolants.end(),interps); + return res ? L_TRUE : L_FALSE; +} +#endif + + +class iz3interp : public iz3mgr { +public: + void proof_to_interpolant(z3pf proof, + const std::vector &cnsts, + const std::vector &parents, + std::vector &interps, + const std::vector &theory, + interpolation_options_struct *options = 0 + ){ + + profiling::timer_start("Interpolation prep"); + + // get rid of frames not used in proof + + std::vector cnsts_vec; + std::vector parents_vec; + frame_reducer fr(*this); + fr.get_frames(cnsts,parents,cnsts_vec,parents_vec,proof); + + int num = cnsts_vec.size(); + std::vector interps_vec(num-1); + + // create a secondary prover + iz3secondary *sp = iz3foci::create(this,num,parents_vec.empty()?0:&parents_vec[0]); + + // create a translator + iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec,parents_vec,theory); + + // set the translation options, if needed + if(options) + for(hash_map::iterator it = options->map.begin(), en = options->map.end(); it != en; ++it) + tr->set_option(it->first, it->second); + + // create a proof object to hold the translation + iz3proof pf(tr); + + profiling::timer_stop("Interpolation prep"); + + // translate into an interpolatable proof + profiling::timer_start("Proof translation"); + tr->translate(proof,pf); + profiling::timer_stop("Proof translation"); + + // translate the proof into interpolants + profiling::timer_start("Proof interpolation"); + for(int i = 0; i < num-1; i++){ + interps_vec[i] = pf.interpolate(tr->range_downward(i),tr->weak_mode()); + interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(i)); + } + profiling::timer_stop("Proof interpolation"); + + // put back in the removed frames + fr.fix_interpolants(interps_vec); + + interps = interps_vec; + delete tr; + delete sp; + } + + iz3interp(scoped_ptr &_m_manager) + : iz3mgr(_m_manager) {} +}; + +void iz3interpolate(scoped_ptr &_m_manager, + ast *proof, + const ptr_vector &cnsts, + const ::vector &parents, + ptr_vector &interps, + const ptr_vector theory, + interpolation_options_struct * options) +{ + std::vector _cnsts(cnsts.size()); + std::vector _parents(parents.size()); + std::vector _interps; + std::vector _theory(theory.size()); + for(unsigned i = 0; i < cnsts.size(); i++) + _cnsts[i] = cnsts[i]; + for(unsigned i = 0; i < parents.size(); i++) + _parents[i] = parents[i]; + for(unsigned i = 0; i < theory.size(); i++) + _theory[i] = theory[i]; + ast_r _proof(proof); + iz3interp itp(_m_manager); + itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options); + interps.resize(_interps.size()); + for(unsigned i = 0; i < interps.size(); i++) + _interps[i] = interps[i]; +} + diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h new file mode 100644 index 000000000..4e76b74fd --- /dev/null +++ b/src/interp/iz3interp.h @@ -0,0 +1,40 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3interp.h + +Abstract: + + Interpolation based on proof translation. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#ifndef IZ3_INTERP_H +#define IZ3_INTERP_H + +#include "iz3hash.h" + +struct interpolation_options_struct { + stl_ext::hash_map map; +}; + + +typedef interpolation_options_struct *interpolation_options; + +void iz3interpolate(scoped_ptr &_m_manager, + ast *proof, + const ptr_vector &cnsts, + const ::vector &parents, + ptr_vector &interps, + const ptr_vector theory, + interpolation_options_struct * options = 0); + +#endif diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index b1f07f5ce..741b26478 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -65,7 +65,7 @@ iz3mgr::ast iz3mgr::make(opr op, ast &arg0){ return make(op,1,&a); } -iz3mgr::ast iz3mgr::make(opr op, ast &arg0, ast &arg1){ +iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1){ raw_ast *args[2]; args[0] = arg0.raw(); args[1] = arg1.raw(); @@ -367,3 +367,10 @@ iz3mgr::opr iz3mgr::op(ast &t){ } return Other; } + + +iz3mgr::pfrule iz3mgr::pr(const ast &t){ + func_decl *d = to_app(t.raw())->get_decl(); + assert(m_basic_fid == d->get_family_id()); + return d->get_decl_kind(); +} diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 32ec25d70..efcbb0d6f 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -63,9 +63,20 @@ class ast_r { bool eq(const ast_r &other) const { return _ast == other._ast; } + bool lt(const ast_r &other) const { + return _ast < other._ast; + } friend bool operator==(const ast_r &x, const ast_r&y){ return x.eq(y); } + friend bool operator!=(const ast_r &x, const ast_r&y){ + return !x.eq(y); + } + friend bool operator<(const ast_r &x, const ast_r&y){ + return x.lt(y); + } + size_t hash() const {return (size_t)_ast;} + bool null() const {return !_ast;} }; @@ -101,6 +112,8 @@ class iz3mgr { // typedef decl_kind opr; typedef func_decl *symb; typedef sort *type; + typedef ast_r z3pf; + typedef decl_kind pfrule; enum opr { True, @@ -163,7 +176,7 @@ class iz3mgr { ast make(opr op, const std::vector &args); ast make(opr op); ast make(opr op, ast &arg0); - ast make(opr op, ast &arg0, ast &arg1); + ast make(opr op, const ast &arg0, const ast &arg1); ast make(opr op, ast &arg0, ast &arg1, ast &arg2); ast make(symb sym, const std::vector &args); ast make(symb sym); @@ -354,6 +367,16 @@ class iz3mgr { ast mk_true() { return make(True); } + /** methods for destructing proof terms */ + + pfrule pr(const z3pf &t); + + int num_prems(const z3pf &t){return to_app(t.raw())->get_num_args()-1;} + + z3pf prem(const z3pf &t, int n){return arg(t,n);} + + z3pf conc(const z3pf &t){return arg(t,num_prems(t));} + /** For debugging */ void show(ast); diff --git a/src/interp/iz3scopes.cpp b/src/interp/iz3scopes.cpp index 90ff3b829..382779be0 100755 --- a/src/interp/iz3scopes.cpp +++ b/src/interp/iz3scopes.cpp @@ -31,7 +31,7 @@ int scopes::tree_lca(int n1, int n2){ if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; while(n1 != n2){ if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; - assert(n1 >= 0 && n2 >= 0 && n1 < parents.size() && n2 < parents.size()); + assert(n1 >= 0 && n2 >= 0 && n1 < (int)parents.size() && n2 < (int)parents.size()); if(n1 < n2) n1 = parents[n1]; else n2 = parents[n2]; } diff --git a/src/interp/iz3translate.h b/src/interp/iz3translate.h new file mode 100755 index 000000000..40dc6a165 --- /dev/null +++ b/src/interp/iz3translate.h @@ -0,0 +1,57 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3translate.h + +Abstract: + + Interface for proof translations from Z3 proofs to interpolatable + proofs. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + + +#ifndef IZ3TRANSLATION_H +#define IZ3TRANSLATION_H + +#include "iz3proof.h" +#include "iz3secondary.h" + +// This is a interface class for translation from Z3 proof terms to +// an interpolatable proof + +class iz3translation : public iz3base { +public: + virtual iz3proof::node translate(ast, iz3proof &) = 0; + virtual ast quantify(ast e, const range &rng){return e;} + virtual ~iz3translation(){} + + static iz3translation *create(iz3mgr &mgr, + iz3secondary *secondary, + const std::vector &frames, + const std::vector &parents, + const std::vector &theory); + + protected: + iz3translation(iz3mgr &mgr, + const std::vector &_cnsts, + const std::vector &_parents, + const std::vector &_theory) + : iz3base(mgr,_cnsts,_parents,_theory) {} +}; + +//#define IZ3_TRANSLATE_DIRECT2 +#define IZ3_TRANSLATE_DIRECT + +#endif + + + diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp new file mode 100755 index 000000000..0500d88ab --- /dev/null +++ b/src/interp/iz3translate_direct.cpp @@ -0,0 +1,1797 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3translate_direct.cpp + +Abstract: + + Translate a Z3 proof into the interpolating proof calculus. + Translation is direct, without transformations on the target proof + representaiton. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#include "iz3translate.h" +#include "iz3proof.h" +#include "iz3profiling.h" + +#include +#include +#include +#include +#include +#include +#include + +//using std::vector; +#ifndef WIN32 +using namespace stl_ext; +#endif + + +static int lemma_count = 0; +#define SHOW_LEMMA_COUNT -1 + +// One half of a resolution. We need this to distinguish +// between resolving as a clause and as a unit clause. +// if pivot == conclusion(proof) it is unit. + +struct Z3_resolvent { + iz3base::ast proof; + bool is_unit; + iz3base::ast pivot; + Z3_resolvent(const iz3base::ast &_proof, bool _is_unit, const iz3base::ast &_pivot){ + proof = _proof; + is_unit = _is_unit; + pivot = _pivot; + } +}; + +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Z3_resolvent &p) const { + return (p.proof.hash() + p.pivot.hash()); + } + }; +} + +#ifdef WIN32 + +template <> inline +size_t stdext::hash_value(const Z3_resolvent& p) +{ + std::hash h; + return h(p); +} + + +namespace std { + template <> + class less { + public: + bool operator()(const Z3_resolvent &x, const Z3_resolvent &y) const { + size_t ixproof = (size_t) x.proof; + size_t iyproof = (size_t) y.proof; + if(ixproof < iyproof) return true; + if(ixproof > iyproof) return false; + return x.pivot < y.pivot; + } + }; +} + +#else + +bool operator==(const Z3_resolvent &x, const Z3_resolvent &y) { + return x.proof == y.proof && x.pivot == y.pivot; +} + + +#endif + +typedef std::vector ResolventAppSet; + +struct non_local_lits { + ResolventAppSet proofs; // the proof nodes being raised + non_local_lits(ResolventAppSet &_proofs){ + proofs.swap(_proofs); + } +}; + +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const non_local_lits &p) const { + size_t h = 0; + for(ResolventAppSet::const_iterator it = p.proofs.begin(), en = p.proofs.end(); it != en; ++it) + h += (size_t)*it; + return h; + } + }; +} + +#ifdef WIN32 + +template <> inline +size_t stdext::hash_value(const non_local_lits& p) +{ + std::hash h; + return h(p); +} + +namespace std { + template <> + class less { + public: + bool operator()(const non_local_lits &x, const non_local_lits &y) const { + ResolventAppSet::const_iterator itx = x.proofs.begin(); + ResolventAppSet::const_iterator ity = y.proofs.begin(); + while(true){ + if(ity == y.proofs.end()) return false; + if(itx == x.proofs.end()) return true; + size_t xi = (size_t) *itx; + size_t yi = (size_t) *ity; + if(xi < yi) return true; + if(xi > yi) return false; + ++itx; ++ity; + } + } + }; +} + +#else + +bool operator==(const non_local_lits &x, const non_local_lits &y) { + ResolventAppSet::const_iterator itx = x.proofs.begin(); + ResolventAppSet::const_iterator ity = y.proofs.begin(); + while(true){ + if(ity == y.proofs.end()) return itx == x.proofs.end(); + if(itx == x.proofs.end()) return ity == y.proofs.end(); + if(*itx != *ity) return false; + ++itx; ++ity; + } +} + + +#endif + +/* This translator goes directly from Z3 proofs to interpolatable + proofs without an intermediate representation as an iz3proof. */ + +class iz3translation_direct : public iz3translation { +public: + + typedef ast Zproof; // type of non-interpolating proofs + typedef iz3proof Iproof; // type of interpolating proofs + + /* Here we have lots of hash tables for memoizing various methods and + other such global data structures. + */ + + typedef hash_map AstToInt; + AstToInt locality; // memoizes locality of Z3 proof terms + + typedef std::pair EquivEntry; + typedef hash_map EquivTab; + EquivTab equivs; // maps non-local terms to equivalent local terms, with proof + + typedef hash_set AstHashSet; + AstHashSet equivs_visited; // proofs already checked for equivalences + + + typedef std::pair, hash_map > AstToIpf; + AstToIpf translation; // Zproof nodes to Iproof nodes + + AstHashSet antes_added; // Z3 proof terms whose antecedents have been added to the list + std::vector > antes; // list of antecedent/frame pairs + std::vector local_antes; // list of local antecedents + + Iproof *iproof; // the interpolating proof we are constructing + + AstToInt frame_map; // map assertions to frames + int frames; // number of frames + + typedef std::set AstSet; + typedef hash_map AstToAstSet; + AstToAstSet hyp_map; // map proof terms to hypothesis set + + struct LocVar { // localization vars + ast var; // a fresh variable + ast term; // term it represents + int frame; // frame in which it's defined + LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} + }; + + std::vector localization_vars; // localization vars in order of creation + typedef hash_map AstToAst; + AstToAst localization_map; // maps terms to their localization vars + + typedef hash_map AstToBool; + AstToBool occurs_in_memo; // memo of occurs_in function + + AstHashSet cont_eq_memo; // memo of cont_eq function + + AstToAst subst_memo; // memo of subst function + + iz3secondary *secondary; // the secondary prover + + // Unique table for sets of non-local resolutions + hash_map non_local_lits_unique; + + // Unique table for resolvents + hash_map Z3_resolvent_unique; + + // Translation memo for case of non-local resolutions + hash_map non_local_translation; + + public: + + +#define from_ast(x) (x) + + // determine locality of a proof term + // return frame of derivation if local, or -1 if not + // result INT_MAX means the proof term is a tautology + // memoized in hash_map "locality" + + int get_locality_rec(ast proof){ + std::pair foo(proof,INT_MAX); + std::pair bar = locality.insert(foo); + int &res = bar.first->second; + if(!bar.second) return res; + if(pr(proof) == PR_ASSERTED){ + ast ass = conc(proof); + AstToInt::iterator it = frame_map.find(ass); + assert(it != frame_map.end()); + res = it->second; + } + else { + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + int bar = get_locality_rec(arg); + if(res == INT_MAX || res == bar) res = bar; + else if(bar != INT_MAX) res = -1; + } + } + return res; + } + + + int get_locality(ast proof){ + // if(lia_z3_axioms_only) return -1; + int res = get_locality_rec(proof); + if(res != -1){ + ast con = conc(proof); + range rng = ast_scope(con); + + // hack: if a clause contains "true", it reduces to "true", + // which means we won't compute the range correctly. we handle + // this case by computing the ranges of the literals separately + + if(is_true(con)){ + std::vector lits; + get_Z3_lits(conc(proof),lits); + for(unsigned i = 0; i < lits.size(); i++) + rng = range_glb(rng,ast_scope(lits[i])); + } + + if(!range_is_empty(rng)){ + AstSet &hyps = get_hyps(proof); + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ + ast hyp = *it; + rng = range_glb(rng,ast_scope(hyp)); + } + } + + if(res == INT_MAX){ + if(range_is_empty(rng)) + res = -1; + else res = range_max(rng); + } + else { + if(!in_range(res,rng)) + res = -1; + } + } + return res; + } + + AstSet &get_hyps(ast proof){ + std::pair foo(proof,AstSet()); + std::pair bar = hyp_map.insert(foo); + AstSet &res = bar.first->second; + if(!bar.second) return res; + pfrule dk = pr(proof); + if(dk == PR_HYPOTHESIS){ + ast con = conc(proof); + res.insert(con); + } + else { + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + AstSet &arg_hyps = get_hyps(arg); + res.insert(arg_hyps.begin(),arg_hyps.end()); + } + if(dk == PR_LEMMA){ + ast con = conc(proof); + res.erase(mk_not(con)); + if(is_or(con)){ + int clause_size = num_args(con); + for(int i = 0; i < clause_size; i++){ + ast neglit = mk_not(arg(con,i)); + res.erase(neglit); + } + } + } + } + return res; + } + + + // Find all the judgements of the form p <-> q, where + // p is local and q is non-local, recording them in "equivs" + // the map equivs_visited is used to record the already visited proof terms + + void find_equivs(ast proof){ + if(equivs_visited.find(proof) != equivs_visited.end()) + return; + equivs_visited.insert(proof); + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++) // do all the sub_terms + find_equivs(prem(proof,i)); + ast con = conc(proof); // get the conclusion + if(is_iff(con)){ + ast iff = con; + for(int i = 0; i < 2; i++) + if(!is_local(arg(iff,i)) && is_local(arg(iff,1-i))){ + std::pair > foo(arg(iff,i),std::pair(arg(iff,1-i),proof)); + equivs.insert(foo); + } + } + } + + // get the lits of a Z3 clause as foci terms + void get_Z3_lits(ast t, std::vector &lits){ + opr dk = op(t); + if(dk == False) + return; // false = empty clause + if(dk == Or){ + unsigned nargs = num_args(t); + lits.resize(nargs); + for(unsigned i = 0; i < nargs; i++) // do all the sub_terms + lits[i] = arg(t,i); + } + else { + lits.push_back(t); + } + } + + // resolve two clauses represented as vectors of lits. replace first clause + void resolve(ast pivot, std::vector &cls1, std::vector &cls2){ + ast neg_pivot = mk_not(pivot); + for(unsigned i = 0; i < cls1.size(); i++){ + if(cls1[i] == pivot){ + cls1[i] = cls1.back(); + cls1.pop_back(); + bool found_pivot2 = false; + for(unsigned j = 0; j < cls2.size(); j++){ + if(cls2[j] == neg_pivot) + found_pivot2 = true; + else + cls1.push_back(cls2[j]); + } + assert(found_pivot2); + return; + } + } + assert(0 && "resolve failed"); + } + + // get lits resulting from unit resolution up to and including "position" + // TODO: this is quadratic -- fix it + void do_unit_resolution(ast proof, int position, std::vector &lits){ + ast orig_clause = conc(prem(proof,0)); + get_Z3_lits(orig_clause,lits); + for(int i = 1; i <= position; i++){ + std::vector unit(1); + unit[0] = conc(prem(proof,i)); + resolve(mk_not(unit[0]),lits,unit); + } + } + + + // clear the localization variables + void clear_localization(){ + localization_vars.clear(); + localization_map.clear(); + } + + // create a fresh variable for localization + ast fresh_localization_var(ast term, int frame){ + std::ostringstream s; + s << "%" << (localization_vars.size()); + ast var = make_var(s.str().c_str(),get_type(term)); + sym_range(sym(var)) = range_full(); // make this variable global + localization_vars.push_back(LocVar(var,term,frame)); + return var; + } + + + // "localize" a term to a given frame range by + // creating new symbols to represent non-local subterms + + ast localize_term(ast e, const range &rng){ + if(ranges_intersect(ast_scope(e),rng)) + return e; // this term occurs in range, so it's O.K. + AstToAst::iterator it = localization_map.find(e); + if(it != localization_map.end()) + return it->second; + + // if is is non-local, we must first localise the arguments to + // the range of its function symbol + if(op(e) == Uninterpreted){ + symb f = sym(e); + range frng = sym_range(f); + int nargs = num_args(e); + if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ + if(ranges_intersect(frng,rng)) // localize to desired range if possible + {frng = range_glb(frng,rng);} + std::vector largs(nargs); + for(int i = 0; i < nargs; i++){ + largs[i] = localize_term(arg(e,i),frng); + frng = range_glb(frng,ast_scope(largs[i])); + } + e = make(f,largs); + assert(is_local(e)); + } + } + + if(ranges_intersect(ast_scope(e),rng)) + return e; // this term occurs in range, so it's O.K. + + // choose a frame for the constraint that is close to range + int frame = range_near(ast_scope(e),rng); + + ast new_var = fresh_localization_var(e,frame); + localization_map[e] = new_var; + ast cnst = make(Equal,new_var,e); + antes.push_back(std::pair(cnst,frame)); + return new_var; + } + + // some patterm matching functions + + // match logical or with nargs arguments + // assumes AIG form + bool match_or(ast e, ast *args, int nargs){ + if(op(e) != Or) return false; + int n = num_args(e); + if(n != nargs) return false; + for(int i = 0; i < nargs; i++) + args[i] = arg(e,i); + return true; + } + + // match operator f with exactly nargs arguments + bool match_op(ast e, opr f, ast *args, int nargs){ + if(op(e) != f) return false; + int n = num_args(e); + if(n != nargs) return false; + for(int i = 0; i < nargs; i++) + args[i] = arg(e,i); + return true; + } + + // see if the given formula can be interpreted as + // an axiom instance (e.g., an array axiom instance). + // if so, add it to "antes" in an appropriate frame. + // this may require "localization" + + void get_axiom_instance(ast e){ + + // "store" axiom + // (or (= w q) (= (select (store a1 w y) q) (select a1 q))) + // std::cout << "ax: "; show(e); + ast lits[2],eq_ops_l[2],eq_ops_r[2],sel_ops[2], sto_ops[3], sel_ops2[2] ; + if(match_or(e,lits,2)) + if(match_op(lits[0],Equal,eq_ops_l,2)) + if(match_op(lits[1],Equal,eq_ops_r,2)) + for(int i = 0; i < 2; i++){ // try the second equality both ways + if(match_op(eq_ops_r[0],Select,sel_ops,2)) + if(match_op(sel_ops[0],Store,sto_ops,3)) + if(match_op(eq_ops_r[1],Select,sel_ops2,2)) + for(int j = 0; j < 2; j++){ // try the first equality both ways + if(eq_ops_l[0] == sto_ops[1] + && eq_ops_l[1] == sel_ops[1] + && eq_ops_l[1] == sel_ops2[1] + && sto_ops[0] == sel_ops2[0]) + if(is_local(sel_ops[0])) // store term must be local + { + ast sto = sel_ops[0]; + ast addr = localize_term(eq_ops_l[1],ast_scope(sto)); + ast res = make(Or, + make(Equal,eq_ops_l[0],addr), + make(Equal, + make(Select,sto,addr), + make(Select,sel_ops2[0],addr))); + int frame = range_min(ast_scope(res)); + antes.push_back(std::pair(res,frame)); + return; + } + std::swap(eq_ops_l[0],eq_ops_l[1]); + } + std::swap(eq_ops_r[0],eq_ops_r[1]); + } + } + + // a quantifier instantation looks like (~ forall x. P) \/ P[z/x] + // we need to find a time frame for P, then localize P[z/x] in this frame + + void get_quantifier_instance(ast e){ + ast disjs[2]; + if(match_or(e,disjs,2)){ + if(is_local(disjs[0])){ + ast res = localize_term(disjs[1], ast_scope(disjs[0])); + int frame = range_min(ast_scope(res)); + antes.push_back(std::pair(res,frame)); + return; + } + } + } + + ast get_judgement(ast proof){ + ast con = from_ast(conc(proof)); + AstSet &hyps = get_hyps(proof); + std::vector hyps_vec; + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) + hyps_vec.push_back(*it); + if(hyps_vec.size() == 0) return con; + con = make(Or,mk_not(make(And,hyps_vec)),con); + return con; + } + + // add the premises of a proof term to the "antes" list + + void add_antes(ast proof){ + if(antes_added.find(proof) != antes_added.end()) return; + antes_added.insert(proof); + int frame = get_locality(proof); + if(frame != -1) + if(1){ + ast e = get_judgement(proof); + if(frame >= frames) frame = frames-1; // can happen if there are no symbols + antes.push_back(std::pair(e,frame)); + return; + } + pfrule dk = pr(proof); + if(dk == PR_ASSERTED){ + ast ass = conc(proof); + AstToInt::iterator it = frame_map.find(ass); + assert(it != frame_map.end()); + frame = it->second; + if(frame >= frames) frame = frames-1; // can happen if a theory fact + antes.push_back(std::pair(ass,frame)); + return; + } + if(dk == PR_TH_LEMMA && num_prems(proof) == 0){ + get_axiom_instance(conc(proof)); + } + if(dk == PR_QUANT_INST && num_prems(proof) == 0){ + get_quantifier_instance(conc(proof)); + } + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + add_antes(arg); + } + } + + // does variable occur in expression? + int occurs_in1(ast var, ast e){ + std::pair foo(e,false); + std::pair bar = occurs_in_memo.insert(foo); + bool &res = bar.first->second; + if(bar.second){ + if(e == var) res = true; + int nargs = num_args(e); + for(int i = 0; i < nargs; i++) + res |= occurs_in1(var,arg(e,i)); + } + return res; + } + + int occurs_in(ast var, ast e){ + occurs_in_memo.clear(); + return occurs_in1(var,e); + } + + // find a controlling equality for a given variable v in a term + // a controlling equality is of the form v = t, which, being + // false would force the formula to have the specifid truth value + // returns t, or null if no such + + ast cont_eq(bool truth, ast v, ast e){ + if(is_not(e)) return cont_eq(!truth,v,arg(e,0)); + if(cont_eq_memo.find(e) != cont_eq_memo.end()) + return (ast)0; + cont_eq_memo.insert(e); + if(!truth && op(e) == Equal){ + if(arg(e,0) == v) return(arg(e,1)); + if(arg(e,1) == v) return(arg(e,0)); + } + if((!truth && op(e) == And) || (truth && op(e) == Or)){ + int nargs = num_args(e); + for(int i = 0; i < nargs; i++){ + ast res = cont_eq(truth, v, arg(e,i)); + if(!res.null()) return res; + } + } + return ast(); + } + + // substitute a term t for unbound occurrences of variable v in e + + ast subst(ast var, ast t, ast e){ + if(e == var) return t; + std::pair foo(e,(ast)0); + std::pair bar = subst_memo.insert(foo); + ast &res = bar.first->second; + if(bar.second){ + int nargs = num_args(e); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = subst(var,t,arg(e,i)); + opr f = op(e); + if(f == Equal && args[0] == args[1]) res = mk_true(); + else res = clone(e,args); + } + return res; + } + + // apply a quantifier to a formula, with some optimizations + // 1) bound variable does not occur -> no quantifier + // 2) bound variable must be equal to some term -> substitute + + ast apply_quant(opr quantifier, ast var, ast e){ + if(!occurs_in(var,e))return e; + cont_eq_memo.clear(); + ast cterm = cont_eq(quantifier == Forall, var, e); + if(!cterm.null()){ + subst_memo.clear(); + return subst(var,cterm,e); + } + std::vector bvs; bvs.push_back(var); + return make_quant(quantifier,bvs,e); + } + + // add quantifiers over the localization vars + // to an interpolant for frames lo-hi + + ast add_quants(ast e, int lo, int hi){ + for(int i = localization_vars.size() - 1; i >= 0; i--){ + LocVar &lv = localization_vars[i]; + opr quantifier = (lv.frame >= lo && lv.frame <= hi) ? Exists : Forall; + e = apply_quant(quantifier,lv.var,e); + } + return e; + } + + int get_lits_locality(std::vector &lits){ + range rng = range_full(); + for(std::vector::iterator it = lits.begin(), en = lits.end(); it != en; ++it){ + ast lit = *it; + rng = range_glb(rng,ast_scope(lit)); + } + if(range_is_empty(rng)) return -1; + int hi = range_max(rng); + if(hi >= frames) return frames - 1; + return hi; + } + + + struct invalid_lemma {}; + + + + + // prove a lemma (clause) using current antes list + // return proof of the lemma + // use the secondary prover + + int prove_lemma(std::vector &lits){ + + + // first try localization + if(antes.size() == 0){ + int local_frame = get_lits_locality(lits); + if(local_frame != -1) + return iproof->make_assumption(local_frame,lits); // no proof needed for purely local fact + } + + // group the assumptions by frame + std::vector preds(frames); + for(unsigned i = 0; i < preds.size(); i++) + preds[i] = mk_true(); + for(unsigned i = 0; i < antes.size(); i++){ + int frame = antes[i].second; + preds[frame] = mk_and(preds[frame],antes[i].first); // conjoin it to frame + } + + for(unsigned i = 0; i < lits.size(); i++){ + int frame; + if(!weak_mode()){ + frame = range_max(ast_scope(lits[i])); + if(frame >= frames) frame = frames-1; // could happen if contains no symbols + } + else { + frame = range_min(ast_scope(lits[i])); + if(frame < 0){ + frame = range_max(ast_scope(lits[i])); // could happen if contains no symbols + if(frame >= frames) frame = frames-1; + } + } + preds[frame] = mk_and(preds[frame],mk_not(lits[i])); + } + + + std::vector itps; // holds interpolants + + +#if 1 + // std::cout << "lemma: " << ++lemma_count << "\n"; + if(lemma_count == SHOW_LEMMA_COUNT){ + for(unsigned i = 0; i < lits.size(); i++) + show_lit(lits[i]); + std::cerr << "lemma written to file lemma.smt:\n"; + iz3base foo(*this,preds,std::vector(),std::vector()); + foo.print("lemma.smt"); + throw invalid_lemma(); + } +#endif + +#if 0 + std::cout << "\nLemma:\n"; + for(unsigned i = 0; i < lits.size(); i++) + show_lit(lits[i]); +#endif + + // interpolate using secondary prover + profiling::timer_start("foci"); + int sat = secondary->interpolate(preds,itps); + profiling::timer_stop("foci"); + + // if sat, lemma isn't valid, something is wrong + if(sat){ + std::cerr << "invalid lemma written to file invalid_lemma.smt:\n"; + iz3base foo(*this,preds,std::vector(),std::vector()); + foo.print("invalid_lemma.smt"); + throw invalid_lemma(); + } + assert(sat == 0); // if sat, lemma doesn't hold! + + // quantifiy the localization vars + for(unsigned i = 0; i < itps.size(); i++) + itps[i] = add_quants(itps[i],0,i); + + // Make a lemma, storing interpolants + Iproof::node res = iproof->make_lemma(lits,itps); + +#if 0 + std::cout << "Lemma interps\n"; + for(unsigned i = 0; i < itps.size(); i++) + show(itps[i]); +#endif + + // Reset state for the next lemma + antes.clear(); + antes_added.clear(); + clear_localization(); // use a fresh localization for each lemma + + return res; + } + + // sanity check: make sure that any non-local lit is really resolved + // with something in the non_local_lits set + + void check_non_local(ast lit, non_local_lits *nll){ + if(nll) + for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ + ast con = (*it)->pivot; + if(con == mk_not(lit)) return; + } + assert(0 && "bug in non-local resolution handling"); + } + + + void get_local_conclusion_lits(ast proof, bool expect_clause, AstSet &lits){ + std::vector reslits; + if(expect_clause) + get_Z3_lits(conc(proof),reslits); + else reslits.push_back(conc(proof)); + for(unsigned i = 0; i < reslits.size(); i++) + if(is_local(reslits[i])) + lits.insert(reslits[i]); + AstSet &pfhyps = get_hyps(proof); + for(AstSet::iterator hit = pfhyps.begin(), hen = pfhyps.end(); hit != hen; ++hit) + if(is_local(*hit)) + lits.insert(mk_not(*hit)); + } + + + void collect_resolvent_lits(Z3_resolvent *res, const AstSet &hyps, std::vector &lits){ + if(!res->is_unit){ + std::vector reslits; + get_Z3_lits(conc(res->proof),reslits); + for(unsigned i = 0; i < reslits.size(); i++) + if(reslits[i] != res->pivot) + lits.push_back(reslits[i]); + } + AstSet &pfhyps = get_hyps(res->proof); + for(AstSet::iterator hit = pfhyps.begin(), hen = pfhyps.end(); hit != hen; ++hit) + if(hyps.find(*hit) == hyps.end()) + lits.push_back(mk_not(*hit)); + } + + void filter_resolvent_lits(non_local_lits *nll, std::vector &lits){ + std::vector orig_lits; orig_lits.swap(lits); + std::set pivs; + for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ + Z3_resolvent *res = *it; + pivs.insert(res->pivot); + pivs.insert(mk_not(res->pivot)); + } + for(unsigned i = 0; i < orig_lits.size(); i++) + if(pivs.find(orig_lits[i]) == pivs.end()) + lits.push_back(orig_lits[i]); + } + + void collect_all_resolvent_lits(non_local_lits *nll, std::vector &lits){ + if(nll){ + std::vector orig_lits; orig_lits.swap(lits); + std::set pivs; + for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ + Z3_resolvent *res = *it; + pivs.insert(res->pivot); + pivs.insert(mk_not(res->pivot)); + } + for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ + Z3_resolvent *res = *it; + { + std::vector reslits; + if(!res->is_unit) get_Z3_lits(conc(res->proof),reslits); + else reslits.push_back(conc(res->proof)); + for(unsigned i = 0; i < reslits.size(); i++) +#if 0 + if(reslits[i] != res->pivot && pivs.find(reslits[i]) == pivs.end()) +#endif + if(is_local(reslits[i])) + lits.push_back(reslits[i]); + } + } + for(unsigned i = 0; i < orig_lits.size(); i++) + if(pivs.find(orig_lits[i]) == pivs.end()) + lits.push_back(orig_lits[i]); + } + } + + void collect_proof_clause(ast proof, bool expect_clause, std::vector &lits){ + if(expect_clause) + get_Z3_lits(conc(proof),lits); + else + lits.push_back(from_ast(conc(proof))); + AstSet &hyps = get_hyps(proof); + for(AstSet::iterator hit = hyps.begin(), hen = hyps.end(); hit != hen; ++hit) + lits.push_back(mk_not(*hit)); + } + + + // turn a bunch of literals into a lemma, replacing + // non-local lits with their local equivalents + // adds the accumulated antecedents (antes) as + // proof obligations of the lemma + + Iproof::node fix_lemma(std::vector &con_lits, AstSet &hyps, non_local_lits *nll){ + std::vector lits(con_lits); + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) + lits.push_back(mk_not(*it)); + if(nll){ + for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ + Z3_resolvent *res = *it; + collect_resolvent_lits(res,hyps,lits); + add_antes(res->proof); + } + filter_resolvent_lits(nll,lits); + } + for(unsigned int i = 0; i < lits.size(); i++){ + EquivTab::iterator it = equivs.find(lits[i]); + if(it != equivs.end()){ + lits[i] = it->second.first; // replace with local equivalent + add_antes(it->second.second); // collect the premises that prove this + } + else { + if(!is_local(lits[i])){ + check_non_local(lits[i],nll); + lits[i] = mk_false(); + } + } + } + // TODO: should check here that derivation is local? + Iproof::node res = prove_lemma(lits); + return res; + } + + int num_lits(ast ast){ + opr dk = op(ast); + if(dk == False) + return 0; + if(dk == Or){ + unsigned nargs = num_args(ast); + int n = 0; + for(unsigned i = 0; i < nargs; i++) // do all the sub_terms + n += num_lits(arg(ast,i)); + return n; + } + else + return 1; + } + + struct non_lit_local_ante {}; + + bool local_antes_simple; + + bool add_local_antes(ast proof, AstSet &hyps, bool expect_clause = false){ + if(antes_added.find(proof) != antes_added.end()) return true; + antes_added.insert(proof); + ast con = from_ast(conc(proof)); + pfrule dk = pr(proof); + if(is_local(con) || equivs.find(con) != equivs.end()){ + if(!expect_clause || num_lits(conc(proof)) == 1){ + AstSet &this_hyps = get_hyps(proof); + if(std::includes(hyps.begin(),hyps.end(),this_hyps.begin(),this_hyps.end())){ + // if(hyps.find(con) == hyps.end()) + if(/* lemma_count == SHOW_LEMMA_COUNT - 1 && */ !is_literal_or_lit_iff(conc(proof))){ + std::cout << "\nnon-lit local ante\n"; + show_step(proof); + show(conc(proof)); + throw non_lit_local_ante(); + } + local_antes.push_back(proof); + return true; + } + else + ; //std::cout << "bar!\n"; + } + } + if(dk == PR_ASSERTED + //|| dk == PR_HYPOTHESIS + //|| dk == PR_TH_LEMMA + || dk == PR_QUANT_INST + //|| dk == PR_UNIT_RESOLUTION + //|| dk == PR_LEMMA + ) + return false; + if(dk == PR_HYPOTHESIS && hyps.find(con) != hyps.end()) + ; //std::cout << "blif!\n"; + if(dk == PR_HYPOTHESIS + || dk == PR_LEMMA) + ; //std::cout << "foo!\n"; + if(dk == PR_TH_LEMMA && num_prems(proof) == 0){ + // Check if this is an axiom instance + get_axiom_instance(conc(proof)); + } + + // #define SIMPLE_PROOFS +#ifdef SIMPLE_PROOFS + if(!(dk == PR_TRANSITIVITY + || dk == PR_MONOTONICITY)) + local_antes_simple = false; +#endif + + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + try { + if(!add_local_antes(arg, hyps, dk == PR_UNIT_RESOLUTION && i == 0)) + return false; + } + catch (non_lit_local_ante) { + std::cout << "\n"; + show_step(proof); + show(conc(proof)); + throw non_lit_local_ante(); + } + } + return true; + } + + std::vector lit_trace; + hash_set marked_proofs; + + bool proof_has_lit(ast proof, ast lit){ + AstSet &hyps = get_hyps(proof); + if(hyps.find(mk_not(lit)) != hyps.end()) + return true; + std::vector lits; + ast con = conc(proof); + get_Z3_lits(con, lits); + for(unsigned i = 0; i < lits.size(); i++) + if(lits[i] == lit) + return true; + return false; + } + + + void trace_lit_rec(ast lit, ast proof, AstHashSet &memo){ + if(memo.find(proof) == memo.end()){ + memo.insert(proof); + AstSet &hyps = get_hyps(proof); + std::vector lits; + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) + lits.push_back(mk_not(*it)); + ast con = conc(proof); + get_Z3_lits(con, lits); + for(unsigned i = 0; i < lits.size(); i++){ + if(lits[i] == lit){ + print_expr(std::cout,proof); + std::cout << "\n"; + marked_proofs.insert(proof); + pfrule dk = pr(proof); + if(dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA){ + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + trace_lit_rec(lit,arg,memo); + } + } + else + lit_trace.push_back(proof); + } + } + } + } + + ast traced_lit; + + int trace_lit(ast lit, ast proof){ + marked_proofs.clear(); + lit_trace.clear(); + traced_lit = lit; + AstHashSet memo; + trace_lit_rec(lit,proof,memo); + return lit_trace.size(); + } + + bool is_literal_or_lit_iff(ast lit){ + if(my_is_literal(lit)) return true; + if(op(lit) == Iff){ + return my_is_literal(arg(lit,0)) && my_is_literal(arg(lit,1)); + } + return false; + } + + bool my_is_literal(ast lit){ + ast abslit = is_not(lit) ? arg(lit,0) : lit; + int f = op(abslit); + return !(f == And || f == Or || f == Iff); + } + + void print_lit(ast lit){ + ast abslit = is_not(lit) ? arg(lit,0) : lit; + if(!is_literal_or_lit_iff(lit)){ + if(is_not(lit)) std::cout << "~"; + std::cout << "["; + print_expr(std::cout,abslit); + std::cout << "]"; + } + else + print_expr(std::cout,lit); + } + + void show_lit(ast lit){ + print_lit(lit); + std::cout << "\n"; + } + + void print_z3_lit(ast a){ + print_lit(from_ast(a)); + } + + void show_z3_lit(ast a){ + print_z3_lit(a); + std::cout << "\n"; + } + + + void show_con(ast proof, bool brief){ + if(!traced_lit.null() && proof_has_lit(proof,traced_lit)) + std::cout << "(*) "; + ast con = conc(proof); + AstSet &hyps = get_hyps(proof); + int count = 0; + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ + if(brief && ++count > 5){ + std::cout << "... "; + break; + } + print_lit(*it); + std::cout << " "; + } + std::cout << "|- "; + std::vector lits; + get_Z3_lits(con,lits); + for(unsigned i = 0; i < lits.size(); i++){ + print_lit(lits[i]); + std::cout << " "; + } + std::cout << "\n"; + } + + void show_step( ast proof){ + std::cout << "\n"; + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + std::cout << "(" << i << ") "; + ast arg = prem(proof,i); + show_con(arg,true); + } + std::cout << "|------ "; + std::cout << string_of_symbol(sym(proof)) << "\n"; + show_con(proof,false); + } + + void show_marked( ast proof){ + std::cout << "\n"; + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + if(!traced_lit.null() && proof_has_lit(arg,traced_lit)){ + std::cout << "(" << i << ") "; + show_con(arg,true); + } + } + } + + std::vector pfhist; + int pfhist_pos; + + void pfgoto(ast proof){ + if(pfhist.size() == 0) + pfhist_pos = 0; + else pfhist_pos++; + pfhist.resize(pfhist_pos); + pfhist.push_back(proof); + show_step(proof); + } + + void show_nll(non_local_lits *nll){ + if(!nll)return; + for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ + Z3_resolvent *res = *it; + show_step(res->proof); + std::cout << "Pivot: "; + show(res->pivot); + std::cout << std::endl; + } + } + + void pfback(){ + if(pfhist_pos > 0){ + pfhist_pos--; + show_step(pfhist[pfhist_pos]); + } + } + + void pffwd(){ + if(pfhist_pos < ((int)pfhist.size()) - 1){ + pfhist_pos++; + show_step(pfhist[pfhist_pos]); + } + } + + void pfprem(int i){ + if(pfhist.size() > 0){ + ast proof = pfhist[pfhist_pos]; + unsigned nprems = num_prems(proof); + if(i >= 0 && i < (int)nprems) + pfgoto(prem(proof,i)); + } + } + + int extract_th_lemma_common(std::vector &lits, non_local_lits *nll, bool lemma_nll = true){ + std::vector la = local_antes; + local_antes.clear(); // clear antecedents for next lemma + antes_added.clear(); + // std::vector lits; + AstSet hyps; // no hyps + for(unsigned i = 0; i < la.size(); i++) + lits.push_back(mk_not(from_ast(conc(la[i])))); + // lits.push_back(from_ast(conc(proof))); + Iproof::node res =fix_lemma(lits,hyps, lemma_nll ? nll : 0); + for(unsigned i = 0; i < la.size(); i++){ + Iproof::node q = translate_main(la[i],nll,false); + ast pnode = from_ast(conc(la[i])); + assert(is_local(pnode) || equivs.find(pnode) != equivs.end()); + Iproof::node neg = res; + Iproof::node pos = q; + if(is_not(pnode)){ + pnode = mk_not(pnode); + std::swap(neg,pos); + } + try { + res = iproof->make_resolution(pnode,neg,pos); + } + catch (const iz3proof::proof_error){ + std::cout << "\nresolution error in theory lemma\n"; + std::cout << "lits:\n"; + for(unsigned j = 0; j < lits.size(); j++) + show_lit(lits[j]); + std::cout << "\nstep:\n"; + show_step(la[i]); + throw invalid_lemma(); + } + } + return res; + } + + Iproof::node extract_simple_proof(ast proof, hash_set &leaves){ + if(leaves.find(proof) != leaves.end()) + return iproof->make_hypothesis(conc(proof)); + ast con = from_ast(conc(proof)); + pfrule dk = pr(proof); + unsigned nprems = num_prems(proof); + std::vector args(nprems); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + args[i] = extract_simple_proof(arg,leaves); + } + + switch(dk){ + case PR_TRANSITIVITY: + return iproof->make_transitivity(con,args[0],args[1]); + case PR_MONOTONICITY: + return iproof->make_congruence(con,args); + case PR_REFLEXIVITY: + return iproof->make_reflexivity(con); + case PR_SYMMETRY: + return iproof->make_symmetry(con,args[0]); + } + assert(0 && "extract_simple_proof: unknown op"); + return 0; + } + + int extract_th_lemma_simple(ast proof, std::vector &lits){ + std::vector la = local_antes; + local_antes.clear(); // clear antecedents for next lemma + antes_added.clear(); + + hash_set leaves; + for(unsigned i = 0; i < la.size(); i++) + leaves.insert(la[i]); + + Iproof::node ipf = extract_simple_proof(proof,leaves); + ast con = from_ast(conc(proof)); + Iproof::node hyp = iproof->make_hypothesis(mk_not(con)); + ipf = iproof->make_eqcontra(ipf,hyp); + + // std::vector lits; + AstSet hyps; // no hyps + for(unsigned i = 0; i < la.size(); i++) + lits.push_back(mk_not(from_ast(conc(la[i])))); + // lits.push_back(from_ast(conc(proof))); + + Iproof::node res = iproof->make_contra(ipf,lits); + + for(unsigned i = 0; i < la.size(); i++){ + Iproof::node q = translate_main(la[i],0,false); + ast pnode = from_ast(conc(la[i])); + assert(is_local(pnode) || equivs.find(pnode) != equivs.end()); + Iproof::node neg = res; + Iproof::node pos = q; + if(is_not(pnode)){ + pnode = mk_not(pnode); + std::swap(neg,pos); + } + try { + res = iproof->make_resolution(pnode,neg,pos); + } + catch (const iz3proof::proof_error){ + std::cout << "\nresolution error in theory lemma\n"; + std::cout << "lits:\n"; + for(unsigned j = 0; j < lits.size(); j++) + show_lit(lits[j]); + std::cout << "\nstep:\n"; + show_step(la[i]); + throw invalid_lemma(); + } + } + return res; + } + +// #define NEW_EXTRACT_TH_LEMMA + + void get_local_hyps(ast proof, std::set &res){ + std::set hyps = get_hyps(proof); + for(std::set::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ + ast hyp = *it; + if(is_local(hyp)) + res.insert(hyp); + } + } + + int extract_th_lemma(ast proof, std::vector &lits, non_local_lits *nll){ + pfrule dk = pr(proof); + unsigned nprems = num_prems(proof); +#ifdef NEW_EXTRACT_TH_LEMMA + if(nprems == 0 && !nll) +#else + if(nprems == 0) +#endif + return 0; + if(nprems == 0 && dk == PR_TH_LEMMA) + // Check if this is an axiom instance + get_axiom_instance(conc(proof)); + + local_antes_simple = true; + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + if(!add_local_antes(arg,get_hyps(proof))){ + local_antes.clear(); // clear antecedents for next lemma + antes_added.clear(); + antes.clear(); + return 0; + } + } +#ifdef NEW_EXTRACT_TH_LEMMA + bool lemma_nll = nargs > 1; + if(nll && !lemma_nll){ + lemma_nll = false; + // std::cout << "lemma count = " << nll_lemma_count << "\n"; + for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ + Z3_resolvent *res = *it; + ast arg = res->proof; + std::set loc_hyps; get_local_hyps(arg,loc_hyps); + if(!add_local_antes(arg,loc_hyps)){ + local_antes.clear(); // clear antecedents for next lemma + antes_added.clear(); + antes.clear(); + return 0; + } + } + collect_all_resolvent_lits(nll,lits); + } + int my_count = nll_lemma_count++; + int res; + try { + res = extract_th_lemma_common(lits,nll,lemma_nll); + } + catch (const invalid_lemma &) { + std::cout << "\n\nlemma: " << my_count; + std::cout << "\n\nproof node: \n"; + show_step(proof); + std::cout << "\n\nnon-local: \n"; + show_nll(nll); + pfgoto(nll->proofs[0]->proof); + show(conc(pfhist.back())); + pfprem(1); + show(conc(pfhist.back())); + pfprem(0); + show(conc(pfhist.back())); + pfprem(0); + show(conc(pfhist.back())); + pfprem(0); + show(conc(pfhist.back())); + std::cout << "\n\nliterals: \n"; + for(int i = 0; i < lits.size(); i++) + show_lit(lits[i]); + throw invalid_lemma(); + } + + return res; +#else +#ifdef SIMPLE_PROOFS + if(local_antes_simple && !nll) + return extract_th_lemma_simple(proof, lits); +#endif + return extract_th_lemma_common(lits,nll); +#endif + } + + int extract_th_lemma_ur(ast proof, int position, std::vector &lits, non_local_lits *nll){ + for(int i = 0; i <= position; i++){ + ast arg = prem(proof,i); + if(!add_local_antes(arg,get_hyps(proof),i==0)){ + local_antes.clear(); // clear antecedents for next lemma + antes_added.clear(); + antes.clear(); + return 0; + } + } + return extract_th_lemma_common(lits,nll); + } + + // see if any of the pushed resolvents are resolutions + // push the current proof into the latest such + int push_into_resolvent(ast proof, std::vector &lits, non_local_lits *nll, bool expect_clause){ + if(!nll) return 0; + if(num_args(proof) > 1) return 0; + ResolventAppSet resos = nll->proofs; + int pos = resos.size()-1; + for( ResolventAppSet::reverse_iterator it = resos.rbegin(), en = resos.rend(); it != en; ++it, --pos){ + Z3_resolvent *reso = *it; + ast ante = reso->proof; + ast pivot = reso->pivot; + bool is_unit = reso->is_unit; + pfrule dk = pr(ante); + bool pushable = dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA; + if(!pushable && num_args(ante) > 1){ +#if 0 + if (!is_local(conc(ante))) + std::cout << "non-local "; + std::cout << "pushable!\n"; +#endif + pushable = true; + } + if(pushable){ + // remove the resolvent from list and add new clause as resolvent + resos.erase((++it).base()); + for(; pos < (int)resos.size(); pos++){ + Z3_resolvent *r = resos[pos]; + resos[pos] = find_resolvent(r->proof,r->is_unit,mk_not(pivot)); + } + resos.push_back(find_resolvent(proof,!expect_clause,mk_not(pivot))); + non_local_lits *new_nll = find_nll(resos); + try { + int res = translate_main(ante,new_nll,!is_unit); + return res; + } + catch (const invalid_lemma &) { + std::cout << "\n\npushing: \n"; + std::cout << "nproof node: \n"; + show_step(proof); + std::cout << "\n\nold non-local: \n"; + show_nll(nll); + std::cout << "\n\nnew non-local: \n"; + show_nll(new_nll); + throw invalid_lemma(); + } + } + } + return 0; // no pushed resolvents are resolution steps + } + + non_local_lits *find_nll(ResolventAppSet &proofs){ + if(proofs.empty()) + return (non_local_lits *)0; + std::pair foo(non_local_lits(proofs),(non_local_lits *)0); + std::pair::iterator,bool> bar = + non_local_lits_unique.insert(foo); + non_local_lits *&res = bar.first->second; + if(bar.second) + res = new non_local_lits(bar.first->first); + return res; + } + + Z3_resolvent *find_resolvent(ast proof, bool unit, ast pivot){ + std::pair foo(Z3_resolvent(proof,unit,pivot),(Z3_resolvent *)0); + std::pair::iterator,bool> bar = + Z3_resolvent_unique.insert(foo); + Z3_resolvent *&res = bar.first->second; + if(bar.second) + res = new Z3_resolvent(bar.first->first); + return res; + } + + // translate a unit resolution at position pos of given app + int translate_ur(ast proof, int position, non_local_lits *nll){ + ast ante = prem(proof,position); + if(position <= 0) + return translate_main(ante, nll); + ast pnode = conc(ante); + ast pnode_abs = !is_not(pnode) ? pnode : mk_not(pnode); + if(is_local(pnode) || equivs.find(pnode) != equivs.end()){ + Iproof::node neg = translate_ur(proof,position-1,nll); + Iproof::node pos = translate_main(ante, nll, false); + if(is_not(pnode)){ + pnode = mk_not(pnode); + std::swap(neg,pos); + } + try { + return iproof->make_resolution(pnode,neg,pos); + } + catch (const iz3proof::proof_error){ + std::cout << "resolution error in unit_resolution, position" << position << "\n"; + show_step(proof); + throw invalid_lemma(); + } + } + else { + // non-local pivot we have no local equivalent for + if(true){ + // try pushing the non-local resolution up + pfrule dk = pr(ante); + non_local_lits *old_nll = nll; + if(dk == PR_HYPOTHESIS) + ; //std::cout << "non-local hyp!\n"; // resolving with a hyp is a no-op + else { + ResolventAppSet new_proofs; + if(nll) new_proofs = nll->proofs; + Z3_resolvent *reso = find_resolvent(ante,true,pnode); + new_proofs.push_back(reso); + nll = find_nll(new_proofs); + } + try { + return translate_ur(proof,position-1,nll); + } + catch (const invalid_lemma &) { + if(old_nll != nll){ + std::cout << "\n\nadded_nll: \n"; + std::cout << "nproof node: \n"; + show_step(proof); + std::cout << "\n\new non-local step: \n"; + show_step(nll->proofs.back()->proof); + } + throw invalid_lemma(); + } + + } + else { + // just make a lemma + std::vector lits; + do_unit_resolution(proof,position,lits); + int res; + if(!(res = extract_th_lemma_ur(proof,position,lits,nll))){ + for(int i = 0; i <= position; i++){ + z3pf p = prem(proof,i); + add_antes(p); + } + res = fix_lemma(lits,get_hyps(proof),nll); + } + return res; + } + } + } + + non_local_lits *update_nll(ast proof, bool expect_clause, non_local_lits *nll){ + std::vector lits; + collect_proof_clause(proof,expect_clause,lits); + AstSet litset; + litset.insert(lits.begin(),lits.end()); + ResolventAppSet to_keep; + for(int i = nll->proofs.size()-1; i >= 0; --i){ + ast traced_lit = (nll->proofs[i])->pivot; + ast traced_lit_neg = mk_not(traced_lit); + if(litset.find(traced_lit) != litset.end() || litset.find(traced_lit_neg) != litset.end()){ + to_keep.push_back(nll->proofs[i]); + std::vector reslits; + AstSet dummy; + collect_resolvent_lits(nll->proofs[i],dummy,reslits); + litset.insert(reslits.begin(),reslits.end()); + } + } + if(to_keep.size() == nll->proofs.size()) return nll; + ResolventAppSet new_proofs; + for(int i = to_keep.size() - 1; i >= 0; --i) + new_proofs.push_back(to_keep[i]); + return find_nll(new_proofs); + } + + // translate a Z3 proof term into a foci proof term + + Iproof::node translate_main(ast proof, non_local_lits *nll, bool expect_clause = true){ + non_local_lits *old_nll = nll; + if(nll) nll = update_nll(proof,expect_clause,nll); + AstToIpf &tr = nll ? non_local_translation[nll] : translation; + hash_map &trc = expect_clause ? tr.first : tr.second; + std::pair foo(proof,INT_MAX); + std::pair bar = trc.insert(foo); + int &res = bar.first->second; + if(!bar.second) return res; + + + try { + int frame = get_locality(proof); + if(frame != -1){ + ast e = from_ast(conc(proof)); + if(frame >= frames) frame = frames - 1; + std::vector foo; + if(expect_clause) + get_Z3_lits(conc(proof),foo); + else + foo.push_back(e); + AstSet &hyps = get_hyps(proof); + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) + foo.push_back(mk_not(*it)); + res = iproof->make_assumption(frame,foo); + return res; + } + + pfrule dk = pr(proof); + unsigned nprems = num_prems(proof); + if(dk == PR_UNIT_RESOLUTION){ + res = translate_ur(proof, nprems - 1, nll); + } + else if(dk == PR_LEMMA){ + ast contra = prem(proof,0); // this is a proof of false from some hyps + res = translate_main(contra, nll); + if(!expect_clause){ + std::vector foo; // the negations of the hyps form a clause + foo.push_back(from_ast(conc(proof))); + AstSet &hyps = get_hyps(proof); + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) + foo.push_back(mk_not(*it)); + res = iproof->make_contra(res,foo); + } + } + else { + std::vector lits; + ast con = conc(proof); + if(expect_clause) + get_Z3_lits(con, lits); + else + lits.push_back(from_ast(con)); +#ifdef NEW_EXTRACT_TH_LEMMA + if(!(res = push_into_resolvent(proof,lits,nll,expect_clause))){ + if(!(res = extract_th_lemma(proof,lits,nll))){ +#else + if(!(res = extract_th_lemma(proof,lits,nll))){ + if(!(res = push_into_resolvent(proof,lits,nll,expect_clause))){ +#endif + std::cout << "extract theory lemma failed\n"; + add_antes(proof); + res = fix_lemma(lits,get_hyps(proof),nll); + } + } + } +#ifdef CHECK_PROOFS + + if(0){ + AstSet zpf_con_lits, ipf_con_lits; + get_local_conclusion_lits(proof, expect_clause, zpf_con_lits); + if(nll){ + for(unsigned i = 0; i < nll->proofs.size(); i++) + get_local_conclusion_lits(nll->proofs[i]->proof,!nll->proofs[i]->is_unit,zpf_con_lits); + } + std::vector ipf_con; + iproof->get_conclusion(res,ipf_con); + for(unsigned i = 0; i < ipf_con.size(); i++) + ipf_con_lits.insert(ipf_con[i]); + if(!(ipf_con_lits == zpf_con_lits)){ + std::cout << "proof error:\n"; + std::cout << "expected lits:\n"; + for(AstSet::iterator hit = zpf_con_lits.begin(), hen = zpf_con_lits.end(); hit != hen; ++hit) + show_lit(*hit); + std::cout << "got lits:\n"; + for(AstSet::iterator hit = ipf_con_lits.begin(), hen = ipf_con_lits.end(); hit != hen; ++hit) + show_lit(*hit); + std::cout << "\nproof step:"; + show_step(proof); + std::cout << "\n"; + throw invalid_lemma(); + } + } +#endif + + return res; + } + + catch (const invalid_lemma &) { + if(old_nll != nll){ + std::cout << "\n\nupdated nll: \n"; + std::cout << "nproof node: \n"; + show_step(proof); + std::cout << "\n\new non-local: \n"; + show_nll(nll); + } + throw invalid_lemma(); + } + + } + + // Proof translation is in two stages: + // 1) Translate ast proof term to Zproof + // 2) Translate Zproof to Iproof + + Iproof::node translate(ast proof, Iproof &dst){ + iproof = &dst; + Iproof::node Ipf = translate_main(proof,0); // builds result in dst + return Ipf; + } + + iz3translation_direct(iz3mgr &mgr, + iz3secondary *_secondary, + const std::vector &cnsts, + const std::vector &parents, + const std::vector &theory) + : iz3translation(mgr, cnsts, parents, theory) + { + secondary = _secondary; + for(unsigned i = 0; i < cnsts.size(); i++) + frame_map[cnsts[i]] = i; + for(unsigned i = 0; i < theory.size(); i++) + frame_map[theory[i]] = INT_MAX; + frames = cnsts.size(); + traced_lit = 0; + } +}; + + + + +#ifdef IZ3_TRANSLATE_DIRECT + +iz3translation *iz3translation::create(iz3mgr &mgr, + iz3secondary *secondary, + const std::vector &cnsts, + const std::vector &parents, + const std::vector &theory){ + return new iz3translation_direct(mgr,secondary,cnsts,parents,theory); +} + + +#if 0 + +void iz3translation_direct_trace_lit(iz3translation_direct *p, ast lit, ast proof){ + p->trace_lit(lit, proof); +} + +void iz3translation_direct_show_step(iz3translation_direct *p, ast proof){ + p->show_step(proof); +} + +void iz3translation_direct_show_marked(iz3translation_direct *p, ast proof){ + p->show_marked(proof); +} + +void iz3translation_direct_show_lit(iz3translation_direct *p, ast lit){ + p->show_lit(lit); +} + +void iz3translation_direct_show_z3_lit(iz3translation_direct *p, ast a){ + p->show_z3_lit(a); +} + +void iz3translation_direct_pfgoto(iz3translation_direct *p, ast proof){ + p->pfgoto(proof); +} + +void iz3translation_direct_show_nll(iz3translation_direct *p, non_local_lits *nll){ + p->show_nll(nll); +} + +void iz3translation_direct_pfback(iz3translation_direct *p ){ + p->pfback(); +} + +void iz3translation_direct_pffwd(iz3translation_direct *p ){ + p->pffwd(); +} + +void iz3translation_direct_pfprem(iz3translation_direct *p, int i){ + p->pfprem(i); +} + + +struct stdio_fixer { + stdio_fixer(){ + std::cout.rdbuf()->pubsetbuf(0,0); + } + +} my_stdio_fixer; + +#endif + +#endif + + From 12d2d3beef80c5f214c419dd5a66b13161da70f9 Mon Sep 17 00:00:00 2001 From: Kenneth McMillan Date: Mon, 4 Mar 2013 19:53:46 -0800 Subject: [PATCH 004/509] minor fixes for OSX --- src/interp/iz3foci.cpp | 6 +++--- src/interp/iz3mgr.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp index 31d8b56c7..0317a9834 100755 --- a/src/interp/iz3foci.cpp +++ b/src/interp/iz3foci.cpp @@ -73,9 +73,9 @@ public: // create a symbol corresponding to a DeBruijn index of a particular type // the type has to be encoded into the name because the same index can // occur with different types - foci2::symb make_deBruijn_symbol(int index, int type){ + foci2::symb make_deBruijn_symbol(int index, type ty){ std::ostringstream s; - s << "#" << index << "#" << type; + // s << "#" << index << "#" << type; return foci->mk_func(s.str()); } @@ -181,7 +181,7 @@ public: case Variable: { // a deBruijn index int index = get_variable_index_value(t); type ty = get_type(t); - foci2::symb symbol = make_deBruijn_symbol(index,(int)(ty)); + foci2::symb symbol = make_deBruijn_symbol(index,ty); res = foci->mk_app(symbol,std::vector()); } default: diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index b1f07f5ce..83ec1578a 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -264,8 +264,8 @@ void iz3mgr::pretty_print(std::ostream &f, const std::string &s){ col = indent; continue; } - unsigned paren = s.find('(',pos); - if(paren != std::string::npos){ + int paren = s.find('(',pos); + if(paren != (int)std::string::npos){ int chars = paren - pos + 1; f << s.substr(pos,chars); indent += pretty_indent_chars; From d66211c0078424af64b79c05c69dd4e7db4f99a3 Mon Sep 17 00:00:00 2001 From: Kenneth McMillan Date: Mon, 4 Mar 2013 23:48:01 -0800 Subject: [PATCH 005/509] working on interpolation API --- src/api/api_interp.cpp | 10 +++++----- src/api/z3_api.h | 27 +++++++++++++-------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 2768e97e3..d32bf8b4e 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -54,8 +54,8 @@ extern "C" { Z3_ast proof, int num, Z3_ast *cnsts, - int *parents, - Z3_interpolation_options options, + unsigned *parents, + Z3_params options, Z3_ast *interps, int num_theory, Z3_ast *theory @@ -92,7 +92,7 @@ extern "C" { pre_parents_vec, interpolants, theory_vec, - (interpolation_options) options); + 0); // ignore params for now FIXME // copy result back for(unsigned i = 0; i < interpolants.size(); i++) @@ -104,8 +104,8 @@ extern "C" { Z3_lbool Z3_interpolate(Z3_context ctx, int num, Z3_ast *cnsts, - int *parents, - Z3_interpolation_options options, + unsigned *parents, + Z3_params options, Z3_ast *interps, Z3_model *model, Z3_literals *labels, diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 12db470fd..975b45940 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7750,24 +7750,23 @@ END_MLAPI_EXCLUDE are considered to have global scope (i.e., may appear in any interpolant formula). - def_API('Z3_interpolate', LBOOL, (_in(CONTEXT),), (_in(INT),), (_in_ptr(AST),), - (_in_ptr(INT),), (_in(PARAMS),), (_in_ptr(AST),), - (_out(MODEL),), (_out(LITERALS),), (_in(BOOL),), (_in(INT),), (_in_ptr(AST,0),)) + def_API('Z3_interpolate', INT, (_in(CONTEXT), _in(INT), _in_array(1,AST), _in_array(1,UINT), _in(PARAMS), _out_array(1,AST), _out(MODEL), _out(LITERALS), _in(BOOL), _in(INT), _in_array(9,AST),)) + */ - Z3_lbool Z3_API Z3_interpolate(Z3_context ctx, - int num, - Z3_ast *cnsts, - int *parents, - Z3_interpolation_options options, - Z3_ast *interps, - Z3_model *model = 0, - Z3_literals *labels = 0, - bool incremental = false, - int num_theory = 0, - Z3_ast *theory = 0); + Z3_lbool Z3_API Z3_interpolate(__in Z3_context ctx, + __in int num, + __in_ecount(num) Z3_ast *cnsts, + __in_ecount(num) unsigned *parents, + __in Z3_params options, + __out_ecount(num-1) Z3_ast *interps, + __out Z3_model *model = 0, + __out Z3_literals *labels = 0, + __in bool incremental = false, + __in int num_theory = 0, + __in_ecount(num_theory) Z3_ast *theory = 0); /** Return a string summarizing cumulative time used for interpolation. This string is purely for entertainment purposes From ae9276ad9b5fa5525013ce67f4ae73ae56a9df83 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 5 Mar 2013 21:56:09 -0800 Subject: [PATCH 006/509] more work on interpolation --- examples/interp/iz3.cpp | 446 +++++++++++++++++++++++++++++ scripts/mk_project.py | 8 +- scripts/mk_util.py | 23 +- src/api/api_interp.cpp | 249 +++++++++++++++- src/api/z3_api.h | 55 +++- src/interp/foci2stub/foci2.cpp | 25 ++ src/interp/{ => foci2stub}/foci2.h | 0 src/interp/iz3interp.cpp | 2 +- src/interp/iz3interp.h | 2 +- src/interp/iz3mgr.cpp | 49 +++- src/interp/iz3mgr.h | 2 +- src/interp/iz3profiling.cpp | 138 +++++++++ 12 files changed, 978 insertions(+), 21 deletions(-) create mode 100755 examples/interp/iz3.cpp create mode 100644 src/interp/foci2stub/foci2.cpp rename src/interp/{ => foci2stub}/foci2.h (100%) create mode 100755 src/interp/iz3profiling.cpp diff --git a/examples/interp/iz3.cpp b/examples/interp/iz3.cpp new file mode 100755 index 000000000..99a538549 --- /dev/null +++ b/examples/interp/iz3.cpp @@ -0,0 +1,446 @@ +#include +#include +#include +#include +#include +#include "z3.h" + + + +int usage(const char **argv){ + std::cerr << "usage: " << argv[0] << " [options] file.smt" << std::endl; + std::cerr << std::endl; + std::cerr << "options:" << std::endl; + std::cerr << " -t,--tree tree interpolation" << std::endl; + std::cerr << " -c,--check check result" << std::endl; + std::cerr << " -p,--profile profile execution" << std::endl; + std::cerr << " -w,--weak weak interpolants" << std::endl; + std::cerr << " -f,--flat ouput flat formulas" << std::endl; + std::cerr << " -o ouput to SMT-LIB file" << std::endl; + std::cerr << " -a,--anon anonymize" << std::endl; + std::cerr << " -s,--simple simple proof mode" << std::endl; + std::cerr << std::endl; + return 1; +} + +int main(int argc, const char **argv) { + + bool tree_mode = false; + bool check_mode = false; + bool profile_mode = false; + bool incremental_mode = false; + std::string output_file; + bool flat_mode = false; + bool anonymize = false; + + Z3_config cfg = Z3_mk_config(); + // Z3_interpolation_options options = Z3_mk_interpolation_options(); + Z3_params options = 0; + + /* Parse the command line */ + int argn = 1; + while(argn < argc-1){ + std::string flag = argv[argn]; + if(flag[0] == '-'){ + if(flag == "-t" || flag == "--tree") + tree_mode = true; + else if(flag == "-c" || flag == "--check") + check_mode = true; + else if(flag == "-p" || flag == "--profile") + profile_mode = true; +#if 0 + else if(flag == "-w" || flag == "--weak") + Z3_set_interpolation_option(options,"weak","1"); + else if(flag == "--secondary") + Z3_set_interpolation_option(options,"secondary","1"); +#endif + else if(flag == "-i" || flag == "--incremental") + incremental_mode = true; + else if(flag == "-o"){ + argn++; + if(argn >= argc) return usage(argv); + output_file = argv[argn]; + } + else if(flag == "-f" || flag == "--flat") + flat_mode = true; + else if(flag == "-a" || flag == "--anon") + anonymize = true; + else if(flag == "-s" || flag == "--simple") + Z3_set_param_value(cfg,"PREPROCESS","false"); + else + return usage(argv); + } + argn++; + } + if(argn != argc-1) + return usage(argv); + const char *filename = argv[argn]; + + + /* Create a Z3 context to contain formulas */ + Z3_context ctx = Z3_mk_interpolation_context(cfg); + + if(!flat_mode) + Z3_set_ast_print_mode(ctx,Z3_PRINT_SMTLIB_COMPLIANT); + + /* Read an interpolation problem */ + + int num; + Z3_ast *constraints; + int *parents = 0; + const char *error; + bool ok; + int num_theory; + Z3_ast *theory; + + ok = Z3_read_interpolation_problem(ctx, &num, &constraints, tree_mode ? &parents : 0, filename, &error, &num_theory, &theory); + + /* If parse failed, print the error message */ + + if(!ok){ + std::cerr << error << "\n"; + return 1; + } + + /* if we get only one formula, and it is a conjunction, split it into conjuncts. */ + if(!tree_mode && num == 1){ + Z3_app app = Z3_to_app(ctx,constraints[0]); + Z3_func_decl func = Z3_get_app_decl(ctx,app); + Z3_decl_kind dk = Z3_get_decl_kind(ctx,func); + if(dk == Z3_OP_AND){ + int nconjs = Z3_get_app_num_args(ctx,app); + if(nconjs > 1){ + std::cout << "Splitting formula into " << nconjs << " conjuncts...\n"; + num = nconjs; + constraints = new Z3_ast[num]; + for(int k = 0; k < num; k++) + constraints[k] = Z3_get_app_arg(ctx,app,k); + } + } + } + +#if 0 + /* Write out anonymized version. */ + + if(anonymize){ + Z3_anonymize_ast_vector(ctx,num,constraints); + std::string ofn = output_file.empty() ? "anon.smt" : output_file; + Z3_write_interpolation_problem(ctx, num, constraints, parents, ofn.c_str()); + std::cout << "anonymized problem written to " << ofn << "\n"; + exit(0); + } +#endif + + /* Compute an interpolant, or get a model. */ + + Z3_ast *interpolants = (Z3_ast *)malloc((num-1) * sizeof(Z3_ast)); + Z3_model model = 0; + Z3_lbool result; + + if(!incremental_mode){ + /* In non-incremental mode, we just pass the constraints. */ + result = Z3_interpolate(ctx, num, constraints, (unsigned int *)parents, options, interpolants, &model, 0, false, num_theory, theory); + } + else { + + /* This is a somewhat useless demonstration of incremental mode. + Here, we assert the constraints in the context, then pass them to + iZ3 in an array, so iZ3 knows the sequence. Note it's safe to pass + "true", even though we haven't techically asserted if. */ + + Z3_push(ctx); + std::vector asserted(num); + + /* We start with nothing asserted. */ + for(int i = 0; i < num; i++) + asserted[i] = Z3_mk_true(ctx); + + /* Now we assert the constrints one at a time until UNSAT. */ + + for(int i = 0; i < num; i++){ + asserted[i] = constraints[i]; + Z3_assert_cnstr(ctx,constraints[i]); // assert one constraint + result = Z3_interpolate(ctx, num, &asserted[0], (unsigned int *)parents, options, interpolants, &model, 0, true, 0, 0); + if(result == Z3_L_FALSE){ + for(unsigned j = 0; j < num-1; j++) + /* Since we want the interpolant formulas to survive a "pop", we + "persist" them here. */ + Z3_persist_ast(ctx,interpolants[j],1); + break; + } + } + Z3_pop(ctx,1); + } + + switch (result) { + + /* If UNSAT, print the interpolants */ + case Z3_L_FALSE: + printf("unsat\n"); + if(output_file.empty()){ + printf("interpolant:\n"); + for(int i = 0; i < num-1; i++) + printf("%s\n", Z3_ast_to_string(ctx, interpolants[i])); + } + else { +#if 0 + Z3_write_interpolation_problem(ctx,num-1,interpolants,0,output_file.c_str()); + printf("interpolant written to %s\n",output_file.c_str()); +#endif + } +#if 0 + if(check_mode){ + std::cout << "Checking interpolant...\n"; + bool chk; + chk = Z3_check_interpolant(ctx,num,constraints,parents,interpolants,&error,num_theory,theory); + if(chk) + std::cout << "Interpolant is correct\n"; + else { + std::cout << "Interpolant is incorrect\n"; + std::cout << error; + return 1; + } + } +#endif + break; + case Z3_L_UNDEF: + printf("fail\n"); + break; + case Z3_L_TRUE: + printf("sat\n"); + printf("model:\n%s\n", Z3_model_to_string(ctx, model)); + break; + } + + std::cout << Z3_interpolation_profile(ctx); + + /* Delete the model if there is one */ + + if (model) + Z3_del_model(ctx, model); + + /* Delete logical context. */ + + Z3_del_context(ctx); + free(interpolants); + + return 0; +} + + +#if 0 + + + +int test(){ + int i; + + /* Create a Z3 context to contain formulas */ + + Z3_config cfg = Z3_mk_config(); + Z3_context ctx = iz3_mk_context(cfg); + + int num = 2; + + Z3_ast *constraints = (Z3_ast *)malloc(num * sizeof(Z3_ast)); + +#if 1 + Z3_sort arr = Z3_mk_array_sort(ctx,Z3_mk_int_sort(ctx),Z3_mk_bool_sort(ctx)); + Z3_symbol as = Z3_mk_string_symbol(ctx, "a"); + Z3_symbol bs = Z3_mk_string_symbol(ctx, "b"); + Z3_symbol xs = Z3_mk_string_symbol(ctx, "x"); + + Z3_ast a = Z3_mk_const(ctx,as,arr); + Z3_ast b = Z3_mk_const(ctx,bs,arr); + Z3_ast x = Z3_mk_const(ctx,xs,Z3_mk_int_sort(ctx)); + + Z3_ast c1 = Z3_mk_eq(ctx,a,Z3_mk_store(ctx,b,x,Z3_mk_true(ctx))); + Z3_ast c2 = Z3_mk_not(ctx,Z3_mk_select(ctx,a,x)); +#else + Z3_symbol xs = Z3_mk_string_symbol(ctx, "x"); + Z3_ast x = Z3_mk_const(ctx,xs,Z3_mk_bool_sort(ctx)); + Z3_ast c1 = Z3_mk_eq(ctx,x,Z3_mk_true(ctx)); + Z3_ast c2 = Z3_mk_eq(ctx,x,Z3_mk_false(ctx)); + +#endif + + constraints[0] = c1; + constraints[1] = c2; + + /* print out the result for grins. */ + + // Z3_string smtout = Z3_benchmark_to_smtlib_string (ctx, "foo", "QFLIA", "sat", "", num, constraints, Z3_mk_true(ctx)); + + // Z3_string smtout = Z3_ast_to_string(ctx,constraints[0]); + // Z3_string smtout = Z3_context_to_string(ctx); + // puts(smtout); + + iz3_print(ctx,num,constraints,"iZ3temp.smt"); + + /* Make room for interpolants. */ + + Z3_ast *interpolants = (Z3_ast *)malloc((num-1) * sizeof(Z3_ast)); + + /* Make room for the model. */ + + Z3_model model = 0; + + /* Call the prover */ + + Z3_lbool result = iz3_interpolate(ctx, num, constraints, interpolants, &model); + + switch (result) { + + /* If UNSAT, print the interpolants */ + case Z3_L_FALSE: + printf("unsat, interpolants:\n"); + for(i = 0; i < num-1; i++) + printf("%s\n", Z3_ast_to_string(ctx, interpolants[i])); + break; + case Z3_L_UNDEF: + printf("fail\n"); + break; + case Z3_L_TRUE: + printf("sat\n"); + printf("model:\n%s\n", Z3_model_to_string(ctx, model)); + break; + } + + /* Delete the model if there is one */ + + if (model) + Z3_del_model(ctx, model); + + /* Delete logical context (note, we call iz3_del_context, not + Z3_del_context */ + + iz3_del_context(ctx); + + return 1; +} + +struct z3_error { + Z3_error_code c; + z3_error(Z3_error_code _c) : c(_c) {} +}; + +extern "C" { + static void throw_z3_error(Z3_error_code c){ + throw z3_error(c); + } +} + +int main(int argc, const char **argv) { + + /* Create a Z3 context to contain formulas */ + + Z3_config cfg = Z3_mk_config(); + Z3_context ctx = iz3_mk_context(cfg); + Z3_set_error_handler(ctx, throw_z3_error); + + /* Make some constraints, by parsing an smtlib formatted file given as arg 1 */ + + try { + Z3_parse_smtlib_file(ctx, argv[1], 0, 0, 0, 0, 0, 0); + } + catch(const z3_error &err){ + std::cerr << "Z3 error: " << Z3_get_error_msg(err.c) << "\n"; + std::cerr << Z3_get_smtlib_error(ctx) << "\n"; + return(1); + } + + /* Get the constraints from the parser. */ + + int num = Z3_get_smtlib_num_formulas(ctx); + + if(num == 0){ + std::cerr << "iZ3 error: File contains no formulas.\n"; + return 1; + } + + + Z3_ast *constraints = (Z3_ast *)malloc(num * sizeof(Z3_ast)); + + int i; + for (i = 0; i < num; i++) + constraints[i] = Z3_get_smtlib_formula(ctx, i); + + /* if we get only one formula, and it is a conjunction, split it into conjuncts. */ + if(num == 1){ + Z3_app app = Z3_to_app(ctx,constraints[0]); + Z3_func_decl func = Z3_get_app_decl(ctx,app); + Z3_decl_kind dk = Z3_get_decl_kind(ctx,func); + if(dk == Z3_OP_AND){ + int nconjs = Z3_get_app_num_args(ctx,app); + if(nconjs > 1){ + std::cout << "Splitting formula into " << nconjs << " conjuncts...\n"; + num = nconjs; + constraints = new Z3_ast[num]; + for(int k = 0; k < num; k++) + constraints[k] = Z3_get_app_arg(ctx,app,k); + } + } + } + + + /* print out the result for grins. */ + + // Z3_string smtout = Z3_benchmark_to_smtlib_string (ctx, "foo", "QFLIA", "sat", "", num, constraints, Z3_mk_true(ctx)); + + // Z3_string smtout = Z3_ast_to_string(ctx,constraints[0]); + // Z3_string smtout = Z3_context_to_string(ctx); + // puts(smtout); + + // iz3_print(ctx,num,constraints,"iZ3temp.smt"); + + /* Make room for interpolants. */ + + Z3_ast *interpolants = (Z3_ast *)malloc((num-1) * sizeof(Z3_ast)); + + /* Make room for the model. */ + + Z3_model model = 0; + + /* Call the prover */ + + Z3_lbool result = iz3_interpolate(ctx, num, constraints, interpolants, &model); + + switch (result) { + + /* If UNSAT, print the interpolants */ + case Z3_L_FALSE: + printf("unsat, interpolants:\n"); + for(i = 0; i < num-1; i++) + printf("%s\n", Z3_ast_to_string(ctx, interpolants[i])); + std::cout << "Checking interpolants...\n"; + const char *error; + if(iZ3_check_interpolant(ctx, num, constraints, 0, interpolants, &error)) + std::cout << "Interpolant is correct\n"; + else { + std::cout << "Interpolant is incorrect\n"; + std::cout << error << "\n"; + } + break; + case Z3_L_UNDEF: + printf("fail\n"); + break; + case Z3_L_TRUE: + printf("sat\n"); + printf("model:\n%s\n", Z3_model_to_string(ctx, model)); + break; + } + + /* Delete the model if there is one */ + + if (model) + Z3_del_model(ctx, model); + + /* Delete logical context (note, we call iz3_del_context, not + Z3_del_context */ + + iz3_del_context(ctx); + + return 0; +} + +#endif diff --git a/scripts/mk_project.py b/scripts/mk_project.py index e4c788cea..f5156ede6 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -53,13 +53,16 @@ def init_project_def(): add_lib('fpa', ['core_tactics', 'bv_tactics', 'sat_tactic'], 'tactic/fpa') add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') - # TODO: split muz_qe into muz, qe. Perhaps, we should also consider breaking muz into muz and pdr. + # TODO: split muz_qe inbto muz, qe. Perhaps, we should also consider breaking muz into muz and pdr. add_lib('muz_qe', ['smt', 'sat', 'smt2parser']) add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'muz_qe'], 'tactic/smtlogics') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') - add_lib('interp', ['solver']) + add_dll('foci2', ['util'], 'interp/foci2stub', + dll_name='foci2', + export_files=['foci2stub.cpp']) + add_lib('interp', ['solver','foci2']) API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h'] add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure', 'interp'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) @@ -76,6 +79,7 @@ def init_project_def(): set_z3py_dir('api/python') # Examples add_cpp_example('cpp_example', 'c++') + add_cpp_example('iz3', 'interp') add_c_example('c_example', 'c') add_c_example('maxsat') add_dotnet_example('dotnet_example', 'dotnet') diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 09aada950..b0c7f7f58 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -29,6 +29,7 @@ CXX=getenv("CXX", None) CC=getenv("CC", None) CPPFLAGS=getenv("CPPFLAGS", "") CXXFLAGS=getenv("CXXFLAGS", "") +EXAMP_DEBUG_FLAG='' LDFLAGS=getenv("LDFLAGS", "") JNI_HOME=getenv("JNI_HOME", None) @@ -665,6 +666,10 @@ class Component: self.src_dir = os.path.join(SRC_DIR, path) self.to_src_dir = os.path.join(REV_BUILD_DIR, self.src_dir) + def get_link_name(self): + return os.path.join(self.build_dir, self.name) + '$(LIB_EXT)' + + # Find fname in the include paths for the given component. # ownerfile is only used for creating error messages. # That is, we were looking for fname when processing ownerfile @@ -880,7 +885,7 @@ class ExeComponent(Component): out.write(obj) for dep in deps: c_dep = get_component(dep) - out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name)) + out.write(' ' + c_dep.get_link_name()) out.write('\n') out.write('\t$(LINK) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % exefile) for obj in objs: @@ -888,7 +893,7 @@ class ExeComponent(Component): out.write(obj) for dep in deps: c_dep = get_component(dep) - out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name)) + out.write(' ' + c_dep.get_link_name()) out.write(' $(LINK_EXTRA_FLAGS)\n') out.write('%s: %s\n\n' % (self.name, exefile)) @@ -954,6 +959,12 @@ class DLLComponent(Component): self.install = install self.static = static + def get_link_name(self): + if self.static: + return os.path.join(self.build_dir, self.name) + '$(LIB_EXT)' + else: + return self.name + '$(SO_EXT)' + def mk_makefile(self, out): Component.mk_makefile(self, out) # generate rule for (SO_EXT) @@ -976,7 +987,7 @@ class DLLComponent(Component): for dep in deps: if not dep in self.reexports: c_dep = get_component(dep) - out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name)) + out.write(' ' + c_dep.get_link_name()) out.write('\n') out.write('\t$(LINK) $(SLINK_OUT_FLAG)%s $(SLINK_FLAGS)' % dllfile) for obj in objs: @@ -985,7 +996,7 @@ class DLLComponent(Component): for dep in deps: if not dep in self.reexports: c_dep = get_component(dep) - out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name)) + out.write(' ' + c_dep.get_link_name()) out.write(' $(SLINK_EXTRA_FLAGS)') if IS_WINDOWS: out.write(' /DEF:%s.def' % os.path.join(self.to_src_dir, self.name)) @@ -1228,7 +1239,7 @@ class CppExampleComponent(ExampleComponent): out.write(' ') out.write(os.path.join(self.to_ex_dir, cppfile)) out.write('\n') - out.write('\t%s $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), exefile)) + out.write('\t%s $(EXAMP_DEBUG_FLAG) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), exefile)) # Add include dir components out.write(' -I%s' % get_component(API_COMPONENT).to_src_dir) out.write(' -I%s' % get_component(CPP_COMPONENT).to_src_dir) @@ -1476,6 +1487,7 @@ def mk_config(): CXXFLAGS = '%s -D_NO_OMP_' % CXXFLAGS if DEBUG_MODE: CXXFLAGS = '%s -g -Wall' % CXXFLAGS + EXAMP_DEBUG_FLAG = '-g' else: if GPROF: CXXFLAGS = '%s -O3 -D _EXTERNAL_RELEASE' % CXXFLAGS @@ -1519,6 +1531,7 @@ def mk_config(): config.write('CC=%s\n' % CC) config.write('CXX=%s\n' % CXX) config.write('CXXFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS)) + config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG) config.write('CXX_OUT_FLAG=-o \n') config.write('OBJ_EXT=.o\n') config.write('LIB_EXT=.a\n') diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index d32bf8b4e..246d5cd8c 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -33,6 +33,11 @@ Revision History: #include"smt_implied_equalities.h" #include"iz3interp.h" #include"iz3profiling.h" +#include"iz3hash.h" + +#ifndef WIN32 +using namespace stl_ext; +#endif typedef interpolation_options_struct *Z3_interpolation_options; @@ -40,7 +45,7 @@ extern "C" { Z3_context Z3_mk_interpolation_context(Z3_config cfg){ if(!cfg) cfg = Z3_mk_config(); - Z3_set_param_value(cfg, "PROOF_MODE", "2"); + Z3_set_param_value(cfg, "PROOF", "true"); Z3_set_param_value(cfg, "MODEL", "true"); Z3_set_param_value(cfg, "PRE_SIMPLIFIER","false"); Z3_set_param_value(cfg, "SIMPLIFY_CLAUSES","false"); @@ -69,7 +74,7 @@ extern "C" { pre_cnsts_vec[i] = a; } - vector pre_parents_vec; // get parents in a vector + ::vector pre_parents_vec; // get parents in a vector if(parents){ pre_parents_vec.resize(num); for(int i = 0; i < num; i++) @@ -109,7 +114,7 @@ extern "C" { Z3_ast *interps, Z3_model *model, Z3_literals *labels, - bool incremental, + int incremental, int num_theory, Z3_ast *theory ){ @@ -198,4 +203,242 @@ extern "C" { opts->map[name] = value; } + + }; + + +static void tokenize(const std::string &str, std::vector &tokens){ + for(unsigned i = 0; i < str.size();){ + if(str[i] == ' '){i++; continue;} + unsigned beg = i; + while(i < str.size() && str[i] != ' ')i++; + if(i > beg) + tokens.push_back(str.substr(beg,i-beg)); + } +} + +static void get_file_params(const char *filename, hash_map ¶ms){ + std::ifstream f(filename); + if(f){ + std::string first_line; + std::getline(f,first_line); + // std::cout << "first line: '" << first_line << "'" << std::endl; + if(first_line.size() >= 2 && first_line[0] == ';' && first_line[1] == '!'){ + std::vector tokens; + tokenize(first_line.substr(2,first_line.size()-2),tokens); + for(unsigned i = 0; i < tokens.size(); i++){ + std::string &tok = tokens[i]; + int eqpos = tok.find('='); + if(eqpos >= 0 && eqpos < (int)tok.size()){ + std::string left = tok.substr(0,eqpos); + std::string right = tok.substr(eqpos+1,tok.size()-eqpos-1); + params[left] = right; + } + } + } + f.close(); + } +} + +extern "C" { + + static void iZ3_write_seq(Z3_context ctx, int num, Z3_ast *cnsts, const char *filename, int num_theory, Z3_ast *theory){ + int num_fmlas = num+num_theory; + std::vector fmlas(num_fmlas); + if(num_theory) + std::copy(theory,theory+num_theory,fmlas.begin()); + for(int i = 0; i < num_theory; i++) + fmlas[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),fmlas[i]); + std::copy(cnsts,cnsts+num,fmlas.begin()+num_theory); + Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"none","AUFLIA","unknown","",num_fmlas-1,&fmlas[0],fmlas[num_fmlas-1]); + std::ofstream f(filename); + if(num_theory) + f << ";! THEORY=" << num_theory << "\n"; + f << smt; + f.close(); + } + + void Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, const char *filename, int num_theory, Z3_ast *theory){ + if(!parents){ + iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); + return; + } + std::vector tcnsts(num); + hash_map syms; + for(int j = 0; j < num - 1; j++){ + std::ostringstream oss; + oss << "$P" << j; + std::string name = oss.str(); + Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); + Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); + syms[j] = symbol; + tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); + } + tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); + for(int j = num-2; j >= 0; j--){ + int parent = parents[j]; + // assert(parent >= 0 && parent < num); + tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); + } + iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); + } + + static std::vector read_cnsts; + static std::vector read_parents; + static std::ostringstream read_error; + static std::string read_msg; + static std::vector read_theory; + + static bool iZ3_parse(Z3_context ctx, const char *filename, const char **error, std::vector &assertions){ + read_error.clear(); + try { + std::string foo(filename); + if(!foo.empty() && foo[foo.size()-1] == '2'){ + Z3_ast ass = Z3_parse_smtlib2_file(ctx, filename, 0, 0, 0, 0, 0, 0); + Z3_app app = Z3_to_app(ctx,ass); + int nconjs = Z3_get_app_num_args(ctx,app); + assertions.resize(nconjs); + for(int k = 0; k < nconjs; k++) + assertions[k] = Z3_get_app_arg(ctx,app,k); + } + else { + Z3_parse_smtlib_file(ctx, filename, 0, 0, 0, 0, 0, 0); + int numa = Z3_get_smtlib_num_assumptions(ctx); + int numf = Z3_get_smtlib_num_formulas(ctx); + int num = numa + numf; + + assertions.resize(num); + for(int j = 0; j < num; j++){ + if(j < numa) + assertions[j] = Z3_get_smtlib_assumption(ctx,j); + else + assertions[j] = Z3_get_smtlib_formula(ctx,j-numa); + } + } + } + catch(...) { + read_error << "SMTLIB parse error: " << Z3_get_smtlib_error(ctx); + read_msg = read_error.str(); + *error = read_msg.c_str(); + return false; + } + Z3_set_error_handler(ctx, 0); + return true; + } + + + int Z3_read_interpolation_problem(Z3_context ctx, int *_num, Z3_ast **cnsts, int **parents, const char *filename, const char **error, int *ret_num_theory, Z3_ast **theory ){ + + hash_map file_params; + get_file_params(filename,file_params); + + int num_theory = 0; + if(file_params.find("THEORY") != file_params.end()) + num_theory = atoi(file_params["THEORY"].c_str()); + + std::vector assertions; + if(!iZ3_parse(ctx,filename,error,assertions)) + return false; + + if(num_theory > (int)assertions.size()) + num_theory = assertions.size(); + int num = assertions.size() - num_theory; + + read_cnsts.resize(num); + read_parents.resize(num); + read_theory.resize(num_theory); + + for(int j = 0; j < num_theory; j++) + read_theory[j] = assertions[j]; + for(int j = 0; j < num; j++) + read_cnsts[j] = assertions[j+num_theory]; + + if(ret_num_theory) + *ret_num_theory = num_theory; + if(theory) + *theory = &read_theory[0]; + + if(!parents){ + *_num = num; + *cnsts = &read_cnsts[0]; + return true; + } + + for(int j = 0; j < num; j++) + read_parents[j] = SHRT_MAX; + + hash_map pred_map; + + for(int j = 0; j < num; j++){ + Z3_ast lhs = 0, rhs = read_cnsts[j]; + + if(Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx,Z3_to_app(ctx,rhs))) == Z3_OP_IMPLIES){ + Z3_app app1 = Z3_to_app(ctx,rhs); + Z3_ast lhs1 = Z3_get_app_arg(ctx,app1,0); + Z3_ast rhs1 = Z3_get_app_arg(ctx,app1,1); + if(Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx,Z3_to_app(ctx,lhs1))) == Z3_OP_AND){ + Z3_app app2 = Z3_to_app(ctx,lhs1); + int nconjs = Z3_get_app_num_args(ctx,app2); + for(int k = nconjs - 1; k >= 0; --k) + rhs1 = Z3_mk_implies(ctx,Z3_get_app_arg(ctx,app2,k),rhs1); + rhs = rhs1; + } + } + + while(1){ + Z3_app app = Z3_to_app(ctx,rhs); + Z3_func_decl func = Z3_get_app_decl(ctx,app); + Z3_decl_kind dk = Z3_get_decl_kind(ctx,func); + if(dk == Z3_OP_IMPLIES){ + if(lhs){ + Z3_ast child = lhs; + if(pred_map.find(child) == pred_map.end()){ + read_error << "formula " << j+1 << ": unknown: " << Z3_ast_to_string(ctx,child); + goto fail; + } + int child_num = pred_map[child]; + if(read_parents[child_num] != SHRT_MAX){ + read_error << "formula " << j+1 << ": multiple reference: " << Z3_ast_to_string(ctx,child); + goto fail; + } + read_parents[child_num] = j; + } + lhs = Z3_get_app_arg(ctx,app,0); + rhs = Z3_get_app_arg(ctx,app,1); + } + else { + if(!lhs){ + read_error << "formula " << j+1 << ": should be (implies {children} fmla parent)"; + goto fail; + } + read_cnsts[j] = lhs; + Z3_ast name = rhs; + if(pred_map.find(name) != pred_map.end()){ + read_error << "formula " << j+1 << ": duplicate symbol"; + goto fail; + } + pred_map[name] = j; + break; + } + } + } + + for(int j = 0; j < num-1; j++) + if(read_parents[j] == SHRT_MIN){ + read_error << "formula " << j+1 << ": unreferenced"; + goto fail; + } + + *_num = num; + *cnsts = &read_cnsts[0]; + *parents = &read_parents[0]; + return true; + + fail: + read_msg = read_error.str(); + *error = read_msg.c_str(); + return false; + + } +} diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 975b45940..76c34121f 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7750,8 +7750,6 @@ END_MLAPI_EXCLUDE are considered to have global scope (i.e., may appear in any interpolant formula). - def_API('Z3_interpolate', INT, (_in(CONTEXT), _in(INT), _in_array(1,AST), _in_array(1,UINT), _in(PARAMS), _out_array(1,AST), _out(MODEL), _out(LITERALS), _in(BOOL), _in(INT), _in_array(9,AST),)) - */ @@ -7762,11 +7760,11 @@ END_MLAPI_EXCLUDE __in_ecount(num) unsigned *parents, __in Z3_params options, __out_ecount(num-1) Z3_ast *interps, - __out Z3_model *model = 0, - __out Z3_literals *labels = 0, - __in bool incremental = false, - __in int num_theory = 0, - __in_ecount(num_theory) Z3_ast *theory = 0); + __out Z3_model *model, + __out Z3_literals *labels, + __in int incremental, + __in int num_theory, + __in_ecount(num_theory) Z3_ast *theory); /** Return a string summarizing cumulative time used for interpolation. This string is purely for entertainment purposes @@ -7779,6 +7777,49 @@ END_MLAPI_EXCLUDE Z3_string Z3_API Z3_interpolation_profile(Z3_context ctx); + /** + \brief Read an interpolation problem from file. + + \param ctx The Z3 context. This resets the error handler of ctx. + \param filename The file name to read. + \param num Returns length of sequence. + \param cnsts Returns sequence of formulas (do not free) + \param parents Returns the parents vector (or NULL for sequence) + \param error Returns an error message in case of failure (do not free the string) + + Returns true on success. + + File formats: Currently two formats are supported, based on + SMT-LIB2. For sequence interpolants, the sequence of constraints is + represented by the sequence of "assert" commands in the file. + + For tree interpolants, one symbol of type bool is associated to + each vertex of the tree. For each vertex v there is an "assert" + of the form: + + (implies (and c1 ... cn f) v) + + where c1 .. cn are the children of v (which must precede v in the file) + and f is the formula assiciated to node v. The last formula in the + file is the root vertex, and is represented by the predicate "false". + + A solution to a tree interpolation problem can be thought of as a + valuation of the vertices that makes all the implications true + where each value is represented using the common symbols between + the formulas in the subtree and the remainder of the formulas. + */ + + + int Z3_API + Z3_read_interpolation_problem(__in Z3_context ctx, + __out int *num, + __out_ecount(*num) Z3_ast **cnsts, + __out_ecount(*num) int **parents, + __in const char *filename, + __out const char **error, + __out int *num_theory, + __out_ecount(*num_theory) Z3_ast **theory); + #endif diff --git a/src/interp/foci2stub/foci2.cpp b/src/interp/foci2stub/foci2.cpp new file mode 100644 index 000000000..31908855b --- /dev/null +++ b/src/interp/foci2stub/foci2.cpp @@ -0,0 +1,25 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + foci2.cpp + +Abstract: + + Fake foci2, to be replaced + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + + +#include "foci2.h" + +FOCI2_EXPORT foci2 *foci2::create(const std::string &){ + return 0; +} diff --git a/src/interp/foci2.h b/src/interp/foci2stub/foci2.h similarity index 100% rename from src/interp/foci2.h rename to src/interp/foci2stub/foci2.h diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 15bdf0c22..0289c90c7 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -233,7 +233,7 @@ void iz3interpolate(scoped_ptr &_m_manager, const ptr_vector &cnsts, const ::vector &parents, ptr_vector &interps, - const ptr_vector theory, + const ptr_vector &theory, interpolation_options_struct * options) { std::vector _cnsts(cnsts.size()); diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h index 4e76b74fd..65ba1f15a 100644 --- a/src/interp/iz3interp.h +++ b/src/interp/iz3interp.h @@ -34,7 +34,7 @@ void iz3interpolate(scoped_ptr &_m_manager, const ptr_vector &cnsts, const ::vector &parents, ptr_vector &interps, - const ptr_vector theory, + const ptr_vector &theory, interpolation_options_struct * options = 0); #endif diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index f44c68dc2..93f541430 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -44,7 +44,54 @@ iz3mgr::ast iz3mgr::make_var(const std::string &name, type ty){ } iz3mgr::ast iz3mgr::make(opr op, int n, raw_ast **args){ - return m().mk_app(m().get_basic_family_id(), op, 0, 0, n, (expr **)args); + switch(op) { + case True: return mki(m_basic_fid,OP_TRUE,n,args); + case False: return mki(m_basic_fid,OP_FALSE,n,args); + case Equal: return mki(m_basic_fid,OP_EQ,n,args); + case Distinct: return mki(m_basic_fid,OP_DISTINCT,n,args); + case Ite: return mki(m_basic_fid,OP_ITE,n,args); + case And: return mki(m_basic_fid,OP_AND,n,args); + case Or: return mki(m_basic_fid,OP_OR,n,args); + case Iff: return mki(m_basic_fid,OP_IFF,n,args); + case Xor: return mki(m_basic_fid,OP_XOR,n,args); + case Not: return mki(m_basic_fid,OP_NOT,n,args); + case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args); + case Oeq: return mki(m_basic_fid,OP_OEQ,n,args); + case Leq: return mki(m_arith_fid,OP_LE,n,args); + case Geq: return mki(m_arith_fid,OP_GE,n,args); + case Lt: return mki(m_arith_fid,OP_LT,n,args); + case Gt: return mki(m_arith_fid,OP_GT,n,args); + case Plus: return mki(m_arith_fid,OP_ADD,n,args); + case Sub: return mki(m_arith_fid,OP_SUB,n,args); + case Uminus: return mki(m_arith_fid,OP_UMINUS,n,args); + case Times: return mki(m_arith_fid,OP_MUL,n,args); + case Div: return mki(m_arith_fid,OP_DIV,n,args); + case Idiv: return mki(m_arith_fid,OP_IDIV,n,args); + case Rem: return mki(m_arith_fid,OP_REM,n,args); + case Mod: return mki(m_arith_fid,OP_MOD,n,args); + case Power: return mki(m_arith_fid,OP_POWER,n,args); + case ToReal: return mki(m_arith_fid,OP_TO_REAL,n,args); + case ToInt: return mki(m_arith_fid,OP_TO_INT,n,args); + case IsInt: return mki(m_arith_fid,OP_IS_INT,n,args); + case Store: return mki(m_array_fid,OP_STORE,n,args); + case Select: return mki(m_array_fid,OP_SELECT,n,args); + case ConstArray: return mki(m_array_fid,OP_CONST_ARRAY,n,args); + case ArrayDefault: return mki(m_array_fid,OP_ARRAY_DEFAULT,n,args); + case ArrayMap: return mki(m_array_fid,OP_ARRAY_MAP,n,args); + case SetUnion: return mki(m_array_fid,OP_SET_UNION,n,args); + case SetIntersect: return mki(m_array_fid,OP_SET_INTERSECT,n,args); + case SetDifference: return mki(m_array_fid,OP_SET_DIFFERENCE,n,args); + case SetComplement: return mki(m_array_fid,OP_SET_COMPLEMENT,n,args); + case SetSubSet: return mki(m_array_fid,OP_SET_SUBSET,n,args); + case AsArray: return mki(m_array_fid,OP_AS_ARRAY,n,args); + default: + assert(0); + return ast(); + } +} + +iz3mgr::ast iz3mgr::mki(family_id fid, decl_kind dk, int n, raw_ast **args){ + return m().mk_app(fid, dk, 0, 0, n, (expr **)args); } iz3mgr::ast iz3mgr::make(opr op, const std::vector &args){ diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index efcbb0d6f..c96a20c7b 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -420,8 +420,8 @@ class iz3mgr { scoped_ptr m_manager; private: + ast mki(family_id fid, decl_kind sk, int n, raw_ast **args); ast make(opr op, int n, raw_ast **args); - ast make(symb sym, int n, raw_ast **args); family_id m_basic_fid; diff --git a/src/interp/iz3profiling.cpp b/src/interp/iz3profiling.cpp new file mode 100755 index 000000000..f18866f30 --- /dev/null +++ b/src/interp/iz3profiling.cpp @@ -0,0 +1,138 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3profiling.h + +Abstract: + + Some routines for measuring performance. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#include "iz3profiling.h" + +#include +#include +#include +#include +#include + + +// FIXME fill in these stubs + +#define clock_t int + +static clock_t current_time(){ + return 0; +} + +static void output_time(std::ostream &os, clock_t time){ + os << ((double)time)/1000; +} + + +namespace profiling { + + void show_time(){ + output_time(std::cout,current_time()); + std::cout << "\n"; + } + + typedef std::map nmap; + + struct node { + std::string name; + clock_t time; + clock_t start_time; + nmap sub; + struct node *parent; + + node(); + } top; + + node::node(){ + time = 0; + parent = 0; + } + + struct node *current; + + struct init { + init(){ + top.name = "TOTAL"; + current = ⊤ + } + } initializer; + + struct time_entry { + clock_t t; + time_entry(){t = 0;}; + void add(clock_t incr){t += incr;} + }; + + struct ltstr + { + bool operator()(const char* s1, const char* s2) const + { + return strcmp(s1, s2) < 0; + } + }; + + typedef std::map tmap; + + static std::ostream *pfs; + + void print_node(node &top, int indent, tmap &totals){ + for(int i = 0; i < indent; i++) (*pfs) << " "; + (*pfs) << top.name; + int dots = 70 - 2 * indent - top.name.size(); + for(int i = 0; i second,indent+1,totals); + } + + void print(std::ostream &os) { + pfs = &os; + top.time = 0; + for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++) + top.time += it->second.time; + tmap totals; + print_node(top,0,totals); + (*pfs) << "TOTALS:" << std::endl; + for(tmap::iterator it = totals.begin(); it != totals.end(); it++){ + (*pfs) << (it->first) << " "; + output_time(*pfs, it->second.t); + (*pfs) << std::endl; + } + } + + void timer_start(const char *name){ + node &child = current->sub[name]; + if(child.name.empty()){ // a new node + child.parent = current; + child.name = name; + } + child.start_time = current_time(); + current = &child; + } + + void timer_stop(const char *name){ + if(current->name != name || !current->parent){ + std::cerr << "imbalanced timer_start and timer_stop"; + exit(1); + } + current->time += (current_time() - current->start_time); + current = current->parent; + } +} From 2b93537366b84c3665580d51cbcef9aa6565431b Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 6 Mar 2013 18:26:46 -0800 Subject: [PATCH 007/509] debugging interpolation --- scripts/mk_util.py | 22 ++++++++- src/api/api_interp.cpp | 21 +++++++-- src/interp/iz3base.cpp | 4 +- src/interp/iz3base.h | 2 +- src/interp/iz3foci.cpp | 2 +- src/interp/iz3interp.cpp | 20 ++++---- src/interp/iz3interp.h | 2 +- src/interp/iz3mgr.cpp | 14 +++--- src/interp/iz3mgr.h | 75 +++++++++++++++++++++++------- src/interp/iz3translate_direct.cpp | 6 +-- 10 files changed, 121 insertions(+), 47 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index b0c7f7f58..9239eb255 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -71,6 +71,7 @@ VER_BUILD=None VER_REVISION=None PREFIX=os.path.split(os.path.split(os.path.split(PYTHON_PACKAGE_DIR)[0])[0])[0] GMP=False +FOCI2=False VS_PAR=False VS_PAR_NUM=8 GPROF=False @@ -199,6 +200,14 @@ def test_gmp(cc): t.commit() return exec_compiler_cmd([cc, CPPFLAGS, 'tstgmp.cpp', LDFLAGS, '-lgmp']) == 0 +def test_foci2(cc): + if is_verbose(): + print("Testing FOCI2...") + t = TempFile('tstfoci2.cpp') + t.add('#include\nint main() { mpz_t t; mpz_init(t); mpz_clear(t); return 0; }\n') + t.commit() + return exec_compiler_cmd([cc, CPPFLAGS, 'tstfoci2.cpp', LDFLAGS, '-lfoci2']) == 0 + def test_openmp(cc): if is_verbose(): print("Testing OpenMP...") @@ -444,6 +453,7 @@ def display_help(exit_code): if not IS_WINDOWS: print(" -g, --gmp use GMP.") print(" --gprof enable gprof") + print(" --foci2 use FOCI2.") print("") print("Some influential environment variables:") if not IS_WINDOWS: @@ -459,7 +469,7 @@ def display_help(exit_code): # Parse configuration option for mk_make script def parse_options(): global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM - global DOTNET_ENABLED, JAVA_ENABLED, STATIC_LIB, PREFIX, GMP, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH + global DOTNET_ENABLED, JAVA_ENABLED, STATIC_LIB, PREFIX, GMP, FOCI2, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH try: options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:dsxhmcvtnp:gj', @@ -508,6 +518,8 @@ def parse_options(): VS_PAR_NUM = int(arg) elif opt in ('-g', '--gmp'): GMP = True + elif opt in ('-f', '--foci2'): + FOCI2 = True elif opt in ('-j', '--java'): JAVA_ENABLED = True elif opt == '--gprof': @@ -1458,7 +1470,7 @@ def mk_config(): print('JNI Bindings: %s' % JNI_HOME) print('Java Compiler: %s' % JAVAC) else: - global CXX, CC, GMP, CPPFLAGS, CXXFLAGS, LDFLAGS + global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS ARITH = "internal" check_ar() CXX = find_cxx_compiler() @@ -1475,6 +1487,12 @@ def mk_config(): SLIBEXTRAFLAGS = '%s -lgmp' % SLIBEXTRAFLAGS else: CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS + if FOCI2: + test_foci2(CXX) + LDFLAGS = '%s -lfoci2' % LDFLAGS + SLIBEXTRAFLAGS = '%s -lfoci2' % SLIBEXTRAFLAGS + else: + CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS if GIT_HASH: CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH) CXXFLAGS = '%s -c' % CXXFLAGS diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 246d5cd8c..75adbb7d7 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -90,7 +90,7 @@ extern "C" { ptr_vector interpolants(num-1); // make space for result - scoped_ptr _m(&mk_c(ctx)->m()); + ast_manager &_m = mk_c(ctx)->m(); iz3interpolate(_m, to_ast(proof), pre_cnsts_vec, @@ -100,10 +100,12 @@ extern "C" { 0); // ignore params for now FIXME // copy result back - for(unsigned i = 0; i < interpolants.size(); i++) + for(unsigned i = 0; i < interpolants.size(); i++){ + mk_c(ctx)->save_ast_trail(interpolants[i]); interps[i] = of_ast(interpolants[i]); + _m.dec_ref(interpolants[i]); + } } - } Z3_lbool Z3_interpolate(Z3_context ctx, @@ -120,6 +122,8 @@ extern "C" { ){ + profiling::timer_start("Solve"); + if(!incremental){ profiling::timer_start("Z3 assert"); @@ -159,6 +163,10 @@ extern "C" { interps, num_theory, theory); + + if(!incremental) + for(int i = 0; i < num-1; i++) + Z3_persist_ast(ctx,interps[i],1); break; case Z3_L_UNDEF: @@ -172,6 +180,13 @@ extern "C" { break; } + profiling::timer_start("Z3 pop"); + if(!incremental) + Z3_pop(ctx,1); + profiling::timer_stop("Z3 pop"); + + profiling::timer_stop("Solve"); + return result; } diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 296688086..51bf59798 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -140,7 +140,7 @@ iz3base::ast iz3base::simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map foo(n,(ast)0); + std::pair foo(n,ast()); std::pair::iterator,bool> bar = memo.insert(foo); ast &res = bar.first->second; if(!bar.second) return res; @@ -159,7 +159,7 @@ iz3base::ast iz3base::simplify_with_lit(ast n, ast lit){ iz3base::ast iz3base::simplify(ast n){ if(is_not(n)) return mk_not(simplify(mk_not(n))); - std::pair memo_obj(n,(ast)0); + std::pair memo_obj(n,ast()); std::pair::iterator,bool> memo = simplify_memo.insert(memo_obj); ast &res = memo.first->second; if(!memo.second) return res; diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index 8a8ad332a..b379be30c 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -56,7 +56,7 @@ class iz3base : public iz3mgr, public scopes { /** Constructor */ - iz3base(scoped_ptr &_m_manager, + iz3base(ast_manager &_m_manager, const std::vector &_cnsts, const std::vector &_parents, const std::vector &_theory) diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp index 0317a9834..53e4ec84b 100755 --- a/src/interp/iz3foci.cpp +++ b/src/interp/iz3foci.cpp @@ -197,7 +197,7 @@ public: // convert an expr to Z3 ast ast to_Z3_ast(foci2::ast i){ - std::pair foo(i,(ast)0); + std::pair foo(i,ast()); std::pair bar = node_to_ast.insert(foo); if(!bar.second) return bar.first->second; ast &res = bar.first->second; diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 0289c90c7..6da83004d 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -224,11 +224,11 @@ public: delete sp; } - iz3interp(scoped_ptr &_m_manager) + iz3interp(ast_manager &_m_manager) : iz3mgr(_m_manager) {} }; -void iz3interpolate(scoped_ptr &_m_manager, +void iz3interpolate(ast_manager &_m_manager, ast *proof, const ptr_vector &cnsts, const ::vector &parents, @@ -236,21 +236,21 @@ void iz3interpolate(scoped_ptr &_m_manager, const ptr_vector &theory, interpolation_options_struct * options) { - std::vector _cnsts(cnsts.size()); + iz3interp itp(_m_manager); + std::vector _cnsts(cnsts.size()); std::vector _parents(parents.size()); - std::vector _interps; - std::vector _theory(theory.size()); + std::vector _interps; + std::vector _theory(theory.size()); for(unsigned i = 0; i < cnsts.size(); i++) - _cnsts[i] = cnsts[i]; + _cnsts[i] = itp.cook(cnsts[i]); for(unsigned i = 0; i < parents.size(); i++) _parents[i] = parents[i]; for(unsigned i = 0; i < theory.size(); i++) - _theory[i] = theory[i]; - ast_r _proof(proof); - iz3interp itp(_m_manager); + _theory[i] = itp.cook(theory[i]); + iz3mgr::ast _proof = itp.cook(proof); itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options); interps.resize(_interps.size()); for(unsigned i = 0; i < interps.size(); i++) - _interps[i] = interps[i]; + interps[i] = itp.uncook(_interps[i]); } diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h index 65ba1f15a..d87aefba8 100644 --- a/src/interp/iz3interp.h +++ b/src/interp/iz3interp.h @@ -29,7 +29,7 @@ struct interpolation_options_struct { typedef interpolation_options_struct *interpolation_options; -void iz3interpolate(scoped_ptr &_m_manager, +void iz3interpolate(ast_manager &_m_manager, ast *proof, const ptr_vector &cnsts, const ::vector &parents, diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 93f541430..fd9b17edc 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -40,7 +40,7 @@ std::ostream &operator <<(std::ostream &s, const iz3mgr::ast &a){ iz3mgr::ast iz3mgr::make_var(const std::string &name, type ty){ symbol s = symbol(name.c_str()); - return m().mk_const(m().mk_const_decl(s, ty)); + return cook(m().mk_const(m().mk_const_decl(s, ty))); } iz3mgr::ast iz3mgr::make(opr op, int n, raw_ast **args){ @@ -91,7 +91,7 @@ iz3mgr::ast iz3mgr::make(opr op, int n, raw_ast **args){ } iz3mgr::ast iz3mgr::mki(family_id fid, decl_kind dk, int n, raw_ast **args){ - return m().mk_app(fid, dk, 0, 0, n, (expr **)args); + return cook(m().mk_app(fid, dk, 0, 0, n, (expr **)args)); } iz3mgr::ast iz3mgr::make(opr op, const std::vector &args){ @@ -128,7 +128,7 @@ iz3mgr::ast iz3mgr::make(opr op, ast &arg0, ast &arg1, ast &arg2){ } iz3mgr::ast iz3mgr::make(symb sym, int n, raw_ast **args){ - return m().mk_app(sym, n, (expr **) args); + return cook(m().mk_app(sym, n, (expr **) args)); } iz3mgr::ast iz3mgr::make(symb sym, const std::vector &args){ @@ -193,14 +193,16 @@ iz3mgr::ast iz3mgr::make_quant(opr op, const std::vector &bvs, ast &body){ 0, 0, 0, 0 ); - return result.get(); + return cook(result.get()); } +// FIXME replace this with existing Z3 functionality + iz3mgr::ast iz3mgr::clone(ast &t, const std::vector &_args){ if(_args.size() == 0) return t; - ast_manager& m = *m_manager.get(); + ast_manager& m = m_manager; expr* a = to_expr(t.raw()); static std::vector rargs(10); if(rargs.size() < _args.size()) @@ -231,7 +233,7 @@ iz3mgr::ast iz3mgr::clone(ast &t, const std::vector &_args){ default: break; } - return a; + return cook(a); } diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index c96a20c7b..4f247bb81 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -53,32 +53,65 @@ Revision History: typedef ast raw_ast; /** Wrapper around an ast pointer */ -class ast_r { +class ast_i { + protected: raw_ast *_ast; public: raw_ast * const &raw() const {return _ast;} - ast_r(raw_ast *a){_ast = a;} + ast_i(raw_ast *a){_ast = a;} - ast_r(){_ast = 0;} - bool eq(const ast_r &other) const { + ast_i(){_ast = 0;} + bool eq(const ast_i &other) const { return _ast == other._ast; } - bool lt(const ast_r &other) const { + bool lt(const ast_i &other) const { return _ast < other._ast; } - friend bool operator==(const ast_r &x, const ast_r&y){ + friend bool operator==(const ast_i &x, const ast_i&y){ return x.eq(y); } - friend bool operator!=(const ast_r &x, const ast_r&y){ + friend bool operator!=(const ast_i &x, const ast_i&y){ return !x.eq(y); } - friend bool operator<(const ast_r &x, const ast_r&y){ + friend bool operator<(const ast_i &x, const ast_i&y){ return x.lt(y); } size_t hash() const {return (size_t)_ast;} bool null() const {return !_ast;} }; +/** Reference counting verison of above */ +class ast_r : public ast_i { + ast_manager *_m; + public: + ast_r(ast_manager *m, raw_ast *a) : ast_i(a) { + _m = m; + m->inc_ref(a); + } + + ast_r() {_m = 0;} + + ast_r(const ast_r &other) : ast_i(other) { + _m = other._m; + _m->inc_ref(_ast); + } + + ast_r &operator=(const ast_r &other) { + if(_ast) + _m->dec_ref(_ast); + _ast = other._ast; + _m = other._m; + _m->inc_ref(_ast); + return *this; + } + + ~ast_r(){ + if(_ast) + _m->dec_ref(_ast); + } + + +}; // to make ast_r hashable namespace stl_ext { @@ -186,8 +219,14 @@ class iz3mgr { ast make_quant(opr op, const std::vector &bvs, ast &body); ast clone(ast &t, const std::vector &args); - ast_manager &m() {return *m_manager.get();} + ast_manager &m() {return m_manager;} + ast cook(raw_ast *a) {return ast(&m_manager,a);} + + raw_ast *uncook(const ast &a) { + m_manager.inc_ref(a.raw()); + return a.raw(); + } /** Methods for destructing ast. */ @@ -210,13 +249,13 @@ class iz3mgr { ast_kind dk = t.raw()->get_kind(); switch(dk){ case AST_APP: - return to_app(t.raw())->get_arg(i); + return cook(to_app(t.raw())->get_arg(i)); case AST_QUANTIFIER: - return to_quantifier(t.raw())->get_expr(); + return cook(to_quantifier(t.raw())->get_expr()); default:; } assert(0); - return ast((raw_ast *)0); + return ast(); } symb sym(ast t){ @@ -262,7 +301,7 @@ class iz3mgr { } ast get_quantifier_body(const ast &t) { - return to_quantifier(t.raw())->get_expr(); + return cook(to_quantifier(t.raw())->get_expr()); } unsigned get_variable_index_value(const ast &t) { @@ -359,7 +398,7 @@ class iz3mgr { ast make_int(const std::string &s) { sort *r = m().mk_sort(m_arith_fid, INT_SORT); - return m_arith_util.mk_numeral(rational(s.c_str()),r); + return cook(m_arith_util.mk_numeral(rational(s.c_str()),r)); } @@ -392,9 +431,9 @@ class iz3mgr { static void pretty_print(std::ostream &f, const std::string &s); - iz3mgr(scoped_ptr &_m_manager) + iz3mgr(ast_manager &_m_manager) : m_manager(_m_manager), - m_arith_util(*_m_manager) + m_arith_util(_m_manager) { m_basic_fid = m().get_basic_family_id(); m_arith_fid = m().mk_family_id("arith"); @@ -406,7 +445,7 @@ class iz3mgr { iz3mgr(const iz3mgr& other) : m_manager(other.m_manager), - m_arith_util((const arith_util&)*other.m_manager) + m_arith_util(other.m_manager) { m_basic_fid = m().get_basic_family_id(); m_arith_fid = m().mk_family_id("arith"); @@ -417,7 +456,7 @@ class iz3mgr { } protected: - scoped_ptr m_manager; + ast_manager &m_manager; private: ast mki(family_id fid, decl_kind sk, int n, raw_ast **args); diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index 0500d88ab..a16d079c8 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -625,7 +625,7 @@ public: ast cont_eq(bool truth, ast v, ast e){ if(is_not(e)) return cont_eq(!truth,v,arg(e,0)); if(cont_eq_memo.find(e) != cont_eq_memo.end()) - return (ast)0; + return ast(); cont_eq_memo.insert(e); if(!truth && op(e) == Equal){ if(arg(e,0) == v) return(arg(e,1)); @@ -645,7 +645,7 @@ public: ast subst(ast var, ast t, ast e){ if(e == var) return t; - std::pair foo(e,(ast)0); + std::pair foo(e,ast()); std::pair bar = subst_memo.insert(foo); ast &res = bar.first->second; if(bar.second){ @@ -1722,7 +1722,7 @@ public: for(unsigned i = 0; i < theory.size(); i++) frame_map[theory[i]] = INT_MAX; frames = cnsts.size(); - traced_lit = 0; + traced_lit = ast(); } }; From 78848f3ddda030d89c9c47cd9e04923b6638a049 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 26 Mar 2013 17:25:54 -0700 Subject: [PATCH 008/509] working on smt2 and api --- examples/interp/iz3.cpp | 20 ++- scripts/mk_project.py | 11 +- scripts/mk_util.py | 30 +++-- src/api/api_ast.cpp | 2 + src/api/api_interp.cpp | 151 ++++++++++++++++++++- src/api/z3_api.h | 59 ++++++++ src/ast/ast.cpp | 6 + src/ast/ast.h | 3 +- src/cmd_context/basic_cmds.cpp | 18 ++- src/cmd_context/cmd_context.cpp | 44 +++++- src/cmd_context/cmd_context.h | 7 + src/cmd_context/context_params.cpp | 5 + src/cmd_context/context_params.h | 2 + src/cmd_context/interpolant_cmds.cpp | 120 +++++++++++++++++ src/cmd_context/interpolant_cmds.h | 24 ++++ src/interp/foci2.h | 75 +++++++++++ src/interp/iz3base.cpp | 80 ++++++++++- src/interp/iz3base.h | 32 ++++- src/interp/iz3checker.cpp | 192 +++++++++++++++++++++++++++ src/interp/iz3checker.h | 41 ++++++ src/interp/iz3foci.cpp | 5 +- src/interp/iz3interp.cpp | 86 +++++++++++- src/interp/iz3interp.h | 15 +++ src/interp/iz3mgr.cpp | 18 ++- src/interp/iz3mgr.h | 20 ++- src/interp/iz3pp.cpp | 175 ++++++++++++++++++++++++ src/interp/iz3pp.h | 35 +++++ src/interp/iz3scopes.h | 7 + src/interp/iz3translate_direct.cpp | 116 ++++++++++------ src/shell/smtlib_frontend.cpp | 2 + 30 files changed, 1307 insertions(+), 94 deletions(-) create mode 100644 src/cmd_context/interpolant_cmds.cpp create mode 100644 src/cmd_context/interpolant_cmds.h create mode 100755 src/interp/foci2.h create mode 100755 src/interp/iz3checker.cpp create mode 100644 src/interp/iz3checker.h create mode 100644 src/interp/iz3pp.cpp create mode 100644 src/interp/iz3pp.h diff --git a/examples/interp/iz3.cpp b/examples/interp/iz3.cpp index 99a538549..fa7d8b896 100755 --- a/examples/interp/iz3.cpp +++ b/examples/interp/iz3.cpp @@ -32,6 +32,7 @@ int main(int argc, const char **argv) { std::string output_file; bool flat_mode = false; bool anonymize = false; + bool write = false; Z3_config cfg = Z3_mk_config(); // Z3_interpolation_options options = Z3_mk_interpolation_options(); @@ -65,6 +66,8 @@ int main(int argc, const char **argv) { flat_mode = true; else if(flag == "-a" || flag == "--anon") anonymize = true; + else if(flag == "-w" || flag == "--write") + write = true; else if(flag == "-s" || flag == "--simple") Z3_set_param_value(cfg,"PREPROCESS","false"); else @@ -80,7 +83,9 @@ int main(int argc, const char **argv) { /* Create a Z3 context to contain formulas */ Z3_context ctx = Z3_mk_interpolation_context(cfg); - if(!flat_mode) + if(write || anonymize) + Z3_set_ast_print_mode(ctx,Z3_PRINT_SMTLIB2_COMPLIANT); + else if(!flat_mode) Z3_set_ast_print_mode(ctx,Z3_PRINT_SMTLIB_COMPLIANT); /* Read an interpolation problem */ @@ -119,17 +124,17 @@ int main(int argc, const char **argv) { } } -#if 0 /* Write out anonymized version. */ - if(anonymize){ + if(write || anonymize){ +#if 0 Z3_anonymize_ast_vector(ctx,num,constraints); - std::string ofn = output_file.empty() ? "anon.smt" : output_file; - Z3_write_interpolation_problem(ctx, num, constraints, parents, ofn.c_str()); +#endif + std::string ofn = output_file.empty() ? "iz3out.smt2" : output_file; + Z3_write_interpolation_problem(ctx, num, constraints, parents, ofn.c_str(), num_theory, theory); std::cout << "anonymized problem written to " << ofn << "\n"; exit(0); } -#endif /* Compute an interpolant, or get a model. */ @@ -188,7 +193,7 @@ int main(int argc, const char **argv) { printf("interpolant written to %s\n",output_file.c_str()); #endif } -#if 0 +#if 1 if(check_mode){ std::cout << "Checking interpolant...\n"; bool chk; @@ -212,6 +217,7 @@ int main(int argc, const char **argv) { break; } + if(profile_mode) std::cout << Z3_interpolation_profile(ctx); /* Delete the model if there is one */ diff --git a/scripts/mk_project.py b/scripts/mk_project.py index f5156ede6..f23c4fe80 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -33,7 +33,8 @@ def init_project_def(): add_lib('subpaving_tactic', ['core_tactics', 'subpaving'], 'math/subpaving/tactic') add_lib('aig_tactic', ['tactic'], 'tactic/aig') add_lib('solver', ['model', 'tactic']) - add_lib('cmd_context', ['solver', 'rewriter']) + add_lib('interp', ['solver']) + add_lib('cmd_context', ['solver', 'rewriter', 'interp']) add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'arith_tactics'], 'cmd_context/extra_cmds') add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2') add_lib('proof_checker', ['rewriter'], 'ast/proof_checker') @@ -59,10 +60,10 @@ def init_project_def(): add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') - add_dll('foci2', ['util'], 'interp/foci2stub', - dll_name='foci2', - export_files=['foci2stub.cpp']) - add_lib('interp', ['solver','foci2']) +# add_dll('foci2', ['util'], 'interp/foci2stub', +# dll_name='foci2', +# export_files=['foci2stub.cpp']) +# add_lib('interp', ['solver','foci2']) API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h'] add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure', 'interp'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 9239eb255..c2a168b70 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -72,6 +72,7 @@ VER_REVISION=None PREFIX=os.path.split(os.path.split(os.path.split(PYTHON_PACKAGE_DIR)[0])[0])[0] GMP=False FOCI2=False +FOCI2LIB='' VS_PAR=False VS_PAR_NUM=8 GPROF=False @@ -200,13 +201,13 @@ def test_gmp(cc): t.commit() return exec_compiler_cmd([cc, CPPFLAGS, 'tstgmp.cpp', LDFLAGS, '-lgmp']) == 0 -def test_foci2(cc): +def test_foci2(cc,foci2lib): if is_verbose(): print("Testing FOCI2...") t = TempFile('tstfoci2.cpp') - t.add('#include\nint main() { mpz_t t; mpz_init(t); mpz_clear(t); return 0; }\n') + t.add('#include\nint main() { foci2 *f = foci2::create("lia"); return 0; }\n') t.commit() - return exec_compiler_cmd([cc, CPPFLAGS, 'tstfoci2.cpp', LDFLAGS, '-lfoci2']) == 0 + return exec_compiler_cmd([cc, CPPFLAGS, '-Isrc/interp', 'tstfoci2.cpp', LDFLAGS, foci2lib]) == 0 def test_openmp(cc): if is_verbose(): @@ -453,7 +454,7 @@ def display_help(exit_code): if not IS_WINDOWS: print(" -g, --gmp use GMP.") print(" --gprof enable gprof") - print(" --foci2 use FOCI2.") + print(" -f --foci2= use foci2 library at path") print("") print("Some influential environment variables:") if not IS_WINDOWS: @@ -469,18 +470,19 @@ def display_help(exit_code): # Parse configuration option for mk_make script def parse_options(): global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM - global DOTNET_ENABLED, JAVA_ENABLED, STATIC_LIB, PREFIX, GMP, FOCI2, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH + global DOTNET_ENABLED, JAVA_ENABLED, STATIC_LIB, PREFIX, GMP, FOCI2, FOCI2LIB, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH try: options, remainder = getopt.gnu_getopt(sys.argv[1:], - 'b:dsxhmcvtnp:gj', + 'b:df:sxhmcvtnp:gj', ['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj', - 'trace', 'nodotnet', 'staticlib', 'prefix=', 'gmp', 'java', 'parallel=', 'gprof', + 'trace', 'nodotnet', 'staticlib', 'prefix=', 'gmp', 'foci2=', 'java', 'parallel=', 'gprof', 'githash=']) except: print("ERROR: Invalid command line option") display_help(1) for opt, arg in options: + print('opt = %s, arg = %s' % (opt, arg)) if opt in ('-b', '--build'): if arg == 'src': raise MKException('The src directory should not be used to host the Makefile') @@ -520,6 +522,7 @@ def parse_options(): GMP = True elif opt in ('-f', '--foci2'): FOCI2 = True + FOCI2LIB = arg elif opt in ('-j', '--java'): JAVA_ENABLED = True elif opt == '--gprof': @@ -1470,7 +1473,7 @@ def mk_config(): print('JNI Bindings: %s' % JNI_HOME) print('Java Compiler: %s' % JAVAC) else: - global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS + global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG ARITH = "internal" check_ar() CXX = find_cxx_compiler() @@ -1488,11 +1491,12 @@ def mk_config(): else: CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS if FOCI2: - test_foci2(CXX) - LDFLAGS = '%s -lfoci2' % LDFLAGS - SLIBEXTRAFLAGS = '%s -lfoci2' % SLIBEXTRAFLAGS - else: - CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS + if test_foci2(CXX,FOCI2LIB): + LDFLAGS = '%s %s' % (LDFLAGS,FOCI2LIB) + SLIBEXTRAFLAGS = '%s %s' % (SLIBEXTRAFLAGS,FOCI2LIB) + else: + print "FAILED\n" + FOCI2 = False if GIT_HASH: CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH) CXXFLAGS = '%s -c' % CXXFLAGS diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index e93e1a178..5e8b2b861 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -208,6 +208,7 @@ extern "C" { MK_BINARY(Z3_mk_xor, mk_c(c)->get_basic_fid(), OP_XOR, SKIP); MK_NARY(Z3_mk_and, mk_c(c)->get_basic_fid(), OP_AND, SKIP); MK_NARY(Z3_mk_or, mk_c(c)->get_basic_fid(), OP_OR, SKIP); + MK_UNARY(Z3_mk_interp, mk_c(c)->get_basic_fid(), OP_INTERP, SKIP); Z3_ast mk_ite_core(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3) { expr * result = mk_c(c)->m().mk_ite(to_expr(t1), to_expr(t2), to_expr(t3)); @@ -927,6 +928,7 @@ extern "C" { case OP_NOT: return Z3_OP_NOT; case OP_IMPLIES: return Z3_OP_IMPLIES; case OP_OEQ: return Z3_OP_OEQ; + case OP_INTERP: return Z3_OP_INTERP; case PR_UNDEF: return Z3_OP_PR_UNDEF; case PR_TRUE: return Z3_OP_PR_TRUE; diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 75adbb7d7..926493c6b 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -16,6 +16,7 @@ Revision History: --*/ #include +#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" @@ -34,6 +35,8 @@ Revision History: #include"iz3interp.h" #include"iz3profiling.h" #include"iz3hash.h" +#include"iz3pp.h" +#include"iz3checker.h" #ifndef WIN32 using namespace stl_ext; @@ -47,8 +50,8 @@ extern "C" { if(!cfg) cfg = Z3_mk_config(); Z3_set_param_value(cfg, "PROOF", "true"); Z3_set_param_value(cfg, "MODEL", "true"); - Z3_set_param_value(cfg, "PRE_SIMPLIFIER","false"); - Z3_set_param_value(cfg, "SIMPLIFY_CLAUSES","false"); + // Z3_set_param_value(cfg, "PRE_SIMPLIFIER","false"); + // Z3_set_param_value(cfg, "SIMPLIFY_CLAUSES","false"); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); @@ -191,6 +194,64 @@ extern "C" { } + static std::ostringstream itp_err; + + int Z3_check_interpolant(Z3_context ctx, + int num, + Z3_ast *cnsts, + int *parents, + Z3_ast *itp, + const char **error, + int num_theory, + Z3_ast *theory){ + + ast_manager &_m = mk_c(ctx)->m(); + itp_err.clear(); + + // need a solver -- make one here, but how? + params_ref p = params_ref::get_empty(); //FIXME + scoped_ptr sf(mk_smt_solver_factory()); + scoped_ptr sp((*(sf))(_m, p, false, true, false, symbol("AUFLIA"))); + + ptr_vector cnsts_vec(num); // get constraints in a vector + for(int i = 0; i < num; i++){ + ast *a = to_ast(cnsts[i]); + cnsts_vec[i] = a; + } + + ptr_vector itp_vec(num); // get interpolants in a vector + for(int i = 0; i < num-1; i++){ + ast *a = to_ast(itp[i]); + itp_vec[i] = a; + } + + ::vector parents_vec; // get parents in a vector + if(parents){ + parents_vec.resize(num); + for(int i = 0; i < num; i++) + parents_vec[i] = parents[i]; + } + + ptr_vector theory_vec; // get background theory in a vector + if(theory){ + theory_vec.resize(num_theory); + for(int i = 0; i < num_theory; i++) + theory_vec[i] = to_ast(theory[i]); + } + + bool res = iz3check(_m, + sp.get(), + itp_err, + cnsts_vec, + parents_vec, + itp_vec, + theory_vec); + + *error = res ? 0 : itp_err.str().c_str(); + return res; + } + + static std::string Z3_profile_string; Z3_string Z3_interpolation_profile(Z3_context ctx){ @@ -258,6 +319,7 @@ static void get_file_params(const char *filename, hash_map fmlas(num_fmlas); @@ -298,6 +360,89 @@ extern "C" { } iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); } +#else + + + static Z3_ast and_vec(Z3_context ctx,std::vector &c){ + return (c.size() > 1) ? Z3_mk_and(ctx,c.size(),&c[0]) : c[0]; + } + + static Z3_ast parents_vector_to_tree(Z3_context ctx, int num, Z3_ast *cnsts, int *parents){ + Z3_ast res; + if(!parents){ + res = Z3_mk_interp(ctx,cnsts[0]); + for(int i = 1; i < num-1; i++){ + Z3_ast bar[2] = {res,cnsts[i]}; + res = Z3_mk_interp(ctx,Z3_mk_and(ctx,2,bar)); + } + if(num > 1){ + Z3_ast bar[2] = {res,cnsts[num-1]}; + res = Z3_mk_and(ctx,2,bar); + } + } + else { + std::vector > chs(num); + for(int i = 0; i < num-1; i++){ + std::vector &c = chs[i]; + c.push_back(cnsts[i]); + Z3_ast foo = Z3_mk_interp(ctx,and_vec(ctx,c)); + chs[parents[i]].push_back(foo); + } + { + std::vector &c = chs[num-1]; + c.push_back(cnsts[num-1]); + res = and_vec(ctx,c); + } + } + Z3_inc_ref(ctx,res); + return res; + } + + void Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, const char *filename, int num_theory, Z3_ast *theory){ + std::ofstream f(filename); + if(num > 0){ + ptr_vector cnsts_vec(num); // get constraints in a vector + for(int i = 0; i < num; i++){ + expr *a = to_expr(cnsts[i]); + cnsts_vec[i] = a; + } + Z3_ast tree = parents_vector_to_tree(ctx,num,cnsts,parents); + iz3pp(mk_c(ctx)->m(),cnsts_vec,to_expr(tree),f); + Z3_dec_ref(ctx,tree); + } + f.close(); + +#if 0 + + + if(!parents){ + iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); + return; + } + std::vector tcnsts(num); + hash_map syms; + for(int j = 0; j < num - 1; j++){ + std::ostringstream oss; + oss << "$P" << j; + std::string name = oss.str(); + Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); + Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); + syms[j] = symbol; + tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); + } + tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); + for(int j = num-2; j >= 0; j--){ + int parent = parents[j]; + // assert(parent >= 0 && parent < num); + tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); + } + iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); +#endif + + } + + +#endif static std::vector read_cnsts; static std::vector read_parents; @@ -309,7 +454,7 @@ extern "C" { read_error.clear(); try { std::string foo(filename); - if(!foo.empty() && foo[foo.size()-1] == '2'){ + if(foo.size() >= 5 && foo.substr(foo.size()-5) == ".smt2"){ Z3_ast ass = Z3_parse_smtlib2_file(ctx, filename, 0, 0, 0, 0, 0, 0); Z3_app app = Z3_to_app(ctx,ass); int nconjs = Z3_get_app_num_args(ctx,app); diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 76c34121f..a64f1473d 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -249,6 +249,8 @@ typedef enum - Z3_OP_OEQ Binary equivalence modulo namings. This binary predicate is used in proof terms. It captures equisatisfiability and equivalence modulo renamings. + - Z3_OP_INTERP Marks a sub-formula for interpolation. + - Z3_OP_ANUM Arithmetic numeral. - Z3_OP_AGNUM Arithmetic algebraic numeral. Algebraic numbers are used to represent irrational numbers in Z3. @@ -890,6 +892,7 @@ typedef enum { Z3_OP_NOT, Z3_OP_IMPLIES, Z3_OP_OEQ, + Z3_OP_INTERP, // Arithmetic Z3_OP_ANUM = 0x200, @@ -2102,6 +2105,16 @@ END_MLAPI_EXCLUDE */ Z3_ast Z3_API Z3_mk_not(__in Z3_context c, __in Z3_ast a); + /** + \brief \mlh mk_interp c a \endmlh + Create an AST node marking a formula position for interpolation. + + The node \c a must have Boolean sort. + + def_API('Z3_mk_interp', AST, (_in(CONTEXT), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_interp(__in Z3_context c, __in Z3_ast a); + /** \brief \mlh mk_ite c t1 t2 t2 \endmlh Create an AST node representing an if-then-else: ite(t1, t2, @@ -7807,6 +7820,7 @@ END_MLAPI_EXCLUDE valuation of the vertices that makes all the implications true where each value is represented using the common symbols between the formulas in the subtree and the remainder of the formulas. + */ @@ -7822,6 +7836,51 @@ END_MLAPI_EXCLUDE + /** Check the correctness of an interpolant. The Z3 context must + have no constraints asserted when this call is made. That means + that after interpolating, you must first fully pop the Z3 + context before calling this. See Z3_interpolate for meaning of parameters. + + \param ctx The Z3 context. Must be generated by Z3_mk_interpolation_context + \param num The number of constraints in the sequence + \param cnsts Array of constraints (AST's in context ctx) + \param parents The parents vector (or NULL for sequence) + \param interps The interpolant to check + \param error Returns an error message if interpolant incorrect (do not free the string) + + Return value is Z3_L_TRUE if interpolant is verified, Z3_L_FALSE if + incorrect, and Z3_L_UNDEF if unknown. + + */ + + int Z3_API + Z3_check_interpolant(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, Z3_ast *interps, const char **error, + int num_theory, Z3_ast *theory); + + /** Write an interpolation problem to file suitable for reading with + Z3_read_interpolation_problem. The output file is a sequence + of SMT-LIB2 format commands, suitable for reading with command-line Z3 + or other interpolating solvers. + + \param ctx The Z3 context. Must be generated by z3_mk_interpolation_context + \param num The number of constraints in the sequence + \param cnsts Array of constraints + \param parents The parents vector (or NULL for sequence) + \param filename The file name to write + + */ + + void Z3_API + Z3_write_interpolation_problem(Z3_context ctx, + int num, + Z3_ast *cnsts, + int *parents, + const char *filename, + int num_theory, + Z3_ast *theory); + + + #endif diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 8d643a348..797e118a7 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -640,6 +640,7 @@ basic_decl_plugin::basic_decl_plugin(): m_iff_decl(0), m_xor_decl(0), m_not_decl(0), + m_interp_decl(0), m_implies_decl(0), m_proof_sort(0), @@ -863,6 +864,7 @@ void basic_decl_plugin::set_manager(ast_manager * m, family_id id) { m_iff_decl = mk_bool_op_decl("iff", OP_IFF, 2, false, true, false, false, true); m_xor_decl = mk_bool_op_decl("xor", OP_XOR, 2, true, true); m_not_decl = mk_bool_op_decl("not", OP_NOT, 1); + m_interp_decl = mk_bool_op_decl("interp", OP_INTERP, 1); m_implies_decl = mk_implies_decl(); m_proof_sort = m->mk_sort(symbol("Proof"), sort_info(id, PROOF_SORT)); @@ -887,6 +889,7 @@ void basic_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("or", OP_OR)); op_names.push_back(builtin_name("xor", OP_XOR)); op_names.push_back(builtin_name("not", OP_NOT)); + op_names.push_back(builtin_name("interp", OP_INTERP)); op_names.push_back(builtin_name("=>", OP_IMPLIES)); if (logic == symbol::null) { // user friendly aliases @@ -898,6 +901,7 @@ void basic_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("||", OP_OR)); op_names.push_back(builtin_name("equals", OP_EQ)); op_names.push_back(builtin_name("equiv", OP_IFF)); + op_names.push_back(builtin_name("@@", OP_INTERP)); } } @@ -1016,6 +1020,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; + case OP_INTERP: return m_interp_decl; case OP_IFF: return m_iff_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; @@ -1051,6 +1056,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; + case OP_INTERP: return m_interp_decl; case OP_IFF: return m_iff_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; diff --git a/src/ast/ast.h b/src/ast/ast.h index 4c924691c..141c1c9a2 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1006,7 +1006,7 @@ enum basic_sort_kind { }; enum basic_op_kind { - OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_IFF, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, LAST_BASIC_OP, + OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_IFF, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, OP_INTERP, LAST_BASIC_OP, PR_UNDEF, PR_TRUE, PR_ASSERTED, PR_GOAL, PR_MODUS_PONENS, PR_REFLEXIVITY, PR_SYMMETRY, PR_TRANSITIVITY, PR_TRANSITIVITY_STAR, PR_MONOTONICITY, PR_QUANT_INTRO, PR_DISTRIBUTIVITY, PR_AND_ELIM, PR_NOT_OR_ELIM, PR_REWRITE, PR_REWRITE_STAR, PR_PULL_QUANT, @@ -1028,6 +1028,7 @@ protected: func_decl * m_iff_decl; func_decl * m_xor_decl; func_decl * m_not_decl; + func_decl * m_interp_decl; func_decl * m_implies_decl; ptr_vector m_eq_decls; // cached eqs ptr_vector m_ite_decls; // cached ites diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 21a986fda..0e75a56ba 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -240,6 +240,8 @@ protected: symbol m_produce_unsat_cores; symbol m_produce_models; symbol m_produce_assignments; + symbol m_produce_interpolants; + symbol m_check_interpolants; symbol m_regular_output_channel; symbol m_diagnostic_output_channel; symbol m_random_seed; @@ -253,7 +255,9 @@ protected: return s == m_print_success || s == m_print_warning || s == m_expand_definitions || s == m_interactive_mode || s == m_produce_proofs || s == m_produce_unsat_cores || - s == m_produce_models || s == m_produce_assignments || s == m_regular_output_channel || s == m_diagnostic_output_channel || + s == m_produce_models || s == m_produce_assignments || s == m_produce_interpolants || + s == m_check_interpolants || + s == m_regular_output_channel || s == m_diagnostic_output_channel || s == m_random_seed || s == m_verbosity || s == m_global_decls; } @@ -270,6 +274,8 @@ public: m_produce_unsat_cores(":produce-unsat-cores"), m_produce_models(":produce-models"), m_produce_assignments(":produce-assignments"), + m_produce_interpolants(":produce-interpolants"), + m_check_interpolants(":check-interpolants"), m_regular_output_channel(":regular-output-channel"), m_diagnostic_output_channel(":diagnostic-output-channel"), m_random_seed(":random-seed"), @@ -337,6 +343,13 @@ class set_option_cmd : public set_get_option_cmd { check_not_initialized(ctx, m_produce_proofs); ctx.set_produce_proofs(to_bool(value)); } + else if (m_option == m_produce_interpolants) { + check_not_initialized(ctx, m_produce_interpolants); + ctx.set_produce_interpolants(to_bool(value)); + } + else if (m_option == m_check_interpolants) { + ctx.set_check_interpolants(to_bool(value)); + } else if (m_option == m_produce_unsat_cores) { check_not_initialized(ctx, m_produce_unsat_cores); ctx.set_produce_unsat_cores(to_bool(value)); @@ -485,6 +498,9 @@ public: else if (opt == m_produce_proofs) { print_bool(ctx, ctx.produce_proofs()); } + else if (opt == m_produce_interpolants) { + print_bool(ctx, ctx.produce_interpolants()); + } else if (opt == m_produce_unsat_cores) { print_bool(ctx, ctx.produce_unsat_cores()); } diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 261af3092..f53c9850a 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -38,6 +38,9 @@ Notes: #include"model_evaluator.h" #include"for_each_expr.h" #include"scoped_timer.h" +#include"interpolant_cmds.h" +//FIXME Does this break modularity? +// #include"smt_solver.h" func_decls::func_decls(ast_manager & m, func_decl * f): m_decls(TAG(func_decl*, f, 0)) { @@ -323,6 +326,7 @@ cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l): install_basic_cmds(*this); install_ext_basic_cmds(*this); install_core_tactic_cmds(*this); + install_interpolant_cmds(*this); SASSERT(m != 0 || !has_manager()); if (m) init_external_manager(); @@ -380,6 +384,19 @@ void cmd_context::set_produce_proofs(bool f) { m_params.m_proof = f; } +void cmd_context::set_produce_interpolants(bool f) { + // can only be set before initialization + // FIXME currently synonym for produce_proofs + // also sets the default solver to be simple smt + SASSERT(!has_manager()); + m_params.m_proof = f; + // set_solver_factory(mk_smt_solver_factory()); +} + +void cmd_context::set_check_interpolants(bool f) { + m_params.m_check_interpolants = f; +} + bool cmd_context::produce_models() const { return m_params.m_model; } @@ -388,6 +405,15 @@ bool cmd_context::produce_proofs() const { return m_params.m_proof; } +bool cmd_context::produce_interpolants() const { + // FIXME currently synonym for produce_proofs + return m_params.m_proof; +} + +bool cmd_context::check_interpolants() const { + return m_params.m_check_interpolants; +} + bool cmd_context::produce_unsat_cores() const { return m_params.m_unsat_core; } @@ -1456,11 +1482,27 @@ void cmd_context::validate_model() { } } +// FIXME: really interpolants_enabled ought to be a parameter to the solver factory, +// but this is a global change, so for now, we use an alternate solver factory +// for interpolation + void cmd_context::mk_solver() { bool proofs_enabled, models_enabled, unsat_core_enabled; params_ref p; m_params.get_solver_params(m(), p, proofs_enabled, models_enabled, unsat_core_enabled); - m_solver = (*m_solver_factory)(m(), p, proofs_enabled, models_enabled, unsat_core_enabled, m_logic); + if(produce_interpolants()){ + SASSERT(m_interpolating_solver_factory); + m_solver = (*m_interpolating_solver_factory)(m(), p, true /* must have proofs */, models_enabled, unsat_core_enabled, m_logic); + } + else + m_solver = (*m_solver_factory)(m(), p, proofs_enabled, models_enabled, unsat_core_enabled, m_logic); +} + + + +void cmd_context::set_interpolating_solver_factory(solver_factory * f) { + SASSERT(!has_manager()); + m_interpolating_solver_factory = f; } void cmd_context::set_solver_factory(solver_factory * f) { diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 22b2ea046..eac9a39ce 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -186,6 +186,7 @@ protected: svector m_scopes; scoped_ptr m_solver_factory; + scoped_ptr m_interpolating_solver_factory; ref m_solver; ref m_check_sat_result; @@ -253,6 +254,7 @@ public: void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } context_params & params() { return m_params; } + solver_factory &get_solver_factory() { return *m_solver_factory; } void global_params_updated(); // this method should be invoked when global (and module) params are updated. bool set_logic(symbol const & s); bool has_logic() const { return m_logic != symbol::null; } @@ -275,12 +277,16 @@ public: void set_random_seed(unsigned s) { m_random_seed = s; } bool produce_models() const; bool produce_proofs() const; + bool produce_interpolants() const; + bool check_interpolants() const; bool produce_unsat_cores() const; bool well_sorted_check_enabled() const; bool validate_model_enabled() const; void set_produce_models(bool flag); void set_produce_unsat_cores(bool flag); void set_produce_proofs(bool flag); + void set_produce_interpolants(bool flag); + void set_check_interpolants(bool flag); bool produce_assignments() const { return m_produce_assignments; } void set_produce_assignments(bool flag) { m_produce_assignments = flag; } void set_status(status st) { m_status = st; } @@ -294,6 +300,7 @@ public: sexpr_manager & sm() const { if (!m_sexpr_manager) const_cast(this)->m_sexpr_manager = alloc(sexpr_manager); return *m_sexpr_manager; } void set_solver_factory(solver_factory * s); + void set_interpolating_solver_factory(solver_factory * s); void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; } check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); } check_sat_state cs_state() const; diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index 2752967dd..0ec44e0cf 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -61,6 +61,9 @@ void context_params::set(char const * param, char const * value) { else if (p == "proof") { set_bool(m_proof, param, value); } + else if (p == "check_interpolants") { + set_bool(m_check_interpolants, param, value); + } else if (p == "model") { set_bool(m_model, param, value); } @@ -96,6 +99,7 @@ void context_params::updt_params(params_ref const & p) { m_well_sorted_check = p.get_bool("type_check", p.get_bool("well_sorted_check", true)); m_auto_config = p.get_bool("auto_config", true); m_proof = p.get_bool("proof", false); + m_check_interpolants = p.get_bool("check_interpolants", false); m_model = p.get_bool("model", true); m_model_validate = p.get_bool("model_validate", false); m_trace = p.get_bool("trace", false); @@ -111,6 +115,7 @@ void context_params::collect_param_descrs(param_descrs & d) { d.insert("type_check", CPK_BOOL, "type checker (alias for well_sorted_check)", "true"); d.insert("auto_config", CPK_BOOL, "use heuristics to automatically select solver and configure it", "true"); d.insert("proof", CPK_BOOL, "proof generation, it must be enabled when the Z3 context is created", "false"); + d.insert("check_interpolants", CPK_BOOL, "check correctness of interpolants", "false"); d.insert("model", CPK_BOOL, "model generation for solvers, this parameter can be overwritten when creating a solver", "true"); d.insert("model_validate", CPK_BOOL, "validate models produced by solvers", "false"); d.insert("trace", CPK_BOOL, "trace generation for VCC", "false"); diff --git a/src/cmd_context/context_params.h b/src/cmd_context/context_params.h index 526b4f517..7271bb84f 100644 --- a/src/cmd_context/context_params.h +++ b/src/cmd_context/context_params.h @@ -29,6 +29,8 @@ class context_params { public: bool m_auto_config; bool m_proof; + bool m_interpolants; + bool m_check_interpolants; bool m_debug_ref_count; bool m_trace; std::string m_trace_file_name; diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp new file mode 100644 index 000000000..bfade815c --- /dev/null +++ b/src/cmd_context/interpolant_cmds.cpp @@ -0,0 +1,120 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + interpolant_cmds.cpp + +Abstract: + Commands for interpolation. + +Author: + + Leonardo (leonardo) 2011-12-23 + +Notes: + +--*/ +#include +#include"cmd_context.h" +#include"cmd_util.h" +#include"scoped_timer.h" +#include"scoped_ctrl_c.h" +#include"cancel_eh.h" +#include"ast_pp.h" +#include"ast_smt_pp.h" +#include"ast_smt2_pp.h" +#include"parametric_cmd.h" +#include"mpq.h" +#include"expr2var.h" +#include"pp.h" +#include"pp_params.hpp" +#include"iz3interp.h" +#include"iz3checker.h" + +static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, bool check) { + + // get the proof, if there is one + + if (!ctx.produce_interpolants()) + throw cmd_exception("interpolation is not enabled, use command (set-option :produce-interpolants true)"); + if (!ctx.has_manager() || + ctx.cs_state() != cmd_context::css_unsat) + throw cmd_exception("proof is not available"); + expr_ref pr(ctx.m()); + pr = ctx.get_check_sat_result()->get_proof(); + if (pr == 0) + throw cmd_exception("proof is not available"); + + // get the assertions + + ptr_vector::const_iterator it = ctx.begin_assertions(); + ptr_vector::const_iterator end = ctx.end_assertions(); + ptr_vector cnsts(end - it); + for (int i = 0; it != end; ++it, ++i) + cnsts[i] = *it; + + // compute an interpolant + + ptr_vector interps; + + try { + iz3interpolate(ctx.m(),pr.get(),cnsts,t,interps,0); + } + catch (iz3_bad_tree &) { + throw cmd_exception("interpolation pattern contains non-asserted formula"); + } + catch (iz3_incompleteness &) { + throw cmd_exception("incompleteness in interpolator"); + } + + + // if we lived, print it out + for(unsigned i = 0; i < interps.size(); i++){ + ctx.regular_stream() << mk_pp(interps[i], ctx.m()) << std::endl; +#if 0 + ast_smt_pp pp(ctx.m()); + pp.set_logic(ctx.get_logic().str().c_str()); + pp.display_smt2(ctx.regular_stream(), to_expr(interps[i])); + ctx.regular_stream() << std::endl; +#endif + } + + // verify, for the paranoid... + if(check || ctx.check_interpolants()){ + std::ostringstream err; + ast_manager &_m = ctx.m(); + + // need a solver -- make one here FIXME is this right? + bool proofs_enabled, models_enabled, unsat_core_enabled; + params_ref p; + ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); + scoped_ptr sp = (ctx.get_solver_factory())(_m, p, false, true, false, ctx.get_logic()); + + if(iz3check(_m,sp.get(),err,cnsts,t,interps)) + ctx.regular_stream() << "correct\n"; + else + ctx.regular_stream() << "incorrect: " << err.str().c_str() << "\n"; + } + + for(unsigned i = 0; i < interps.size(); i++){ + ctx.m().dec_ref(interps[i]); + } +} + +static void get_interpolant(cmd_context & ctx, expr * t) { + get_interpolant_and_maybe_check(ctx,t,false); +} + +static void get_and_check_interpolant(cmd_context & ctx, expr * t) { + get_interpolant_and_maybe_check(ctx,t,true); +} + +UNARY_CMD(get_interpolant_cmd, "get-interpolant", "", "get interpolant for marked positions in fmla", CPK_EXPR, expr *, get_interpolant(ctx, arg);); + +// UNARY_CMD(get_and_check_interpolant_cmd, "get-and-check-interpolant", "", "get and check interpolant for marked positions in fmla", CPK_EXPR, expr *, get_and_check_interpolant(ctx, arg);); + +void install_interpolant_cmds(cmd_context & ctx) { + ctx.insert(alloc(get_interpolant_cmd)); + // ctx.insert(alloc(get_and_check_interpolant_cmd)); +} diff --git a/src/cmd_context/interpolant_cmds.h b/src/cmd_context/interpolant_cmds.h new file mode 100644 index 000000000..d7ceb5dc2 --- /dev/null +++ b/src/cmd_context/interpolant_cmds.h @@ -0,0 +1,24 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + interpolant_cmds.h + +Abstract: + Commands for interpolation. + +Author: + + Leonardo (leonardo) 2011-12-23 + +Notes: + +--*/ +#ifndef _INTERPOLANT_CMDS_H_ +#define _INTERPOLANT_CMDS_H_ + +class cmd_context; +void install_interpolant_cmds(cmd_context & ctx); + +#endif diff --git a/src/interp/foci2.h b/src/interp/foci2.h new file mode 100755 index 000000000..261dd05dc --- /dev/null +++ b/src/interp/foci2.h @@ -0,0 +1,75 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + foci2.h + +Abstract: + + An interface class for foci2. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#ifndef FOCI2_H +#define FOCI2_H + +#include +#include + +#ifdef WIN32 +#define FOCI2_EXPORT __declspec(dllexport) +#else +#define FOCI2_EXPORT __attribute__ ((visibility ("default"))) +#endif + +class foci2 { + public: + virtual ~foci2(){} + + typedef int ast; + typedef int symb; + + /** Built-in operators */ + enum ops { + And = 0, Or, Not, Iff, Ite, Equal, Plus, Times, Floor, Leq, Div, Bool, Int, Array, Tsym, Fsym, Forall, Exists, Distinct, LastOp + }; + + virtual symb mk_func(const std::string &s) = 0; + virtual symb mk_pred(const std::string &s) = 0; + virtual ast mk_op(ops op, const std::vector args) = 0; + virtual ast mk_op(ops op, ast) = 0; + virtual ast mk_op(ops op, ast, ast) = 0; + virtual ast mk_op(ops op, ast, ast, ast) = 0; + virtual ast mk_int(const std::string &) = 0; + virtual ast mk_rat(const std::string &) = 0; + virtual ast mk_true() = 0; + virtual ast mk_false() = 0; + virtual ast mk_app(symb,const std::vector args) = 0; + + virtual bool get_func(ast, symb &) = 0; + virtual bool get_pred(ast, symb &) = 0; + virtual bool get_op(ast, ops &) = 0; + virtual bool get_true(ast id) = 0; + virtual bool get_false(ast id) = 0; + virtual bool get_int(ast id, std::string &res) = 0; + virtual bool get_rat(ast id, std::string &res) = 0; + virtual const std::string &get_symb(symb) = 0; + + virtual int get_num_args(ast) = 0; + virtual ast get_arg(ast, int) = 0; + + virtual void show_ast(ast) = 0; + + virtual bool interpolate(const std::vector &frames, std::vector &itps, std::vector parents) = 0; + + FOCI2_EXPORT static foci2 *create(const std::string &); +}; + +#endif diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 51bf59798..e39dc9513 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -84,10 +84,9 @@ iz3base::range &iz3base::ast_scope(ast t){ void iz3base::print(const std::string &filename){ ast t = make(And,cnsts); - assert(0 && "not implemented"); - // Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"iZ3","QFLIA","unsat","",0,0,t); - // std::ofstream f(filename.c_str()); - // f << smt; + std::ofstream f(filename.c_str()); + print_sat_problem(f,t); + f.close(); } @@ -246,3 +245,76 @@ bool iz3base::is_sat(ast f){ #endif } + +void iz3base::find_children(const hash_set &cnsts_set, + const ast &tree, + std::vector &cnsts, + std::vector &parents, + std::vector &conjuncts, + std::vector &children, + std::vector &pos_map, + bool merge + ){ + std::vector my_children; + std::vector my_conjuncts; + if(op(tree) == Interp){ // if we've hit an interpolation position... + find_children(cnsts_set,arg(tree,0),cnsts,parents,my_conjuncts,my_children,pos_map,merge); + if(my_conjuncts.empty()) + my_conjuncts.push_back(mk_true()); // need at least one conjunct + int root = cnsts.size() + my_conjuncts.size() - 1; + for(unsigned i = 0; i < my_conjuncts.size(); i++){ + parents.push_back(root); + cnsts.push_back(my_conjuncts[i]); + } + for(unsigned i = 0; i < my_children.size(); i++) + parents[my_children[i]] = root; + children.push_back(root); + pos_map.push_back(root); + } + else { + if(op(tree) == And){ + int nargs = num_args(tree); + for(int i = 0; i < nargs; i++) + find_children(cnsts_set,arg(tree,i),cnsts,parents,my_conjuncts,my_children,pos_map,merge); + } + if(cnsts_set.find(tree) != cnsts_set.end()){ + if(merge && !my_conjuncts.empty()) + my_conjuncts.back() = mk_and(my_conjuncts.back(),tree); + else + my_conjuncts.push_back(tree); + } + for(unsigned i = 0; i < my_children.size(); i++) + children.push_back(my_children[i]); + for(unsigned i = 0; i < my_conjuncts.size(); i++) + conjuncts.push_back(my_conjuncts[i]); + } +} + +void iz3base::to_parents_vec_representation(const std::vector &_cnsts, + const ast &tree, + std::vector &cnsts, + std::vector &parents, + std::vector &theory, + std::vector &pos_map, + bool merge + ){ + std::vector my_children; + std::vector my_conjuncts; + hash_set cnsts_set; + for(unsigned i = 0; i < _cnsts.size(); i++) + cnsts_set.insert(_cnsts[i]); + ast _tree = (op(tree) != Interp) ? make(Interp,tree) : tree; + find_children(cnsts_set,_tree,cnsts,parents,my_conjuncts,my_children,pos_map,merge); + if(op(tree) != Interp) pos_map.pop_back(); + parents[parents.size()-1] = SHRT_MAX; + + // rest of the constraints are the background theory + + hash_set used_set; + for(unsigned i = 0; i < cnsts.size(); i++) + used_set.insert(cnsts[i]); + for(unsigned i = 0; i < _cnsts.size(); i++) + if(used_set.find(_cnsts[i]) == used_set.end()) + theory.push_back(_cnsts[i]); +} + diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index b379be30c..da0a3ce4a 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -65,7 +65,7 @@ class iz3base : public iz3mgr, public scopes { weak = false; } - iz3base(const iz3mgr& other, + iz3base(const iz3mgr& other, const std::vector &_cnsts, const std::vector &_parents, const std::vector &_theory) @@ -74,6 +74,11 @@ class iz3base : public iz3mgr, public scopes { weak = false; } + iz3base(const iz3mgr& other) + : iz3mgr(other), scopes() { + weak = false; + } + /* Set our options */ void set_option(const std::string &name, const std::string &value){ if(name == "weak" && value == "1") weak = true; @@ -102,6 +107,19 @@ class iz3base : public iz3mgr, public scopes { return make(And,cs); } + void to_parents_vec_representation(const std::vector &_cnsts, + const ast &tree, + std::vector &cnsts, + std::vector &parents, + std::vector &theory, + std::vector &pos_map, + bool merge = false + ); + + protected: + std::vector cnsts; + std::vector theory; + private: struct ranges { @@ -120,8 +138,6 @@ class iz3base : public iz3mgr, public scopes { void initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory); - std::vector cnsts; - std::vector theory; bool is_literal(ast n); void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); @@ -129,7 +145,15 @@ class iz3base : public iz3mgr, public scopes { ast simplify_and(std::vector &conjuncts); ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); ast simplify_with_lit(ast n, ast lit); - + void find_children(const stl_ext::hash_set &cnsts_set, + const ast &tree, + std::vector &cnsts, + std::vector &parents, + std::vector &conjuncts, + std::vector &children, + std::vector &pos_map, + bool merge + ); bool weak; }; diff --git a/src/interp/iz3checker.cpp b/src/interp/iz3checker.cpp new file mode 100755 index 000000000..b30e52d38 --- /dev/null +++ b/src/interp/iz3checker.cpp @@ -0,0 +1,192 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3checker.cpp + +Abstract: + + check correctness of interpolant + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#include "iz3base.h" +#include "iz3checker.h" + +#include +#include +#include +#include +#include +#include +#include + + +#ifndef WIN32 +using namespace stl_ext; +#endif + +struct iz3checker : iz3base { + + /* HACK: for tree interpolants, we assume that uninterpreted functions + are global. This is because in the current state of the tree interpolation + code, symbols that appear in sibling sub-trees have to be global, and + we have no way to eliminate such function symbols. When tree interpoaltion is + fixed, we can tree function symbols the same as constant symbols. */ + + bool is_tree; + + void support(const ast &t, std::set &res, hash_set &memo){ + if(memo.find(t) != memo.end()) return; + memo.insert(t); + + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + support(arg(t,i),res,memo); + + switch(op(t)){ + case Uninterpreted: + if(nargs == 0 || !is_tree) { + std::string name = string_of_symbol(sym(t)); + res.insert(name); + } + break; + case Forall: + case Exists: + support(get_quantifier_body(t),res,memo); + break; + default:; + } + } + + bool check(solver *s, std::ostream &err, + const std::vector &cnsts, + const std::vector &parents, + const std::vector &itp, + const std::vector &theory){ + + is_tree = !parents.empty(); + int num = cnsts.size(); + std::vector > children(num); + + for(int i = 0; i < num-1; i++){ + if(parents.size()) + children[parents[i]].push_back(i); + else + children[i+1].push_back(i); + } + + for(int i = 0; i < num; i++){ + s->push(); + for(unsigned j = 0; j < theory.size(); j++) + s->assert_expr(to_expr(theory[j].raw())); + s->assert_expr(to_expr(cnsts[i].raw())); + std::vector &cs = children[i]; + for(unsigned j = 0; j < cs.size(); j++) + s->assert_expr(to_expr(itp[cs[j]].raw())); + if(i != num-1) + s->assert_expr(to_expr(mk_not(itp[i]).raw())); + lbool result = s->check_sat(0,0); + if(result != l_false){ + err << "interpolant " << i << " is incorrect"; + return false; + } + s->pop(1); + } + + std::vector > supports(num); + for(int i = 0; i < num; i++){ + hash_set memo; + support(cnsts[i],supports[i],memo); + } + for(int i = 0; i < num-1; i++){ + std::vector Bside(num); + for(int j = num-1; j >= 0; j--) + Bside[j] = j != i; + for(int j = num-1; j >= 0; j--) + if(!Bside[j]){ + std::vector &cs = children[i]; + for(unsigned k = 0; k < cs.size(); k++) + Bside[cs[k]] = false; + } + std::set Asup, Bsup,common,Isup,bad; + for(int j = num-1; j >= 0; j--){ + std::set &side = Bside[j] ? Bsup : Asup; + side.insert(supports[j].begin(),supports[j].end()); + } + std::set_intersection(Asup.begin(),Asup.end(),Bsup.begin(),Bsup.end(),std::inserter(common,common.begin())); + { + hash_set tmemo; + for(unsigned j = 0; j < theory.size(); j++) + support(theory[j],common,tmemo); // all theory symbols allowed in interps + } + hash_set memo; + support(itp[i],Isup,memo); + std::set_difference(Isup.begin(),Isup.end(),common.begin(),common.end(),std::inserter(bad,bad.begin())); + if(!bad.empty()){ + err << "bad symbols in interpolant " << i << ":"; + std::copy(bad.begin(),bad.end(),std::ostream_iterator(err,",")); + return false; + } + } + return true; + } + + bool check(solver *s, std::ostream &err, + const std::vector &_cnsts, + const ast &tree, + const std::vector &itp){ + + std::vector pos_map; + + // convert to the parents vector representation + + to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map); + + //use the parents vector representation to compute interpolant + return check(s,err,cnsts,parents,itp,theory); + } + + iz3checker(ast_manager &_m) + : iz3base(_m) { + } +}; + +template +std::vector to_std_vector(const ::vector &v){ + std::vector _v(v.size()); + for(unsigned i = 0; i < v.size(); i++) + _v[i] = v[i]; + return _v; +} + + +bool iz3check(ast_manager &_m_manager, + solver *s, + std::ostream &err, + const ptr_vector &cnsts, + const ::vector &parents, + const ptr_vector &interps, + const ptr_vector &theory) +{ + iz3checker chk(_m_manager); + return chk.check(s,err,chk.cook(cnsts),to_std_vector(parents),chk.cook(interps),chk.cook(theory)); +} + +bool iz3check(ast_manager &_m_manager, + solver *s, + std::ostream &err, + const ptr_vector &_cnsts, + ast *tree, + const ptr_vector &interps) +{ + iz3checker chk(_m_manager); + return chk.check(s,err,chk.cook(_cnsts),chk.cook(tree),chk.cook(interps)); +} diff --git a/src/interp/iz3checker.h b/src/interp/iz3checker.h new file mode 100644 index 000000000..5402e4d68 --- /dev/null +++ b/src/interp/iz3checker.h @@ -0,0 +1,41 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3checker.h + +Abstract: + + check correctness of an interpolant + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#ifndef IZ3_CHECKER_H +#define IZ3_CHECKER_H + +#include "iz3mgr.h" +#include "solver.h" + +bool iz3check(ast_manager &_m_manager, + solver *s, + std::ostream &err, + const ptr_vector &cnsts, + const ::vector &parents, + const ptr_vector &interps, + const ptr_vector &theory); + +bool iz3check(ast_manager &_m_manager, + solver *s, + std::ostream &err, + const ptr_vector &cnsts, + ast *tree, + const ptr_vector &interps); + +#endif diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp index 53e4ec84b..38f588ce3 100755 --- a/src/interp/iz3foci.cpp +++ b/src/interp/iz3foci.cpp @@ -158,6 +158,7 @@ public: case Numeral: { std::string s = string_of_numeral(t); res = foci->mk_int(s); + break; } case Forall: case Exists: { @@ -177,12 +178,14 @@ public: foci_bvs.push_back(body); res = foci->mk_op(qop,foci_bvs); node_to_ast[res] = t; // desperate + break; } case Variable: { // a deBruijn index int index = get_variable_index_value(t); type ty = get_type(t); foci2::symb symbol = make_deBruijn_symbol(index,ty); res = foci->mk_app(symbol,std::vector()); + break; } default: { @@ -240,7 +243,7 @@ public: case foci2::Times: res = make(Times,args); break; case foci2::Div: - res = make(Div,args[0],args[1]); break; + res = make(Idiv,args[0],args[1]); break; case foci2::Leq: res = make(Leq,args[0],args[1]); break; case foci2::Distinct: diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 6da83004d..f3bde143d 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -34,6 +34,7 @@ Revision History: #include "iz3interp.h" + #ifndef WIN32 using namespace stl_ext; #endif @@ -164,9 +165,32 @@ static lbool test_secondary(context ctx, } #endif +template +struct killme { + T *p; + killme(){p = 0;} + void set(T *_p) {p = _p;} + ~killme(){ + if(p) + delete p; + } +}; -class iz3interp : public iz3mgr { + +class iz3interp : public iz3base { public: + + killme sp_killer; + killme tr_killer; + + bool is_linear(std::vector &parents){ + for(int i = 0; i < ((int)parents.size())-1; i++) + if(parents[i] != i+1) + return false; + return true; + } + + void proof_to_interpolant(z3pf proof, const std::vector &cnsts, const std::vector &parents, @@ -187,11 +211,17 @@ public: int num = cnsts_vec.size(); std::vector interps_vec(num-1); + // if this is really a sequence problem, we can make it easier + if(is_linear(parents_vec)) + parents_vec.clear(); + // create a secondary prover iz3secondary *sp = iz3foci::create(this,num,parents_vec.empty()?0:&parents_vec[0]); + sp_killer.set(sp); // kill this on exit // create a translator iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec,parents_vec,theory); + tr_killer.set(tr); // set the translation options, if needed if(options) @@ -220,12 +250,37 @@ public: fr.fix_interpolants(interps_vec); interps = interps_vec; - delete tr; - delete sp; } + + // same as above, but represents the tree using an ast + + void proof_to_interpolant(const z3pf &proof, + const std::vector &_cnsts, + const ast &tree, + std::vector &interps, + interpolation_options_struct *options = 0 + ){ + std::vector pos_map; + + // convert to the parents vector representation + + to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map); + + + //use the parents vector representation to compute interpolant + proof_to_interpolant(proof,cnsts,parents,interps,theory,options); + + // get the interps for the tree positions + std::vector _interps = interps; + interps.resize(pos_map.size()); + for(unsigned i = 0; i < pos_map.size(); i++) + interps[i] = i < _interps.size() ? _interps[i] : mk_false(); + } + + iz3interp(ast_manager &_m_manager) - : iz3mgr(_m_manager) {} + : iz3base(_m_manager) {} }; void iz3interpolate(ast_manager &_m_manager, @@ -254,3 +309,26 @@ void iz3interpolate(ast_manager &_m_manager, interps[i] = itp.uncook(_interps[i]); } +void iz3interpolate(ast_manager &_m_manager, + ast *proof, + const ptr_vector &cnsts, + ast *tree, + ptr_vector &interps, + interpolation_options_struct * options) +{ + iz3interp itp(_m_manager); + std::vector _cnsts(cnsts.size()); + std::vector _interps; + for(unsigned i = 0; i < cnsts.size(); i++) + _cnsts[i] = itp.cook(cnsts[i]); + iz3mgr::ast _proof = itp.cook(proof); + iz3mgr::ast _tree = itp.cook(tree); + itp.proof_to_interpolant(_proof,_cnsts,_tree,_interps,options); + interps.resize(_interps.size()); + for(unsigned i = 0; i < interps.size(); i++) + interps[i] = itp.uncook(_interps[i]); +} + + + + diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h index d87aefba8..017791186 100644 --- a/src/interp/iz3interp.h +++ b/src/interp/iz3interp.h @@ -26,6 +26,14 @@ struct interpolation_options_struct { stl_ext::hash_map map; }; +/** This object is thrown if a tree interpolation problem is mal-formed */ +struct iz3_bad_tree { +}; + +/** This object is thrown when iz3 fails due to an incompleteness in + the secondary solver. */ +struct iz3_incompleteness { +}; typedef interpolation_options_struct *interpolation_options; @@ -37,4 +45,11 @@ void iz3interpolate(ast_manager &_m_manager, const ptr_vector &theory, interpolation_options_struct * options = 0); +void iz3interpolate(ast_manager &_m_manager, + ast *proof, + const ptr_vector &cnsts, + ast *tree, + ptr_vector &interps, + interpolation_options_struct * options); + #endif diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index fd9b17edc..807c38227 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -57,6 +57,7 @@ iz3mgr::ast iz3mgr::make(opr op, int n, raw_ast **args){ case Not: return mki(m_basic_fid,OP_NOT,n,args); case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args); case Oeq: return mki(m_basic_fid,OP_OEQ,n,args); + case Interp: return mki(m_basic_fid,OP_INTERP,n,args); case Leq: return mki(m_arith_fid,OP_LE,n,args); case Geq: return mki(m_arith_fid,OP_GE,n,args); case Lt: return mki(m_arith_fid,OP_LT,n,args); @@ -107,7 +108,7 @@ iz3mgr::ast iz3mgr::make(opr op){ return make(op,0,0); } -iz3mgr::ast iz3mgr::make(opr op, ast &arg0){ +iz3mgr::ast iz3mgr::make(opr op, const ast &arg0){ raw_ast *a = arg0.raw(); return make(op,1,&a); } @@ -119,7 +120,7 @@ iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1){ return make(op,2,args); } -iz3mgr::ast iz3mgr::make(opr op, ast &arg0, ast &arg1, ast &arg2){ +iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1, const ast &arg2){ raw_ast *args[3]; args[0] = arg0.raw(); args[1] = arg1.raw(); @@ -335,13 +336,11 @@ void iz3mgr::pretty_print(std::ostream &f, const std::string &s){ } -iz3mgr::opr iz3mgr::op(ast &t){ +iz3mgr::opr iz3mgr::op(const ast &t){ ast_kind dk = t.raw()->get_kind(); switch(dk){ case AST_APP: { expr * e = to_expr(t.raw()); - if (m().is_unique_value(e)) - return Numeral; func_decl *d = to_app(t.raw())->get_decl(); if (null_family_id == d->get_family_id()) return Uninterpreted; @@ -360,6 +359,7 @@ iz3mgr::opr iz3mgr::op(ast &t){ case OP_NOT: return Not; case OP_IMPLIES: return Implies; case OP_OEQ: return Oeq; + case OP_INTERP: return Interp; default: return Other; } @@ -383,6 +383,8 @@ iz3mgr::opr iz3mgr::op(ast &t){ case OP_TO_INT: return ToInt; case OP_IS_INT: return IsInt; default: + if (m().is_unique_value(e)) + return Numeral; return Other; } } @@ -423,3 +425,9 @@ iz3mgr::pfrule iz3mgr::pr(const ast &t){ assert(m_basic_fid == d->get_family_id()); return d->get_decl_kind(); } + +void iz3mgr::print_sat_problem(std::ostream &out, const ast &t){ + ast_smt_pp pp(m()); + pp.set_simplify_implies(false); + pp.display_smt2(out, to_expr(t.raw())); +} diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 4f247bb81..3317b9d7d 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -130,7 +130,7 @@ namespace std { class less { public: size_t operator()(const ast_r &s, const ast_r &t) const { - return s.raw()->get_id() < t.raw()->get_id(); + return s.raw() < t.raw(); // s.raw()->get_id() < t.raw()->get_id(); } }; } @@ -161,6 +161,7 @@ class iz3mgr { Distinct, Xor, Oeq, + Interp, Leq, Geq, Lt, @@ -196,7 +197,7 @@ class iz3mgr { Other }; - opr op(ast &t); + opr op(const ast &t); unsigned ast_id(const ast &x) { @@ -208,9 +209,9 @@ class iz3mgr { ast make_var(const std::string &name, type ty); ast make(opr op, const std::vector &args); ast make(opr op); - ast make(opr op, ast &arg0); + ast make(opr op, const ast &arg0); ast make(opr op, const ast &arg0, const ast &arg1); - ast make(opr op, ast &arg0, ast &arg1, ast &arg2); + ast make(opr op, const ast &arg0, const ast &arg1, const ast &arg2); ast make(symb sym, const std::vector &args); ast make(symb sym); ast make(symb sym, ast &arg0); @@ -223,6 +224,13 @@ class iz3mgr { ast cook(raw_ast *a) {return ast(&m_manager,a);} + std::vector cook(ptr_vector v) { + std::vector _v(v.size()); + for(unsigned i = 0; i < v.size(); i++) + _v[i] = cook(v[i]); + return _v; + } + raw_ast *uncook(const ast &a) { m_manager.inc_ref(a.raw()); return a.raw(); @@ -245,7 +253,7 @@ class iz3mgr { assert(0); } - ast arg(ast t, int i){ + ast arg(const ast &t, int i){ ast_kind dk = t.raw()->get_kind(); switch(dk){ case AST_APP: @@ -427,6 +435,8 @@ class iz3mgr { void print_clause(std::ostream &s, std::vector &cls); + void print_sat_problem(std::ostream &out, const ast &t); + void show_clause(std::vector &cls); static void pretty_print(std::ostream &f, const std::string &s); diff --git a/src/interp/iz3pp.cpp b/src/interp/iz3pp.cpp new file mode 100644 index 000000000..1f9351453 --- /dev/null +++ b/src/interp/iz3pp.cpp @@ -0,0 +1,175 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + iz3pp.cpp + +Abstract: + + Pretty-print interpolation problems + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +/* Copyright 2011 Microsoft Research. */ +#include +#include +#include +#include +#include +#include +#include + +#include "iz3mgr.h" +#include "iz3pp.h" +#include "func_decl_dependencies.h" +#include"for_each_expr.h" +#include"ast_smt_pp.h" +#include"ast_smt2_pp.h" +#include"expr_functors.h" +#include"expr_abstract.h" + + +#ifndef WIN32 +using namespace stl_ext; +#endif + +// TBD: algebraic data-types declarations will not be printed. +class free_func_visitor { + ast_manager& m; + func_decl_set m_funcs; + obj_hashtable m_sorts; +public: + free_func_visitor(ast_manager& m): m(m) {} + void operator()(var * n) { } + void operator()(app * n) { + m_funcs.insert(n->get_decl()); + sort* s = m.get_sort(n); + if (s->get_family_id() == null_family_id) { + m_sorts.insert(s); + } + } + void operator()(quantifier * n) { } + func_decl_set& funcs() { return m_funcs; } + obj_hashtable& sorts() { return m_sorts; } +}; + +class iz3pp_helper : public iz3mgr { +public: + + void print_tree(const ast &tree, hash_map &cnames, std::ostream &out){ + hash_map::iterator foo = cnames.find(to_expr(tree.raw())); + if(foo != cnames.end()){ + symbol nm = foo->second; + if (is_smt2_quoted_symbol(nm)) { + out << mk_smt2_quoted_symbol(nm); + } + else { + out << nm; + } + } + else if(op(tree) == And){ + out << "(and"; + int nargs = num_args(tree); + for(int i = 0; i < nargs; i++){ + out << " "; + print_tree(arg(tree,i), cnames, out); + } + out << ")"; + } + else if(op(tree) == Interp){ + out << "(interp "; + print_tree(arg(tree,0), cnames, out); + out << ")"; + } + else throw iz3pp_bad_tree(); + } + + + iz3pp_helper(ast_manager &_m_manager) + : iz3mgr(_m_manager) {} +}; + +void iz3pp(ast_manager &m, + const ptr_vector &cnsts_vec, + expr *tree, + std::ostream& out) { + + unsigned sz = cnsts_vec.size(); + expr* const* cnsts = &cnsts_vec[0]; + + out << "(set-option :produce-interpolants true)\n"; + + free_func_visitor visitor(m); + expr_mark visited; + bool print_low_level = true; // m_params.print_low_level_smt2(); + +#define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env); + + smt2_pp_environment_dbg env(m); + + for (unsigned i = 0; i < sz; ++i) { + expr* e = cnsts[i]; + for_each_expr(visitor, visited, e); + } + + // name all the constraints + hash_map cnames; + int ctr = 1; + for(unsigned i = 0; i < sz; i++){ + symbol nm; + std::ostringstream s; + s << "f!" << (ctr++); + cnames[cnsts[i]] = symbol(s.str().c_str()); + } + + func_decl_set &funcs = visitor.funcs(); + func_decl_set::iterator it = funcs.begin(), end = funcs.end(); + + obj_hashtable& sorts = visitor.sorts(); + obj_hashtable::iterator sit = sorts.begin(), send = sorts.end(); + + + + for (; sit != send; ++sit) { + PP(*sit); + } + + for (; it != end; ++it) { + func_decl* f = *it; + if(f->get_family_id() == null_family_id){ + PP(f); + out << "\n"; + } + } + + for (unsigned i = 0; i < sz; ++i) { + out << "(assert "; + expr* r = cnsts[i]; + symbol nm = cnames[r]; + out << "(! "; + PP(r); + out << " :named "; + if (is_smt2_quoted_symbol(nm)) { + out << mk_smt2_quoted_symbol(nm); + } + else { + out << nm; + } + out << ")"; + out << ")\n"; + } + out << "(check-sat)\n"; + out << "(get-interpolant "; + iz3pp_helper pp(m); + pp.print_tree(pp.cook(tree),cnames,out); + out << ")\n"; +} + + diff --git a/src/interp/iz3pp.h b/src/interp/iz3pp.h new file mode 100644 index 000000000..3ea5925e8 --- /dev/null +++ b/src/interp/iz3pp.h @@ -0,0 +1,35 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + iz3pp.cpp + +Abstract: + + Pretty-print interpolation problems + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#ifndef IZ3_PP_H +#define IZ3_PP_H + +#include "iz3mgr.h" + +/** Exception thrown in case of mal-formed tree interpoloation + specification */ + +struct iz3pp_bad_tree { +}; + +void iz3pp(ast_manager &m, + const ptr_vector &cnsts_vec, + expr *tree, + std::ostream& out); +#endif diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h index 2ba3a999f..6e2261d0c 100755 --- a/src/interp/iz3scopes.h +++ b/src/interp/iz3scopes.h @@ -32,6 +32,13 @@ class scopes { parents = _parents; } + scopes(){ + } + + void initialize(const std::vector &_parents){ + parents = _parents; + } + /** The parents vector defining the tree structure */ std::vector parents; diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index a16d079c8..d4656d3a8 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -22,6 +22,7 @@ Revision History: #include "iz3translate.h" #include "iz3proof.h" #include "iz3profiling.h" +#include "iz3interp.h" #include #include @@ -336,6 +337,16 @@ public: } } } +#if 0 + AstSet::iterator it = res.begin(), en = res.end(); + if(it != en){ + AstSet::iterator old = it; + ++it; + for(; it != en; ++it, ++old) + if(!(*old < *it)) + std::cout << "foo!"; + } +#endif return res; } @@ -439,25 +450,28 @@ public: if(it != localization_map.end()) return it->second; - // if is is non-local, we must first localise the arguments to + // if is is non-local, we must first localize the arguments to // the range of its function symbol - if(op(e) == Uninterpreted){ - symb f = sym(e); - range frng = sym_range(f); - int nargs = num_args(e); - if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ - if(ranges_intersect(frng,rng)) // localize to desired range if possible - {frng = range_glb(frng,rng);} - std::vector largs(nargs); - for(int i = 0; i < nargs; i++){ - largs[i] = localize_term(arg(e,i),frng); - frng = range_glb(frng,ast_scope(largs[i])); - } - e = make(f,largs); - assert(is_local(e)); + + int nargs = num_args(e); + if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ + range frng = rng; + if(op(e) == Uninterpreted){ + symb f = sym(e); + range srng = sym_range(f); + if(ranges_intersect(srng,rng)) // localize to desired range if possible + frng = range_glb(srng,rng); } + std::vector largs(nargs); + for(int i = 0; i < nargs; i++){ + largs[i] = localize_term(arg(e,i),frng); + frng = range_glb(frng,ast_scope(largs[i])); + } + e = clone(e,largs); + assert(is_local(e)); } + if(ranges_intersect(ast_scope(e),rng)) return e; // this term occurs in range, so it's O.K. @@ -774,10 +788,12 @@ public: // if sat, lemma isn't valid, something is wrong if(sat){ +#if 1 std::cerr << "invalid lemma written to file invalid_lemma.smt:\n"; iz3base foo(*this,preds,std::vector(),std::vector()); foo.print("invalid_lemma.smt"); - throw invalid_lemma(); +#endif + throw iz3_incompleteness(); } assert(sat == 0); // if sat, lemma doesn't hold! @@ -961,12 +977,14 @@ public: AstSet &this_hyps = get_hyps(proof); if(std::includes(hyps.begin(),hyps.end(),this_hyps.begin(),this_hyps.end())){ // if(hyps.find(con) == hyps.end()) +#if 0 if(/* lemma_count == SHOW_LEMMA_COUNT - 1 && */ !is_literal_or_lit_iff(conc(proof))){ std::cout << "\nnon-lit local ante\n"; show_step(proof); show(conc(proof)); throw non_lit_local_ante(); } +#endif local_antes.push_back(proof); return true; } @@ -1019,7 +1037,7 @@ public: std::vector lit_trace; hash_set marked_proofs; - bool proof_has_lit(ast proof, ast lit){ + bool proof_has_lit(const ast &proof, const ast &lit){ AstSet &hyps = get_hyps(proof); if(hyps.find(mk_not(lit)) != hyps.end()) return true; @@ -1033,7 +1051,7 @@ public: } - void trace_lit_rec(ast lit, ast proof, AstHashSet &memo){ + void trace_lit_rec(const ast &lit, const ast &proof, AstHashSet &memo){ if(memo.find(proof) == memo.end()){ memo.insert(proof); AstSet &hyps = get_hyps(proof); @@ -1064,7 +1082,7 @@ public: ast traced_lit; - int trace_lit(ast lit, ast proof){ + int trace_lit(const ast &lit, const ast &proof){ marked_proofs.clear(); lit_trace.clear(); traced_lit = lit; @@ -1073,7 +1091,7 @@ public: return lit_trace.size(); } - bool is_literal_or_lit_iff(ast lit){ + bool is_literal_or_lit_iff(const ast &lit){ if(my_is_literal(lit)) return true; if(op(lit) == Iff){ return my_is_literal(arg(lit,0)) && my_is_literal(arg(lit,1)); @@ -1081,13 +1099,13 @@ public: return false; } - bool my_is_literal(ast lit){ + bool my_is_literal(const ast &lit){ ast abslit = is_not(lit) ? arg(lit,0) : lit; int f = op(abslit); return !(f == And || f == Or || f == Iff); } - void print_lit(ast lit){ + void print_lit(const ast &lit){ ast abslit = is_not(lit) ? arg(lit,0) : lit; if(!is_literal_or_lit_iff(lit)){ if(is_not(lit)) std::cout << "~"; @@ -1099,22 +1117,22 @@ public: print_expr(std::cout,lit); } - void show_lit(ast lit){ + void show_lit(const ast &lit){ print_lit(lit); std::cout << "\n"; } - void print_z3_lit(ast a){ + void print_z3_lit(const ast &a){ print_lit(from_ast(a)); } - void show_z3_lit(ast a){ + void show_z3_lit(const ast &a){ print_z3_lit(a); std::cout << "\n"; } - void show_con(ast proof, bool brief){ + void show_con(const ast &proof, bool brief){ if(!traced_lit.null() && proof_has_lit(proof,traced_lit)) std::cout << "(*) "; ast con = conc(proof); @@ -1138,7 +1156,7 @@ public: std::cout << "\n"; } - void show_step( ast proof){ + void show_step(const ast &proof){ std::cout << "\n"; unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ @@ -1151,7 +1169,7 @@ public: show_con(proof,false); } - void show_marked( ast proof){ + void show_marked( const ast &proof){ std::cout << "\n"; unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ @@ -1166,7 +1184,7 @@ public: std::vector pfhist; int pfhist_pos; - void pfgoto(ast proof){ + void pfgoto(const ast &proof){ if(pfhist.size() == 0) pfhist_pos = 0; else pfhist_pos++; @@ -1245,7 +1263,7 @@ public: return res; } - Iproof::node extract_simple_proof(ast proof, hash_set &leaves){ + Iproof::node extract_simple_proof(const ast &proof, hash_set &leaves){ if(leaves.find(proof) != leaves.end()) return iproof->make_hypothesis(conc(proof)); ast con = from_ast(conc(proof)); @@ -1271,7 +1289,7 @@ public: return 0; } - int extract_th_lemma_simple(ast proof, std::vector &lits){ + int extract_th_lemma_simple(const ast &proof, std::vector &lits){ std::vector la = local_antes; local_antes.clear(); // clear antecedents for next lemma antes_added.clear(); @@ -1321,7 +1339,7 @@ public: // #define NEW_EXTRACT_TH_LEMMA - void get_local_hyps(ast proof, std::set &res){ + void get_local_hyps(const ast &proof, std::set &res){ std::set hyps = get_hyps(proof); for(std::set::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ ast hyp = *it; @@ -1376,6 +1394,7 @@ public: try { res = extract_th_lemma_common(lits,nll,lemma_nll); } +#if 0 catch (const invalid_lemma &) { std::cout << "\n\nlemma: " << my_count; std::cout << "\n\nproof node: \n"; @@ -1397,6 +1416,7 @@ public: show_lit(lits[i]); throw invalid_lemma(); } +#endif return res; #else @@ -1648,7 +1668,7 @@ public: if(!(res = extract_th_lemma(proof,lits,nll))){ if(!(res = push_into_resolvent(proof,lits,nll,expect_clause))){ #endif - std::cout << "extract theory lemma failed\n"; + // std::cout << "extract theory lemma failed\n"; add_antes(proof); res = fix_lemma(lits,get_hyps(proof),nll); } @@ -1724,6 +1744,22 @@ public: frames = cnsts.size(); traced_lit = ast(); } + + ~iz3translation_direct(){ + for(hash_map::iterator + it = non_local_lits_unique.begin(), + en = non_local_lits_unique.end(); + it != en; + ++it) + delete it->second; + + for(hash_map::iterator + it = Z3_resolvent_unique.begin(), + en = Z3_resolvent_unique.end(); + it != en; + ++it) + delete it->second; + } }; @@ -1740,29 +1776,29 @@ iz3translation *iz3translation::create(iz3mgr &mgr, } -#if 0 +#if 1 -void iz3translation_direct_trace_lit(iz3translation_direct *p, ast lit, ast proof){ +void iz3translation_direct_trace_lit(iz3translation_direct *p, iz3mgr::ast lit, iz3mgr::ast proof){ p->trace_lit(lit, proof); } -void iz3translation_direct_show_step(iz3translation_direct *p, ast proof){ +void iz3translation_direct_show_step(iz3translation_direct *p, iz3mgr::ast proof){ p->show_step(proof); } -void iz3translation_direct_show_marked(iz3translation_direct *p, ast proof){ +void iz3translation_direct_show_marked(iz3translation_direct *p, iz3mgr::ast proof){ p->show_marked(proof); } -void iz3translation_direct_show_lit(iz3translation_direct *p, ast lit){ +void iz3translation_direct_show_lit(iz3translation_direct *p, iz3mgr::ast lit){ p->show_lit(lit); } -void iz3translation_direct_show_z3_lit(iz3translation_direct *p, ast a){ +void iz3translation_direct_show_z3_lit(iz3translation_direct *p, iz3mgr::ast a){ p->show_z3_lit(a); } -void iz3translation_direct_pfgoto(iz3translation_direct *p, ast proof){ +void iz3translation_direct_pfgoto(iz3translation_direct *p, iz3mgr::ast proof){ p->pfgoto(proof); } diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index ef0b4ad6b..0e570321e 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -29,6 +29,7 @@ Revision History: #include"polynomial_cmds.h" #include"subpaving_cmds.h" #include"smt_strategic_solver.h" +#include"smt_solver.h" extern bool g_display_statistics; extern void display_config(); @@ -96,6 +97,7 @@ unsigned read_smtlib2_commands(char const * file_name) { cmd_context ctx; ctx.set_solver_factory(mk_smt_strategic_solver_factory()); + ctx.set_interpolating_solver_factory(mk_smt_solver_factory()); install_dl_cmds(ctx); install_dbg_cmds(ctx); From 28266786f3ce96556d982071ba89b5a7a05de07c Mon Sep 17 00:00:00 2001 From: "U-REDMOND\\kenmcmil" Date: Wed, 27 Mar 2013 12:17:52 -0700 Subject: [PATCH 009/509] porting to windows --- scripts/mk_util.py | 2 + src/api/dotnet/Properties/AssemblyInfo.cs | 78 +++++++++++------------ src/cmd_context/cmd_context.cpp | 2 - src/interp/iz3base.cpp | 1 + src/interp/iz3base.h | 2 +- src/interp/iz3mgr.h | 13 +++- src/interp/iz3translate_direct.cpp | 4 +- 7 files changed, 56 insertions(+), 46 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index c2a168b70..8116da3f5 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -909,6 +909,7 @@ class ExeComponent(Component): for dep in deps: c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) + out.write(' ' + FOCI2LIB) out.write(' $(LINK_EXTRA_FLAGS)\n') out.write('%s: %s\n\n' % (self.name, exefile)) @@ -1012,6 +1013,7 @@ class DLLComponent(Component): if not dep in self.reexports: c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) + out.write(' ' + FOCI2LIB) out.write(' $(SLINK_EXTRA_FLAGS)') if IS_WINDOWS: out.write(' /DEF:%s.def' % os.path.join(self.to_src_dir, self.name)) diff --git a/src/api/dotnet/Properties/AssemblyInfo.cs b/src/api/dotnet/Properties/AssemblyInfo.cs index 1ac6cb520..517349177 100644 --- a/src/api/dotnet/Properties/AssemblyInfo.cs +++ b/src/api/dotnet/Properties/AssemblyInfo.cs @@ -1,39 +1,39 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security.Permissions; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Z3 .NET Interface")] -[assembly: AssemblyDescription(".NET Interface to the Z3 Theorem Prover")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft Corporation")] -[assembly: AssemblyProduct("Z3")] -[assembly: AssemblyCopyright("Copyright © Microsoft Corporation 2006")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4853ed71-2078-40f4-8117-bc46646bce0e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("4.2.0.0")] -[assembly: AssemblyVersion("4.3.2.0")] -[assembly: AssemblyFileVersion("4.3.2.0")] - +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Permissions; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Z3 .NET Interface")] +[assembly: AssemblyDescription(".NET Interface to the Z3 Theorem Prover")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyProduct("Z3")] +[assembly: AssemblyCopyright("Copyright © Microsoft Corporation 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4853ed71-2078-40f4-8117-bc46646bce0e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("4.2.0.0")] +[assembly: AssemblyVersion("4.3.2.0")] +[assembly: AssemblyFileVersion("4.3.2.0")] + diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index f53c9850a..20539266b 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -39,8 +39,6 @@ Notes: #include"for_each_expr.h" #include"scoped_timer.h" #include"interpolant_cmds.h" -//FIXME Does this break modularity? -// #include"smt_solver.h" func_decls::func_decls(ast_manager & m, func_decl * f): m_decls(TAG(func_decl*, f, 0)) { diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index e39dc9513..5350a8fb3 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -243,6 +243,7 @@ bool iz3base::is_sat(ast f){ Z3_pop(ctx,1); return res != Z3_L_FALSE; #endif + return false; } diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index da0a3ce4a..c8d4b3c9e 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -145,7 +145,7 @@ class iz3base : public iz3mgr, public scopes { ast simplify_and(std::vector &conjuncts); ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); ast simplify_with_lit(ast n, ast lit); - void find_children(const stl_ext::hash_set &cnsts_set, + void find_children(const hash_set &cnsts_set, const ast &tree, std::vector &cnsts, std::vector &parents, diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 3317b9d7d..e948e9ceb 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -114,7 +114,7 @@ class ast_r : public ast_i { }; // to make ast_r hashable -namespace stl_ext { +namespace hash_space { template <> class hash { public: @@ -124,12 +124,21 @@ namespace stl_ext { }; } +// to make ast_r hashable in windows +#ifdef WIN32 +template <> inline +size_t stdext::hash_value(const ast_r& s) +{ + return s.raw()->get_id(); +} +#endif + // to make ast_r usable in ordered collections namespace std { template <> class less { public: - size_t operator()(const ast_r &s, const ast_r &t) const { + bool operator()(const ast_r &s, const ast_r &t) const { return s.raw() < t.raw(); // s.raw()->get_id() < t.raw()->get_id(); } }; diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index d4656d3a8..da05c6642 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -81,8 +81,8 @@ namespace std { class less { public: bool operator()(const Z3_resolvent &x, const Z3_resolvent &y) const { - size_t ixproof = (size_t) x.proof; - size_t iyproof = (size_t) y.proof; + size_t ixproof = (size_t) x.proof.raw(); + size_t iyproof = (size_t) y.proof.raw(); if(ixproof < iyproof) return true; if(ixproof > iyproof) return false; return x.pivot < y.pivot; From 7a0d49cb32c91a8fa16717dc20e72ac3bc262a56 Mon Sep 17 00:00:00 2001 From: "U-REDMOND\\kenmcmil" Date: Thu, 28 Mar 2013 11:18:20 -0700 Subject: [PATCH 010/509] porting to windows --- src/api/z3_api.h | 11 ++++------- src/interp/iz3base.cpp | 2 +- src/interp/iz3base.h | 2 +- src/interp/iz3foci.cpp | 3 ++- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index a64f1473d..6d8f34c6c 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7788,7 +7788,7 @@ END_MLAPI_EXCLUDE def_API('Z3_interpolation_profile', STRING, (_in(CONTEXT),)) */ - Z3_string Z3_API Z3_interpolation_profile(Z3_context ctx); + Z3_string Z3_API Z3_interpolation_profile(__in Z3_context ctx); /** \brief Read an interpolation problem from file. @@ -7824,8 +7824,7 @@ END_MLAPI_EXCLUDE */ - int Z3_API - Z3_read_interpolation_problem(__in Z3_context ctx, + int Z3_API Z3_read_interpolation_problem(__in Z3_context ctx, __out int *num, __out_ecount(*num) Z3_ast **cnsts, __out_ecount(*num) int **parents, @@ -7853,8 +7852,7 @@ END_MLAPI_EXCLUDE */ - int Z3_API - Z3_check_interpolant(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, Z3_ast *interps, const char **error, + int Z3_API Z3_check_interpolant(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, Z3_ast *interps, const char **error, int num_theory, Z3_ast *theory); /** Write an interpolation problem to file suitable for reading with @@ -7870,8 +7868,7 @@ END_MLAPI_EXCLUDE */ - void Z3_API - Z3_write_interpolation_problem(Z3_context ctx, + void Z3_API Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 5350a8fb3..0fdce156a 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -247,7 +247,7 @@ bool iz3base::is_sat(ast f){ } -void iz3base::find_children(const hash_set &cnsts_set, +void iz3base::find_children(const stl_ext::hash_set &cnsts_set, const ast &tree, std::vector &cnsts, std::vector &parents, diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index c8d4b3c9e..da0a3ce4a 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -145,7 +145,7 @@ class iz3base : public iz3mgr, public scopes { ast simplify_and(std::vector &conjuncts); ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); ast simplify_with_lit(ast n, ast lit); - void find_children(const hash_set &cnsts_set, + void find_children(const stl_ext::hash_set &cnsts_set, const ast &tree, std::vector &cnsts, std::vector &parents, diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp index 38f588ce3..251599041 100755 --- a/src/interp/iz3foci.cpp +++ b/src/interp/iz3foci.cpp @@ -307,7 +307,8 @@ public: int interpolate(const std::vector &cnsts, std::vector &itps){ assert((int)cnsts.size() == frames); - foci = foci2::create("lia"); + std::string lia("lia"); + foci = foci2::create(lia); if(!foci){ std::cerr << "iZ3: cannot find foci lia solver.\n"; assert(0); From bbe036bc03a35f8fb2aeeb1f74e8e58ff4dec029 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 28 Mar 2013 11:38:11 -0700 Subject: [PATCH 011/509] adding build instructions --- README | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/README b/README index 1ca0cd577..5e3f4d11d 100644 --- a/README +++ b/README @@ -3,26 +3,43 @@ Z3 is licensed under MSR-LA (Microsoft Research License Agreement). See http://z3.codeplex.com/license for more information about this license. Z3 can be built using Visual Studio Command Prompt and make/g++. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +This is the interpolating branch. Build instruction differ from the +main branch. Please read carefully. + +Interpolation depends on the Cadence foci2 library obtainable from +here: + +http://www.kenmcmil.com/foci2/ + +Download the appropriate files for your operating system and +architecture. Note, the default Z3 build on intel architecture +is x86, not x64, so you need the 32-bit version of foci2. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1) Building Z3 on Windows using Visual Studio Command Prompt - python scripts/mk_make.py + python scripts/mk_make.py -f cd build nmake + copy libfoci.dll 2) Building Z3 using make/g++ and Python Execute: - python scripts/mk_make.py + python scripts/mk_make.py -f cd build make sudo make install +Then put libfoci.so somewhere in your LD_LIBRARY_PATH. + By default, it will install z3 executable at PREFIX/bin, libraries at PREFIX/lib, and include files at PREFIX/include, where PREFIX is the installation prefix used for installing Python in your system. It is usually /usr for most Linux distros, and /usr/local for FreeBSD. Use the following commands to install in a different prefix (e.g., /home/leo) - python scripts/mk_make.py --prefix=/home/leo + python scripts/mk_make.py --prefix=/home/leo -f cd build make make install @@ -37,7 +54,7 @@ To uninstall Z3, use 4) Building Z3 using clang and clang++ on Linux/OSX Remark: clang does not support OpenMP yet. - CXX=clang++ CC=clang python scripts/mk_make.py + CXX=clang++ CC=clang python scripts/mk_make.py -f cd build make From e651f45bc0ed7e2f272ea7adbe27a505fbae4fb7 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 9 Apr 2013 15:52:30 -0700 Subject: [PATCH 012/509] added sequences to get-interpolant and compute-interpolant --- src/ast/ast.h | 1 + src/cmd_context/cmd_context.h | 1 + src/cmd_context/interpolant_cmds.cpp | 206 ++++++++++++++++++++++----- src/interp/iz3interp.cpp | 62 ++++++++ src/interp/iz3interp.h | 21 +++ 5 files changed, 253 insertions(+), 38 deletions(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index 141c1c9a2..2753d0006 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -2002,6 +2002,7 @@ public: app * mk_distinct_expanded(unsigned num_args, expr * const * args); app * mk_true() { return m_true; } app * mk_false() { return m_false; } + app * mk_interp(expr * arg) { return mk_app(m_basic_family_id, OP_INTERP, arg); } func_decl* mk_and_decl() { sort* domain[2] = { m_bool_sort, m_bool_sort }; diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index eac9a39ce..e34975183 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -255,6 +255,7 @@ public: void reset_cancel() { set_cancel(false); } context_params & params() { return m_params; } solver_factory &get_solver_factory() { return *m_solver_factory; } + solver_factory &get_interpolating_solver_factory() { return *m_interpolating_solver_factory; } void global_params_updated(); // this method should be invoked when global (and module) params are updated. bool set_logic(symbol const & s); bool has_logic() const { return m_logic != symbol::null; } diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index bfade815c..ca22b3907 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -32,44 +32,9 @@ Notes: #include"iz3interp.h" #include"iz3checker.h" -static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, bool check) { - - // get the proof, if there is one - - if (!ctx.produce_interpolants()) - throw cmd_exception("interpolation is not enabled, use command (set-option :produce-interpolants true)"); - if (!ctx.has_manager() || - ctx.cs_state() != cmd_context::css_unsat) - throw cmd_exception("proof is not available"); - expr_ref pr(ctx.m()); - pr = ctx.get_check_sat_result()->get_proof(); - if (pr == 0) - throw cmd_exception("proof is not available"); - - // get the assertions - - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); - ptr_vector cnsts(end - it); - for (int i = 0; it != end; ++it, ++i) - cnsts[i] = *it; - - // compute an interpolant +static void show_interpolant_and_maybe_check(cmd_context & ctx, ptr_vector &cnsts, expr *t, ptr_vector &interps, bool check) +{ - ptr_vector interps; - - try { - iz3interpolate(ctx.m(),pr.get(),cnsts,t,interps,0); - } - catch (iz3_bad_tree &) { - throw cmd_exception("interpolation pattern contains non-asserted formula"); - } - catch (iz3_incompleteness &) { - throw cmd_exception("incompleteness in interpolator"); - } - - - // if we lived, print it out for(unsigned i = 0; i < interps.size(); i++){ ctx.regular_stream() << mk_pp(interps[i], ctx.m()) << std::endl; #if 0 @@ -100,6 +65,52 @@ static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, bool ch for(unsigned i = 0; i < interps.size(); i++){ ctx.m().dec_ref(interps[i]); } + +} + +static void check_can_interpolate(cmd_context & ctx){ + if (!ctx.produce_interpolants()) + throw cmd_exception("interpolation is not enabled, use command (set-option :produce-interpolants true)"); +} + + +static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, bool check) { + + check_can_interpolate(ctx); + + // get the proof, if there is one + + if (!ctx.has_manager() || + ctx.cs_state() != cmd_context::css_unsat) + throw cmd_exception("proof is not available"); + expr_ref pr(ctx.m()); + pr = ctx.get_check_sat_result()->get_proof(); + if (pr == 0) + throw cmd_exception("proof is not available"); + + // get the assertions from the context + + ptr_vector::const_iterator it = ctx.begin_assertions(); + ptr_vector::const_iterator end = ctx.end_assertions(); + ptr_vector cnsts(end - it); + for (int i = 0; it != end; ++it, ++i) + cnsts[i] = *it; + + // compute an interpolant + + ptr_vector interps; + + try { + iz3interpolate(ctx.m(),pr.get(),cnsts,t,interps,0); + } + catch (iz3_bad_tree &) { + throw cmd_exception("interpolation pattern contains non-asserted formula"); + } + catch (iz3_incompleteness &) { + throw cmd_exception("incompleteness in interpolator"); + } + + show_interpolant_and_maybe_check(ctx, cnsts, t, interps, check); } static void get_interpolant(cmd_context & ctx, expr * t) { @@ -110,11 +121,130 @@ static void get_and_check_interpolant(cmd_context & ctx, expr * t) { get_interpolant_and_maybe_check(ctx,t,true); } -UNARY_CMD(get_interpolant_cmd, "get-interpolant", "", "get interpolant for marked positions in fmla", CPK_EXPR, expr *, get_interpolant(ctx, arg);); + +static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, bool check){ + + // get the proof, if there is one + + check_can_interpolate(ctx); + + // create a fresh solver suitable for interpolation + bool proofs_enabled, models_enabled, unsat_core_enabled; + params_ref p; + ast_manager &_m = ctx.m(); + ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); + scoped_ptr sp = (ctx.get_interpolating_solver_factory())(_m, p, true, models_enabled, false, ctx.get_logic()); + + ptr_vector cnsts; + ptr_vector interps; + model_ref m; + + // compute an interpolant + + lbool res; + try { + res = iz3interpolate(_m, *sp.get(), t, cnsts, interps, m, 0); + } + catch (iz3_incompleteness &) { + throw cmd_exception("incompleteness in interpolator"); + } + + switch(res){ + case l_false: + ctx.regular_stream() << "unsat\n"; + show_interpolant_and_maybe_check(ctx, cnsts, t, interps, check); + break; + + case l_true: + ctx.regular_stream() << "sat\n"; + // TODO: how to return the model to the context, if it exists? + break; + + case l_undef: + ctx.regular_stream() << "unknown\n"; + // TODO: how to return the model to the context, if it exists? + break; + } + + for(unsigned i = 0; i < cnsts.size(); i++) + ctx.m().dec_ref(cnsts[i]); + +} + +static expr *make_tree(cmd_context & ctx, const ptr_vector &exprs){ + if(exprs.size() == 0) + throw cmd_exception("not enough arguments"); + expr *foo = exprs[0]; + for(unsigned i = 1; i < exprs.size(); i++){ + foo = ctx.m().mk_and(ctx.m().mk_interp(foo),exprs[i]); + } + return foo; +} + +static void get_interpolant(cmd_context & ctx, const ptr_vector &exprs) { + expr *foo = make_tree(ctx,exprs); + ctx.m().inc_ref(foo); + get_interpolant(ctx,foo); + ctx.m().dec_ref(foo); +} + +static void compute_interpolant(cmd_context & ctx, const ptr_vector &exprs) { + expr *foo = make_tree(ctx, exprs); + ctx.m().inc_ref(foo); + compute_interpolant_and_maybe_check(ctx,foo,false); + ctx.m().dec_ref(foo); +} + + +// UNARY_CMD(get_interpolant_cmd, "get-interpolant", "", "get interpolant for marked positions in fmla", CPK_EXPR, expr *, get_interpolant(ctx, arg);); // UNARY_CMD(get_and_check_interpolant_cmd, "get-and-check-interpolant", "", "get and check interpolant for marked positions in fmla", CPK_EXPR, expr *, get_and_check_interpolant(ctx, arg);); +class get_interpolant_cmd : public parametric_cmd { +protected: + ptr_vector m_targets; +public: + get_interpolant_cmd(char const * name = "get-interpolant"):parametric_cmd(name) {} + + virtual char const * get_usage() const { return "+"; } + + virtual char const * get_main_descr() const { + return "get interpolant for formulas"; + } + + virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + } + + virtual void prepare(cmd_context & ctx) { + parametric_cmd::prepare(ctx); + m_targets.resize(0); + } + + virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + return CPK_EXPR; + } + + virtual void set_next_arg(cmd_context & ctx, expr * arg) { + m_targets.push_back(arg); + } + + virtual void execute(cmd_context & ctx) { + get_interpolant(ctx,m_targets); + } +}; + +class compute_interpolant_cmd : public get_interpolant_cmd { +public: + compute_interpolant_cmd(char const * name = "compute-interpolant"):get_interpolant_cmd(name) {} + + virtual void execute(cmd_context & ctx) { + compute_interpolant(ctx,m_targets); + } + +}; + void install_interpolant_cmds(cmd_context & ctx) { ctx.insert(alloc(get_interpolant_cmd)); + ctx.insert(alloc(compute_interpolant_cmd)); // ctx.insert(alloc(get_and_check_interpolant_cmd)); } diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index f3bde143d..6a9c54716 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -278,6 +278,37 @@ public: interps[i] = i < _interps.size() ? _interps[i] : mk_false(); } + bool has_interp(hash_map &memo, const ast &t){ + if(memo.find(t) != memo.end()) + return memo[t]; + bool res = false; + if(op(t) == Interp) + res = true; + else if(op(t) == And){ + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + res |= has_interp(memo, arg(t,i)); + } + memo[t] = res; + return res; + } + + void collect_conjuncts(std::vector &cnsts, hash_map &memo, const ast &t){ + if(!has_interp(memo,t)) + cnsts.push_back(t); + else { + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + collect_conjuncts(cnsts, memo, arg(t,i)); + } + } + + void assert_conjuncts(solver &s, std::vector &cnsts, const ast &t){ + hash_map memo; + collect_conjuncts(cnsts,memo,t); + for(unsigned i = 0; i < cnsts.size(); i++) + s.assert_expr(to_expr(cnsts[i].raw())); + } iz3interp(ast_manager &_m_manager) : iz3base(_m_manager) {} @@ -329,6 +360,37 @@ void iz3interpolate(ast_manager &_m_manager, interps[i] = itp.uncook(_interps[i]); } +lbool iz3interpolate(ast_manager &_m_manager, + solver &s, + ast *tree, + ptr_vector &cnsts, + ptr_vector &interps, + model_ref &m, + interpolation_options_struct * options) +{ + iz3interp itp(_m_manager); + iz3mgr::ast _tree = itp.cook(tree); + std::vector _cnsts; + itp.assert_conjuncts(s,_cnsts,_tree); + lbool res = s.check_sat(0,0); + if(res == l_false){ + ast *proof = s.get_proof(); + iz3mgr::ast _proof = itp.cook(proof); + std::vector _interps; + itp.proof_to_interpolant(_proof,_cnsts,_tree,_interps,options); + interps.resize(_interps.size()); + for(unsigned i = 0; i < interps.size(); i++) + interps[i] = itp.uncook(_interps[i]); + } + else if(m){ + s.get_model(m); + } + cnsts.resize(_cnsts.size()); + for(unsigned i = 0; i < cnsts.size(); i++) + cnsts[i] = itp.uncook(_cnsts[i]); + return res; +} + diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h index 017791186..7174205f1 100644 --- a/src/interp/iz3interp.h +++ b/src/interp/iz3interp.h @@ -21,6 +21,7 @@ Revision History: #define IZ3_INTERP_H #include "iz3hash.h" +#include "solver.h" struct interpolation_options_struct { stl_ext::hash_map map; @@ -37,6 +38,9 @@ struct iz3_incompleteness { typedef interpolation_options_struct *interpolation_options; +/* Compute an interpolant from a proof. This version uses the parents vector + representation, for compatibility with the old API. */ + void iz3interpolate(ast_manager &_m_manager, ast *proof, const ptr_vector &cnsts, @@ -45,6 +49,9 @@ void iz3interpolate(ast_manager &_m_manager, const ptr_vector &theory, interpolation_options_struct * options = 0); +/* Compute an interpolant from a proof. This version uses the ast + representation, for compatibility with the new API. */ + void iz3interpolate(ast_manager &_m_manager, ast *proof, const ptr_vector &cnsts, @@ -52,4 +59,18 @@ void iz3interpolate(ast_manager &_m_manager, ptr_vector &interps, interpolation_options_struct * options); +/* Compute an interpolant from an ast representing an interpolation + problem, if unsat, else return a model (if enabled). Uses the + given solver to produce the proof/model. Also returns a vector + of the constraints in the problem, helpful for checking correctness. +*/ + +lbool iz3interpolate(ast_manager &_m_manager, + solver &s, + ast *tree, + ptr_vector &cnsts, + ptr_vector &interps, + model_ref &m, + interpolation_options_struct * options); + #endif From 6495d7b88c2f7b1a76af46736d86b0d6095069ef Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 15 Apr 2013 12:22:04 -0700 Subject: [PATCH 013/509] fixed so produce-interpolants option is not needed for compute-interpolant --- src/cmd_context/interpolant_cmds.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index ca22b3907..09bee80b2 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -123,16 +123,16 @@ static void get_and_check_interpolant(cmd_context & ctx, expr * t) { static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, bool check){ - - // get the proof, if there is one - - check_can_interpolate(ctx); // create a fresh solver suitable for interpolation bool proofs_enabled, models_enabled, unsat_core_enabled; params_ref p; ast_manager &_m = ctx.m(); + // TODO: the following is a HACK to enable proofs in the old smt solver + // When we stop using that solver, this hack can be removed + _m.toggle_proof_mode(PGM_FINE); ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); + p.set_bool("proof", true); scoped_ptr sp = (ctx.get_interpolating_solver_factory())(_m, p, true, models_enabled, false, ctx.get_logic()); ptr_vector cnsts; From 71275652a7fb650bfdc332d3f978f8f8ef5eebcb Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 15 Apr 2013 14:37:08 -0700 Subject: [PATCH 014/509] added simp of interpolants before print --- src/cmd_context/interpolant_cmds.cpp | 48 ++++++++++++++++++---------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index 09bee80b2..e202f1f69 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -32,11 +32,25 @@ Notes: #include"iz3interp.h" #include"iz3checker.h" -static void show_interpolant_and_maybe_check(cmd_context & ctx, ptr_vector &cnsts, expr *t, ptr_vector &interps, bool check) +static void show_interpolant_and_maybe_check(cmd_context & ctx, + ptr_vector &cnsts, + expr *t, + ptr_vector &interps, + params_ref &m_params, + bool check) { + if (m_params.get_bool("som", false)) + m_params.set_bool("flat", true); + th_rewriter s(ctx.m(), m_params); + for(unsigned i = 0; i < interps.size(); i++){ - ctx.regular_stream() << mk_pp(interps[i], ctx.m()) << std::endl; + + expr_ref r(ctx.m()); + proof_ref pr(ctx.m()); + s(to_expr(interps[i]),r,pr); + + ctx.regular_stream() << mk_pp(r.get(), ctx.m()) << std::endl; #if 0 ast_smt_pp pp(ctx.m()); pp.set_logic(ctx.get_logic().str().c_str()); @@ -45,6 +59,8 @@ static void show_interpolant_and_maybe_check(cmd_context & ctx, ptr_vector #endif } + s.cleanup(); + // verify, for the paranoid... if(check || ctx.check_interpolants()){ std::ostringstream err; @@ -74,7 +90,7 @@ static void check_can_interpolate(cmd_context & ctx){ } -static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, bool check) { +static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check) { check_can_interpolate(ctx); @@ -110,19 +126,19 @@ static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, bool ch throw cmd_exception("incompleteness in interpolator"); } - show_interpolant_and_maybe_check(ctx, cnsts, t, interps, check); + show_interpolant_and_maybe_check(ctx, cnsts, t, interps, m_params, check); } -static void get_interpolant(cmd_context & ctx, expr * t) { - get_interpolant_and_maybe_check(ctx,t,false); +static void get_interpolant(cmd_context & ctx, expr * t, params_ref &m_params) { + get_interpolant_and_maybe_check(ctx,t,m_params,false); } -static void get_and_check_interpolant(cmd_context & ctx, expr * t) { - get_interpolant_and_maybe_check(ctx,t,true); +static void get_and_check_interpolant(cmd_context & ctx, params_ref &m_params, expr * t) { + get_interpolant_and_maybe_check(ctx,t,m_params,true); } -static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, bool check){ +static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check){ // create a fresh solver suitable for interpolation bool proofs_enabled, models_enabled, unsat_core_enabled; @@ -152,7 +168,7 @@ static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, boo switch(res){ case l_false: ctx.regular_stream() << "unsat\n"; - show_interpolant_and_maybe_check(ctx, cnsts, t, interps, check); + show_interpolant_and_maybe_check(ctx, cnsts, t, interps, m_params, check); break; case l_true: @@ -181,17 +197,17 @@ static expr *make_tree(cmd_context & ctx, const ptr_vector &exprs){ return foo; } -static void get_interpolant(cmd_context & ctx, const ptr_vector &exprs) { +static void get_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { expr *foo = make_tree(ctx,exprs); ctx.m().inc_ref(foo); - get_interpolant(ctx,foo); + get_interpolant(ctx,foo,m_params); ctx.m().dec_ref(foo); } -static void compute_interpolant(cmd_context & ctx, const ptr_vector &exprs) { +static void compute_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { expr *foo = make_tree(ctx, exprs); ctx.m().inc_ref(foo); - compute_interpolant_and_maybe_check(ctx,foo,false); + compute_interpolant_and_maybe_check(ctx,foo,m_params,false); ctx.m().dec_ref(foo); } @@ -229,7 +245,7 @@ public: } virtual void execute(cmd_context & ctx) { - get_interpolant(ctx,m_targets); + get_interpolant(ctx,m_targets,m_params); } }; @@ -238,7 +254,7 @@ public: compute_interpolant_cmd(char const * name = "compute-interpolant"):get_interpolant_cmd(name) {} virtual void execute(cmd_context & ctx) { - compute_interpolant(ctx,m_targets); + compute_interpolant(ctx,m_targets,m_params); } }; From 8488ca24d28536a2b9f24ee6efbb99de40dd3f82 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sat, 20 Apr 2013 18:18:45 -0700 Subject: [PATCH 015/509] first commit of duality --- scripts/mk_project.py | 3 +- src/api/dotnet/Properties/AssemblyInfo.cs | 4 +- src/duality/duality.h | 792 ++++++++ src/duality/duality_hash.h | 169 ++ src/duality/duality_profiling.cpp | 201 ++ src/duality/duality_profiling.h | 38 + src/duality/duality_rpfp.cpp | 1991 +++++++++++++++++++ src/duality/duality_solver.cpp | 2200 +++++++++++++++++++++ src/duality/duality_wrapper.cpp | 523 +++++ src/duality/duality_wrapper.h | 1343 +++++++++++++ src/interp/iz3mgr.h | 1 + src/muz_qe/dl_context.cpp | 32 + src/muz_qe/dl_context.h | 12 +- src/muz_qe/dl_util.h | 1 + 14 files changed, 7305 insertions(+), 5 deletions(-) create mode 100644 src/duality/duality.h create mode 100755 src/duality/duality_hash.h create mode 100755 src/duality/duality_profiling.cpp create mode 100755 src/duality/duality_profiling.h create mode 100644 src/duality/duality_rpfp.cpp create mode 100644 src/duality/duality_solver.cpp create mode 100644 src/duality/duality_wrapper.cpp create mode 100755 src/duality/duality_wrapper.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index f23c4fe80..5682cc5d7 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -54,8 +54,9 @@ def init_project_def(): add_lib('fpa', ['core_tactics', 'bv_tactics', 'sat_tactic'], 'tactic/fpa') add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') + add_lib('duality', ['smt', 'interp']) # TODO: split muz_qe inbto muz, qe. Perhaps, we should also consider breaking muz into muz and pdr. - add_lib('muz_qe', ['smt', 'sat', 'smt2parser']) + add_lib('muz_qe', ['smt', 'sat', 'smt2parser', 'duality']) add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'muz_qe'], 'tactic/smtlogics') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') diff --git a/src/api/dotnet/Properties/AssemblyInfo.cs b/src/api/dotnet/Properties/AssemblyInfo.cs index 517349177..1cd0fe7b8 100644 --- a/src/api/dotnet/Properties/AssemblyInfo.cs +++ b/src/api/dotnet/Properties/AssemblyInfo.cs @@ -34,6 +34,6 @@ using System.Security.Permissions; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("4.2.0.0")] -[assembly: AssemblyVersion("4.3.2.0")] -[assembly: AssemblyFileVersion("4.3.2.0")] +[assembly: AssemblyVersion("4.3.2.0")] +[assembly: AssemblyFileVersion("4.3.2.0")] diff --git a/src/duality/duality.h b/src/duality/duality.h new file mode 100644 index 000000000..c75ecfa61 --- /dev/null +++ b/src/duality/duality.h @@ -0,0 +1,792 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality.h + +Abstract: + + main header for duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + +#pragma once + +#include "duality_wrapper.h" +#include +#include + +// make hash_map and hash_set available +#ifndef WIN32 +using namespace stl_ext; +#endif + +namespace Duality { + + /* Generic operations on Z3 formulas */ + + struct Z3User { + + context &ctx; + solver &slvr; + + typedef func_decl FuncDecl; + typedef expr Term; + + Z3User(context &_ctx, solver &_slvr) : ctx(_ctx), slvr(_slvr){} + + const char *string_of_int(int n); + + Term conjoin(const std::vector &args); + + Term sum(const std::vector &args); + + Term CloneQuantifier(const Term &t, const Term &new_body); + + Term SubstRec(hash_map &memo, const Term &t); + + void Strengthen(Term &x, const Term &y); + + // return the func_del of an app if it is uninterpreted + + bool get_relation(const Term &t, func_decl &R); + + // return true if term is an individual variable + // TODO: have to check that it is not a background symbol + + bool is_variable(const Term &t); + + FuncDecl SuffixFuncDecl(Term t, int n); + + + Term SubstRecHide(hash_map &memo, const Term &t, int number); + + void CollectConjuncts(const Term &f, std::vector &lits, bool pos = true); + + void SortTerms(std::vector &terms); + + Term SortSum(const Term &t); + + void Summarize(const Term &t); + + int CumulativeDecisions(); + + Term SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t); + + Term SubstBound(hash_map &subst, const Term &t); + + private: + + void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); + +}; + + /** This class represents a relation post-fixed point (RPFP) problem as + * a "problem graph". The graph consists of Nodes and hyper-edges. + * + * A node consists of + * - Annotation, a symbolic relation + * - Bound, a symbolic relation giving an upper bound on Annotation + * + * + * A hyper-edge consists of: + * - Children, a sequence of children Nodes, + * - F, a symbolic relational transformer, + * - Parent, a single parent Node. + * + * The graph is "solved" when: + * - For every Node n, n.Annotation subseteq n.Bound + * - For every hyperedge e, e.F(e.Children.Annotation) subseteq e.Parent.Annotation + * + * where, if x is a sequence of Nodes, x.Annotation is the sequences + * of Annotations of the nodes in the sequence. + * + * A symbolic Transformer consists of + * - RelParams, a sequence of relational symbols + * - IndParams, a sequence of individual symbols + * - Formula, a formula over RelParams and IndParams + * + * A Transformer t represents a function that takes sequence R of relations + * and yields the relation lambda (t.Indparams). Formula(R/RelParams). + * + * As a special case, a nullary Transformer (where RelParams is the empty sequence) + * represents a fixed relation. + * + * An RPFP consists of + * - Nodes, a set of Nodes + * - Edges, a set of hyper-edges + * - Context, a prover context that contains formula AST's + * + * Multiple RPFP's can use the same Context, but you should be careful + * that only one RPFP asserts constraints in the context at any time. + * + * */ + class RPFP : public Z3User + { + public: + + class Edge; + class Node; + bool HornClauses; + + + /** Interface class for interpolating solver. */ + + class LogicSolver { + public: + + context *ctx; /** Z3 context for formulas */ + solver *slvr; /** Z3 solver */ + bool need_goals; /** Can the solver use the goal tree to optimize interpolants? */ + + /** Tree interpolation. This method assumes the formulas in TermTree + "assumptions" are currently asserted in the solver. The return + value indicates whether the assertions are satisfiable. In the + UNSAT case, a tree interpolant is returned in "interpolants". + In the SAT case, a model is returned. + */ + + virtual + lbool interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals = 0 + ) = 0; + + /** Assert a background axiom. */ + virtual void assert_axiom(const expr &axiom) = 0; + + /** Return a string describing performance. */ + virtual std::string profile() = 0; + + virtual void write_interpolation_problem(const std::string &file_name, + const std::vector &assumptions, + const std::vector &theory + ){} + }; + + /** This solver uses iZ3. */ + class iZ3LogicSolver : public LogicSolver { + public: + interpolating_context *ictx; /** iZ3 context for formulas */ + interpolating_solver *islvr; /** iZ3 solver */ + + lbool interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals = 0) + { + literals _labels; + return islvr->interpolate_tree(assumptions,interpolants,_model,_labels,true); + } + + void assert_axiom(const expr &axiom){ + islvr->AssertInterpolationAxiom(axiom); + } + + std::string profile(){ + return islvr->profile(); + } + +#if 0 + iZ3LogicSolver(config &_config){ + ctx = ictx = new interpolating_context(_config); + slvr = islvr = new interpolating_solver(*ictx); + need_goals = false; + islvr->SetWeakInterpolants(true); + } +#endif + + iZ3LogicSolver(context c){ + ctx = ictx = new interpolating_context(c); + slvr = islvr = new interpolating_solver(*ictx); + need_goals = false; + islvr->SetWeakInterpolants(true); + } + + void write_interpolation_problem(const std::string &file_name, + const std::vector &assumptions, + const std::vector &theory + ){ +#if 0 + islvr->write_interpolation_problem(file_name,assumptions,theory); +#endif + } + ~iZ3LogicSolver(){ + delete ictx; + delete islvr; + } + }; + +#if 0 + /** Create a logic solver from a Z3 configuration. */ + static iZ3LogicSolver *CreateLogicSolver(config &_config){ + return new iZ3LogicSolver(_config); + } +#endif + + /** Create a logic solver from a low-level Z3 context. + Only use this if you know what you're doing. */ + static iZ3LogicSolver *CreateLogicSolver(context c){ + return new iZ3LogicSolver(c); + } + + LogicSolver *ls; + + private: + int nodeCount; + int edgeCount; + + class stack_entry + { + public: + std::list edges; + std::list nodes; + }; + + + model dualModel; + literals dualLabels; + std::list stack; + std::vector axioms; // only saved here for printing purposes + + public: + + /** Construct an RPFP graph with a given interpolating prover context. It is allowed to + have multiple RPFP's use the same context, but you should never have teo RPFP's + with the same conext asserting nodes or edges at the same time. Note, if you create + axioms in one RPFP, them create a second RPFP with the same context, the second will + inherit the axioms. + */ + + RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx), *(_ls->slvr)), dualModel(*(_ls->ctx)) + { + ls = _ls; + nodeCount = 0; + edgeCount = 0; + stack.push_back(stack_entry()); + HornClauses = false; + } + + ~RPFP(); + + /** Symbolic representation of a relational transformer */ + class Transformer + { + public: + std::vector RelParams; + std::vector IndParams; + Term Formula; + RPFP *owner; + hash_map labels; + + Transformer *Clone() + { + return new Transformer(*this); + } + + void SetEmpty(){ + Formula = owner->ctx.bool_val(false); + } + + void SetFull(){ + Formula = owner->ctx.bool_val(true); + } + + bool IsEmpty(){ + return eq(Formula,owner->ctx.bool_val(false)); + } + + bool IsFull(){ + return eq(Formula,owner->ctx.bool_val(true)); + } + + void UnionWith(const Transformer &other){ + Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); + Formula = Formula || t; + } + + void IntersectWith(const Transformer &other){ + Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); + Formula = Formula && t; + } + + bool SubsetEq(const Transformer &other){ + Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); + expr test = Formula && !t; + owner->slvr.push(); + owner->slvr.add(test); + check_result res = owner->slvr.check(); + owner->slvr.pop(1); + return res == unsat; + } + + void Complement(){ + Formula = !Formula; + } + + void Simplify(){ + Formula = Formula.simplify(); + } + + Transformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula, RPFP *_owner) + : RelParams(_RelParams), IndParams(_IndParams), Formula(_Formula) {owner = _owner;} + }; + + /** Create a symbolic transformer. */ + Transformer CreateTransformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula) + { + // var ops = new Util.ContextOps(ctx); + // var foo = ops.simplify_lhs(_Formula); + // t.Formula = foo.Item1; + // t.labels = foo.Item2; + return Transformer(_RelParams,_IndParams,_Formula,this); + } + + /** Create a relation (nullary relational transformer) */ + Transformer CreateRelation(const std::vector &_IndParams, const Term &_Formula) + { + return CreateTransformer(std::vector(), _IndParams, _Formula); + } + + /** A node in the RPFP graph */ + class Node + { + public: + FuncDecl Name; + Transformer Annotation; + Transformer Bound; + Transformer Underapprox; + RPFP *owner; + int number; + Edge *Outgoing; + std::vector Incoming; + Term dual; + Node *map; + + Node(const FuncDecl &_Name, const Transformer &_Annotation, const Transformer &_Bound, const Transformer &_Underapprox, const Term &_dual, RPFP *_owner, int _number) + : Name(_Name), Annotation(_Annotation), Bound(_Bound), Underapprox(_Underapprox), dual(_dual) {owner = _owner; number = _number; Outgoing = 0;} + }; + + /** Create a node in the graph. The input is a term R(v_1...v_n) + * where R is an arbitrary relational symbol and v_1...v_n are + * arbitary distinct variables. The names are only of mnemonic value, + * however, the number and type of arguments determine the type + * of the relation at this node. */ + + Node *CreateNode(const Term &t) + { + std::vector _IndParams; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + _IndParams.push_back(t.arg(i)); + Node *n = new Node(t.decl(), + CreateRelation(_IndParams,ctx.bool_val(true)), + CreateRelation(_IndParams,ctx.bool_val(true)), + CreateRelation(_IndParams,ctx.bool_val(false)), + expr(ctx), this, ++nodeCount + ); + nodes.push_back(n); + return n; + } + + /** Clone a node (can be from another graph). */ + + Node *CloneNode(Node *old) + { + Node *n = new Node(old->Name, + old->Annotation, + old->Bound, + old->Underapprox, + expr(ctx), + this, + ++nodeCount + ); + nodes.push_back(n); + n->map = old; + return n; + } + + /** This class represents a hyper-edge in the RPFP graph */ + + class Edge + { + public: + Transformer F; + Node *Parent; + std::vector Children; + RPFP *owner; + int number; + // these should be internal... + Term dual; + hash_map relMap; + hash_map varMap; + Edge *map; + + Edge(Node *_Parent, const Transformer &_F, const std::vector &_Children, RPFP *_owner, int _number) + : F(_F), Parent(_Parent), Children(_Children), dual(expr(_owner->ctx)) { + owner = _owner; + number = _number; + } + }; + + + /** Create a hyper-edge. */ + Edge *CreateEdge(Node *_Parent, const Transformer &_F, const std::vector &_Children) + { + Edge *e = new Edge(_Parent,_F,_Children,this,++edgeCount); + _Parent->Outgoing = e; + for(unsigned i = 0; i < _Children.size(); i++) + _Children[i]->Incoming.push_back(e); + edges.push_back(e); + return e; + } + + /** Create an edge that lower-bounds its parent. */ + Edge *CreateLowerBoundEdge(Node *_Parent) + { + return CreateEdge(_Parent, _Parent->Annotation, std::vector()); + } + + + /** For incremental solving, asserts the constraint associated + * with this edge in the SMT context. If this edge is removed, + * you must pop the context accordingly. The second argument is + * the number of pushes we are inside. */ + + void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); + + + /** For incremental solving, asserts the negation of the upper bound associated + * with a node. + * */ + + void AssertNode(Node *n); + + /** Assert a background axiom. Background axioms can be used to provide the + * theory of auxilliary functions or relations. All symbols appearing in + * background axioms are considered global, and may appear in both transformer + * and relational solutions. Semantically, a solution to the RPFP gives + * an interpretation of the unknown relations for each interpretation of the + * auxilliary symbols that is consistent with the axioms. Axioms should be + * asserted before any calls to Push. They cannot be de-asserted by Pop. */ + + void AssertAxiom(const Term &t); + +#if 0 + /** Do not call this. */ + + void RemoveAxiom(const Term &t); +#endif + + /** Solve an RPFP graph. This means either strengthen the annotation + * so that the bound at the given root node is satisfied, or + * show that this cannot be done by giving a dual solution + * (i.e., a counterexample). + * + * In the current implementation, this only works for graphs that + * are: + * - tree-like + * + * - closed. + * + * In a tree-like graph, every nod has out most one incoming and one out-going edge, + * and there are no cycles. In a closed graph, every node has exactly one out-going + * edge. This means that the leaves of the tree are all hyper-edges with no + * children. Such an edge represents a relation (nullary transformer) and thus + * a lower bound on its parent. The parameter root must be the root of this tree. + * + * If Solve returns LBool.False, this indicates success. The annotation of the tree + * has been updated to satisfy the upper bound at the root. + * + * If Solve returns LBool.True, this indicates a counterexample. For each edge, + * you can then call Eval to determine the values of symbols in the transformer formula. + * You can also call Empty on a node to determine if its value in the counterexample + * is the empty relation. + * + * \param root The root of the tree + * \param persist Number of context pops through which result should persist + * + * + */ + + lbool Solve(Node *root, int persist); + + /** Get the constraint tree (but don't solve it) */ + + TermTree *GetConstraintTree(Node *root); + + /** Dispose of the dual model (counterexample) if there is one. */ + + void DisposeDualModel(); + + /** Check satisfiability of asserted edges and nodes. Same functionality as + * Solve, except no primal solution (interpolant) is generated in the unsat case. */ + + check_result Check(Node *root, std::vector underapproxes = std::vector(), + std::vector *underapprox_core = 0); + + /** Update the model, attempting to make the propositional literals in assumps true. If possible, + return sat, else return unsat and keep the old model. */ + + check_result CheckUpdateModel(Node *root, std::vector assumps); + + /** Determines the value in the counterexample of a symbol occuring in the transformer formula of + * a given edge. */ + + Term Eval(Edge *e, Term t); + + /** Return the fact derived at node p in a counterexample. */ + + Term EvalNode(Node *p); + + /** Returns true if the given node is empty in the primal solution. For proecudure summaries, + this means that the procedure is not called in the current counter-model. */ + + bool Empty(Node *p); + + /** Compute an underapproximation of every node in a tree rooted at "root", + based on a previously computed counterexample. */ + + Term ComputeUnderapprox(Node *root, int persist); + + /** Push a scope. Assertions made after Push can be undone by Pop. */ + + void Push(); + + /** Exception thrown when bad clause is encountered */ + + struct bad_clause { + std::string msg; + int i; + bad_clause(const std::string &_msg, int _i){ + msg = _msg; + i = _i; + } + }; + + struct parse_error { + std::string msg; + parse_error(const std::string &_msg){ + msg = _msg; + } + }; + + struct file_not_found { + }; + + struct bad_format { + }; + + /** Pop a scope (see Push). Note, you cannot pop axioms. */ + + void Pop(int num_scopes); + + /** Convert a collection of clauses to Nodes and Edges in the RPFP. + + Predicate unknowns are uninterpreted predicates not + occurring in the background theory. + + Clauses are of the form + + B => P(t_1,...,t_k) + + where P is a predicate unknown and predicate unknowns + occur only positivey in H and only under existential + quantifiers in prenex form. + + Each predicate unknown maps to a node. Each clause maps to + an edge. Let C be a clause B => P(t_1,...,t_k) where the + sequence of predicate unknowns occurring in B (in order + of occurrence) is P_1..P_n. The clause maps to a transformer + T where: + + T.Relparams = P_1..P_n + T.Indparams = x_1...x+k + T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k + + Throws exception bad_clause(msg,i) if a clause i is + in the wrong form. + + */ + + +#ifdef WIN32 + __declspec(dllexport) +#endif + void FromClauses(const std::vector &clauses); + + void FromFixpointContext(fixedpoint fp, std::vector &queries); + + void WriteSolution(std::ostream &s); + + void WriteCounterexample(std::ostream &s, Node *node); + + enum FileFormat {DualityFormat, SMT2Format, HornFormat}; + + /** Write the RPFP to a file (currently in SMTLIB 1.2 format) */ + void WriteProblemToFile(std::string filename, FileFormat format = DualityFormat); + + /** Read the RPFP from a file (in specificed format) */ + void ReadProblemFromFile(std::string filename, FileFormat format = DualityFormat); + + /** Translate problem to Horn clause form */ + void ToClauses(std::vector &cnsts, FileFormat format = DualityFormat); + + /** Translate the RPFP to a fixed point context, with queries */ + fixedpoint ToFixedPointProblem(std::vector &queries); + + /** Nodes of the graph. */ + std::vector nodes; + + /** Edges of the graph. */ + std::vector edges; + + Term SubstParams(const std::vector &from, + const std::vector &to, const Term &t); + + Term Localize(Edge *e, const Term &t); + + void EvalNodeAsConstraint(Node *p, Transformer &res); + + TermTree *GetGoalTree(Node *root); + + private: + + + Term SuffixVariable(const Term &t, int n); + + Term HideVariable(const Term &t, int n); + + void RedVars(Node *node, Term &b, std::vector &v); + + Term RedDualRela(Edge *e, std::vector &args, int idx); + + Term LocalizeRec(Edge *e, hash_map &memo, const Term &t); + + void SetEdgeMaps(Edge *e); + + Term ReducedDualEdge(Edge *e); + + TermTree *ToTermTree(Node *root); + + TermTree *ToGoalTree(Node *root); + + void DecodeTree(Node *root, TermTree *interp, int persist); + + Term GetUpperBound(Node *n); + + TermTree *AddUpperBound(Node *root, TermTree *t); + +#if 0 + void WriteInterps(System.IO.StreamWriter f, TermTree t); +#endif + + void WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s); + + void WriteEdgeAssignment(std::ostream &s, Edge *e); + + + // Scan the clause body for occurrences of the predicate unknowns + + Term ScanBody(hash_map &memo, + const Term &t, + hash_map &pmap, + std::vector &res, + std::vector &nodes); + + + Term RemoveLabelsRec(hash_map &memo, const Term &t); + + Term RemoveLabels(const Term &t); + + Term GetAnnotation(Node *n); + + + Term GetUnderapprox(Node *n); + + Term UnderapproxFlag(Node *n); + + hash_map underapprox_flag_rev; + + Node *UnderapproxFlagRev(const Term &flag); + + Term ProjectFormula(std::vector &keep_vec, const Term &f); + + int SubtermTruth(hash_map &memo, const Term &); + + void ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set *done, bool truth, hash_set &dont_cares); + + void Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares); + + Term UnderapproxFormula(const Term &f, hash_set &dont_cares); + + Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); + + hash_map resolve_ite_memo; + + Term ResolveIte(hash_map &memo, const Term &t, std::vector &lits, + hash_set *done, hash_set &dont_cares); + + struct ArrayValue { + bool defined; + std::map entries; + expr def_val; + }; + + void EvalArrayTerm(const Term &t, ArrayValue &res); + + Term EvalArrayEquality(const Term &f); + + Term ModelValueAsConstraint(const Term &t); + + }; + + /** RPFP solver base class. */ + + class Solver { + + public: + + struct Counterexample { + RPFP *tree; + RPFP::Node *root; + Counterexample(){ + tree = 0; + root = 0; + } + }; + + /** Solve the problem. You can optionally give an old + counterexample to use as a guide. This is chiefly useful for + abstraction refinement metholdologies, and is only used as a + heuristic. */ + + virtual bool Solve() = 0; + + virtual Counterexample GetCounterexample() = 0; + + virtual bool SetOption(const std::string &option, const std::string &value) = 0; + + /** Learn heuristic information from another solver. This + is chiefly useful for abstraction refinement, when we want to + solve a series of similar problems. */ + + virtual void LearnFrom(Solver *old_solver) = 0; + + virtual ~Solver(){} + + static Solver *Create(const std::string &solver_class, RPFP *rpfp); + + + }; +} diff --git a/src/duality/duality_hash.h b/src/duality/duality_hash.h new file mode 100755 index 000000000..f6767c037 --- /dev/null +++ b/src/duality/duality_hash.h @@ -0,0 +1,169 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3hash.h + +Abstract: + + Wrapper for stl hash tables + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +// pull in the headers for has_map and hash_set +// these live in non-standard places + +#ifndef IZ3_HASH_H +#define IZ3_HASH_H + +//#define USE_UNORDERED_MAP +#ifdef USE_UNORDERED_MAP + +#define stl_ext std +#define hash_space std +#include +#include +#define hash_map unordered_map +#define hash_set unordered_set + +#else + +#if __GNUC__ >= 3 +#undef __DEPRECATED +#define stl_ext __gnu_cxx +#define hash_space stl_ext +#include +#include +#else +#ifdef WIN32 +#define stl_ext stdext +#define hash_space std +#include +#include +#else +#define stl_ext std +#define hash_space std +#include +#include +#endif +#endif + +#endif + +#include + +// stupid STL doesn't include hash function for class string + +#ifndef WIN32 + +namespace stl_ext { + template <> + class hash { + stl_ext::hash H; + public: + size_t operator()(const std::string &s) const { + return H(s.c_str()); + } + }; +} + +#endif + +namespace hash_space { + template <> + class hash > { + public: + size_t operator()(const std::pair &p) const { + return p.first + p.second; + } + }; +} + +#ifdef WIN32 +template <> inline +size_t stdext::hash_value >(const std::pair& p) +{ // hash _Keyval to size_t value one-to-one + return p.first + p.second; +} +#endif + +namespace hash_space { + template + class hash > { + public: + size_t operator()(const std::pair &p) const { + return (size_t)p.first + (size_t)p.second; + } + }; +} + +#if 0 +template inline +size_t stdext::hash_value >(const std::pair& p) +{ // hash _Keyval to size_t value one-to-one + return (size_t)p.first + (size_t)p.second; +} +#endif + +#ifdef WIN32 + +namespace std { + template <> + class less > { + public: + bool operator()(const pair &x, const pair &y) const { + return x.first < y.first || x.first == y.first && x.second < y.second; + } + }; + +} + +namespace std { + template + class less > { + public: + bool operator()(const pair &x, const pair &y) const { + return (size_t)x.first < (size_t)y.first || (size_t)x.first == (size_t)y.first && (size_t)x.second < (size_t)y.second; + } + }; + +} + +#endif + + +#ifndef WIN32 + +namespace stl_ext { + template + class hash { + public: + size_t operator()(const T *p) const { + return (size_t) p; + } + }; +} + +#endif + +#ifdef WIN32 + + + + +template +class hash_map : public stl_ext::hash_map > > {}; + +template +class hash_set : public stl_ext::hash_set > > {}; + +#endif + +#endif diff --git a/src/duality/duality_profiling.cpp b/src/duality/duality_profiling.cpp new file mode 100755 index 000000000..b516e03ca --- /dev/null +++ b/src/duality/duality_profiling.cpp @@ -0,0 +1,201 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + duality_profiling.cpp + +Abstract: + + collection performance information for duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + + +#include +#include +#include +#include +#include + +#ifdef WIN32 + +// include "Windows.h" + + +#if 0 +typedef __int64 clock_t; + +static clock_t current_time(){ + LARGE_INTEGER lpPerformanceCount; + lpPerformanceCount.QuadPart = 0; + QueryPerformanceCounter(&lpPerformanceCount); + return lpPerformanceCount.QuadPart; +} + +static void output_time(std::ostream &os, clock_t time){ + LARGE_INTEGER lpFrequency; + lpFrequency.QuadPart = 1; + QueryPerformanceFrequency(&lpFrequency); + os << ((double)time)/lpFrequency.QuadPart; +} +#else + +typedef double clock_t; + +static clock_t current_time(){ + FILETIME lpCreationTime; + FILETIME lpExitTime; + FILETIME lpKernelTime; + FILETIME lpUserTime; + + GetProcessTimes( GetCurrentProcess(), + &lpCreationTime, &lpExitTime, &lpKernelTime, &lpUserTime ); + + + double usr_time = ((double) lpUserTime.dwLowDateTime)/10000000. + + ((double) lpUserTime.dwHighDateTime)/(10000000. * pow(2.0,32.0)); + return usr_time; +} + +static void output_time(std::ostream &os, clock_t time){ + os << time; +} + +#endif + +#else + +#include +#include +#include +#include + +static clock_t current_time(){ +#if 0 + struct tms buf; + times(&buf); + return buf.tms_utime; +#else + struct rusage r; + getrusage(RUSAGE_SELF, &r); + return 1000 * r.ru_utime.tv_sec + r.ru_utime.tv_usec / 1000; +#endif +} + +static void output_time(std::ostream &os, clock_t time){ +#if 0 + os << ((double)time)/sysconf(_SC_CLK_TCK); +#else + os << ((double)time)/1000; +#endif +} + +#endif + + + +namespace Duality { + + void show_time(){ + output_time(std::cout,current_time()); + std::cout << "\n"; + } + + typedef std::map nmap; + + struct node { + std::string name; + clock_t time; + clock_t start_time; + nmap sub; + struct node *parent; + + node(); + } top; + + node::node(){ + time = 0; + parent = 0; + } + + struct node *current; + + struct init { + init(){ + top.name = "TOTAL"; + current = ⊤ + } + } initializer; + + struct time_entry { + clock_t t; + time_entry(){t = 0;}; + void add(clock_t incr){t += incr;} + }; + + struct ltstr + { + bool operator()(const char* s1, const char* s2) const + { + return strcmp(s1, s2) < 0; + } + }; + + typedef std::map tmap; + + static std::ostream *pfs; + + void print_node(node &top, int indent, tmap &totals){ + for(int i = 0; i < indent; i++) (*pfs) << " "; + (*pfs) << top.name; + int dots = 70 - 2 * indent - top.name.size(); + for(int i = 0; i second,indent+1,totals); + } + + void print_profile(std::ostream &os) { + pfs = &os; + top.time = 0; + for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++) + top.time += it->second.time; + tmap totals; + print_node(top,0,totals); + (*pfs) << "TOTALS:" << std::endl; + for(tmap::iterator it = totals.begin(); it != totals.end(); it++){ + (*pfs) << (it->first) << " "; + output_time(*pfs, it->second.t); + (*pfs) << std::endl; + } + } + + void timer_start(const char *name){ + node &child = current->sub[name]; + if(child.name.empty()){ // a new node + child.parent = current; + child.name = name; + } + child.start_time = current_time(); + current = &child; + } + + void timer_stop(const char *name){ + if(current->name != name || !current->parent){ + std::cerr << "imbalanced timer_start and timer_stop"; + exit(1); + } + current->time += (current_time() - current->start_time); + current = current->parent; + } +} diff --git a/src/duality/duality_profiling.h b/src/duality/duality_profiling.h new file mode 100755 index 000000000..ff70fae23 --- /dev/null +++ b/src/duality/duality_profiling.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + duality_profiling.h + +Abstract: + + collection performance information for duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + +#ifndef DUALITYPROFILING_H +#define DUALITYPROFILING_H + +#include + +namespace Duality { + /** Start a timer with given name */ + void timer_start(const char *); + /** Stop a timer with given name */ + void timer_stop(const char *); + /** Print out timings */ + void print_profile(std::ostream &s); + /** Show the current time. */ + void show_time(); +} + +#endif + diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp new file mode 100644 index 000000000..6b176a574 --- /dev/null +++ b/src/duality/duality_rpfp.cpp @@ -0,0 +1,1991 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality_rpfp.h + +Abstract: + + implements relational post-fixedpoint problem + (RPFP) data structure. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + + + +#include "duality.h" +#include "duality_profiling.h" +#include +#include + +#ifndef WIN32 +// #define Z3OPS +#endif + +// TODO: do we need these? +#ifdef Z3OPS + +class Z3_subterm_truth { + public: + virtual bool eval(Z3_ast f) = 0; + ~Z3_subterm_truth(){} +}; + +Z3_subterm_truth *Z3_mk_subterm_truth(Z3_context ctx, Z3_model model); + +#endif + +#include + +// TODO: use something official for this +int debug_gauss = 0; + +namespace Duality { + + static char string_of_int_buffer[20]; + + const char *Z3User::string_of_int(int n){ + sprintf(string_of_int_buffer,"%d",n); + return string_of_int_buffer; + } + + RPFP::Term RPFP::SuffixVariable(const Term &t, int n) + { + std::string name = t.decl().name().str() + "_" + string_of_int(n); + return ctx.constant(name.c_str(), t.get_sort()); + } + + RPFP::Term RPFP::HideVariable(const Term &t, int n) + { + std::string name = std::string("@p_") + t.decl().name().str() + "_" + string_of_int(n); + return ctx.constant(name.c_str(), t.get_sort()); + } + + void RPFP::RedVars(Node *node, Term &b, std::vector &v) + { + int idx = node->number; + if(HornClauses) + b = ctx.bool_val(true); + else { + std::string name = std::string("@b_") + string_of_int(idx); + symbol sym = ctx.str_symbol(name.c_str()); + b = ctx.constant(sym,ctx.bool_sort()); + } + // ctx.constant(name.c_str(), ctx.bool_sort()); + v = node->Annotation.IndParams; + for(unsigned i = 0; i < v.size(); i++) + v[i] = SuffixVariable(v[i],idx); + } + + void Z3User::SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t){ + if(memo.find(t) != memo.end()) + return; + memo.insert(t); + decl_kind k = t.decl().get_decl_kind(); + if(k == And || k == Or || k == Not || k == Implies || k == Iff){ + ops++; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + SummarizeRec(memo,lits,ops,t.arg(i)); + } + else + lits.push_back(t); + } + + int Z3User::CumulativeDecisions(){ +#if 0 + std::string stats = Z3_statistics_to_string(ctx); + int pos = stats.find("decisions:"); + pos += 10; + int end = stats.find('\n',pos); + std::string val = stats.substr(pos,end-pos); + return atoi(val.c_str()); +#endif + return slvr.get_num_decisions(); + } + + + void Z3User::Summarize(const Term &t){ + hash_set memo; std::vector lits; int ops = 0; + SummarizeRec(memo, lits, ops, t); + std::cout << ops << ": "; + for(unsigned i = 0; i < lits.size(); i++){ + if(i > 0) std::cout << ", "; + std::cout << lits[i]; + } + } + + Z3User::Term Z3User::conjoin(const std::vector &args){ + return ctx.make(And,args); + } + + Z3User::Term Z3User::sum(const std::vector &args){ + return ctx.make(Plus,args); + } + + RPFP::Term RPFP::RedDualRela(Edge *e, std::vector &args, int idx){ + Node *child = e->Children[idx]; + Term b(ctx); + std::vector v; + RedVars(child, b, v); + for (unsigned i = 0; i < args.size(); i++) + { + if (eq(args[i].get_sort(),ctx.bool_sort())) + args[i] = ctx.make(Iff,args[i], v[i]); + else + args[i] = args[i] == v[i]; + } + return args.size() > 0 ? (b && conjoin(args)) : b; + } + + Z3User::Term Z3User::CloneQuantifier(const Term &t, const Term &new_body){ +#if 0 + Z3_context c = ctx; + Z3_ast a = t; + std::vector pats; + int num_pats = Z3_get_quantifier_num_patterns(c,a); + for(int i = 0; i < num_pats; i++) + pats.push_back(Z3_get_quantifier_pattern_ast(c,a,i)); + std::vector no_pats; + int num_no_pats = Z3_get_quantifier_num_patterns(c,a); + for(int i = 0; i < num_no_pats; i++) + no_pats.push_back(Z3_get_quantifier_no_pattern_ast(c,a,i)); + int bound = Z3_get_quantifier_num_bound(c,a); + std::vector sorts; + std::vector names; + for(int i = 0; i < bound; i++){ + sorts.push_back(Z3_get_quantifier_bound_sort(c,a,i)); + names.push_back(Z3_get_quantifier_bound_name(c,a,i)); + } + Z3_ast foo = Z3_mk_quantifier_ex(c, + Z3_is_quantifier_forall(c,a), + Z3_get_quantifier_weight(c,a), + 0, + 0, + num_pats, + &pats[0], + num_no_pats, + &no_pats[0], + bound, + &sorts[0], + &names[0], + new_body); + return expr(ctx,foo); +#endif + return clone_quantifier(t,new_body); + } + + RPFP::Term RPFP::LocalizeRec(Edge *e, hash_map &memo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + hash_map::iterator it = e->varMap.find(t); + if (it != e->varMap.end()){ + res = it->second; + return res; + } + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(LocalizeRec(e, memo, t.arg(i))); + hash_map::iterator rit = e->relMap.find(f); + if(rit != e->relMap.end()) + res = RedDualRela(e,args,(rit->second)); + else { + if (args.size() == 0 && f.get_decl_kind() == Uninterpreted) + { + res = HideVariable(t,e->number); + } + else + { + res = f(args.size(),&args[0]); + } + } + } + else if (t.is_quantifier()) + { + Term body = LocalizeRec(e,memo,t.body()); + res = CloneQuantifier(t,body); + } + else res = t; + return res; + } + + void RPFP::SetEdgeMaps(Edge *e){ + timer_start("SetEdgeMaps"); + e->relMap.clear(); + e->varMap.clear(); + for(unsigned i = 0; i < e->F.RelParams.size(); i++){ + e->relMap[e->F.RelParams[i]] = i; + } + Term b(ctx); + std::vector v; + RedVars(e->Parent, b, v); + for(unsigned i = 0; i < e->F.IndParams.size(); i++){ + // func_decl parentParam = e->Parent.Annotation.IndParams[i]; + expr oldname = e->F.IndParams[i]; + expr newname = v[i]; + e->varMap[oldname] = newname; + } + timer_stop("SetEdgeMaps"); + + } + + + RPFP::Term RPFP::Localize(Edge *e, const Term &t){ + timer_start("Localize"); + hash_map memo; + Term res = LocalizeRec(e,memo,t); + timer_stop("Localize"); + return res; + } + + + RPFP::Term RPFP::ReducedDualEdge(Edge *e) + { + SetEdgeMaps(e); + timer_start("RedVars"); + Term b(ctx); + std::vector v; + RedVars(e->Parent, b, v); + timer_stop("RedVars"); + // ast_to_track = (ast)b; + return implies(b, Localize(e, e->F.Formula)); + } + + TermTree *RPFP::ToTermTree(Node *root) + { + Edge *e = root->Outgoing; + if(!e) return new TermTree(ctx.bool_val(true), std::vector()); + std::vector children(e->Children.size()); + for(unsigned i = 0; i < children.size(); i++) + children[i] = ToTermTree(e->Children[i]); + // Term top = ReducedDualEdge(e); + Term top = e->dual.null() ? ctx.bool_val(true) : e->dual; + return new TermTree(top, children); + } + + TermTree *RPFP::GetGoalTree(Node *root){ + std::vector children(1); + children[0] = ToGoalTree(root); + return new TermTree(ctx.bool_val(false),children); + } + + TermTree *RPFP::ToGoalTree(Node *root) + { + Term b(ctx); + std::vector v; + RedVars(root, b, v); + Term goal = root->Name(v); + Edge *e = root->Outgoing; + if(!e) return new TermTree(goal, std::vector()); + std::vector children(e->Children.size()); + for(unsigned i = 0; i < children.size(); i++) + children[i] = ToGoalTree(e->Children[i]); + // Term top = ReducedDualEdge(e); + return new TermTree(goal, children); + } + + Z3User::Term Z3User::SubstRec(hash_map &memo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(SubstRec(memo, t.arg(i))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + res = CloneQuantifier(t,SubstRec(memo, t.body())); + else res = t; + return res; + } + + Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ + std::string name = std::string("@q_") + t.decl().name().str() + "_" + string_of_int(number); + res = ctx.constant(name.c_str(), t.get_sort()); + return res; + } + for(int i = 0; i < nargs; i++) + args.push_back(SubstRec(memo, t.arg(i))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + res = CloneQuantifier(t,SubstRec(memo, t.body())); + else res = t; + return res; + } + + RPFP::Term RPFP::SubstParams(const std::vector &from, + const std::vector &to, const Term &t){ + hash_map memo; + bool some_diff = false; + for(unsigned i = 0; i < from.size(); i++) + if(i < to.size() && !eq(from[i],to[i])){ + memo[from[i]] = to[i]; + some_diff = true; + } + return some_diff ? SubstRec(memo,t) : t; + } + + + void Z3User::Strengthen(Term &x, const Term &y) + { + if (eq(x,ctx.bool_val(true))) + x = y; + else + x = x && y; + } + + void RPFP::DecodeTree(Node *root, TermTree *interp, int persist) + { + std::vector &ic = interp->getChildren(); + if (ic.size() > 0) + { + std::vector &nc = root->Outgoing->Children; + for (unsigned i = 0; i < nc.size(); i++) + DecodeTree(nc[i], ic[i], persist); + } + hash_map memo; + Term b; + std::vector v; + RedVars(root, b, v); + memo[b] = ctx.bool_val(true); + for (unsigned i = 0; i < v.size(); i++) + memo[v[i]] = root->Annotation.IndParams[i]; + Term annot = SubstRec(memo, interp->getTerm()); + // Strengthen(ref root.Annotation.Formula, annot); + root->Annotation.Formula = annot; +#if 0 + if(persist != 0) + Z3_persist_ast(ctx,root->Annotation.Formula,persist); +#endif + } + + RPFP::Term RPFP::GetUpperBound(Node *n) + { + // TODO: cache this + Term b(ctx); std::vector v; + RedVars(n, b, v); + hash_map memo; + for (unsigned int i = 0; i < v.size(); i++) + memo[n->Bound.IndParams[i]] = v[i]; + Term cnst = SubstRec(memo, n->Bound.Formula); + return b && !cnst; + } + + RPFP::Term RPFP::GetAnnotation(Node *n) + { + if(eq(n->Annotation.Formula,ctx.bool_val(true))) + return n->Annotation.Formula; + // TODO: cache this + Term b(ctx); std::vector v; + RedVars(n, b, v); + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[n->Annotation.IndParams[i]] = v[i]; + Term cnst = SubstRec(memo, n->Annotation.Formula); + return !b || cnst; + } + + RPFP::Term RPFP::GetUnderapprox(Node *n) + { + // TODO: cache this + Term b(ctx); std::vector v; + RedVars(n, b, v); + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[n->Underapprox.IndParams[i]] = v[i]; + Term cnst = SubstRecHide(memo, n->Underapprox.Formula, n->number); + return !b || cnst; + } + + TermTree *RPFP::AddUpperBound(Node *root, TermTree *t) + { + Term f = !((ast)(root->dual)) ? ctx.bool_val(true) : root->dual; + std::vector c(1); c[0] = t; + return new TermTree(f, c); + } + +#if 0 + void RPFP::WriteInterps(System.IO.StreamWriter f, TermTree t) + { + foreach (var c in t.getChildren()) + WriteInterps(f, c); + f.WriteLine(t.getTerm()); + } +#endif + + + /** For incremental solving, asserts the constraint associated + * with this edge in the SMT context. If this edge is removed, + * you must pop the context accordingly. The second argument is + * the number of pushes we are inside. The constraint formula + * will survive "persist" pops of the context. If you plan + * to reassert the edge after popping the context once, + * you can save time re-constructing the formula by setting + * "persist" to one. If you set "persist" too high, however, + * you could have a memory leak. + * + * The flag "with children" causes the annotations of the children + * to be asserted. The flag underapprox causes the underapproximations + * of the children to be asserted *conditionally*. See Check() on + * how to actually enforce these constraints. + * + */ + + void RPFP::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) + { + if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) + return; + if (e->dual.null()) + { + timer_start("ReducedDualEdge"); + e->dual = ReducedDualEdge(e); + timer_stop("ReducedDualEdge"); + timer_start("getting children"); + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + e->dual = e->dual && GetAnnotation(e->Children[i]); + if(underapprox){ + std::vector cus(e->Children.size()); + for(unsigned i = 0; i < e->Children.size(); i++) + cus[i] = !UnderapproxFlag(e->Children[i]) || GetUnderapprox(e->Children[i]); + expr cnst = conjoin(cus); + e->dual = e->dual && cnst; + } + timer_stop("getting children"); + timer_start("Persisting"); + std::list::reverse_iterator it = stack.rbegin(); + for(int i = 0; i < persist && it != stack.rend(); i++) + it++; + if(it != stack.rend()) + it->edges.push_back(e); +#if 0 + if(persist != 0) + Z3_persist_ast(ctx,e->dual,persist); +#endif + timer_stop("Persisting"); + //Console.WriteLine("{0}", cnst); + } + timer_start("solver add"); + slvr.add(e->dual); + timer_stop("solver add"); + } + + + /** For incremental solving, asserts the negation of the upper bound associated + * with a node. + * */ + + void RPFP::AssertNode(Node *n) + { + if (n->dual.null()) + { + n->dual = GetUpperBound(n); + stack.back().nodes.push_back(n); + slvr.add(n->dual); + } + } + + /** Assert a background axiom. Background axioms can be used to provide the + * theory of auxilliary functions or relations. All symbols appearing in + * background axioms are considered global, and may appear in both transformer + * and relational solutions. Semantically, a solution to the RPFP gives + * an interpretation of the unknown relations for each interpretation of the + * auxilliary symbols that is consistent with the axioms. Axioms should be + * asserted before any calls to Push. They cannot be de-asserted by Pop. */ + + void RPFP::AssertAxiom(const Term &t) + { + ls->assert_axiom(t); + axioms.push_back(t); // for printing only + } + +#if 0 + /** Do not call this. */ + + void RPFP::RemoveAxiom(const Term &t) + { + slvr.RemoveInterpolationAxiom(t); + } +#endif + + /** Solve an RPFP graph. This means either strengthen the annotation + * so that the bound at the given root node is satisfied, or + * show that this cannot be done by giving a dual solution + * (i.e., a counterexample). + * + * In the current implementation, this only works for graphs that + * are: + * - tree-like + * + * - closed. + * + * In a tree-like graph, every nod has out most one incoming and one out-going edge, + * and there are no cycles. In a closed graph, every node has exactly one out-going + * edge. This means that the leaves of the tree are all hyper-edges with no + * children. Such an edge represents a relation (nullary transformer) and thus + * a lower bound on its parent. The parameter root must be the root of this tree. + * + * If Solve returns LBool.False, this indicates success. The annotation of the tree + * has been updated to satisfy the upper bound at the root. + * + * If Solve returns LBool.True, this indicates a counterexample. For each edge, + * you can then call Eval to determine the values of symbols in the transformer formula. + * You can also call Empty on a node to determine if its value in the counterexample + * is the empty relation. + * + * \param root The root of the tree + * \param persist Number of context pops through which result should persist + * + * + */ + + lbool RPFP::Solve(Node *root, int persist) + { + timer_start("Solve"); + TermTree *tree = GetConstraintTree(root); + TermTree *interpolant = NULL; + TermTree *goals = NULL; + if(ls->need_goals) + goals = GetGoalTree(root); + + // if (dualModel != null) dualModel.Dispose(); + // if (dualLabels != null) dualLabels.Dispose(); + + timer_start("interpolate_tree"); + lbool res = ls->interpolate_tree(tree, interpolant, dualModel,goals); + timer_stop("interpolate_tree"); + if (res == l_false) + { + DecodeTree(root, interpolant->getChildren()[0], persist); + } + + timer_stop("Solve"); + return res; + } + + /** Get the constraint tree (but don't solve it) */ + + TermTree *RPFP::GetConstraintTree(Node *root) + { + return AddUpperBound(root, ToTermTree(root)); + } + + /** Dispose of the dual model (counterexample) if there is one. */ + + void RPFP::DisposeDualModel() + { + dualModel = model(ctx,NULL); + } + + RPFP::Term RPFP::UnderapproxFlag(Node *n){ + expr flag = ctx.constant((std::string("@under") + string_of_int(n->number)).c_str(), ctx.bool_sort()); + underapprox_flag_rev[flag] = n; + return flag; + } + + RPFP::Node *RPFP::UnderapproxFlagRev(const Term &flag){ + return underapprox_flag_rev[flag]; + } + + /** Check satisfiability of asserted edges and nodes. Same functionality as + * Solve, except no primal solution (interpolant) is generated in the unsat case. + * The vector underapproxes gives the set of node underapproximations to be enforced + * (assuming these were conditionally asserted by AssertEdge). + * + */ + + check_result RPFP::Check(Node *root, std::vector underapproxes, std::vector *underapprox_core ) + { + // if (dualModel != null) dualModel.Dispose(); + check_result res; + if(!underapproxes.size()) + res = slvr.check(); + else { + std::vector us(underapproxes.size()); + for(unsigned i = 0; i < underapproxes.size(); i++) + us[i] = UnderapproxFlag(underapproxes[i]); + slvr.check(); // TODO: no idea why I need to do this + if(underapprox_core){ + std::vector unsat_core(us.size()); + unsigned core_size = 0; + res = slvr.check(us.size(),&us[0],&core_size,&unsat_core[0]); + underapprox_core->resize(core_size); + for(unsigned i = 0; i < core_size; i++) + (*underapprox_core)[i] = UnderapproxFlagRev(unsat_core[i]); + } + else { + res = slvr.check(us.size(),&us[0]); + bool dump = false; + if(dump){ + std::vector cnsts; + // cnsts.push_back(axioms[0]); + cnsts.push_back(root->dual); + cnsts.push_back(root->Outgoing->dual); + ls->write_interpolation_problem("temp.smt",cnsts,std::vector()); + } + } + // check_result temp = slvr.check(); + } + dualModel = slvr.get_model(); + return res; + } + + check_result RPFP::CheckUpdateModel(Node *root, std::vector assumps){ + // check_result temp1 = slvr.check(); // no idea why I need to do this + check_result res = slvr.check_keep_model(assumps.size(),&assumps[0]); + dualModel = slvr.get_model(); + return res; + } + + /** Determines the value in the counterexample of a symbol occuring in the transformer formula of + * a given edge. */ + + RPFP::Term RPFP::Eval(Edge *e, Term t) + { + Term tl = Localize(e, t); + return dualModel.eval(tl); + } + + + + /** Returns true if the given node is empty in the primal solution. For proecudure summaries, + this means that the procedure is not called in the current counter-model. */ + + bool RPFP::Empty(Node *p) + { + Term b; std::vector v; + RedVars(p, b, v); + // dualModel.show_internals(); + // std::cout << "b: " << b << std::endl; + expr bv = dualModel.eval(b); + // std::cout << "bv: " << bv << std::endl; + bool res = !eq(bv,ctx.bool_val(true)); + return res; + } + + RPFP::Term RPFP::EvalNode(Node *p) + { + Term b; std::vector v; + RedVars(p, b, v); + std::vector args; + for(unsigned i = 0; i < v.size(); i++) + args.push_back(dualModel.eval(v[i])); + return (p->Name)(args); + } + + void RPFP::EvalArrayTerm(const RPFP::Term &t, ArrayValue &res){ + if(t.is_app()){ + decl_kind k = t.decl().get_decl_kind(); + if(k == AsArray){ + func_decl fd = t.decl().get_func_decl_parameter(0); + func_interp r = dualModel.get_func_interp(fd); + int num = r.num_entries(); + res.defined = true; + for(int i = 0; i < num; i++){ + expr arg = r.get_arg(i,0); + expr value = r.get_value(i); + res.entries[arg] = value; + } + res.def_val = r.else_value(); + return; + } + else if(k == Store){ + EvalArrayTerm(t.arg(0),res); + if(!res.defined)return; + expr addr = t.arg(1); + expr val = t.arg(2); + if(addr.is_numeral() && val.is_numeral()){ + if(eq(val,res.def_val)) + res.entries.erase(addr); + else + res.entries[addr] = val; + } + else + res.defined = false; + return; + } + } + res.defined = false; + } + + int eae_count = 0; + + RPFP::Term RPFP::EvalArrayEquality(const RPFP::Term &f){ + ArrayValue lhs,rhs; + eae_count++; + EvalArrayTerm(f.arg(0),lhs); + EvalArrayTerm(f.arg(1),rhs); + if(lhs.defined && rhs.defined){ + if(eq(lhs.def_val,rhs.def_val)) + if(lhs.entries == rhs.entries) + return ctx.bool_val(true); + return ctx.bool_val(false); + } + return f; + } + + /** Compute truth values of all boolean subterms in current model. + Assumes formulas has been simplified by Z3, so only boolean ops + ands and, or, not. Returns result in memo. + */ + + int RPFP::SubtermTruth(hash_map &memo, const Term &f){ + if(memo.find(f) != memo.end()){ + return memo[f]; + } + int res; + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + res = SubtermTruth(memo,!f.arg(0) || f.arg(1)); + goto done; + } + if(k == And) { + res = 1; + for(int i = 0; i < nargs; i++){ + int ar = SubtermTruth(memo,f.arg(i)); + if(ar == 0){ + res = 0; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Or) { + res = 0; + for(int i = 0; i < nargs; i++){ + int ar = SubtermTruth(memo,f.arg(i)); + if(ar == 1){ + res = 1; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Not) { + int ar = SubtermTruth(memo,f.arg(0)); + res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); + goto done; + } + } + { + expr bv = dualModel.eval(f); + if(bv.is_app() && bv.decl().get_decl_kind() == Equal && + bv.arg(0).is_array()){ + bv = EvalArrayEquality(bv); + } + // Hack!!!! array equalities can occur negatively! + if(bv.is_app() && bv.decl().get_decl_kind() == Not && + bv.arg(0).decl().get_decl_kind() == Equal && + bv.arg(0).arg(0).is_array()){ + bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); + } + if(eq(bv,ctx.bool_val(true))) + res = 1; + else if(eq(bv,ctx.bool_val(false))) + res = 0; + else + res = 2; + } + done: + memo[f] = res; + return res; + } + +#ifdef Z3OPS + static Z3_subterm_truth *stt; +#endif + + int ir_count = 0; + + void RPFP::ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set *done, bool truth, hash_set &dont_cares){ + if(done[truth].find(f) != done[truth].end()) + return; /* already processed */ +#if 0 + int this_count = ir_count++; + if(this_count == 50092) + std::cout << "foo!\n"; +#endif + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + ImplicantRed(memo,!f.arg(0) || f.arg(1),lits,done,truth,dont_cares); + goto done; + } + if(k == Iff){ + int b = SubtermTruth(memo,f.arg(0)); + if(b == 2) + throw "disaster in ImplicantRed"; + ImplicantRed(memo,f.arg(1),lits,done,truth ? b : !b,dont_cares); + goto done; + } + if(truth ? k == And : k == Or) { + for(int i = 0; i < nargs; i++) + ImplicantRed(memo,f.arg(i),lits,done,truth,dont_cares); + goto done; + } + if(truth ? k == Or : k == And) { + for(int i = 0; i < nargs; i++){ + Term a = f.arg(i); +#if 0 + if(i == nargs - 1){ // last chance! + ImplicantRed(memo,a,lits,done,truth,dont_cares); + goto done; + } +#endif + timer_start("SubtermTruth"); +#ifdef Z3OPS + bool b = stt->eval(a); +#else + int b = SubtermTruth(memo,a); +#endif + timer_stop("SubtermTruth"); + if(truth ? (b == 1) : (b == 0)){ + ImplicantRed(memo,a,lits,done,truth,dont_cares); + goto done; + } + } + /* Unreachable! */ + throw "error in RPFP::ImplicantRed"; + goto done; + } + else if(k == Not) { + ImplicantRed(memo,f.arg(0),lits,done,!truth,dont_cares); + goto done; + } + } + { + if(dont_cares.find(f) == dont_cares.end()){ + expr rf = ResolveIte(memo,f,lits,done,dont_cares); + expr bv = truth ? rf : !rf; + lits.push_back(bv); + } + } + done: + done[truth].insert(f); + } + + RPFP::Term RPFP::ResolveIte(hash_map &memo, const Term &t, std::vector &lits, + hash_set *done, hash_set &dont_cares){ + if(resolve_ite_memo.find(t) != resolve_ite_memo.end()) + return resolve_ite_memo[t]; + Term res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + if(f.get_decl_kind() == Ite){ + timer_start("SubtermTruth"); +#ifdef Z3OPS + bool sel = stt->eval(t.arg(0)); +#else + int xval = SubtermTruth(memo,t.arg(0)); + bool sel; + if(xval == 0)sel = false; + else if(xval == 1)sel = true; + else + throw "unresolved ite in model"; +#endif + timer_stop("SubtermTruth"); + ImplicantRed(memo,t.arg(0),lits,done,sel,dont_cares); + res = ResolveIte(memo,t.arg(sel?1:2),lits,done,dont_cares); + } + else { + for(int i = 0; i < nargs; i++) + args.push_back(ResolveIte(memo,t.arg(i),lits,done,dont_cares)); + res = f(args.size(),&args[0]); + } + } + else res = t; + resolve_ite_memo[t] = res; + return res; + } + + void RPFP::Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares){ + hash_set done[2]; + ImplicantRed(memo,f,lits,done,true, dont_cares); + } + + + /** Underapproximate a formula using current counterexample. */ + + RPFP::Term RPFP::UnderapproxFormula(const Term &f, hash_set &dont_cares){ + /* first compute truth values of subterms */ + hash_map memo; + #ifdef Z3OPS + stt = Z3_mk_subterm_truth(ctx,dualModel); + #endif + // SubtermTruth(memo,f); + /* now compute an implicant */ + std::vector lits; + Implicant(memo,f,lits, dont_cares); +#ifdef Z3OPS + delete stt; stt = 0; +#endif + /* return conjunction of literals */ + return conjoin(lits); + } + + struct VariableProjector : Z3User { + + struct elim_cand { + Term var; + int sup; + Term val; + }; + + typedef expr Term; + + hash_set keep; + hash_map var_ord; + int num_vars; + std::vector elim_cands; + hash_map > sup_map; + hash_map elim_map; + std::vector ready_cands; + hash_map cand_map; + params simp_params; + + VariableProjector(Z3User &_user, std::vector &keep_vec) : + Z3User(_user), simp_params() + { + num_vars = 0; + for(unsigned i = 0; i < keep_vec.size(); i++){ + keep.insert(keep_vec[i]); + var_ord[keep_vec[i]] = num_vars++; + } + } + + int VarNum(const Term &v){ + if(var_ord.find(v) == var_ord.end()) + var_ord[v] = num_vars++; + return var_ord[v]; + } + + bool IsVar(const Term &t){ + return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; + } + + bool IsPropLit(const Term &t, Term &a){ + if(IsVar(t)){ + a = t; + return true; + } + else if(t.is_app() && t.decl().get_decl_kind() == Not) + return IsPropLit(t.arg(0),a); + return false; + } + + void CountOtherVarsRec(hash_map &memo, + const Term &t, + int id, + int &count){ + std::pair foo(t,0); + std::pair::iterator, bool> bar = memo.insert(foo); + // int &res = bar.first->second; + if(!bar.second) return; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ + if(cand_map.find(t) != cand_map.end()){ + count++; + sup_map[t].push_back(id); + } + } + for(int i = 0; i < nargs; i++) + CountOtherVarsRec(memo, t.arg(i), id, count); + } + else if (t.is_quantifier()) + CountOtherVarsRec(memo, t.body(), id, count); + } + + void NewElimCand(const Term &lhs, const Term &rhs){ + if(debug_gauss){ + std::cout << "mapping " << lhs << " to " << rhs << std::endl; + } + elim_cand cand; + cand.var = lhs; + cand.sup = 0; + cand.val = rhs; + elim_cands.push_back(cand); + cand_map[lhs] = elim_cands.size()-1; + } + + void MakeElimCand(const Term &lhs, const Term &rhs){ + if(eq(lhs,rhs)) + return; + if(!IsVar(lhs)){ + if(IsVar(rhs)){ + MakeElimCand(rhs,lhs); + return; + } + else{ + std::cout << "would have mapped a non-var\n"; + return; + } + } + if(IsVar(rhs) && VarNum(rhs) > VarNum(lhs)){ + MakeElimCand(rhs,lhs); + return; + } + if(keep.find(lhs) != keep.end()) + return; + if(cand_map.find(lhs) == cand_map.end()) + NewElimCand(lhs,rhs); + else { + int cand_idx = cand_map[lhs]; + if(IsVar(rhs) && cand_map.find(rhs) == cand_map.end() + && keep.find(rhs) == keep.end()) + NewElimCand(rhs,elim_cands[cand_idx].val); + elim_cands[cand_idx].val = rhs; + } + } + + Term FindRep(const Term &t){ + if(cand_map.find(t) == cand_map.end()) + return t; + Term &res = elim_cands[cand_map[t]].val; + if(IsVar(res)){ + assert(VarNum(res) < VarNum(t)); + res = FindRep(res); + return res; + } + return t; + } + + void GaussElimCheap(const std::vector &lits_in, + std::vector &lits_out){ + for(unsigned i = 0; i < lits_in.size(); i++){ + Term lit = lits_in[i]; + if(lit.is_app()){ + decl_kind k = lit.decl().get_decl_kind(); + if(k == Equal || k == Iff) + MakeElimCand(FindRep(lit.arg(0)),FindRep(lit.arg(1))); + } + } + + for(unsigned i = 0; i < elim_cands.size(); i++){ + elim_cand &cand = elim_cands[i]; + hash_map memo; + CountOtherVarsRec(memo,cand.val,i,cand.sup); + if(cand.sup == 0) + ready_cands.push_back(i); + } + + while(!ready_cands.empty()){ + elim_cand &cand = elim_cands[ready_cands.back()]; + ready_cands.pop_back(); + Term rep = FindRep(cand.var); + if(!eq(rep,cand.var)) + if(cand_map.find(rep) != cand_map.end()){ + int rep_pos = cand_map[rep]; + cand.val = elim_cands[rep_pos].val; + } + Term val = SubstRec(elim_map,cand.val); + if(debug_gauss){ + std::cout << "subbing " << cand.var << " --> " << val << std::endl; + } + elim_map[cand.var] = val; + std::vector &sup = sup_map[cand.var]; + for(unsigned i = 0; i < sup.size(); i++){ + int c = sup[i]; + if((--elim_cands[c].sup) == 0) + ready_cands.push_back(c); + } + } + + for(unsigned i = 0; i < lits_in.size(); i++){ + Term lit = lits_in[i]; + lit = SubstRec(elim_map,lit); + lit = lit.simplify(); + if(eq(lit,ctx.bool_val(true))) + continue; + Term a; + if(IsPropLit(lit,a)) + if(keep.find(lit) == keep.end()) + continue; + lits_out.push_back(lit); + } + } + + // maps variables to constrains in which the occur pos, neg + hash_map la_index[2]; + hash_map la_coeffs[2]; + std::vector la_pos_vars; + bool fixing; + + void IndexLAcoeff(const Term &coeff1, const Term &coeff2, Term t, int id){ + Term coeff = coeff1 * coeff2; + coeff = coeff.simplify(); + Term is_pos = (coeff >= ctx.int_val(0)); + is_pos = is_pos.simplify(); + if(eq(is_pos,ctx.bool_val(true))) + IndexLA(true,coeff,t, id); + else + IndexLA(false,coeff,t, id); + } + + void IndexLAremove(const Term &t){ + if(IsVar(t)){ + la_index[0][t] = -1; // means ineligible to be eliminated + la_index[1][t] = -1; // (more that one occurrence, or occurs not in linear comb) + } + else if(t.is_app()){ + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + IndexLAremove(t.arg(i)); + } + // TODO: quantifiers? + } + + + void IndexLA(bool pos, const Term &coeff, const Term &t, int id){ + if(t.is_numeral()) + return; + if(t.is_app()){ + int nargs = t.num_args(); + switch(t.decl().get_decl_kind()){ + case Plus: + for(int i = 0; i < nargs; i++) + IndexLA(pos,coeff,t.arg(i), id); + break; + case Sub: + IndexLA(pos,coeff,t.arg(0), id); + IndexLA(!pos,coeff,t.arg(1), id); + break; + case Times: + if(t.arg(0).is_numeral()) + IndexLAcoeff(coeff,t.arg(0),t.arg(1), id); + else if(t.arg(1).is_numeral()) + IndexLAcoeff(coeff,t.arg(1),t.arg(0), id); + break; + default: + if(IsVar(t) && (fixing || la_index[pos].find(t) == la_index[pos].end())){ + la_index[pos][t] = id; + la_coeffs[pos][t] = coeff; + if(pos && !fixing) + la_pos_vars.push_back(t); // this means we only add a var once + } + else + IndexLAremove(t); + } + } + } + + void IndexLAstart(bool pos, const Term &t, int id){ + IndexLA(pos,(pos ? ctx.int_val(1) : ctx.int_val(-1)), t, id); + } + + void IndexLApred(bool pos, const Term &p, int id){ + if(p.is_app()){ + switch(p.decl().get_decl_kind()){ + case Not: + IndexLApred(!pos, p.arg(0),id); + break; + case Leq: + case Lt: + IndexLAstart(!pos, p.arg(0), id); + IndexLAstart(pos, p.arg(1), id); + break; + case Geq: + case Gt: + IndexLAstart(pos,p.arg(0), id); + IndexLAstart(!pos,p.arg(1), id); + break; + default: + IndexLAremove(p); + } + } + } + + void IndexLAfix(const Term &p, int id){ + fixing = true; + IndexLApred(true,p,id); + fixing = false; + } + + bool IsCanonIneq(const Term &lit, Term &term, Term &bound){ + // std::cout << Z3_simplify_get_help(ctx) << std::endl; + bool pos = lit.decl().get_decl_kind() != Not; + Term atom = pos ? lit : lit.arg(0); + if(atom.decl().get_decl_kind() == Leq){ + if(pos){ + bound = atom.arg(0); + term = atom.arg(1).simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + } + else { + bound = (atom.arg(1) + ctx.int_val(1)); + term = atom.arg(0); + // std::cout << "simplifying bound: " << bound << std::endl; + bound = bound.simplify(); + term = term.simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + } + return true; + } + else if(atom.decl().get_decl_kind() == Geq){ + if(pos){ + bound = atom.arg(1); // integer axiom + term = atom.arg(0).simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + return true; + } + else{ + bound = -(atom.arg(1) - ctx.int_val(1)); // integer axiom + term = -atom.arg(0); + bound = bound.simplify(); + term = term.simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + } + return true; + } + return false; + } + + Term CanonIneqTerm(const Term &p){ + Term term,bound; + Term ps = p.simplify(); + bool ok = IsCanonIneq(ps,term,bound); + assert(ok); + return term - bound; + } + + void ElimRedundantBounds(std::vector &lits){ + hash_map best_bound; + for(unsigned i = 0; i < lits.size(); i++){ + lits[i] = lits[i].simplify(simp_params); + Term term,bound; + if(IsCanonIneq(lits[i],term,bound)){ + if(best_bound.find(term) == best_bound.end()) + best_bound[term] = i; + else { + int best = best_bound[term]; + Term bterm,bbound; + IsCanonIneq(lits[best],bterm,bbound); + Term comp = bound > bbound; + comp = comp.simplify(); + if(eq(comp,ctx.bool_val(true))){ + lits[best] = ctx.bool_val(true); + best_bound[term] = i; + } + else { + lits[i] = ctx.bool_val(true); + } + } + } + } + } + + void FourierMotzkinCheap(const std::vector &lits_in, + std::vector &lits_out){ + simp_params.set(":som",true); + simp_params.set(":sort-sums",true); + fixing = false; lits_out = lits_in; + ElimRedundantBounds(lits_out); + for(unsigned i = 0; i < lits_out.size(); i++) + IndexLApred(true,lits_out[i],i); + + for(unsigned i = 0; i < la_pos_vars.size(); i++){ + Term var = la_pos_vars[i]; + if(la_index[false].find(var) != la_index[false].end()){ + int pos_idx = la_index[true][var]; + int neg_idx = la_index[false][var]; + if(pos_idx >= 0 && neg_idx >= 0){ + if(keep.find(var) != keep.end()){ + std::cout << "would have eliminated keep var\n"; + continue; + } + Term tpos = CanonIneqTerm(lits_out[pos_idx]); + Term tneg = CanonIneqTerm(lits_out[neg_idx]); + Term pos_coeff = la_coeffs[true][var]; + Term neg_coeff = -la_coeffs[false][var]; + Term comb = neg_coeff * tpos + pos_coeff * tneg; + Term ineq = ctx.int_val(0) <= comb; + ineq = ineq.simplify(); + lits_out[pos_idx] = ineq; + lits_out[neg_idx] = ctx.bool_val(true); + IndexLAfix(ineq,pos_idx); + } + } + } + } + + Term ProjectFormula(const Term &f){ + std::vector lits, new_lits1, new_lits2; + CollectConjuncts(f,lits); + timer_start("GaussElimCheap"); + GaussElimCheap(lits,new_lits1); + timer_stop("GaussElimCheap"); + timer_start("FourierMotzkinCheap"); + FourierMotzkinCheap(new_lits1,new_lits2); + timer_stop("FourierMotzkinCheap"); + return conjoin(new_lits2); + } + }; + + void Z3User::CollectConjuncts(const Term &f, std::vector &lits, bool pos){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + CollectConjuncts(f.arg(0), lits, !pos); + else if(pos && f.is_app() && f.decl().get_decl_kind() == And){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + CollectConjuncts(f.arg(i),lits,true); + } + else if(!pos && f.is_app() && f.decl().get_decl_kind() == Or){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + CollectConjuncts(f.arg(i),lits,false); + } + else if(pos) + lits.push_back(f); + else + lits.push_back(!f); + } + + struct TermLt { + bool operator()(const expr &x, const expr &y){ + unsigned xid = x.get_id(); + unsigned yid = y.get_id(); + return xid < yid; + } + }; + + void Z3User::SortTerms(std::vector &terms){ + TermLt foo; + std::sort(terms.begin(),terms.end(),foo); + } + + Z3User::Term Z3User::SortSum(const Term &t){ + if(!(t.is_app() && t.decl().get_decl_kind() == Plus)) + return t; + int nargs = t.num_args(); + if(nargs < 2) return t; + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = t.arg(i); + SortTerms(args); + if(nargs == 2) + return args[0] + args[1]; + return sum(args); + } + + + RPFP::Term RPFP::ProjectFormula(std::vector &keep_vec, const Term &f){ + VariableProjector vp(*this,keep_vec); + return vp.ProjectFormula(f); + } + + /** Compute an underapproximation of every node in a tree rooted at "root", + based on a previously computed counterexample. The underapproximation + may contain free variables that are implicitly existentially quantified. + */ + + RPFP::Term RPFP::ComputeUnderapprox(Node *root, int persist){ + /* if terminated underapprox is empty set (false) */ + bool show_model = false; + if(show_model) + std::cout << dualModel << std::endl; + if(!root->Outgoing){ + root->Underapprox.SetEmpty(); + return ctx.bool_val(true); + } + /* if not used in cex, underapprox is empty set (false) */ + if(Empty(root)){ + root->Underapprox.SetEmpty(); + return ctx.bool_val(true); + } + /* compute underapprox of children first */ + std::vector &chs = root->Outgoing->Children; + std::vector chu(chs.size()); + for(unsigned i = 0; i < chs.size(); i++) + chu[i] = ComputeUnderapprox(chs[i],persist); + + Term b; std::vector v; + RedVars(root, b, v); + /* underapproximate the edge formula */ + hash_set dont_cares; + dont_cares.insert(b); + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); + Term eu = UnderapproxFormula(root->Outgoing->dual,dont_cares); + timer_stop("UnderapproxFormula"); + /* combine with children */ + chu.push_back(eu); + eu = conjoin(chu); + /* project onto appropriate variables */ + eu = ProjectFormula(v,eu); + eu = eu.simplify(); + +#if 0 + /* check the result is consistent */ + { + hash_map memo; + int res = SubtermTruth(memo, eu); + if(res != 1) + throw "inconsistent projection"; + } +#endif + + /* rename to appropriate variable names */ + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[v[i]] = root->Annotation.IndParams[i]; /* copy names from annotation */ + Term funder = SubstRec(memo, eu); + root->Underapprox = CreateRelation(root->Annotation.IndParams,funder); +#if 0 + if(persist) + Z3_persist_ast(ctx,root->Underapprox.Formula,persist); +#endif + return eu; + } + + + RPFP::Term RPFP::ModelValueAsConstraint(const Term &t){ + if(t.is_array()){ + ArrayValue arr; + Term e = dualModel.eval(t); + EvalArrayTerm(e, arr); + if(arr.defined){ + std::vector cs; + for(std::map::iterator it = arr.entries.begin(), en = arr.entries.end(); it != en; ++it){ + expr foo = select(t,expr(ctx,it->first)) == expr(ctx,it->second); + cs.push_back(foo); + } + return conjoin(cs); + } + } + else { + expr r = dualModel.get_const_interp(t.decl()); + if(!r.null()){ + expr res = t == expr(ctx,r); + return res; + } + } + return ctx.bool_val(true); + } + + void RPFP::EvalNodeAsConstraint(Node *p, Transformer &res) + { + Term b; std::vector v; + RedVars(p, b, v); + std::vector args; + for(unsigned i = 0; i < v.size(); i++){ + expr val = ModelValueAsConstraint(v[i]); + if(!eq(val,ctx.bool_val(true))) + args.push_back(val); + } + expr cnst = conjoin(args); + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[v[i]] = p->Annotation.IndParams[i]; /* copy names from annotation */ + Term funder = SubstRec(memo, cnst); + res = CreateRelation(p->Annotation.IndParams,funder); + } + + + /** Push a scope. Assertions made after Push can be undone by Pop. */ + + void RPFP::Push() + { + stack.push_back(stack_entry()); + slvr.push(); + } + + /** Pop a scope (see Push). Note, you cannot pop axioms. */ + + void RPFP::Pop(int num_scopes) + { + slvr.pop(num_scopes); + for (int i = 0; i < num_scopes; i++) + { + stack_entry &back = stack.back(); + for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) + (*it)->dual = expr(ctx,NULL); + for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) + (*it)->dual = expr(ctx,NULL); + stack.pop_back(); + } + } + + + + + // This returns a new FuncDel with same sort as top-level function + // of term t, but with numeric suffix appended to name. + + Z3User::FuncDecl Z3User::SuffixFuncDecl(Term t, int n) + { + std::string name = t.decl().name().str() + "_" + string_of_int(n); + std::vector sorts; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + sorts.push_back(t.arg(i).get_sort()); + return ctx.function(name.c_str(), nargs, &sorts[0], t.get_sort()); + } + + // Scan the clause body for occurrences of the predicate unknowns + + RPFP::Term RPFP::ScanBody(hash_map &memo, + const Term &t, + hash_map &pmap, + std::vector &parms, + std::vector &nodes) + { + if(memo.find(t) != memo.end()) + return memo[t]; + Term res(ctx); + if (t.is_app()) { + func_decl f = t.decl(); + if(pmap.find(f) != pmap.end()){ + nodes.push_back(pmap[f]); + f = SuffixFuncDecl(t,parms.size()); + parms.push_back(f); + } + int nargs = t.num_args(); + std::vector args; + for(int i = 0; i < nargs; i++) + args.push_back(ScanBody(memo,t.arg(i),pmap,parms,nodes)); + res = f(nargs,&args[0]); + } + else if (t.is_quantifier()) + res = CloneQuantifier(t,ScanBody(memo,t.body(),pmap,parms,nodes)); + else + res = t; + memo[t] = res; + return res; + } + + // return the func_del of an app if it is uninterpreted + + bool Z3User::get_relation(const Term &t, func_decl &R){ + if(!t.is_app()) + return false; + R = t.decl(); + return R.get_decl_kind() == Uninterpreted; + } + + // return true if term is an individual variable + // TODO: have to check that it is not a background symbol + + bool Z3User::is_variable(const Term &t){ + if(!t.is_app()) + return false; + return t.decl().get_decl_kind() == Uninterpreted + && t.num_args() == 0; + } + + RPFP::Term RPFP::RemoveLabelsRec(hash_map &memo, const Term &t){ + if(memo.find(t) != memo.end()) + return memo[t]; + Term res(ctx); + if (t.is_app()){ + func_decl f = t.decl(); + if(t.num_args() == 1 && f.name().str().substr(0,3) == "lbl"){ + res = RemoveLabelsRec(memo,t.arg(0)); + } + else { + int nargs = t.num_args(); + std::vector args; + for(int i = 0; i < nargs; i++) + args.push_back(RemoveLabelsRec(memo,t.arg(i))); + res = f(nargs,&args[0]); + } + } + else if (t.is_quantifier()) + res = CloneQuantifier(t,RemoveLabelsRec(memo,t.body())); + else + res = t; + memo[t] = res; + return res; + } + + RPFP::Term RPFP::RemoveLabels(const Term &t){ + hash_map memo ; + return RemoveLabelsRec(memo,t); + } + + Z3User::Term Z3User::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo[level].insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(SubstBoundRec(memo, subst, level, t.arg(i))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()){ + int bound = t.get_quantifier_num_bound(); + res = CloneQuantifier(t,SubstBoundRec(memo, subst, level + bound, t.body())); + } + else if (t.is_var()) { + int idx = t.get_index_value(); + if(idx >= level && subst.find(idx-level) != subst.end()){ + res = subst[idx-level]; + } + else res = t; + } + else res = t; + return res; + } + + Z3User::Term Z3User::SubstBound(hash_map &subst, const Term &t){ + hash_map > memo; + return SubstBoundRec(memo, subst, 0, t); + } + + /** Convert a collection of clauses to Nodes and Edges in the RPFP. + + Predicate unknowns are uninterpreted predicates not + occurring in the background theory. + + Clauses are of the form + + B => P(t_1,...,t_k) + + where P is a predicate unknown and predicate unknowns + occur only positivey in H and only under existential + quantifiers in prenex form. + + Each predicate unknown maps to a node. Each clause maps to + an edge. Let C be a clause B => P(t_1,...,t_k) where the + sequence of predicate unknowns occurring in B (in order + of occurrence) is P_1..P_n. The clause maps to a transformer + T where: + + T.Relparams = P_1..P_n + T.Indparams = x_1...x+k + T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k + + Throws exception bad_clause(msg,i) if a clause i is + in the wrong form. + + */ + + + static bool canonical_clause(const expr &clause){ + if(clause.decl().get_decl_kind() != Implies) + return false; + expr arg = clause.arg(1); + return arg.is_app() && (arg.decl().get_decl_kind() == False || + arg.decl().get_decl_kind() == Uninterpreted); + } + + void RPFP::FromClauses(const std::vector &unskolemized_clauses){ + hash_map pmap; + func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); + + std::vector clauses(unskolemized_clauses); + // first, skolemize the clauses + + for(unsigned i = 0; i < clauses.size(); i++){ + expr &t = clauses[i]; + if (t.is_quantifier() && t.is_quantifier_forall()) { + int bound = t.get_quantifier_num_bound(); + std::vector sorts; + std::vector names; + hash_map subst; + for(int j = 0; j < bound; j++){ + 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)); + subst[bound-1-j] = skolem; + } + t = SubstBound(subst,t.body()); + } + } + + // create the nodes from the heads of the clauses + + for(unsigned i = 0; i < clauses.size(); i++){ + Term &clause = clauses[i]; + if(!canonical_clause(clause)) + clause = implies((!clause).simplify(),ctx.bool_val(false)); + Term head = clause.arg(1); + func_decl R(ctx); + bool is_query = false; + if (eq(head,ctx.bool_val(false))){ + R = fail_pred; + // R = ctx.constant("@Fail", ctx.bool_sort()).decl(); + is_query = true; + } + else if(!get_relation(head,R)) + throw bad_clause("rhs must be a predicate application",i); + if(pmap.find(R) == pmap.end()){ + + // If the node doesn't exitst, create it. The Indparams + // are arbitrary, but we use the rhs arguments if they + // are variables for mnomonic value. + + hash_set seen; + std::vector Indparams; + for(unsigned j = 0; j < head.num_args(); j++){ + Term arg = head.arg(j); + if(!is_variable(arg) || seen.find(arg) != seen.end()){ + std::string name = std::string("@a_") + string_of_int(j); + arg = ctx.constant(name.c_str(),arg.get_sort()); + } + seen.insert(arg); + Indparams.push_back(arg); + } + Node *node = CreateNode(R(Indparams.size(),&Indparams[0])); + //nodes.push_back(node); + pmap[R] = node; + if (is_query) + node->Bound = CreateRelation(std::vector(), ctx.bool_val(false)); + } + } + + // create the edges + + for(unsigned i = 0; i < clauses.size(); i++){ + Term clause = clauses[i]; + Term body = clause.arg(0); + Term head = clause.arg(1); + func_decl R(ctx); + if (eq(head,ctx.bool_val(false))) + R = fail_pred; + //R = ctx.constant("@Fail", ctx.bool_sort()).decl(); + else get_relation(head,R); + Node *Parent = pmap[R]; + std::vector Indparams; + hash_set seen; + for(unsigned j = 0; j < head.num_args(); j++){ + Term arg = head.arg(j); + if(!is_variable(arg) || seen.find(arg) != seen.end()){ + std::string name = std::string("@a_") + string_of_int(j); + Term var = ctx.constant(name.c_str(),arg.get_sort()); + body = body && (arg == var); + arg = var; + } + seen.insert(arg); + Indparams.push_back(arg); + } + + // We extract the relparams positionally + + std::vector Relparams; + hash_map scan_memo; + std::vector Children; + body = ScanBody(scan_memo,body,pmap,Relparams,Children); + body = RemoveLabels(body); + body = body.simplify(); + + // Create the edge + Transformer T = CreateTransformer(Relparams,Indparams,body); + // Edge *edge = + CreateEdge(Parent,T,Children); + // edges.push_back(edge); + } + } + + + + void RPFP::WriteSolution(std::ostream &s){ + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + Term asgn = (node->Name)(node->Annotation.IndParams) == node->Annotation.Formula; + s << asgn << std::endl; + } + } + + void RPFP::WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s) + { + std::pair foo(t,0); + std::pair::iterator, bool> bar = memo.insert(foo); + // int &res = bar.first->second; + if(!bar.second) return; + hash_map::iterator it = e->varMap.find(t); + if (it != e->varMap.end()){ + return; + } + if (t.is_app()) + { + func_decl f = t.decl(); + // int idx; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + WriteEdgeVars(e, memo, t.arg(i),s); + if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ + Term rename = HideVariable(t,e->number); + Term value = dualModel.eval(rename); + s << " (= " << t << " " << value << ")\n"; + } + } + else if (t.is_quantifier()) + WriteEdgeVars(e,memo,t.body(),s); + return; + } + + void RPFP::WriteEdgeAssignment(std::ostream &s, Edge *e){ + s << "(\n"; + hash_map memo; + WriteEdgeVars(e, memo, e->F.Formula ,s); + s << ")\n"; + } + + void RPFP::WriteCounterexample(std::ostream &s, Node *node){ + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ + Node *child = node->Outgoing->Children[i]; + if(!Empty(child)) + WriteCounterexample(s,child); + } + s << "(" << node->number << " : " << EvalNode(node) << " <- "; + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ + Node *child = node->Outgoing->Children[i]; + if(!Empty(child)) + s << " " << node->Outgoing->Children[i]->number; + } + s << ")" << std::endl; + WriteEdgeAssignment(s,node->Outgoing); + } + + RPFP::Term RPFP::ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + // int idx; + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(ToRuleRec(e, memo, t.arg(i),quants)); + hash_map::iterator rit = e->relMap.find(f); + if(rit != e->relMap.end()){ + Node* child = e->Children[rit->second]; + FuncDecl op = child->Name; + res = op(args.size(),&args[0]); + } + else { + res = f(args.size(),&args[0]); + if(nargs == 0 && t.decl().get_decl_kind() == Uninterpreted) + quants.push_back(t); + } + } + else if (t.is_quantifier()) + { + Term body = ToRuleRec(e,memo,t.body(),quants); + res = CloneQuantifier(t,body); + } + else res = t; + return res; + } + + + void RPFP::ToClauses(std::vector &cnsts, FileFormat format){ + cnsts.resize(edges.size()); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *edge = edges[i]; + SetEdgeMaps(edge); + std::vector quants; + hash_map memo; + Term lhs = ToRuleRec(edge, memo, edge->F.Formula,quants); + Term rhs = (edge->Parent->Name)(edge->F.IndParams.size(),&edge->F.IndParams[0]); + for(unsigned j = 0; j < edge->F.IndParams.size(); j++) + ToRuleRec(edge,memo,edge->F.IndParams[j],quants); // just to get quants + Term cnst = implies(lhs,rhs); +#if 0 + for(unsigned i = 0; i < quants.size(); i++){ + std::cout << expr(ctx,(Z3_ast)quants[i]) << " : " << sort(ctx,Z3_get_sort(ctx,(Z3_ast)quants[i])) << std::endl; + } +#endif + if(format != DualityFormat) + cnst= forall(quants,cnst); + cnsts[i] = cnst; + } + // int num_rules = cnsts.size(); + + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + if(!node->Bound.IsFull()){ + Term lhs = (node->Name)(node->Bound.IndParams) && !node->Bound.Formula; + Term cnst = implies(lhs,ctx.bool_val(false)); + if(format != DualityFormat){ + std::vector quants; + for(unsigned j = 0; j < node->Bound.IndParams.size(); j++) + quants.push_back(node->Bound.IndParams[j]); + if(format == HornFormat) + cnst= exists(quants,!cnst); + else + cnst= forall(quants, cnst); + } + cnsts.push_back(cnst); + } + } + + } + + +RPFP::~RPFP(){ + for(unsigned i = 0; i < nodes.size(); i++) + delete nodes[i]; + for(unsigned i = 0; i < edges.size(); i++) + delete edges[i]; + } +} + +#if 0 +void show_ast(expr *a){ + std::cout << *a; +} +#endif diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp new file mode 100644 index 000000000..d5c6579d1 --- /dev/null +++ b/src/duality/duality_solver.cpp @@ -0,0 +1,2200 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality_solver.h + +Abstract: + + implements relational post-fixedpoint problem + (RPFP) solver + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + +#include "duality.h" +#include "duality_profiling.h" + +#include +#include +#include +#include + +// TODO: make these official options or get rid of them + +#define NEW_CAND_SEL +// #define LOCALIZE_CONJECTURES +// #define CANDS_FROM_UPDATES +#define CANDS_FROM_COVER_FAIL +#define DEPTH_FIRST_EXPAND +#define MINIMIZE_CANDIDATES +// #define MINIMIZE_CANDIDATES_HARDER +#define BOUNDED +#define CHECK_CANDS_FROM_IND_SET +#define UNDERAPPROX_NODES +#define NEW_EXPAND +#define EARLY_EXPAND +// #define TOP_DOWN +#define EFFORT_BOUNDED_STRAT +#define SKIP_UNDERAPPROX_NODES + + +namespace Duality { + + // TODO: must be a better place for this... + static char string_of_int_buffer[20]; + + static const char *string_of_int(int n){ + sprintf(string_of_int_buffer,"%d",n); + return string_of_int_buffer; + } + + /** Generic object for producing diagnostic output. */ + + class Reporter { + protected: + RPFP *rpfp; + public: + Reporter(RPFP *_rpfp){ + rpfp = _rpfp; + } + virtual void Extend(RPFP::Node *node){} + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update){} + virtual void Bound(RPFP::Node *node){} + virtual void Expand(RPFP::Edge *edge){} + virtual void AddCover(RPFP::Node *covered, std::vector &covering){} + virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){} + virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){} + virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){} + virtual void Dominates(RPFP::Node *node, RPFP::Node *other){} + virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){} + virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} + virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} + virtual ~Reporter(){} + }; + + Reporter *CreateStdoutReporter(RPFP *rpfp); + + /** Object we throw in case of catastrophe. */ + + struct InternalError { + std::string msg; + InternalError(const std::string _msg) + : msg(_msg) {} + }; + + + /** This is the main solver. It takes anarbitrary (possibly cyclic) + RPFP and either annotates it with a solution, or returns a + counterexample derivation in the form of an embedd RPFP tree. */ + + class Duality : public Solver { + + public: + Duality(RPFP *_rpfp) + : ctx(_rpfp->ctx), + slvr(_rpfp->slvr), + nodes(_rpfp->nodes), + edges(_rpfp->edges) + { + rpfp = _rpfp; + reporter = 0; + heuristic = 0; + FullExpand = false; + NoConj = false; + FeasibleEdges = true; + UseUnderapprox = true; + Report = false; + StratifiedInlining = false; + RecursionBound = -1; + } + + typedef RPFP::Node Node; + typedef RPFP::Edge Edge; + + /** This struct represents a candidate for extending the + unwinding. It consists of an edge to instantiate + and a vector of children for the new instance. */ + + struct Candidate { + Edge *edge; std::vector + Children; + }; + + /** Comparison operator, allowing us to sort Nodes + by their number field. */ + + struct lnode + { + bool operator()(const Node* s1, const Node* s2) const + { + return s1->number < s2->number; + } + }; + + typedef std::set Unexpanded; // sorted set of Nodes + + /** This class provides a heuristic for expanding a derivation + tree. */ + + class Heuristic { + RPFP *rpfp; + + /** Heuristic score for unwinding nodes. Currently this + counts the number of updates. */ + struct score { + int updates; + score() : updates(0) {} + }; + hash_map scores; + + public: + Heuristic(RPFP *_rpfp){ + rpfp = _rpfp; + } + virtual void Update(RPFP::Node *node){ + scores[node].updates++; + } + + /** Heuristic choice of nodes to expand. Takes a set "choices" + and returns a subset "best". We currently choose the + nodes with the fewest updates. + */ +#if 0 + virtual void ChooseExpand(const std::set &choices, std::set &best){ + int best_score = INT_MAX; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it)->map; + int score = scores[node].updates; + best_score = std::min(best_score,score); + } + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) + if(scores[(*it)->map].updates == best_score) + best.insert(*it); + } +#else + virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority=false){ + if(high_priority) return; + int best_score = INT_MAX; + int worst_score = 0; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it)->map; + int score = scores[node].updates; + best_score = std::min(best_score,score); + worst_score = std::max(worst_score,score); + } + int cutoff = best_score + (worst_score-best_score)/2; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) + if(scores[(*it)->map].updates <= cutoff) + best.insert(*it); + } +#endif + }; + + + class Covering; // see below + + // These members represent the state of the algorithm. + + RPFP *rpfp; // the input RPFP + Reporter *reporter; // object for logging + Heuristic *heuristic; // expansion heuristic + context &ctx; // Z3 context + solver &slvr; // Z3 solver + std::vector &nodes; // Nodes of input RPFP + std::vector &edges; // Edges of input RPFP + std::vector leaves; // leaf nodes of unwinding (unused) + Unexpanded unexpanded; // unexpanded nodes + std::list candidates; // candidates for expansion + // maps children to edges in input RPFP + hash_map > edges_by_child; + // maps each node in input RPFP to its expanded instances + hash_map > insts_of_node; + // maps each node in input RPFP to all its instances + hash_map > all_of_node; + RPFP *unwinding; // the unwinding + Covering *indset; // proposed inductive subset + Counterexample cex; // counterexample + std::list to_expand; + hash_set updated_nodes; + hash_map underapprox_map; // maps underapprox nodes to the nodes they approximate + int last_decisions; + hash_set overapproxes; + +#ifdef BOUNDED + struct Counter { + int val; + Counter(){val = 0;} + }; + typedef std::map NodeToCounter; + hash_map back_edges; // counts of back edges +#endif + + /** Solve the problem. */ + virtual bool Solve(){ + reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); +#ifndef LOCALIZE_CONJECTURES + heuristic = !cex.tree ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); +#else + heuristic = !cex.tree ? (Heuristic *)(new LocalHeuristic(rpfp)) + : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); +#endif + unwinding = new RPFP(rpfp->ls); + unwinding->HornClauses = rpfp->HornClauses; + indset = new Covering(this); + last_decisions = 0; + CreateEdgesByChildMap(); + CreateLeaves(); +#ifndef TOP_DOWN + if(FeasibleEdges)NullaryCandidates(); + else InstantiateAllEdges(); +#else + for(unsigned i = 0; i < leaves.size(); i++) + if(!SatisfyUpperBound(leaves[i])) + return false; +#endif + StratifiedLeafCount = -1; + timer_start("SolveMain"); + bool res = SolveMain(); // does the actual work + timer_stop("SolveMain"); + // print_profile(std::cout); + delete indset; + delete heuristic; + return res; + } + +#if 0 + virtual void Restart(RPFP *_rpfp){ + rpfp = _rpfp; + delete unwinding; + nodes = _rpfp->nodes; + edges = _rpfp->edges; + leaves.clear(); + unexpanded.clear(); // unexpanded nodes + candidates.clear(); // candidates for expansion + edges_by_child.clear(); + insts_of_node.clear(); + all_of_node.clear(); + to_expand.clear(); + } +#endif + + virtual void LearnFrom(Solver *old_solver){ + cex = old_solver->GetCounterexample(); + } + + /** Return the counterexample */ + virtual Counterexample GetCounterexample(){ + return cex; + } + + // options + bool FullExpand; // do not use partial expansion of derivation tree + bool NoConj; // do not use conjectures (no forced covering) + bool FeasibleEdges; // use only feasible edges in unwinding + bool UseUnderapprox; // use underapproximations + bool Report; // spew on stdout + bool StratifiedInlining; // Do stratified inlining as preprocessing step + int RecursionBound; // Recursion bound for bounded verification + + bool SetBoolOption(bool &opt, const std::string &value){ + if(value == "0") { + opt = false; + return true; + } + if(value == "1") { + opt = true; + return true; + } + return false; + } + + bool SetIntOption(int &opt, const std::string &value){ + opt = atoi(value.c_str()); + return true; + } + + /** Set options (not currently used) */ + virtual bool SetOption(const std::string &option, const std::string &value){ + if(option == "full_expand"){ + return SetBoolOption(FullExpand,value); + } + if(option == "no_conj"){ + return SetBoolOption(NoConj,value); + } + if(option == "feasible_edges"){ + return SetBoolOption(FeasibleEdges,value); + } + if(option == "use_underapprox"){ + return SetBoolOption(UseUnderapprox,value); + } + if(option == "report"){ + return SetBoolOption(Report,value); + } + if(option == "stratified_inlining"){ + return SetBoolOption(StratifiedInlining,value); + } + if(option == "recursion_bound"){ + return SetIntOption(RecursionBound,value); + } + return false; + } + + /** Create an instance of a node in the unwinding. Set its + annotation to true, and mark it unexpanded. */ + Node* CreateNodeInstance(Node *node, int number = 0){ + RPFP::Node *inst = unwinding->CloneNode(node); + inst->Annotation.SetFull(); + if(number < 0) inst->number = number; + unexpanded.insert(inst); + all_of_node[node].push_back(inst); + return inst; + } + + /** Create an instance of an edge in the unwinding, with given + parent and children. */ + void CreateEdgeInstance(Edge *edge, Node *parent, const std::vector &children){ + RPFP::Edge *inst = unwinding->CreateEdge(parent,edge->F,children); + inst->map = edge; + } + + void MakeLeaf(Node *node, bool do_not_expand = false){ + node->Annotation.SetEmpty(); + Edge *e = unwinding->CreateLowerBoundEdge(node); +#ifdef TOP_DOWN + node->Annotation.SetFull(); // allow this node to cover others +#endif + if(StratifiedInlining) + node->Annotation.SetFull(); // allow this node to cover others + else + updated_nodes.insert(node); + e->map = 0; + reporter->Extend(node); +#ifdef EARLY_EXPAND + if(!do_not_expand) + TryExpandNode(node); +#endif + // e->F.SetEmpty(); + } + + void MakeOverapprox(Node *node){ + node->Annotation.SetFull(); + Edge *e = unwinding->CreateLowerBoundEdge(node); + overapproxes.insert(node); + e->map = 0; + } + + /** We start the unwinding with leaves that under-approximate + each relation with false. */ + void CreateLeaves(){ + unexpanded.clear(); + leaves.clear(); + for(unsigned i = 0; i < nodes.size(); i++){ + RPFP::Node *node = CreateNodeInstance(nodes[i]); + if(0 && nodes[i]->Outgoing->Children.size() == 0) + CreateEdgeInstance(nodes[i]->Outgoing,node,std::vector()); + else { + if(!StratifiedInlining) + MakeLeaf(node); + else { + MakeOverapprox(node); + LeafMap[nodes[i]] = node; + } + } + leaves.push_back(node); + } + } + + /** Create the map from children to edges in the input RPFP. This + is used to generate candidates for expansion. */ + void CreateEdgesByChildMap(){ + edges_by_child.clear(); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *e = edges[i]; + std::set done; + for(unsigned j = 0; j < e->Children.size(); j++){ + Node *c = e->Children[j]; + if(done.find(c) == done.end()) // avoid duplicates + edges_by_child[c].push_back(e); + done.insert(c); + } + } + } + + void NullaryCandidates(){ + for(unsigned i = 0; i < edges.size(); i++){ + RPFP::Edge *edge = edges[i]; + if(edge->Children.size() == 0){ + Candidate cand; + cand.edge = edge; + candidates.push_back(cand); + } + } + } + + void InstantiateAllEdges(){ + hash_map leaf_map; + for(unsigned i = 0; i < leaves.size(); i++){ + leaf_map[leaves[i]->map] = leaves[i]; + insts_of_node[leaves[i]->map].push_back(leaves[i]); + } + unexpanded.clear(); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *edge = edges[i]; + Candidate c; c.edge = edge; + 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); + } + for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it) + indset->Add(*it); + for(unsigned i = 0; i < leaves.size(); i++){ + std::vector &foo = insts_of_node[leaves[i]->map]; + foo.erase(foo.begin()); + } + } + + bool ProducedBySI(Edge *edge, std::vector &children){ + if(LeafMap.find(edge->Parent) == LeafMap.end()) return false; + Node *other = LeafMap[edge->Parent]; + if(other->Outgoing->map != edge) return false; + std::vector &ochs = other->Outgoing->Children; + for(unsigned i = 0; i < children.size(); i++) + if(ochs[i] != children[i]) return false; + return true; + } + + /** Add a candidate for expansion, but not if Stratified inlining has already + produced it */ + + void AddCandidate(Edge *edge, std::vector &children){ + if(StratifiedInlining && ProducedBySI(edge,children)) + return; + candidates.push_back(Candidate()); + candidates.back().edge = edge; + candidates.back().Children = children; + } + + /** Generate candidates for expansion, given a vector of candidate + sets for each argument position. This recursively produces + the cross product. + */ + void GenCandidatesRec(int pos, Edge *edge, + const std::vector > &vec, + std::vector &children){ + if(pos == (int)vec.size()){ + AddCandidate(edge,children); + } + else { + for(unsigned i = 0; i < vec[pos].size(); i++){ + children[pos] = vec[pos][i]; + GenCandidatesRec(pos+1,edge,vec,children); + } + } + } + + /** Setup for above recursion. */ + void GenCandidates(int pos, Edge *edge, + const std::vector > &vec){ + std::vector children(vec.size()); + GenCandidatesRec(0,edge,vec,children); + } + + /** Expand a node. We find all the candidates for expansion using + this node and other already expanded nodes. This is a little + tricky, since a node may be used for multiple argument + positions of an edge, and we don't want to produce duplicates. + */ + +#ifndef NEW_EXPAND + void ExpandNode(Node *node){ + std::vector &nedges = edges_by_child[node->map]; + for(unsigned i = 0; i < nedges.size(); i++){ + Edge *edge = nedges[i]; + for(unsigned npos = 0; npos < edge->Children.size(); ++npos){ + if(edge->Children[npos] == node->map){ + std::vector > vec(edge->Children.size()); + vec[npos].push_back(node); + for(unsigned j = 0; j < edge->Children.size(); j++){ + if(j != npos){ + std::vector &insts = insts_of_node[edge->Children[j]]; + for(unsigned k = 0; k < insts.size(); k++) + if(indset->Candidate(insts[k])) + vec[j].push_back(insts[k]); + } + if(j < npos && edge->Children[j] == node->map) + vec[j].push_back(node); + } + GenCandidates(0,edge,vec); + } + } + } + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + } +#else + /** If the current proposed solution is not inductive, + use the induction failure to generate candidates for extension. */ + void ExpandNode(Node *node){ + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + timer_start("GenCandIndFailUsing"); + std::vector &nedges = edges_by_child[node->map]; + for(unsigned i = 0; i < nedges.size(); i++){ + Edge *edge = nedges[i]; + slvr.push(); + RPFP *checker = new RPFP(rpfp->ls); + Node *root = CheckerJustForEdge(edge,checker,true); + if(root){ + expr using_cond = ctx.bool_val(false); + for(unsigned npos = 0; npos < edge->Children.size(); ++npos) + if(edge->Children[npos] == node->map) + using_cond = using_cond || checker->Localize(root->Outgoing->Children[npos]->Outgoing,NodeMarker(node)); + slvr.add(using_cond); + if(checker->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,checker,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + } + slvr.pop(1); + delete checker; + } + timer_stop("GenCandIndFailUsing"); + } +#endif + + void ExpandNodeFromOther(Node *node, Node *other){ + std::vector &in = other->Incoming; + for(unsigned i = 0; i < in.size(); i++){ + Edge *edge = in[i]; + Candidate cand; + cand.edge = edge->map; + cand.Children = edge->Children; + for(unsigned j = 0; j < cand.Children.size(); j++) + if(cand.Children[j] == other) + cand.Children[j] = node; + candidates.push_front(cand); + } + // unexpanded.erase(node); + // insts_of_node[node->map].push_back(node); + } + + /** Expand a node based on some uncovered node it dominates. + This pushes cahdidates onto the *front* of the candidate + queue, so these expansions are done depth-first. */ + bool ExpandNodeFromCoverFail(Node *node){ + if(!node->Outgoing || node->Outgoing->Children.size() == 0) + return false; + Node *other = indset->GetSimilarNode(node); + if(!other) + return false; +#ifdef UNDERAPPROX_NODES + Node *under_node = CreateUnderapproxNode(node); + underapprox_map[under_node] = node; + indset->CoverByNode(node,under_node); + ExpandNodeFromOther(under_node,other); + ExpandNode(under_node); +#else + ExpandNodeFromOther(node,other); + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); +#endif + return true; + } + + + /** Make a boolean variable to act as a "marker" for a node. */ + expr NodeMarker(Node *node){ + std::string name = std::string("@m_") + string_of_int(node->number); + return ctx.constant(name.c_str(),ctx.bool_sort()); + } + + /** Union the annotation of dst into src. If with_markers is + true, we conjoin the annotation formula of dst with its + marker. This allows us to discover which disjunct is + true in a satisfying assignment. */ + void UnionAnnotations(RPFP::Transformer &dst, Node *src, bool with_markers = false){ + if(!with_markers) + dst.UnionWith(src->Annotation); + else { + RPFP::Transformer t = src->Annotation; + t.Formula = t.Formula && NodeMarker(src); + dst.UnionWith(t); + } + } + + void GenNodeSolutionFromIndSet(Node *node, RPFP::Transformer &annot, bool with_markers = false){ + annot.SetEmpty(); + std::vector &insts = insts_of_node[node]; + for(unsigned j = 0; j < insts.size(); j++) + if(indset->Contains(insts[j])) + UnionAnnotations(annot,insts[j],with_markers); + annot.Simplify(); + } + + /** Generate a proposed solution of the input RPFP from + the unwinding, by unioning the instances of each node. */ + void GenSolutionFromIndSet(bool with_markers = false){ + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + GenNodeSolutionFromIndSet(node,node->Annotation,with_markers); + } + } + +#ifdef BOUNDED + bool NodePastRecursionBound(Node *node){ + if(RecursionBound < 0) return false; + NodeToCounter &backs = back_edges[node]; + for(NodeToCounter::iterator it = backs.begin(), en = backs.end(); it != en; ++it){ + if(it->second.val > RecursionBound) + return true; + } + return false; + } +#endif + + /** Test whether a given extension candidate actually represents + an induction failure. Right now we approximate this: if + the resulting node in the unwinding could be labeled false, + it clearly is not an induction failure. */ + + bool CandidateFeasible(const Candidate &cand){ + if(!FeasibleEdges) return true; + RPFP *checker = new RPFP(rpfp->ls); + // std::cout << "Checking feasibility of extension " << cand.edge->Parent->number << std::endl; + checker->Push(); + std::vector chs(cand.Children.size()); + Node *root = checker->CloneNode(cand.edge->Parent); +#ifdef BOUNDED + for(unsigned i = 0; i < cand.Children.size(); i++) + if(NodePastRecursionBound(cand.Children[i])) + return false; +#endif +#ifdef NEW_CAND_SEL + GenNodeSolutionFromIndSet(cand.edge->Parent,root->Bound); +#else + root->Bound.SetEmpty(); +#endif + checker->AssertNode(root); + for(unsigned i = 0; i < cand.Children.size(); i++) + chs[i] = checker->CloneNode(cand.Children[i]); + Edge *e = checker->CreateEdge(root,cand.edge->F,chs); + checker->AssertEdge(e,0,true); + // std::cout << "Checking SAT: " << e->dual << std::endl; + bool res = checker->Check(root) != unsat; + // std::cout << "Result: " << res << std::endl; + if(!res)reporter->Reject(cand.edge,cand.Children); + checker->Pop(1); + delete checker; + return res; + } + + + /* For stratified inlining, we need a topological sort of the + nodes. */ + + hash_map TopoSort; + int TopoSortCounter; + + void DoTopoSortRec(Node *node){ + if(TopoSort.find(node) != TopoSort.end()) + return; + TopoSort[node] = TopoSortCounter++; // just to break cycles + Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge + if(edge){ + std::vector &chs = edge->Children; + for(unsigned i = 0; i < chs.size(); i++) + DoTopoSortRec(chs[i]); + } + TopoSort[node] = TopoSortCounter++; + } + + void DoTopoSort(){ + TopoSort.clear(); + TopoSortCounter = 0; + for(unsigned i = 0; i < nodes.size(); i++) + DoTopoSortRec(nodes[i]); + } + + /** 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(){ + DoTopoSort(); + for(unsigned i = 0; i < leaves.size(); i++){ + Node *node = leaves[i]; + if(!SatisfyUpperBound(node)) + return false; + } + // don't leave any dangling nodes! +#ifndef EFFORT_BOUNDED_STRAT + for(unsigned i = 0; i < leaves.size(); i++) + if(!leaves[i]->Outgoing) + MakeLeaf(leaves[i],true); +#endif + return true; + } + + /** Here, we do the downward expansion for stratified inlining */ + + hash_map LeafMap, StratifiedLeafMap; + + Edge *GetNodeOutgoing(Node *node, int last_decs = 0){ + if(overapproxes.find(node) == overapproxes.end()) return node->Outgoing; /* already expanded */ + overapproxes.erase(node); +#ifdef EFFORT_BOUNDED_STRAT + if(last_decs > 5000){ + // RPFP::Transformer save = node->Annotation; + node->Annotation.SetEmpty(); + Edge *e = unwinding->CreateLowerBoundEdge(node); + // node->Annotation = save; + insts_of_node[node->map].push_back(node); + std::cout << "made leaf: " << node->number << std::endl; + return e; + } +#endif + Edge *edge = node->map->Outgoing; + std::vector &chs = edge->Children; + std::vector nchs(chs.size()); + for(unsigned i = 0; i < chs.size(); i++){ + Node *child = chs[i]; + if(TopoSort[child] < TopoSort[node->map]) + nchs[i] = LeafMap[child]; + else { + if(StratifiedLeafMap.find(child) == StratifiedLeafMap.end()){ + RPFP::Node *nchild = CreateNodeInstance(child,StratifiedLeafCount--); + MakeLeaf(nchild); + StratifiedLeafMap[child] = nchild; + indset->SetDominated(nchild); + } + nchs[i] = StratifiedLeafMap[child]; + } + } + CreateEdgeInstance(edge,node,nchs); + reporter->Extend(node); + return node->Outgoing; + } + + void SetHeuristicOldNode(Node *node){ + LocalHeuristic *h = dynamic_cast(heuristic); + if(h) + h->SetOldNode(node); + } + + /** This does the actual solving work. We try to generate + candidates for extension. If we succed, we extend the + unwinding. If we fail, we have a solution. */ + bool SolveMain(){ + if(StratifiedInlining && !DoStratifiedInlining()) + return false; +#ifdef BOUNDED + DoTopoSort(); +#endif + while(true){ + timer_start("ProduceCandidatesForExtension"); + ProduceCandidatesForExtension(); + timer_stop("ProduceCandidatesForExtension"); + if(candidates.empty()){ + GenSolutionFromIndSet(); + return true; + } + Candidate cand = candidates.front(); + candidates.pop_front(); + if(CandidateFeasible(cand)) + if(!Extend(cand)) + return false; + } + } + + // hack: put something local into the underapproximation formula + // without this, interpolants can be pretty bad + void AddThing(expr &conj){ + std::string name = "@thing"; + expr thing = ctx.constant(name.c_str(),ctx.bool_sort()); + if(conj.is_app() && conj.decl().get_decl_kind() == And){ + std::vector conjs(conj.num_args()+1); + for(unsigned i = 0; i+1 < conjs.size(); i++) + conjs[i] = conj.arg(i); + conjs[conjs.size()-1] = thing; + conj = rpfp->conjoin(conjs); + } + } + + + Node *CreateUnderapproxNode(Node *node){ + // cex.tree->ComputeUnderapprox(cex.root,0); + RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */); + under_node->Annotation.IntersectWith(cex.root->Underapprox); + AddThing(under_node->Annotation.Formula); + Edge *e = unwinding->CreateLowerBoundEdge(under_node); + under_node->Annotation.SetFull(); // allow this node to cover others + back_edges[under_node] = back_edges[node]; + e->map = 0; + reporter->Extend(under_node); + return under_node; + } + + /** Try to prove a conjecture about a node. If successful + update the unwinding annotation appropriately. */ + bool ProveConjecture(Node *node, const RPFP::Transformer &t,Node *other = 0, Counterexample *_cex = 0){ + reporter->Conjecture(node,t); + timer_start("ProveConjecture"); + RPFP::Transformer save = node->Bound; + node->Bound.IntersectWith(t); + +#ifndef LOCALIZE_CONJECTURES + bool ok = SatisfyUpperBound(node); +#else + SetHeuristicOldNode(other); + bool ok = SatisfyUpperBound(node); + SetHeuristicOldNode(0); +#endif + + if(ok){ + timer_stop("ProveConjecture"); + return true; + } +#ifdef UNDERAPPROX_NODES + if(0 && last_decisions > 5000){ + std::cout << "making an underapprox\n"; + ExpandNodeFromCoverFail(node); + } +#endif + if(_cex) *_cex = cex; + else delete cex.tree; // delete the cex if not required + node->Bound = save; // put back original bound + timer_stop("ProveConjecture"); + return false; + } + + /** If a node is part of the inductive subset, expand it. + We ask the inductive subset to exclude the node if possible. + */ + void TryExpandNode(RPFP::Node *node){ + if(indset->Close(node)) return; + if(!NoConj && indset->Conjecture(node)){ +#ifdef UNDERAPPROX_NODES + /* TODO: temporary fix. this prevents an infinite loop in case + the node is covered by multiple others. This should be + removed when covering by a set is implemented. + */ + if(indset->Contains(node)){ + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + } +#endif + return; + } +#ifdef UNDERAPPROX_NODES + if(!indset->Contains(node)) + return; // could be covered by an underapprox node +#endif + indset->Add(node); +#if defined(CANDS_FROM_COVER_FAIL) && !defined(UNDERAPPROX_NODES) + if(ExpandNodeFromCoverFail(node)) + return; +#endif + ExpandNode(node); + } + + /** Make the conjunction of markers for all (expanded) instances of + a node in the input RPFP. */ + expr AllNodeMarkers(Node *node){ + expr res = ctx.bool_val(true); + std::vector &insts = insts_of_node[node]; + for(int k = insts.size()-1; k >= 0; k--) + res = res && NodeMarker(insts[k]); + return res; + } + + void RuleOutNodesPastBound(Node *node, RPFP::Transformer &t){ +#ifdef BOUNDED + if(RecursionBound < 0)return; + std::vector &insts = insts_of_node[node]; + for(unsigned i = 0; i < insts.size(); i++) + if(NodePastRecursionBound(insts[i])) + t.Formula = t.Formula && !NodeMarker(insts[i]); +#endif + } + + + void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction){ +#ifdef BOUNDED + if(RecursionBound >= 0 && NodePastRecursionBound(node)) + return; +#endif + RPFP::Transformer temp = node->Annotation; + expr marker = NodeMarker(node); + temp.Formula = (!marker || temp.Formula); + annot.IntersectWith(temp); + marker_disjunction = marker_disjunction || marker; + } + + bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false){ + bool res = false; + annot.SetFull(); + expr marker_disjunction = ctx.bool_val(false); + std::vector &insts = expanded_only ? insts_of_node[node] : all_of_node[node]; + for(unsigned j = 0; j < insts.size(); j++){ + Node *node = insts[j]; + if(indset->Contains(insts[j])){ + GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction); res = true; + } + } + annot.Formula = annot.Formula && marker_disjunction; + annot.Simplify(); + return res; + } + + /** Make a checker to determine if an edge in the input RPFP + is satisfied. */ + Node *CheckerJustForEdge(Edge *edge, RPFP *checker, bool expanded_only = false){ + Node *root = checker->CloneNode(edge->Parent); + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); + if(root->Bound.IsFull()) + return 0; + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + if(!GenNodeSolutionWithMarkers(oc,nc->Annotation,expanded_only)) + return 0; + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } + +#ifndef MINIMIZE_CANDIDATES_HARDER + +#if 0 + /** Make a checker to detheermine if an edge in the input RPFP + is satisfied. */ + Node *CheckerForEdge(Edge *edge, RPFP *checker){ + Node *root = checker->CloneNode(edge->Parent); + root->Bound = edge->Parent->Annotation; + root->Bound.Formula = (!AllNodeMarkers(edge->Parent)) || root->Bound.Formula; + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + nc->Annotation = oc->Annotation; + RuleOutNodesPastBound(oc,nc->Annotation); + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } + +#else + /** Make a checker to determine if an edge in the input RPFP + is satisfied. */ + Node *CheckerForEdge(Edge *edge, RPFP *checker){ + Node *root = checker->CloneNode(edge->Parent); + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); +#if 0 + if(root->Bound.IsFull()) + return = 0; +#endif + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } +#endif + + /** If an edge is not satisfied, produce an extension candidate + using instances of its children that violate the parent annotation. + We find these using the marker predicates. */ + void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ + candidate.edge = edge; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Edge *lb = root->Outgoing->Children[j]->Outgoing; + std::vector &insts = insts_of_node[edge->Children[j]]; +#ifndef MINIMIZE_CANDIDATES + for(int k = insts.size()-1; k >= 0; k--) +#else + for(unsigned k = 0; k < insts.size(); k++) +#endif + { + Node *inst = insts[k]; + if(indset->Contains(inst)){ + if(checker->Empty(lb->Parent) || + eq(checker->Eval(lb,NodeMarker(inst)),ctx.bool_val(true))){ + candidate.Children.push_back(inst); + goto next_child; + } + } + } + throw InternalError("No candidate from induction failure"); + next_child:; + } + } +#else + + + /** Make a checker to determine if an edge in the input RPFP + is satisfied. */ + Node *CheckerForEdge(Edge *edge, RPFP *checker){ + Node *root = checker->CloneNode(edge->Parent); + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); + if(root->Bound.IsFull()) + return = 0; + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } + + /** If an edge is not satisfied, produce an extension candidate + using instances of its children that violate the parent annotation. + We find these using the marker predicates. */ + void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ + candidate.edge = edge; + std::vector assumps; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Edge *lb = root->Outgoing->Children[j]->Outgoing; + std::vector &insts = insts_of_node[edge->Children[j]]; + for(unsigned k = 0; k < insts.size(); k++) + { + Node *inst = insts[k]; + expr marker = NodeMarker(inst); + if(indset->Contains(inst)){ + if(checker->Empty(lb->Parent) || + eq(checker->Eval(lb,marker),ctx.bool_val(true))){ + candidate.Children.push_back(inst); + assumps.push_back(checker->Localize(lb,marker)); + goto next_child; + } + assumps.push_back(checker->Localize(lb,marker)); + if(checker->CheckUpdateModel(root,assumps) != unsat){ + candidate.Children.push_back(inst); + goto next_child; + } + assumps.pop_back(); + } + } + throw InternalError("No candidate from induction failure"); + next_child:; + } + } + +#endif + + + /** If the current proposed solution is not inductive, + use the induction failure to generate candidates for extension. */ + void GenCandidatesFromInductionFailure(bool full_scan = false){ + timer_start("GenCandIndFail"); + GenSolutionFromIndSet(true /* add markers */); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *edge = edges[i]; + if(!full_scan && updated_nodes.find(edge->Parent) == updated_nodes.end()) + continue; + slvr.push(); + RPFP *checker = new RPFP(rpfp->ls); + Node *root = CheckerForEdge(edge,checker); + if(checker->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,checker,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + slvr.pop(1); + delete checker; + } + updated_nodes.clear(); + timer_stop("GenCandIndFail"); +#ifdef CHECK_CANDS_FROM_IND_SET + for(std::list::iterator it = candidates.begin(), en = candidates.end(); it != en; ++it){ + if(!CandidateFeasible(*it)) + throw "produced infeasible candidate"; + } +#endif + if(!full_scan && candidates.empty()){ + std::cout << "No candidates from updates. Trying full scan.\n"; + GenCandidatesFromInductionFailure(true); + } + } + +#ifdef CANDS_FROM_UPDATES + /** If the given edge is not inductive in the current proposed solution, + use the induction failure to generate candidates for extension. */ + void GenCandidatesFromEdgeInductionFailure(RPFP::Edge *edge){ + GenSolutionFromIndSet(true /* add markers */); + for(unsigned i = 0; i < edges.size(); i++){ + slvr.push(); + Edge *edge = edges[i]; + RPFP *checker = new RPFP(rpfp->ls); + Node *root = CheckerForEdge(edge,checker); + if(checker->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,checker,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + slvr.pop(1); + delete checker; + } + } +#endif + + /** Find the unexpanded nodes in the inductive subset. */ + void FindNodesToExpand(){ + for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it){ + Node *node = *it; + if(indset->Candidate(node)) + to_expand.push_back(node); + } + } + + /** Try to create some extension candidates from the unexpanded + nodes. */ + void ProduceSomeCandidates(){ + while(candidates.empty() && !to_expand.empty()){ + Node *node = to_expand.front(); + to_expand.pop_front(); + TryExpandNode(node); + } + } + + std::list postponed_candidates; + + /** Try to produce some extension candidates, first from unexpanded + nides, and if this fails, from induction failure. */ + void ProduceCandidatesForExtension(){ + if(candidates.empty()) + ProduceSomeCandidates(); + while(candidates.empty()){ + FindNodesToExpand(); + if(to_expand.empty()) break; + ProduceSomeCandidates(); + } + if(candidates.empty()){ +#ifdef DEPTH_FIRST_EXPAND + if(postponed_candidates.empty()){ + GenCandidatesFromInductionFailure(); + postponed_candidates.swap(candidates); + } + if(!postponed_candidates.empty()){ + candidates.push_back(postponed_candidates.front()); + postponed_candidates.pop_front(); + } +#else + GenCandidatesFromInductionFailure(); +#endif + } + } + + /** Update the unwinding solution, using an interpolant for the + derivation tree. */ + void UpdateWithInterpolant(Node *node, RPFP *tree, Node *top){ + if(top->Outgoing) + for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) + UpdateWithInterpolant(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); + if(!node->Annotation.SubsetEq(top->Annotation)){ + reporter->Update(node,top->Annotation); + indset->Update(node,top->Annotation); + updated_nodes.insert(node->map); + node->Annotation.IntersectWith(top->Annotation); + } + heuristic->Update(node); + } + + /** Update unwinding lower bounds, using a counterexample. */ + + void UpdateWithCounterexample(Node *node, RPFP *tree, Node *top){ + if(top->Outgoing) + for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) + UpdateWithCounterexample(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); + if(!top->Underapprox.SubsetEq(node->Underapprox)){ + reporter->UpdateUnderapprox(node,top->Underapprox); + // indset->Update(node,top->Annotation); + node->Underapprox.UnionWith(top->Underapprox); + heuristic->Update(node); + } + } + + /** Try to update the unwinding to satisfy the upper bound of a + node. */ + bool SatisfyUpperBound(Node *node){ + if(node->Bound.IsFull()) return true; + reporter->Bound(node); + int start_decs = rpfp->CumulativeDecisions(); + DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); + bool res = dt.Derive(unwinding,node,UseUnderapprox); + int end_decs = rpfp->CumulativeDecisions(); + 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; + if(UseUnderapprox){ + UpdateWithCounterexample(node,dt.tree,dt.top); + } + } + else { + UpdateWithInterpolant(node,dt.tree,dt.top); + delete dt.tree; + } + return !res; + } + + /* If the counterexample derivation is partial due to + use of underapproximations, complete it. */ + + void BuildFullCex(Node *node){ + 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"; + cex.tree = dt.tree; + cex.root = dt.top; + } + + void UpdateBackEdges(Node *node){ +#ifdef BOUNDED + std::vector &chs = node->Outgoing->Children; + for(unsigned i = 0; i < chs.size(); i++){ + Node *child = chs[i]; + bool is_back = TopoSort[child->map] >= TopoSort[node->map]; + NodeToCounter &nov = back_edges[node]; + NodeToCounter chv = back_edges[child]; + if(is_back) + chv[child->map].val++; + for(NodeToCounter::iterator it = chv.begin(), en = chv.end(); it != en; ++it){ + Node *back = it->first; + Counter &c = nov[back]; + c.val = std::max(c.val,it->second.val); + } + } +#endif + } + + /** Extend the unwinding, keeping it solved. */ + bool Extend(Candidate &cand){ + Node *node = CreateNodeInstance(cand.edge->Parent); + CreateEdgeInstance(cand.edge,node,cand.Children); + UpdateBackEdges(node); + reporter->Extend(node); + bool res = SatisfyUpperBound(node); + if(res) indset->CloseDescendants(node); + else { +#ifdef UNDERAPPROX_NODES + ExpandUnderapproxNodes(cex.tree, cex.root); +#endif + if(UseUnderapprox) BuildFullCex(node); + return res; + } +#ifdef EARLY_EXPAND + TryExpandNode(node); +#endif + return res; + } + + void ExpandUnderapproxNodes(RPFP *tree, Node *root){ + Node *node = root->map; + if(underapprox_map.find(node) != underapprox_map.end()){ + RPFP::Transformer cnst = root->Annotation; + tree->EvalNodeAsConstraint(root, cnst); + cnst.Complement(); + Node *orig = underapprox_map[node]; + RPFP::Transformer save = orig->Bound; + orig->Bound = cnst; + DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); + bool res = dt.Derive(unwinding,orig,UseUnderapprox,true,tree); + if(!res){ + UpdateWithInterpolant(orig,dt.tree,dt.top); + throw "bogus underapprox!"; + } + ExpandUnderapproxNodes(tree,dt.top); + } + else if(root->Outgoing){ + std::vector &chs = root->Outgoing->Children; + for(unsigned i = 0; i < chs.size(); i++) + ExpandUnderapproxNodes(tree,chs[i]); + } + } + + + /** This class represents a derivation tree. */ + class DerivationTree { + public: + + DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) + : slvr(rpfp->slvr), + ctx(rpfp->ctx) + { + duality = _duality; + reporter = _reporter; + heuristic = _heuristic; + full_expand = _full_expand; + } + + Duality *duality; + Reporter *reporter; + Heuristic *heuristic; + solver &slvr; + context &ctx; + RPFP *tree; + RPFP::Node *top; + std::list leaves; + bool full_expand; + bool underapprox; + bool constrained; + bool false_approx; + std::vector underapprox_core; + int start_decs, last_decs; + + /* We build derivation trees in one of three modes: + + 1) In normal mode, we build the full tree without considering + underapproximations. + + 2) In underapprox mode, we use underapproximations to cut off + the tree construction. THis means the resulting tree may not + be complete. + + 3) In constrained mode, we build the full tree but use + underapproximations as upper bounds. This mode is used to + complete the partial derivation constructed in underapprox + mode. + */ + + bool Derive(RPFP *rpfp, RPFP::Node *root, bool _underapprox, bool _constrained = false, RPFP *_tree = 0){ + underapprox = _underapprox; + constrained = _constrained; + false_approx = true; + timer_start("Derive"); + tree = _tree ? _tree : new RPFP(rpfp->ls); + tree->HornClauses = rpfp->HornClauses; + tree->Push(); // so we can clear out the solver later when finished + top = CreateApproximatedInstance(root); + tree->AssertNode(top); // assert the negation of the top-level spec + timer_start("Build"); + bool res = Build(); + timer_stop("Build"); + timer_start("Pop"); + tree->Pop(1); + timer_stop("Pop"); + timer_stop("Derive"); + return res; + } + +#define WITH_CHILDREN + + Node *CreateApproximatedInstance(RPFP::Node *from){ + Node *to = tree->CloneNode(from); + to->Annotation = from->Annotation; +#ifndef WITH_CHILDREN + tree->CreateLowerBoundEdge(to); +#endif + leaves.push_back(to); + return to; + } + + bool CheckWithUnderapprox(){ + timer_start("CheckWithUnderapprox"); + std::vector leaves_vector(leaves.size()); + std::copy(leaves.begin(),leaves.end(),leaves_vector.begin()); + check_result res = tree->Check(top,leaves_vector); + timer_stop("CheckWithUnderapprox"); + return res != unsat; + } + + bool Build(){ +#ifdef EFFORT_BOUNDED_STRAT + start_decs = tree->CumulativeDecisions(); +#endif + // while(ExpandSomeNodes(true)); // do high-priority expansions + while (true) + { +#ifndef WITH_CHILDREN + timer_start("asserting leaves"); + timer_start("pushing"); + tree->Push(); + timer_stop("pushing"); + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) + tree->AssertEdge((*it)->Outgoing,1); // assert the overapproximation, and keep it past pop + timer_stop("asserting leaves"); + lbool res = tree->Solve(top, 2); // incremental solve, keep interpolants for two pops + timer_start("popping leaves"); + tree->Pop(1); + timer_stop("popping leaves"); +#else + lbool res; + if((underapprox || false_approx) && top->Outgoing && CheckWithUnderapprox()){ + if(constrained) goto expand_some_nodes; // in constrained mode, keep expanding + goto we_are_sat; // else if underapprox is sat, we stop + } + // tree->Check(top); + res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop +#endif + if (res == l_false) + return false; + + expand_some_nodes: + if(ExpandSomeNodes()) + continue; + + we_are_sat: + if(underapprox && !constrained){ + timer_start("ComputeUnderapprox"); + tree->ComputeUnderapprox(top,1); + timer_stop("ComputeUnderapprox"); + } + else { +#ifdef UNDERAPPROX_NODES +#ifndef SKIP_UNDERAPPROX_NODES + timer_start("ComputeUnderapprox"); + tree->ComputeUnderapprox(top,1); + timer_stop("ComputeUnderapprox"); +#endif +#endif + } + return true; + } + } + + void ExpandNode(RPFP::Node *p){ + // tree->RemoveEdge(p->Outgoing); + Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); + std::vector &cs = edge->Children; + std::vector children(cs.size()); + for(unsigned i = 0; i < cs.size(); i++) + children[i] = CreateApproximatedInstance(cs[i]); + Edge *ne = tree->CreateEdge(p, p->map->Outgoing->F, children); + ne->map = p->map->Outgoing->map; +#ifndef WITH_CHILDREN + tree->AssertEdge(ne); // assert the edge in the solver +#else + tree->AssertEdge(ne,0,!full_expand,(underapprox || false_approx)); // assert the edge in the solver +#endif + reporter->Expand(ne); + } + +#define UNDERAPPROXCORE +#ifndef UNDERAPPROXCORE + void ExpansionChoices(std::set &best){ + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) + if (!tree->Empty(*it)) // if used in the counter-model + choices.insert(*it); + heuristic->ChooseExpand(choices, best); + } +#else +#if 0 + void ExpansionChoices(std::set &best){ + std::vector unused_set, used_set; + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ + Node *n = *it; + if (!tree->Empty(n)) + used_set.push_back(n); + else + unused_set.push_back(n); + } + if(tree->Check(top,unused_set) == unsat) + throw "error in ExpansionChoices"; + for(unsigned i = 0; i < used_set.size(); i++){ + Node *n = used_set[i]; + unused_set.push_back(n); + if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ + unused_set.pop_back(); + choices.insert(n); + } + else + std::cout << "Using underapprox of " << n->number << std::endl; + } + heuristic->ChooseExpand(choices, best); + } +#else + void ExpansionChoicesFull(std::set &best, bool high_priority){ + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) + if (high_priority || !tree->Empty(*it)) // if used in the counter-model + choices.insert(*it); + heuristic->ChooseExpand(choices, best, high_priority); + } + + void ExpansionChoicesRec(std::vector &unused_set, std::vector &used_set, + std::set &choices, int from, int to){ + if(from == to) return; + int orig_unused = unused_set.size(); + unused_set.resize(orig_unused + (to - from)); + std::copy(used_set.begin()+from,used_set.begin()+to,unused_set.begin()+orig_unused); + if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ + unused_set.resize(orig_unused); + if(to - from == 1){ +#if 1 + std::cout << "Not using underapprox of " << used_set[from] ->number << std::endl; +#endif + choices.insert(used_set[from]); + } + else { + int mid = from + (to - from)/2; + ExpansionChoicesRec(unused_set, used_set, choices, from, mid); + ExpansionChoicesRec(unused_set, used_set, choices, mid, to); + } + } + else { +#if 1 + std::cout << "Using underapprox of "; + for(int i = from; i < to; i++){ + std::cout << used_set[i]->number << " "; + if(used_set[i]->map->Underapprox.IsEmpty()) + std::cout << "(false!) "; + } + std::cout << std::endl; +#endif + } + } + + std::set old_choices; + + void ExpansionChoices(std::set &best, bool high_priority){ + if(!underapprox || constrained){ + ExpansionChoicesFull(best, high_priority); + return; + } + std::vector unused_set, used_set; + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ + Node *n = *it; + if (!tree->Empty(n)){ + if(old_choices.find(n) != old_choices.end() || n->map->Underapprox.IsEmpty()) + choices.insert(n); + else + used_set.push_back(n); + } + else + unused_set.push_back(n); + } + if(tree->Check(top,unused_set) == unsat) + throw "error in ExpansionChoices"; + ExpansionChoicesRec(unused_set, used_set, choices, 0, used_set.size()); + old_choices = choices; + heuristic->ChooseExpand(choices, best, high_priority); + } +#endif +#endif + + bool ExpandSomeNodes(bool high_priority = false){ +#ifdef EFFORT_BOUNDED_STRAT + last_decs = tree->CumulativeDecisions() - start_decs; +#endif + timer_start("ExpandSomeNodes"); + timer_start("ExpansionChoices"); + std::set choices; + ExpansionChoices(choices,high_priority); + timer_stop("ExpansionChoices"); + std::list leaves_copy = leaves; // copy so can modify orig + leaves.clear(); + for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ + if(choices.find(*it) != choices.end()) + ExpandNode(*it); + else leaves.push_back(*it); + } + timer_stop("ExpandSomeNodes"); + return !choices.empty(); + } + + }; + + class Covering { + + struct cover_info { + Node *covered_by; + std::list covers; + bool dominated; + std::set dominates; + cover_info(){ + covered_by = 0; + dominated = false; + } + }; + + typedef hash_map cover_map; + cover_map cm; + Duality *parent; + bool some_updates; + + Node *&covered_by(Node *node){ + return cm[node].covered_by; + } + + std::list &covers(Node *node){ + return cm[node].covers; + } + + std::vector &insts_of_node(Node *node){ + return parent->insts_of_node[node]; + } + + Reporter *reporter(){ + return parent->reporter; + } + + std::set &dominates(Node *x){ + return cm[x].dominates; + } + + bool dominates(Node *x, Node *y){ + std::set &d = cm[x].dominates; + return d.find(y) != d.end(); + } + + bool &dominated(Node *x){ + return cm[x].dominated; + } + + public: + + Covering(Duality *_parent){ + parent = _parent; + some_updates = false; + } + + bool IsCoveredRec(hash_set &memo, Node *node){ + if(memo.find(node) != memo.end()) + return false; + memo.insert(node); + if(covered_by(node)) return true; + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) + if(IsCoveredRec(memo,node->Outgoing->Children[i])) + return true; + return false; + } + + bool IsCovered(Node *node){ + hash_set memo; + return IsCoveredRec(memo,node); + } + +#ifndef UNDERAPPROX_NODES + void RemoveCoveringsBy(Node *node){ + std::list &cs = covers(node); + for(std::list::iterator it = cs.begin(), en = cs.end(); it != en; it++){ + covered_by(*it) = 0; + reporter()->RemoveCover(*it,node); + } + cs.clear(); + } +#else + void RemoveCoveringsBy(Node *node){ + std::vector &cs = parent->all_of_node[node->map]; + for(std::vector::iterator it = cs.begin(), en = cs.end(); it != en; it++){ + Node *other = *it; + if(covered_by(other) && CoverOrder(node,other)){ + covered_by(other) = 0; + reporter()->RemoveCover(*it,node); + } + } + } +#endif + + void RemoveAscendantCoveringsRec(hash_set &memo, Node *node){ + if(memo.find(node) != memo.end()) + return; + memo.insert(node); + RemoveCoveringsBy(node); + for(std::vector::iterator it = node->Incoming.begin(), en = node->Incoming.end(); it != en; ++it) + RemoveAscendantCoveringsRec(memo,(*it)->Parent); + } + + void RemoveAscendantCoverings(Node *node){ + hash_set memo; + RemoveAscendantCoveringsRec(memo,node); + } + + bool CoverOrder(Node *covering, Node *covered){ +#ifdef UNDERAPPROX_NODES + if(parent->underapprox_map.find(covered) != parent->underapprox_map.end()) + return false; + if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) + return covering->number < covered->number || parent->underapprox_map[covering] == covered; +#endif + return covering->number < covered->number; + } + + bool CheckCover(Node *covered, Node *covering){ + return + CoverOrder(covering,covered) + && covered->Annotation.SubsetEq(covering->Annotation) + && !IsCovered(covering); + } + + bool CoverByNode(Node *covered, Node *covering){ + if(CheckCover(covered,covering)){ + covered_by(covered) = covering; + covers(covering).push_back(covered); + std::vector others; others.push_back(covering); + reporter()->AddCover(covered,others); + RemoveAscendantCoverings(covered); + return true; + } + else + return false; + } + +#ifdef UNDERAPPROX_NODES + bool CoverByAll(Node *covered){ + RPFP::Transformer all = covered->Annotation; + all.SetEmpty(); + std::vector &insts = parent->insts_of_node[covered->map]; + std::vector others; + for(unsigned i = 0; i < insts.size(); i++){ + Node *covering = insts[i]; + if(CoverOrder(covering,covered) && !IsCovered(covering)){ + others.push_back(covering); + all.UnionWith(covering->Annotation); + } + } + if(others.size() && covered->Annotation.SubsetEq(all)){ + covered_by(covered) = covered; // anything non-null will do + reporter()->AddCover(covered,others); + RemoveAscendantCoverings(covered); + return true; + } + else + return false; + } +#endif + + bool Close(Node *node){ + if(covered_by(node)) + return true; +#ifndef UNDERAPPROX_NODES + std::vector &insts = insts_of_node(node->map); + for(unsigned i = 0; i < insts.size(); i++) + if(CoverByNode(node,insts[i])) + return true; +#else + if(CoverByAll(node)) + return true; +#endif + return false; + } + + bool CloseDescendantsRec(hash_set &memo, Node *node){ + if(memo.find(node) != memo.end()) + return false; + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) + if(CloseDescendantsRec(memo,node->Outgoing->Children[i])) + return true; + if(Close(node)) + return true; + memo.insert(node); + return false; + } + + bool CloseDescendants(Node *node){ + timer_start("CloseDescendants"); + hash_set memo; + bool res = CloseDescendantsRec(memo,node); + timer_stop("CloseDescendants"); + return res; + } + + bool Contains(Node *node){ + timer_start("Contains"); + bool res = !IsCovered(node); + timer_stop("Contains"); + return res; + } + + bool Candidate(Node *node){ + timer_start("Candidate"); + bool res = !IsCovered(node) && !dominated(node); + timer_stop("Candidate"); + return res; + } + + void SetDominated(Node *node){ + dominated(node) = true; + } + + bool CouldCover(Node *covered, Node *covering){ +#ifdef UNDERAPPROX_NODES + // if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) + // return parent->underapprox_map[covering] == covered; +#endif + if(CoverOrder(covering,covered) + && !IsCovered(covering)){ + RPFP::Transformer f(covering->Annotation); f.SetEmpty(); +#if defined(TOP_DOWN) || defined(EFFORT_BOUNDED_STRAT) + if(parent->StratifiedInlining) + return true; +#endif + return !covering->Annotation.SubsetEq(f); + } + return false; + } + + bool ContainsCex(Node *node, Counterexample &cex){ + expr val = cex.tree->Eval(cex.root->Outgoing,node->Annotation.Formula); + return eq(val,parent->ctx.bool_val(true)); + } + + /** We conjecture that the annotations of similar nodes may be + true of this one. We start with later nodes, on the + principle that their annotations are likely weaker. We save + a counterexample -- if annotations of other nodes are true + in this counterexample, we don't need to check them. + */ + +#ifndef UNDERAPPROX_NODES + bool Conjecture(Node *node){ + std::vector &insts = insts_of_node(node->map); + Counterexample cex; + for(int i = insts.size() - 1; i >= 0; i--){ + Node *other = insts[i]; + if(CouldCover(node,other)){ + reporter()->Forcing(node,other); + if(cex.tree && !ContainsCex(other,cex)) + continue; + if(cex.tree) {delete cex.tree; cex.tree = 0;} + if(parent->ProveConjecture(node,other->Annotation,other,&cex)) + if(CloseDescendants(node)) + return true; + } + } + if(cex.tree) {delete cex.tree; cex.tree = 0;} + return false; + } +#else + bool Conjecture(Node *node){ + std::vector &insts = insts_of_node(node->map); + Counterexample cex; + RPFP::Transformer Bound = node->Annotation; + Bound.SetEmpty(); + bool some_other = false; + for(int i = insts.size() - 1; i >= 0; i--){ + Node *other = insts[i]; + if(CouldCover(node,other)){ + reporter()->Forcing(node,other); + Bound.UnionWith(other->Annotation); + some_other = true; + } + } + if(some_other && parent->ProveConjecture(node,Bound)){ + CloseDescendants(node); + return true; + } + return false; + } +#endif + + void Update(Node *node, const RPFP::Transformer &update){ + RemoveCoveringsBy(node); + some_updates = true; + } + +#ifndef UNDERAPPROX_NODES + Node *GetSimilarNode(Node *node){ + if(!some_updates) + return 0; + std::vector &insts = insts_of_node(node->map); + for(int i = insts.size()-1; i >= 0; i--){ + Node *other = insts[i]; + if(dominates(node,other)) + if(CoverOrder(other,node) + && !IsCovered(other)) + return other; + } + return 0; + } +#else + Node *GetSimilarNode(Node *node){ + if(!some_updates) + return 0; + std::vector &insts = insts_of_node(node->map); + for(int i = insts.size() - 1; i >= 0; i--){ + Node *other = insts[i]; + if(CoverOrder(other,node) + && !IsCovered(other)) + return other; + } + return 0; + } +#endif + + bool Dominates(Node * node, Node *other){ + if(node == other) return false; + if(other->Outgoing->map == 0) return true; + if(node->Outgoing->map == other->Outgoing->map){ + assert(node->Outgoing->Children.size() == other->Outgoing->Children.size()); + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ + Node *nc = node->Outgoing->Children[i]; + Node *oc = other->Outgoing->Children[i]; + if(!(nc == oc || oc->Outgoing->map ==0 || dominates(nc,oc))) + return false; + } + return true; + } + return false; + } + + void Add(Node *node){ + std::vector &insts = insts_of_node(node->map); + for(unsigned i = 0; i < insts.size(); i++){ + Node *other = insts[i]; + if(Dominates(node,other)){ + cm[node].dominates.insert(other); + cm[other].dominated = true; + reporter()->Dominates(node, other); + } + } + } + + }; + + /* This expansion heuristic makes use of a previuosly obtained + counterexample as a guide. This is for use in abstraction + refinement schemes.*/ + + class ReplayHeuristic : public Heuristic { + + Counterexample &old_cex; + public: + ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) + : Heuristic(_rpfp), old_cex(_old_cex) + { + } + + // Maps nodes of derivation tree into old cex + hash_map cex_map; + + void ShowNodeAndChildren(Node *n){ + std::cout << n->Name.name() << ": "; + std::vector &chs = n->Outgoing->Children; + for(unsigned i = 0; i < chs.size(); i++) + std::cout << chs[i]->Name.name() << " " ; + std::cout << std::endl; + } + + virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority){ + if(!high_priority){ + Heuristic::ChooseExpand(choices,best,false); + return; + } + // first, try to match the derivatino tree nodes to the old cex + std::set matched, unmatched; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it); + if(cex_map.empty()) + cex_map[node] = old_cex.root; // match the root nodes + 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()) + throw "catastrophe in ReplayHeuristic::ChooseExpand"; + Node *old_parent = cex_map[parent]; + std::vector &chs = parent->Outgoing->Children; + if(old_parent && old_parent->Outgoing){ + std::vector &old_chs = old_parent->Outgoing->Children; + for(unsigned i = 0, j=0; i < chs.size(); i++){ + if(j < old_chs.size() && chs[i]->Name.name() == old_chs[j]->Name.name()) + cex_map[chs[i]] = old_chs[j++]; + else { + std::cout << "unmatched child: " << chs[i]->Name.name() << std::endl; + cex_map[chs[i]] = 0; + } + } + goto matching_done; + } + for(unsigned i = 0; i < chs.size(); i++) + cex_map[chs[i]] = 0; + } + matching_done: + Node *old_node = cex_map[node]; + if(!old_node) + unmatched.insert(node); + else if(old_cex.tree->Empty(old_node)) + unmatched.insert(node); + else + matched.insert(node); + } + if (matched.empty() && !high_priority) + Heuristic::ChooseExpand(unmatched,best,false); + else + Heuristic::ChooseExpand(matched,best,false); + } + }; + + + class LocalHeuristic : public Heuristic { + + RPFP::Node *old_node; + public: + LocalHeuristic(RPFP *_rpfp) + : Heuristic(_rpfp) + { + old_node = 0; + } + + void SetOldNode(RPFP::Node *_old_node){ + old_node = _old_node; + cex_map.clear(); + } + + // Maps nodes of derivation tree into old subtree + hash_map cex_map; + + virtual void ChooseExpand(const std::set &choices, std::set &best){ + if(old_node == 0){ + Heuristic::ChooseExpand(choices,best); + return; + } + // first, try to match the derivatino tree nodes to the old cex + std::set matched, unmatched; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it); + if(cex_map.empty()) + cex_map[node] = old_node; // 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()) + throw "catastrophe in ReplayHeuristic::ChooseExpand"; + Node *old_parent = cex_map[parent]; + std::vector &chs = parent->Outgoing->Children; + if(old_parent && old_parent->Outgoing){ + std::vector &old_chs = old_parent->Outgoing->Children; + if(chs.size() == old_chs.size()){ + for(unsigned i = 0; i < chs.size(); i++) + cex_map[chs[i]] = old_chs[i]; + goto matching_done; + } + else + std::cout << "derivation tree does not match old cex" << std::endl; + } + for(unsigned i = 0; i < chs.size(); i++) + cex_map[chs[i]] = 0; + } + matching_done: + Node *old_node = cex_map[node]; + if(!old_node) + unmatched.insert(node); + else if(old_node != node->map) + unmatched.insert(node); + else + matched.insert(node); + } + Heuristic::ChooseExpand(unmatched,best); + } + }; + + + }; + + + class StreamReporter : public Reporter { + std::ostream &s; + public: + StreamReporter(RPFP *_rpfp, std::ostream &_s) + : Reporter(_rpfp), s(_s) {event = 0;} + int event; + void ev(){ + s << "[" << event++ << "]" ; + } + virtual void Extend(RPFP::Node *node){ + ev(); s << "node " << node->number << ": " << node->Name.name(); + std::vector &rps = node->Outgoing->Children; + for(unsigned i = 0; i < rps.size(); i++) + s << " " << rps[i]->number; + s << std::endl; + } + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update){ + ev(); s << "update " << node->number << " " << node->Name.name() << ": "; + rpfp->Summarize(update.Formula); + std::cout << 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; + } + virtual void AddCover(RPFP::Node *covered, std::vector &covering){ + ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; + for(unsigned i = 0; i < covering.size(); i++) + std::cout << covering[i]->number << " "; + std::cout << std::endl; + } + virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){ + ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; + } + virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){ + ev(); s << "forcing " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; + } + 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; + } + virtual void Dominates(RPFP::Node *node, RPFP::Node *other){ + ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; + } + virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){ + ev(); s << "induction failure: " << edge->Parent->Name.name() << ", children ="; + for(unsigned i = 0; i < children.size(); i++) + s << " " << children[i]->number; + s << std::endl; + } + virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){ + ev(); s << "underapprox " << node->number << " " << node->Name.name() << ": " << update.Formula << std::endl; + } + virtual void Reject(RPFP::Edge *edge, const std::vector &children){ + ev(); s << "reject " << edge->Parent->number << " " << edge->Parent->Name.name() << ": "; + for(unsigned i = 0; i < children.size(); i++) + s << " " << children[i]->number; + s << std::endl; + } + + }; + + Solver *Solver::Create(const std::string &solver_class, RPFP *rpfp){ + Duality *s = alloc(Duality,rpfp); + return s; + } + + Reporter *CreateStdoutReporter(RPFP *rpfp){ + return new StreamReporter(rpfp, std::cout); + } +} diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp new file mode 100644 index 000000000..e5a8e31f4 --- /dev/null +++ b/src/duality/duality_wrapper.cpp @@ -0,0 +1,523 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + wrapper.cpp + +Abstract: + + wrap various objects in the style expected by duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + +#include "duality_wrapper.h" +#include +#include "smt_solver.h" +#include "iz3interp.h" +#include "statistics.h" +#include "expr_abstract.h" + +namespace Duality { + + solver::solver(Duality::context& c) : object(c), the_model(c) { + // TODO: the following is a HACK to enable proofs in the old smt solver + // When we stop using that solver, this hack can be removed + m().toggle_proof_mode(PGM_FINE); + params_ref p; + p.set_bool("proof", true); // this is currently useless + p.set_bool("model", true); + p.set_bool("unsat_core", true); + scoped_ptr sf = mk_smt_solver_factory(); + m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); + } + +expr context::constant(const std::string &name, const sort &ty){ + symbol s = str_symbol(name.c_str()); + return cook(m().mk_const(m().mk_const_decl(s, ty))); +} + +expr context::make(decl_kind op, int n, ::expr **args){ + switch(op) { + case True: return mki(m_basic_fid,OP_TRUE,n,args); + case False: return mki(m_basic_fid,OP_FALSE,n,args); + case Equal: return mki(m_basic_fid,OP_EQ,n,args); + case Distinct: return mki(m_basic_fid,OP_DISTINCT,n,args); + case Ite: return mki(m_basic_fid,OP_ITE,n,args); + case And: return mki(m_basic_fid,OP_AND,n,args); + case Or: return mki(m_basic_fid,OP_OR,n,args); + case Iff: return mki(m_basic_fid,OP_IFF,n,args); + case Xor: return mki(m_basic_fid,OP_XOR,n,args); + case Not: return mki(m_basic_fid,OP_NOT,n,args); + case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args); + case Oeq: return mki(m_basic_fid,OP_OEQ,n,args); + case Interp: return mki(m_basic_fid,OP_INTERP,n,args); + case Leq: return mki(m_arith_fid,OP_LE,n,args); + case Geq: return mki(m_arith_fid,OP_GE,n,args); + case Lt: return mki(m_arith_fid,OP_LT,n,args); + case Gt: return mki(m_arith_fid,OP_GT,n,args); + case Plus: return mki(m_arith_fid,OP_ADD,n,args); + case Sub: return mki(m_arith_fid,OP_SUB,n,args); + case Uminus: return mki(m_arith_fid,OP_UMINUS,n,args); + case Times: return mki(m_arith_fid,OP_MUL,n,args); + case Div: return mki(m_arith_fid,OP_DIV,n,args); + case Idiv: return mki(m_arith_fid,OP_IDIV,n,args); + case Rem: return mki(m_arith_fid,OP_REM,n,args); + case Mod: return mki(m_arith_fid,OP_MOD,n,args); + case Power: return mki(m_arith_fid,OP_POWER,n,args); + case ToReal: return mki(m_arith_fid,OP_TO_REAL,n,args); + case ToInt: return mki(m_arith_fid,OP_TO_INT,n,args); + case IsInt: return mki(m_arith_fid,OP_IS_INT,n,args); + case Store: return mki(m_array_fid,OP_STORE,n,args); + case Select: return mki(m_array_fid,OP_SELECT,n,args); + case ConstArray: return mki(m_array_fid,OP_CONST_ARRAY,n,args); + case ArrayDefault: return mki(m_array_fid,OP_ARRAY_DEFAULT,n,args); + case ArrayMap: return mki(m_array_fid,OP_ARRAY_MAP,n,args); + case SetUnion: return mki(m_array_fid,OP_SET_UNION,n,args); + case SetIntersect: return mki(m_array_fid,OP_SET_INTERSECT,n,args); + case SetDifference: return mki(m_array_fid,OP_SET_DIFFERENCE,n,args); + case SetComplement: return mki(m_array_fid,OP_SET_COMPLEMENT,n,args); + case SetSubSet: return mki(m_array_fid,OP_SET_SUBSET,n,args); + case AsArray: return mki(m_array_fid,OP_AS_ARRAY,n,args); + default: + assert(0); + return expr(*this); + } +} + + expr context::mki(family_id fid, ::decl_kind dk, int n, ::expr **args){ + return cook(m().mk_app(fid, dk, 0, 0, n, (::expr **)args)); +} + +expr context::make(decl_kind op, const std::vector &args){ + static std::vector< ::expr*> a(10); + if(a.size() < args.size()) + a.resize(args.size()); + for(unsigned i = 0; i < args.size(); i++) + a[i] = to_expr(args[i].raw()); + return make(op,args.size(), args.size() ? &a[0] : 0); +} + +expr context::make(decl_kind op){ + return make(op,0,0); +} + +expr context::make(decl_kind op, const expr &arg0){ + ::expr *a = to_expr(arg0.raw()); + return make(op,1,&a); +} + +expr context::make(decl_kind op, const expr &arg0, const expr &arg1){ + ::expr *args[2]; + args[0] = to_expr(arg0.raw()); + args[1] = to_expr(arg1.raw()); + return make(op,2,args); +} + +expr context::make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2){ + ::expr *args[3]; + args[0] = to_expr(arg0.raw()); + args[1] = to_expr(arg1.raw()); + args[2] = to_expr(arg2.raw()); + return make(op,3,args); +} + +expr context::make_quant(decl_kind op, const std::vector &bvs, const expr &body){ + if(bvs.size() == 0) return body; + std::vector< ::expr *> foo(bvs.size()); + + + std::vector< ::symbol> names; + std::vector< ::sort *> types; + std::vector< ::expr *> bound_asts; + unsigned num_bound = bvs.size(); + + for (unsigned i = 0; i < num_bound; ++i) { + app* a = to_app(bvs[i].raw()); + ::symbol s(to_app(a)->get_decl()->get_name()); + names.push_back(s); + types.push_back(m().get_sort(a)); + bound_asts.push_back(a); + } + expr_ref abs_body(m()); + expr_abstract(m(), 0, num_bound, &bound_asts[0], to_expr(body.raw()), abs_body); + expr_ref result(m()); + result = m().mk_quantifier( + op == Forall, + names.size(), &types[0], &names[0], abs_body.get(), + 0, + ::symbol(), + ::symbol(), + 0, 0, + 0, 0 + ); + return cook(result.get()); +} + + decl_kind func_decl::get_decl_kind() const { + return ctx().get_decl_kind(*this); + } + + decl_kind context::get_decl_kind(const func_decl &t){ + ::func_decl *d = to_func_decl(t.raw()); + if (null_family_id == d->get_family_id()) + return Uninterpreted; + // return (opr)d->get_decl_kind(); + if (m_basic_fid == d->get_family_id()) { + switch(d->get_decl_kind()) { + case OP_TRUE: return True; + case OP_FALSE: return False; + case OP_EQ: return Equal; + case OP_DISTINCT: return Distinct; + case OP_ITE: return Ite; + case OP_AND: return And; + case OP_OR: return Or; + case OP_IFF: return Iff; + case OP_XOR: return Xor; + case OP_NOT: return Not; + case OP_IMPLIES: return Implies; + case OP_OEQ: return Oeq; + case OP_INTERP: return Interp; + default: + return OtherBasic; + } + } + if (m_arith_fid == d->get_family_id()) { + switch(d->get_decl_kind()) { + case OP_LE: return Leq; + case OP_GE: return Geq; + case OP_LT: return Lt; + case OP_GT: return Gt; + case OP_ADD: return Plus; + case OP_SUB: return Sub; + case OP_UMINUS: return Uminus; + case OP_MUL: return Times; + case OP_DIV: return Div; + case OP_IDIV: return Idiv; + case OP_REM: return Rem; + case OP_MOD: return Mod; + case OP_POWER: return Power; + case OP_TO_REAL: return ToReal; + case OP_TO_INT: return ToInt; + case OP_IS_INT: return IsInt; + default: + return OtherArith; + } + } + if (m_array_fid == d->get_family_id()) { + switch(d->get_decl_kind()) { + case OP_STORE: return Store; + case OP_SELECT: return Select; + case OP_CONST_ARRAY: return ConstArray; + case OP_ARRAY_DEFAULT: return ArrayDefault; + case OP_ARRAY_MAP: return ArrayMap; + case OP_SET_UNION: return SetUnion; + case OP_SET_INTERSECT: return SetIntersect; + case OP_SET_DIFFERENCE: return SetDifference; + case OP_SET_COMPLEMENT: return SetComplement; + case OP_SET_SUBSET: return SetSubSet; + case OP_AS_ARRAY: return AsArray; + default: + return OtherArray; + } + } + + return Other; + } + + + sort_kind context::get_sort_kind(const sort &s){ + family_id fid = to_sort(s.raw())->get_family_id(); + ::decl_kind k = to_sort(s.raw())->get_decl_kind(); + if (m().is_uninterp(to_sort(s.raw()))) { + return UninterpretedSort; + } + else if (fid == m_basic_fid && k == BOOL_SORT) { + return BoolSort; + } + else if (fid == m_arith_fid && k == INT_SORT) { + return IntSort; + } + else if (fid == m_arith_fid && k == REAL_SORT) { + return RealSort; + } + else if (fid == m_array_fid && k == ARRAY_SORT) { + return ArraySort; + } + else { + return UnknownSort; + } + } + + expr func_decl::operator()(unsigned n, expr const * args) const { + std::vector< ::expr *> _args(n); + for(unsigned i = 0; i < n; i++) + _args[i] = to_expr(args[i].raw()); + return ctx().cook(m().mk_app(to_func_decl(raw()),n,&_args[0])); + } + + int solver::get_num_decisions(){ + ::statistics st; + m_solver->collect_statistics(st); + std::ostringstream ss; + st.display(ss); + std::string stats = ss.str(); + int pos = stats.find("decisions:"); + pos += 10; + int end = stats.find('\n',pos); + std::string val = stats.substr(pos,end-pos); + return atoi(val.c_str()); + } + + void context::print_expr(std::ostream &s, const ast &e){ + s << mk_pp(e.raw(), m()); + } + + + expr expr::simplify(const params &_p) const { + ::expr * a = to_expr(raw()); + params_ref p = _p.get(); + th_rewriter m_rw(m(), p); + expr_ref result(m()); + m_rw(a, result); + return ctx().cook(result); + } + + expr expr::simplify() const { + params p; + return simplify(p); + } + + expr clone_quantifier(const expr &q, const expr &b){ + return q.ctx().cook(q.m().update_quantifier(to_quantifier(q.raw()), to_expr(b.raw()))); + } + + func_decl context::fresh_func_decl(char const * prefix, const std::vector &domain, sort const & range){ + std::vector < ::sort * > _domain(domain.size()); + for(unsigned i = 0; i < domain.size(); i++) + _domain[i] = to_sort(domain[i].raw()); + ::func_decl* d = m().mk_fresh_func_decl(prefix, + _domain.size(), + &_domain[0], + to_sort(range.raw())); + return func_decl(*this,d); + } + + func_decl context::fresh_func_decl(char const * prefix, sort const & range){ + ::func_decl* d = m().mk_fresh_func_decl(prefix, + 0, + 0, + to_sort(range.raw())); + return func_decl(*this,d); + } + + + +#if 0 + + + lbool interpolating_solver::interpolate( + const std::vector &assumptions, + std::vector &interpolants, + model &model, + Z3_literals &labels, + bool incremental) + { + Z3_model _model = 0; + Z3_literals _labels = 0; + Z3_lbool lb; + std::vector _assumptions(assumptions.size()); + std::vector _interpolants(assumptions.size()-1); + for(unsigned i = 0; i < assumptions.size(); i++) + _assumptions[i] = assumptions[i]; + std::vector _theory(theory.size()); + for(unsigned i = 0; i < theory.size(); i++) + _theory[i] = theory[i]; + + lb = Z3_interpolate( + ctx(), + _assumptions.size(), + &_assumptions[0], + 0, + 0, + &_interpolants[0], + &_model, + &_labels, + incremental, + _theory.size(), + &_theory[0] + ); + + if(lb == Z3_L_FALSE){ + interpolants.resize(_interpolants.size()); + for (unsigned i = 0; i < _interpolants.size(); ++i) { + interpolants[i] = expr(ctx(),_interpolants[i]); + } + } + + if (_model) { + model = iz3wrapper::model(ctx(), _model); + } + + if(_labels){ + labels = _labels; + } + + return lb; + } + +#endif + + static int linearize_assumptions(int num, + TermTree *assumptions, + std::vector &linear_assumptions, + std::vector &parents){ + for(unsigned i = 0; i < assumptions->getChildren().size(); i++) + num = linearize_assumptions(num, assumptions->getChildren()[i], linear_assumptions, parents); + linear_assumptions[num] = assumptions->getTerm(); + for(unsigned i = 0; i < assumptions->getChildren().size(); i++) + parents[assumptions->getChildren()[i]->getNumber()] = num; + parents[num] = SHRT_MAX; // in case we have no parent + linear_assumptions[num] = assumptions->getTerm(); + return num + 1; + } + + static int unlinearize_interpolants(int num, + TermTree* assumptions, + const std::vector &interpolant, + TermTree * &tree_interpolant) + { + std::vector chs(assumptions->getChildren().size()); + for(unsigned i = 0; i < assumptions->getChildren().size(); i++) + num = unlinearize_interpolants(num, assumptions->getChildren()[i], interpolant,chs[i]); + expr f; + if(num < (int)interpolant.size()) // last interpolant is missing, presumed false + f = interpolant[num]; + tree_interpolant = new TermTree(f,chs); + return num + 1; + } + + + lbool interpolating_solver::interpolate_tree(TermTree *assumptions, + TermTree *&interpolant, + model &model, + literals &labels, + bool incremental + ) + + { + int size = assumptions->number(0); + std::vector linear_assumptions(size); + std::vector parents(size); + linearize_assumptions(0,assumptions,linear_assumptions,parents); + + ptr_vector< ::ast> _interpolants(size-1); + ptr_vector< ::ast>_assumptions(size); + for(int i = 0; i < size; i++) + _assumptions[i] = linear_assumptions[i]; + ::vector _parents(parents.size()); + for(unsigned i = 0; i < parents.size(); i++) + _parents[i] = parents[i]; + ptr_vector< ::ast> _theory(theory.size()); + for(unsigned i = 0; i < theory.size(); i++) + _theory[i] = theory[i]; + + push(); + + if(!incremental){ + for(unsigned i = 0; i < linear_assumptions.size(); i++) + add(linear_assumptions[i]); + } + + check_result res = check(); + + if(res == unsat){ + + // TODO +#if 0 + if(weak_mode) + Z3_set_interpolation_option(options,"weak","1"); +#endif + + ::ast *proof = m_solver->get_proof(); + iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,0); + + std::vector linearized_interpolants(_interpolants.size()); + for(unsigned i = 0; i < _interpolants.size(); i++) + linearized_interpolants[i] = expr(ctx(),_interpolants[i]); + + unlinearize_interpolants(0,assumptions,linearized_interpolants,interpolant); + interpolant->setTerm(ctx().bool_val(false)); + } + + model_ref _m; + m_solver->get_model(_m); + model = Duality::model(ctx(),_m.get()); + +#if 0 + if(_labels){ + labels = _labels; + } +#endif + + pop(); + + return (res == unsat) ? l_false : ((res == sat) ? l_true : l_undef); + + } + + + void interpolating_solver::SetWeakInterpolants(bool weak){ + weak_mode = weak; + } + + + void interpolating_solver::SetPrintToFile(const std::string &filename){ + print_filename = filename; + } + + void interpolating_solver::AssertInterpolationAxiom(const expr & t){ + add(t); + theory.push_back(t); + } + + + void interpolating_solver::RemoveInterpolationAxiom(const expr & t){ + // theory.remove(t); + } + + + const char *interpolating_solver::profile(){ + // return Z3_interpolation_profile(ctx()); + return ""; + } + + + void ast::show() const{ + std::cout << mk_pp(raw(), m()) << std::endl; + } + +#if 0 + void model::show() const { + std::cout << Z3_model_to_string(ctx(), m_model) << std::endl; + } + + Z3_ast ast_to_track = 0; +#endif + + void include_ast_show(ast &a){ + a.show(); + } + +} + + + + diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h new file mode 100755 index 000000000..0afad2df9 --- /dev/null +++ b/src/duality/duality_wrapper.h @@ -0,0 +1,1343 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality_wrapper.h + +Abstract: + + wrap various Z3 classes in the style expected by duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ +#ifndef __DUALITY_WRAPPER_H_ +#define __DUALITY_WRAPPER_H_ + +#include +#include +#include +#include +#include +#include +#include"version.h" +#include + +#include "iz3hash.h" +#include "model.h" +#include "solver.h" + +#include"well_sorted.h" +#include"arith_decl_plugin.h" +#include"bv_decl_plugin.h" +#include"datatype_decl_plugin.h" +#include"array_decl_plugin.h" +#include"ast_translation.h" +#include"ast_pp.h" +#include"ast_ll_pp.h" +#include"ast_smt_pp.h" +#include"ast_smt2_pp.h" +#include"th_rewriter.h" +#include"var_subst.h" +#include"expr_substitution.h" +#include"pp.h" +#include"scoped_ctrl_c.h" +#include"cancel_eh.h" +#include"scoped_timer.h" + +namespace Duality { + + class exception; + class config; + class context; + class symbol; + class params; + class ast; + class sort; + class func_decl; + class expr; + class solver; + class goal; + class tactic; + class probe; + class model; + class func_interp; + class func_entry; + class statistics; + class apply_result; + class fixedpoint; + class literals; + + /** + Duality global configuration object. + */ + + class config { + params_ref m_params; + config & operator=(config const & s); + public: + config(config const & s) : m_params(s.m_params) {} + config(const params_ref &_params) : m_params(_params) {} + config() { } // TODO: create a new params object here + ~config() { } + void set(char const * param, char const * value) { m_params.set_str(param,value); } + void set(char const * param, bool value) { m_params.set_bool(param,value); } + void set(char const * param, int value) { m_params.set_uint(param,value); } + params_ref &get() {return m_params;} + const params_ref &get() const {return m_params;} + }; + + enum decl_kind { + True, + False, + And, + Or, + Not, + Iff, + Ite, + Equal, + Implies, + Distinct, + Xor, + Oeq, + Interp, + Leq, + Geq, + Lt, + Gt, + Plus, + Sub, + Uminus, + Times, + Div, + Idiv, + Rem, + Mod, + Power, + ToReal, + ToInt, + IsInt, + Select, + Store, + ConstArray, + ArrayDefault, + ArrayMap, + SetUnion, + SetIntersect, + SetDifference, + SetComplement, + SetSubSet, + AsArray, + Numeral, + Forall, + Exists, + Variable, + Uninterpreted, + OtherBasic, + OtherArith, + OtherArray, + Other + }; + + enum sort_kind {BoolSort,IntSort,RealSort,ArraySort,UninterpretedSort,UnknownSort}; + + /** + A context has an ast manager global configuration options, etc. + */ + + class context { + protected: + ast_manager &mgr; + config m_config; + + family_id m_basic_fid; + family_id m_array_fid; + family_id m_arith_fid; + family_id m_bv_fid; + family_id m_dt_fid; + family_id m_datalog_fid; + arith_util m_arith_util; + + public: + context(ast_manager &_manager, const config &_config) : mgr(_manager), m_config(_config), m_arith_util(_manager) { + m_basic_fid = m().get_basic_family_id(); + m_arith_fid = m().mk_family_id("arith"); + m_bv_fid = m().mk_family_id("bv"); + m_array_fid = m().mk_family_id("array"); + m_dt_fid = m().mk_family_id("datatype"); + m_datalog_fid = m().mk_family_id("datalog_relation"); + } + ~context() { } + + ast_manager &m() const {return *(ast_manager *)&mgr;} + + void set(char const * param, char const * value) { m_config.set(param,value); } + void set(char const * param, bool value) { m_config.set(param,value); } + void set(char const * param, int value) { m_config.set(param,value); } + + symbol str_symbol(char const * s); + symbol int_symbol(int n); + + sort bool_sort(); + sort int_sort(); + sort real_sort(); + sort bv_sort(unsigned sz); + sort array_sort(sort d, sort r); + + func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range); + func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range); + func_decl function(char const * name, sort const & domain, sort const & range); + func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range); + func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range); + func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); + func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); + func_decl fresh_func_decl(char const * name, const std::vector &domain, sort const & range); + func_decl fresh_func_decl(char const * name, sort const & range); + + expr constant(symbol const & name, sort const & s); + expr constant(char const * name, sort const & s); + expr constant(const std::string &name, sort const & s); + expr bool_const(char const * name); + expr int_const(char const * name); + expr real_const(char const * name); + expr bv_const(char const * name, unsigned sz); + + expr bool_val(bool b); + + expr int_val(int n); + expr int_val(unsigned n); + expr int_val(char const * n); + + expr real_val(int n, int d); + expr real_val(int n); + expr real_val(unsigned n); + expr real_val(char const * n); + + expr bv_val(int n, unsigned sz); + expr bv_val(unsigned n, unsigned sz); + expr bv_val(char const * n, unsigned sz); + + expr num_val(int n, sort const & s); + + expr mki(family_id fid, ::decl_kind dk, int n, ::expr **args); + expr make(decl_kind op, int n, ::expr **args); + expr make(decl_kind op, const std::vector &args); + expr make(decl_kind op); + expr make(decl_kind op, const expr &arg0); + expr make(decl_kind op, const expr &arg0, const expr &arg1); + expr make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2); + + expr make_quant(decl_kind op, const std::vector &bvs, const expr &body); + + + decl_kind get_decl_kind(const func_decl &t); + + sort_kind get_sort_kind(const sort &s); + + void print_expr(std::ostream &s, const ast &e); + + fixedpoint mk_fixedpoint(); + + expr cook(::expr *a); + std::vector cook(ptr_vector< ::expr> v); + ::expr *uncook(const expr &a); + }; + + template + class z3array { + T * m_array; + unsigned m_size; + z3array(z3array const & s); + z3array & operator=(z3array const & s); + public: + z3array(unsigned sz):m_size(sz) { m_array = new T[sz]; } + ~z3array() { delete[] m_array; } + unsigned size() const { return m_size; } + T & operator[](unsigned i) { assert(i < m_size); return m_array[i]; } + T const & operator[](unsigned i) const { assert(i < m_size); return m_array[i]; } + T const * ptr() const { return m_array; } + T * ptr() { return m_array; } + }; + + class object { + protected: + context * m_ctx; + public: + object(): m_ctx((context *)0) {} + object(context & c):m_ctx(&c) {} + object(object const & s):m_ctx(s.m_ctx) {} + context & ctx() const { return *m_ctx; } + friend void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } + ast_manager &m() const {return m_ctx->m();} + }; + + class symbol : public object { + ::symbol m_sym; + public: + symbol(context & c, ::symbol s):object(c), m_sym(s) {} + symbol(symbol const & s):object(s), m_sym(s.m_sym) {} + symbol & operator=(symbol const & s) { m_ctx = s.m_ctx; m_sym = s.m_sym; return *this; } + operator ::symbol() const {return m_sym;} + std::string str() const { + if (m_sym.is_numerical()) { + std::ostringstream buffer; + buffer << m_sym.get_num(); + return buffer.str(); + } + else { + return m_sym.bare_str(); + } + } + friend std::ostream & operator<<(std::ostream & out, symbol const & s){ + return out << s.str(); + } + friend bool operator==(const symbol &x, const symbol &y){ + return x.m_sym == y.m_sym; + } + }; + + class params : public config {}; + + /** Wrapper around an ast pointer */ + class ast_i : public object { + protected: + ::ast *_ast; + public: + ::ast * const &raw() const {return _ast;} + ast_i(context & c, ::ast *a = 0) : object(c) {_ast = a;} + + ast_i(){_ast = 0;} + bool eq(const ast_i &other) const { + return _ast == other._ast; + } + bool lt(const ast_i &other) const { + return _ast < other._ast; + } + friend bool operator==(const ast_i &x, const ast_i&y){ + return x.eq(y); + } + friend bool operator!=(const ast_i &x, const ast_i&y){ + return !x.eq(y); + } + friend bool operator<(const ast_i &x, const ast_i&y){ + return x.lt(y); + } + size_t hash() const {return (size_t)_ast;} + bool null() const {return !_ast;} + }; + + /** Reference counting verison of above */ + class ast : public ast_i { + public: + operator ::ast*() const { return raw(); } + friend bool eq(ast const & a, ast const & b) { return a.raw() == b.raw(); } + + + ast(context &c, ::ast *a = 0) : ast_i(c,a) { + if(_ast) + m().inc_ref(a); + } + + ast() {} + + ast(const ast &other) : ast_i(other) { + if(_ast) + m().inc_ref(_ast); + } + + ast &operator=(const ast &other) { + if(_ast) + m().dec_ref(_ast); + _ast = other._ast; + m_ctx = other.m_ctx; + if(_ast) + m().inc_ref(_ast); + return *this; + } + + ~ast(){ + if(_ast) + m().dec_ref(_ast); + } + + void show() const; + + }; + + class sort : public ast { + public: + sort(context & c):ast(c) {} + sort(context & c, ::sort *s):ast(c, s) {} + sort(sort const & s):ast(s) {} + operator ::sort*() const { return to_sort(raw()); } + sort & operator=(sort const & s) { return static_cast(ast::operator=(s)); } + + bool is_bool() const { return m().is_bool(*this); } + bool is_int() const { return ctx().get_sort_kind(*this) == IntSort; } + bool is_real() const { return ctx().get_sort_kind(*this) == RealSort; } + bool is_arith() const; + bool is_array() const { return ctx().get_sort_kind(*this) == ArraySort; } + bool is_datatype() const; + bool is_relation() const; + bool is_finite_domain() const; + + + sort array_domain() const; + sort array_range() const; + }; + + class func_decl : public ast { + public: + func_decl() : ast() {} + func_decl(context & c):ast(c) {} + func_decl(context & c, ::func_decl *n):ast(c, n) {} + func_decl(func_decl const & s):ast(s) {} + operator ::func_decl*() const { return to_func_decl(*this); } + func_decl & operator=(func_decl const & s) { return static_cast(ast::operator=(s)); } + + unsigned arity() const; + sort domain(unsigned i) const; + sort range() const; + symbol name() const {return symbol(ctx(),to_func_decl(raw())->get_name());} + decl_kind get_decl_kind() const; + + bool is_const() const { return arity() == 0; } + + expr operator()(unsigned n, expr const * args) const; + expr operator()(const std::vector &args) const; + expr operator()(expr const & a) const; + expr operator()(int a) const; + expr operator()(expr const & a1, expr const & a2) const; + expr operator()(expr const & a1, int a2) const; + expr operator()(int a1, expr const & a2) const; + expr operator()(expr const & a1, expr const & a2, expr const & a3) const; + expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const; + expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const; + + func_decl get_func_decl_parameter(unsigned idx){ + return func_decl(ctx(),to_func_decl(to_func_decl(raw())->get_parameters()[idx].get_ast())); + } + + }; + + class expr : public ast { + public: + expr() : ast() {} + expr(context & c):ast(c) {} + expr(context & c, ::ast *n):ast(c, n) {} + expr(expr const & n):ast(n) {} + expr & operator=(expr const & n) { return static_cast(ast::operator=(n)); } + operator ::expr*() const { return to_expr(raw()); } + unsigned get_id() const {return to_expr(raw())->get_id();} + + sort get_sort() const { return sort(ctx(),m().get_sort(to_expr(raw()))); } + + bool is_bool() const { return get_sort().is_bool(); } + bool is_int() const { return get_sort().is_int(); } + bool is_real() const { return get_sort().is_real(); } + bool is_arith() const { return get_sort().is_arith(); } + bool is_array() const { return get_sort().is_array(); } + bool is_datatype() const { return get_sort().is_datatype(); } + bool is_relation() const { return get_sort().is_relation(); } + bool is_finite_domain() const { return get_sort().is_finite_domain(); } + + bool is_numeral() const { + return is_app() && decl().get_decl_kind() == OtherArith && m().is_unique_value(to_expr(raw())); + } + bool is_app() const {return raw()->get_kind() == AST_APP;} + bool is_quantifier() const {return raw()->get_kind() == AST_QUANTIFIER;} + bool is_var() const {return raw()->get_kind() == AST_VAR;} + + // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } + func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} + unsigned num_args() const { + ast_kind dk = raw()->get_kind(); + switch(dk){ + case AST_APP: + return to_app(raw())->get_num_args(); + case AST_QUANTIFIER: + return 1; + case AST_VAR: + return 0; + default:; + } + SASSERT(0); + return 0; + } + expr arg(unsigned i) const { + ast_kind dk = raw()->get_kind(); + switch(dk){ + case AST_APP: + return ctx().cook(to_app(raw())->get_arg(i)); + case AST_QUANTIFIER: + return ctx().cook(to_quantifier(raw())->get_expr()); + default:; + } + assert(0); + return expr(); + } + + expr body() const { + return ctx().cook(to_quantifier(raw())->get_expr()); + } + + friend expr operator!(expr const & a) { + // ::expr *e = a; + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_NOT,a)); + } + + friend expr operator&&(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_AND,a,b)); + } + + friend expr operator||(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_OR,a,b)); + } + + friend expr implies(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_IMPLIES,a,b)); + } + + friend expr operator==(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_EQ,a,b)); + } + + friend expr operator!=(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DISTINCT,a,b)); + } + + friend expr operator+(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_ADD,a,b)); + } + + friend expr operator*(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_MUL,a,b)); + } + + friend expr operator/(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DIV,a,b)); + } + + friend expr operator-(expr const & a) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_UMINUS,a)); + } + + friend expr operator-(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_SUB,a,b)); + } + + friend expr operator<=(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LE,a,b)); + } + + friend expr operator>=(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GE,a,b)); + } + + friend expr operator<(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LT,a,b)); + } + + friend expr operator>(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GT,a,b)); + } + + expr simplify() const; + + expr simplify(params const & p) const; + + friend expr clone_quantifier(const expr &, const expr &); + + friend std::ostream & operator<<(std::ostream & out, expr const & m){ + m.ctx().print_expr(out,m); + return out; + } + + unsigned get_quantifier_num_bound() const { + return to_quantifier(raw())->get_num_decls(); + } + + unsigned get_index_value() const { + var* va = to_var(raw()); + return va->get_idx(); + } + + bool is_quantifier_forall() const { + return to_quantifier(raw())->is_forall(); + } + + sort get_quantifier_bound_sort(unsigned n) const { + return sort(ctx(),to_quantifier(raw())->get_decl_sort(n)); + } + + symbol get_quantifier_bound_name(unsigned n) const { + return symbol(ctx(),to_quantifier(raw())->get_decl_names()[n]); + } + + friend expr forall(const std::vector &quants, const expr &body); + + friend expr exists(const std::vector &quants, const expr &body); + + }; + + +#if 0 + +#if Z3_MAJOR_VERSION > 4 || Z3_MAJOR_VERSION == 4 && Z3_MINOR_VERSION >= 3 + template + class ast_vector_tpl : public object { + Z3_ast_vector m_vector; + void init(Z3_ast_vector v) { Z3_ast_vector_inc_ref(ctx(), v); m_vector = v; } + public: + ast_vector_tpl(context & c):object(c) { init(Z3_mk_ast_vector(c)); } + ast_vector_tpl(context & c, Z3_ast_vector v):object(c) { init(v); } + ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } + ~ast_vector_tpl() { /* Z3_ast_vector_dec_ref(ctx(), m_vector); */ } + operator Z3_ast_vector() const { return m_vector; } + unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } + T operator[](unsigned i) const { Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } + void push_back(T const & e) { Z3_ast_vector_push(ctx(), m_vector, e); check_error(); } + void resize(unsigned sz) { Z3_ast_vector_resize(ctx(), m_vector, sz); check_error(); } + T back() const { return operator[](size() - 1); } + void pop_back() { assert(size() > 0); resize(size() - 1); } + bool empty() const { return size() == 0; } + ast_vector_tpl & operator=(ast_vector_tpl const & s) { + Z3_ast_vector_inc_ref(s.ctx(), s.m_vector); + // Z3_ast_vector_dec_ref(ctx(), m_vector); + m_ctx = s.m_ctx; + m_vector = s.m_vector; + return *this; + } + friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } + }; + + typedef ast_vector_tpl ast_vector; + typedef ast_vector_tpl expr_vector; + typedef ast_vector_tpl sort_vector; + typedef ast_vector_tpl func_decl_vector; + +#endif + +#endif + + class func_interp : public object { + ::func_interp * m_interp; + void init(::func_interp * e) { + m_interp = e; + } + public: + func_interp(context & c, ::func_interp * e):object(c) { init(e); } + func_interp(func_interp const & s):object(s) { init(s.m_interp); } + ~func_interp() { } + operator ::func_interp *() const { return m_interp; } + func_interp & operator=(func_interp const & s) { + m_ctx = s.m_ctx; + m_interp = s.m_interp; + return *this; + } + unsigned num_entries() const { return m_interp->num_entries(); } + expr get_arg(unsigned ent, unsigned arg) const { + return expr(ctx(),m_interp->get_entry(ent)->get_arg(arg)); + } + expr get_value(unsigned ent) const { + return expr(ctx(),m_interp->get_entry(ent)->get_result()); + } + expr else_value() const { + return expr(ctx(),m_interp->get_else()); + } + }; + + + + class model : public object { + model_ref m_model; + void init(::model *m) { + m_model = m; + } + public: + model(context & c, ::model * m = 0):object(c), m_model(m) { } + model(model const & s):object(s), m_model(s.m_model) { } + ~model() { } + operator ::model *() const { return m_model.get(); } + model & operator=(model const & s) { + // ::model *_inc_ref(s.ctx(), s.m_model); + // ::model *_dec_ref(ctx(), m_model); + m_ctx = s.m_ctx; + m_model = s.m_model.get(); + return *this; + } + model & operator=(::model *s) { + m_model = s; + return *this; + } + + expr eval(expr const & n, bool model_completion=false) const { + ::model * _m = m_model.get(); + expr_ref result(ctx().m()); + _m->eval(n, result, model_completion); + return expr(ctx(), result); + } + + void show() const; + + unsigned num_consts() const; + unsigned num_funcs() const; + func_decl get_const_decl(unsigned i) const; + func_decl get_func_decl(unsigned i) const; + unsigned size() const; + func_decl operator[](unsigned i) const; + + expr get_const_interp(func_decl f) const { + return ctx().cook(m_model->get_const_interp(to_func_decl(f.raw()))); + } + + func_interp get_func_interp(func_decl f) const { + return func_interp(ctx(),m_model->get_func_interp(to_func_decl(f.raw()))); + } + +#if 0 + friend std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } +#endif + }; + +#if 0 + class stats : public object { + Z3_stats m_stats; + void init(Z3_stats e) { + m_stats = e; + Z3_stats_inc_ref(ctx(), m_stats); + } + public: + stats(context & c):object(c), m_stats(0) {} + stats(context & c, Z3_stats e):object(c) { init(e); } + stats(stats const & s):object(s) { init(s.m_stats); } + ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } + operator Z3_stats() const { return m_stats; } + stats & operator=(stats const & s) { + Z3_stats_inc_ref(s.ctx(), s.m_stats); + if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); + m_ctx = s.m_ctx; + m_stats = s.m_stats; + return *this; + } + unsigned size() const { return Z3_stats_size(ctx(), m_stats); } + std::string key(unsigned i) const { Z3_string s = Z3_stats_get_key(ctx(), m_stats, i); check_error(); return s; } + bool is_uint(unsigned i) const { Z3_bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } + bool is_double(unsigned i) const { Z3_bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } + unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } + double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } + friend std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } + }; +#endif + + enum check_result { + unsat, sat, unknown + }; + + class fixedpoint : public object { + + public: + void get_rules(std::vector &rules); + void get_assertions(std::vector &rules); + void register_relation(const func_decl &rela); + void add_rule(const expr &clause, const symbol &name); + void assert_cnst(const expr &cnst); + }; + + inline std::ostream & operator<<(std::ostream & out, check_result r) { + if (r == unsat) out << "unsat"; + else if (r == sat) out << "sat"; + else out << "unknown"; + return out; + } + + inline check_result to_check_result(lbool l) { + if (l == l_true) return sat; + else if (l == l_false) return unsat; + return unknown; + } + + class solver : public object { + protected: + ::solver *m_solver; + model the_model; + public: + solver(context & c); + solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; } + solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver;} + ~solver() { } + operator ::solver*() const { return m_solver; } + solver & operator=(solver const & s) { + m_ctx = s.m_ctx; + m_solver = s.m_solver; + the_model = s.the_model; + return *this; + } + // void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } + void push() { m_solver->push(); } + void pop(unsigned n = 1) { m_solver->pop(n); } + // void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } + void add(expr const & e) { m_solver->assert_expr(e); } + check_result check() { + lbool r = m_solver->check_sat(0,0); + model_ref m; + m_solver->get_model(m); + the_model = m.get(); + return to_check_result(r); + } + check_result check_keep_model(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { + model old_model(the_model); + check_result res = check(n,assumptions,core_size,core); + if(the_model == 0) + the_model = old_model; + return res; + } + check_result check(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { + std::vector< ::expr *> _assumptions(n); + for (unsigned i = 0; i < n; i++) { + _assumptions[i] = to_expr(assumptions[i]); + } + the_model = 0; + lbool r = m_solver->check_sat(n,&_assumptions[0]); + + if(core_size && core){ + ptr_vector< ::expr> _core; + m_solver->get_unsat_core(_core); + *core_size = _core.size(); + for(unsigned i = 0; i < *core_size; i++) + core[i] = expr(ctx(),_core[i]); + } + + model_ref m; + m_solver->get_model(m); + the_model = m.get(); + + return to_check_result(r); + } +#if 0 + check_result check(expr_vector assumptions) { + unsigned n = assumptions.size(); + z3array _assumptions(n); + for (unsigned i = 0; i < n; i++) { + check_context(*this, assumptions[i]); + _assumptions[i] = assumptions[i]; + } + Z3_lbool r = Z3_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); + check_error(); + return to_check_result(r); + } +#endif + model get_model() const { return model(ctx(), the_model); } + // std::string reason_unknown() const { Z3_string r = Z3_solver_get_reason_unknown(ctx(), m_solver); check_error(); return r; } + // stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } +#if 0 + expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } + expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } +#endif + // expr proof() const { Z3_ast r = Z3_solver_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } + // friend std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } + + int get_num_decisions(); + + }; + +#if 0 + class goal : public object { + Z3_goal m_goal; + void init(Z3_goal s) { + m_goal = s; + Z3_goal_inc_ref(ctx(), s); + } + public: + goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } + goal(context & c, Z3_goal s):object(c) { init(s); } + goal(goal const & s):object(s) { init(s.m_goal); } + ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } + operator Z3_goal() const { return m_goal; } + goal & operator=(goal const & s) { + Z3_goal_inc_ref(s.ctx(), s.m_goal); + Z3_goal_dec_ref(ctx(), m_goal); + m_ctx = s.m_ctx; + m_goal = s.m_goal; + return *this; + } + void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } + unsigned size() const { return Z3_goal_size(ctx(), m_goal); } + expr operator[](unsigned i) const { Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } + Z3_goal_prec precision() const { return Z3_goal_precision(ctx(), m_goal); } + bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal) != 0; } + unsigned depth() const { return Z3_goal_depth(ctx(), m_goal); } + void reset() { Z3_goal_reset(ctx(), m_goal); } + unsigned num_exprs() const { Z3_goal_num_exprs(ctx(), m_goal); } + bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } + bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } + friend std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } + }; + + class apply_result : public object { + Z3_apply_result m_apply_result; + void init(Z3_apply_result s) { + m_apply_result = s; + Z3_apply_result_inc_ref(ctx(), s); + } + public: + apply_result(context & c, Z3_apply_result s):object(c) { init(s); } + apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } + ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } + operator Z3_apply_result() const { return m_apply_result; } + apply_result & operator=(apply_result const & s) { + Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); + Z3_apply_result_dec_ref(ctx(), m_apply_result); + m_ctx = s.m_ctx; + m_apply_result = s.m_apply_result; + return *this; + } + unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } + goal operator[](unsigned i) const { Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } + goal operator[](int i) const { assert(i >= 0); return this->operator[](static_cast(i)); } + model convert_model(model const & m, unsigned i = 0) const { + check_context(*this, m); + Z3_model new_m = Z3_apply_result_convert_model(ctx(), m_apply_result, i, m); + check_error(); + return model(ctx(), new_m); + } + friend std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } + }; + + class tactic : public object { + Z3_tactic m_tactic; + void init(Z3_tactic s) { + m_tactic = s; + Z3_tactic_inc_ref(ctx(), s); + } + public: + tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } + tactic(context & c, Z3_tactic s):object(c) { init(s); } + tactic(tactic const & s):object(s) { init(s.m_tactic); } + ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } + operator Z3_tactic() const { return m_tactic; } + tactic & operator=(tactic const & s) { + Z3_tactic_inc_ref(s.ctx(), s.m_tactic); + Z3_tactic_dec_ref(ctx(), m_tactic); + m_ctx = s.m_ctx; + m_tactic = s.m_tactic; + return *this; + } + solver mk_solver() const { Z3_solver r = Z3_mk_solver_from_tactic(ctx(), m_tactic); check_error(); return solver(ctx(), r); } + apply_result apply(goal const & g) const { + check_context(*this, g); + Z3_apply_result r = Z3_tactic_apply(ctx(), m_tactic, g); + check_error(); + return apply_result(ctx(), r); + } + apply_result operator()(goal const & g) const { + return apply(g); + } + std::string help() const { char const * r = Z3_tactic_get_help(ctx(), m_tactic); check_error(); return r; } + friend tactic operator&(tactic const & t1, tactic const & t2) { + check_context(t1, t2); + Z3_tactic r = Z3_tactic_and_then(t1.ctx(), t1, t2); + t1.check_error(); + return tactic(t1.ctx(), r); + } + friend tactic operator|(tactic const & t1, tactic const & t2) { + check_context(t1, t2); + Z3_tactic r = Z3_tactic_or_else(t1.ctx(), t1, t2); + t1.check_error(); + return tactic(t1.ctx(), r); + } + friend tactic repeat(tactic const & t, unsigned max=UINT_MAX) { + Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); + t.check_error(); + return tactic(t.ctx(), r); + } + friend tactic with(tactic const & t, params const & p) { + Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); + t.check_error(); + return tactic(t.ctx(), r); + } + friend tactic try_for(tactic const & t, unsigned ms) { + Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); + t.check_error(); + return tactic(t.ctx(), r); + } + }; + + class probe : public object { + Z3_probe m_probe; + void init(Z3_probe s) { + m_probe = s; + Z3_probe_inc_ref(ctx(), s); + } + public: + probe(context & c, char const * name):object(c) { Z3_probe r = Z3_mk_probe(c, name); check_error(); init(r); } + probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } + probe(context & c, Z3_probe s):object(c) { init(s); } + probe(probe const & s):object(s) { init(s.m_probe); } + ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } + operator Z3_probe() const { return m_probe; } + probe & operator=(probe const & s) { + Z3_probe_inc_ref(s.ctx(), s.m_probe); + Z3_probe_dec_ref(ctx(), m_probe); + m_ctx = s.m_ctx; + m_probe = s.m_probe; + return *this; + } + double apply(goal const & g) const { double r = Z3_probe_apply(ctx(), m_probe, g); check_error(); return r; } + double operator()(goal const & g) const { return apply(g); } + friend probe operator<=(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_le(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator<=(probe const & p1, double p2) { return p1 <= probe(p1.ctx(), p2); } + friend probe operator<=(double p1, probe const & p2) { return probe(p2.ctx(), p1) <= p2; } + friend probe operator>=(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_ge(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator>=(probe const & p1, double p2) { return p1 >= probe(p1.ctx(), p2); } + friend probe operator>=(double p1, probe const & p2) { return probe(p2.ctx(), p1) >= p2; } + friend probe operator<(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_lt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator<(probe const & p1, double p2) { return p1 < probe(p1.ctx(), p2); } + friend probe operator<(double p1, probe const & p2) { return probe(p2.ctx(), p1) < p2; } + friend probe operator>(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_gt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator>(probe const & p1, double p2) { return p1 > probe(p1.ctx(), p2); } + friend probe operator>(double p1, probe const & p2) { return probe(p2.ctx(), p1) > p2; } + friend probe operator==(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_eq(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator==(probe const & p1, double p2) { return p1 == probe(p1.ctx(), p2); } + friend probe operator==(double p1, probe const & p2) { return probe(p2.ctx(), p1) == p2; } + friend probe operator&&(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_and(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator||(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_or(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator!(probe const & p) { + Z3_probe r = Z3_probe_not(p.ctx(), p); p.check_error(); return probe(p.ctx(), r); + } + }; + + inline tactic fail_if(probe const & p) { + Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); + p.check_error(); + return tactic(p.ctx(), r); + } + inline tactic when(probe const & p, tactic const & t) { + check_context(p, t); + Z3_tactic r = Z3_tactic_when(t.ctx(), p, t); + t.check_error(); + return tactic(t.ctx(), r); + } + inline tactic cond(probe const & p, tactic const & t1, tactic const & t2) { + check_context(p, t1); check_context(p, t2); + Z3_tactic r = Z3_tactic_cond(t1.ctx(), p, t1, t2); + t1.check_error(); + return tactic(t1.ctx(), r); + } + +#endif + + inline expr context::bool_val(bool b){return b ? make(True) : make(False);} + + inline symbol context::str_symbol(char const * s) { ::symbol r = ::symbol(s); return symbol(*this, r); } + inline symbol context::int_symbol(int n) { ::symbol r = ::symbol(n); return symbol(*this, r); } + + inline sort context::bool_sort() { + ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); + return sort(*this, s); + } + inline sort context::int_sort() { + ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); + return sort(*this, s); + } + inline sort context::real_sort() { + ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); + return sort(*this, s); + } + inline sort context::array_sort(sort d, sort r) { + parameter params[2] = { parameter(d), parameter(to_sort(r)) }; + ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); + return sort(*this, s); + } + + + inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { + std::vector< ::sort *> sv(arity); + for(unsigned i = 0; i < arity; i++) + sv[i] = domain[i]; + ::func_decl* d = m().mk_func_decl(name,arity,&sv[0],range); + return func_decl(*this,d); + } + + inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { + return function(str_symbol(name), arity, domain, range); + } + + inline func_decl context::function(char const * name, sort const & domain, sort const & range) { + sort args[1] = { domain }; + return function(name, 1, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & range) { + sort args[2] = { d1, d2 }; + return function(name, 2, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { + sort args[3] = { d1, d2, d3 }; + return function(name, 3, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { + sort args[4] = { d1, d2, d3, d4 }; + return function(name, 4, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { + sort args[5] = { d1, d2, d3, d4, d5 }; + return function(name, 5, args, range); + } + + + inline expr context::constant(symbol const & name, sort const & s) { + ::expr *r = m().mk_const(m().mk_const_decl(name, s)); + return expr(*this, r); + } + inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } + inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } + inline expr context::int_const(char const * name) { return constant(name, int_sort()); } + inline expr context::real_const(char const * name) { return constant(name, real_sort()); } + inline expr context::bv_const(char const * name, unsigned sz) { return constant(name, bv_sort(sz)); } + + inline expr func_decl::operator()(const std::vector &args) const { + return operator()(args.size(),&args[0]); + } + inline expr func_decl::operator()(expr const & a) const { + return operator()(1,&a); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2) const { + expr args[2] = {a1,a2}; + return operator()(2,args); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3) const { + expr args[3] = {a1,a2,a3}; + return operator()(3,args); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { + expr args[4] = {a1,a2,a3,a4}; + return operator()(4,args); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { + expr args[5] = {a1,a2,a3,a4,a5}; + return operator()(5,args); + } + + + inline expr select(expr const & a, expr const & i) { return a.ctx().make(Select,a,i); } + inline expr store(expr const & a, expr const & i, expr const & v) { return a.ctx().make(Store,a,i,v); } + + inline expr forall(const std::vector &quants, const expr &body){ + return body.ctx().make_quant(Forall,quants,body); + } + + inline expr exists(const std::vector &quants, const expr &body){ + return body.ctx().make_quant(Exists,quants,body); + } + + inline expr context::int_val(int n){ + :: sort *r = m().mk_sort(m_arith_fid, INT_SORT); + return cook(m_arith_util.mk_numeral(rational(n),r)); + } + + + class literals : public object { + }; + + class TermTree { + public: + + TermTree(expr _term){ + term = _term; + } + + TermTree(expr _term, const std::vector &_children){ + term = _term; + children = _children; + } + + inline expr getTerm(){return term;} + + inline std::vector &getChildren(){ + return children; + } + + inline int number(int from){ + for(unsigned i = 0; i < children.size(); i++) + from = children[i]->number(from); + num = from; + return from + 1; + } + + inline int getNumber(){ + return num; + } + + inline void setTerm(expr t){term = t;} + + inline void setChildren(const std::vector & _children){ + children = _children; + } + + inline void setNumber(int _num){ + num = _num; + } + + private: + expr term; + std::vector children; + int num; + }; + + typedef context interpolating_context; + + class interpolating_solver : public solver { + public: + interpolating_solver(context &ctx) + : solver(ctx) + { + weak_mode = false; + } + + public: + lbool interpolate(const std::vector &assumptions, + std::vector &interpolants, + model &_model, + literals &lits, + bool incremental + ); + + lbool interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + literals &lits, + bool incremental + ); + + bool read_interpolation_problem(const std::string &file_name, + std::vector &assumptions, + std::vector &theory, + std::string &error_message + ); + + void write_interpolation_problem(const std::string &file_name, + const std::vector &assumptions, + const std::vector &theory + ); + + void AssertInterpolationAxiom(const expr &expr); + void RemoveInterpolationAxiom(const expr &expr); + + void SetWeakInterpolants(bool weak); + void SetPrintToFile(const std::string &file_name); + + const char *profile(); + + private: + bool weak_mode; + std::string print_filename; + std::vector theory; + }; + + + inline expr context::cook(::expr *a) {return expr(*this,a);} + + inline std::vector context::cook(ptr_vector< ::expr> v) { + std::vector _v(v.size()); + for(unsigned i = 0; i < v.size(); i++) + _v[i] = cook(v[i]); + return _v; + } + + inline ::expr *context::uncook(const expr &a) { + m().inc_ref(a.raw()); + return to_expr(a.raw()); + } + + +}; + +// to make Duality::ast hashable +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Duality::ast &s) const { + return s.raw()->get_id(); + } + }; +} + +// to make Duality::ast hashable in windows +#ifdef WIN32 +template <> inline +size_t stdext::hash_value(const Duality::ast& s) +{ + return s.raw()->get_id(); +} +#endif + +// to make Duality::ast usable in ordered collections +namespace std { + template <> + class less { + public: + bool operator()(const Duality::ast &s, const Duality::ast &t) const { + return s.raw() < t.raw(); // s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + +// to make Duality::func_decl hashable +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Duality::func_decl &s) const { + return s.raw()->get_id(); + } + }; +} + +// to make Duality::func_decl hashable in windows +#ifdef WIN32 +template <> inline +size_t stdext::hash_value(const Duality::func_decl& s) +{ + return s.raw()->get_id(); +} +#endif + +// to make Duality::func_decl usable in ordered collections +namespace std { + template <> + class less { + public: + bool operator()(const Duality::func_decl &s, const Duality::func_decl &t) const { + return s.raw() < t.raw(); // s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + + +#endif + diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index e948e9ceb..4ae345319 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -110,6 +110,7 @@ class ast_r : public ast_i { _m->dec_ref(_ast); } + ast_manager *mgr() const {return _m;} }; diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 7c9d2c965..931a96ced 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -731,6 +731,10 @@ namespace datalog { check_existential_tail(r); check_positive_predicates(r); break; + case DUALITY_ENGINE: + check_existential_tail(r); + check_positive_predicates(r); + break; default: UNREACHABLE(); break; @@ -989,6 +993,9 @@ namespace datalog { else if (e == symbol("tab")) { m_engine = TAB_ENGINE; } + else if (e == symbol("duality")) { + m_engine = DUALITY_ENGINE; + } if (m_engine == LAST_ENGINE) { expr_fast_mark1 mark; @@ -1024,6 +1031,8 @@ namespace datalog { return bmc_query(query); case TAB_ENGINE: return tab_query(query); + case DUALITY_ENGINE: + return duality_query(query); default: UNREACHABLE(); return rel_query(query); @@ -1042,6 +1051,9 @@ namespace datalog { case QPDR_ENGINE: ensure_pdr(); return m_pdr->get_model(); + case DUALITY_ENGINE: + ensure_duality(); + return m_duality->get_model(); default: return model_ref(alloc(model, m)); } @@ -1053,6 +1065,9 @@ namespace datalog { case QPDR_ENGINE: ensure_pdr(); return m_pdr->get_proof(); + case DUALITY_ENGINE: + ensure_duality(); + return m_duality->get_proof(); default: return proof_ref(m.mk_asserted(m.mk_true()), m); } @@ -1064,11 +1079,22 @@ namespace datalog { } } + void context::ensure_duality() { + if (!m_duality.get()) { + m_duality = alloc(Duality::dl_interface, *this); + } + } + lbool context::pdr_query(expr* query) { ensure_pdr(); return m_pdr->query(query); } + lbool context::duality_query(expr* query) { + ensure_duality(); + return m_duality->query(query); + } + void context::ensure_bmc() { if (!m_bmc.get()) { m_bmc = alloc(bmc, *this); @@ -1131,6 +1157,10 @@ namespace datalog { ensure_tab(); m_last_answer = m_tab->get_answer(); return m_last_answer.get(); + case DUALITY_ENGINE: + ensure_duality(); + m_last_answer = m_duality->get_answer(); + return m_last_answer.get(); default: UNREACHABLE(); } @@ -1155,6 +1185,8 @@ namespace datalog { ensure_tab(); m_tab->display_certificate(out); return true; + case DUALITY_ENGINE: + return false; default: return false; } diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index 98380c9c8..c9d0bec77 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -36,6 +36,7 @@ Revision History: #include"pdr_dl_interface.h" #include"dl_bmc_engine.h" #include"tab_context.h" +#include"duality_dl_interface.h" #include"rel_context.h" #include"lbool.h" #include"statistics.h" @@ -102,6 +103,7 @@ namespace datalog { scoped_ptr m_bmc; scoped_ptr m_rel; scoped_ptr m_tab; + scoped_ptr m_duality; bool m_closed; bool m_saturation_was_run; @@ -373,14 +375,16 @@ namespace datalog { /** \brief retrieve model from inductive invariant that shows query is unsat. - \pre engine == 'pdr' - this option is only supported for PDR mode. + \pre engine == 'pdr' || engine == 'duality' - this option is only supported + for PDR mode and Duality mode. */ model_ref get_model(); /** \brief retrieve proof from derivation of the query. - \pre engine == 'pdr' - this option is only supported for PDR mode. + \pre engine == 'pdr' || engine == 'duality'- this option is only supported + for PDR mode and Duality mode. */ proof_ref get_proof(); @@ -438,6 +442,8 @@ namespace datalog { void ensure_tab(); + void ensure_duality(); + void ensure_rel(); void new_query(); @@ -450,6 +456,8 @@ namespace datalog { lbool tab_query(expr* query); + lbool duality_query(expr* query); + void check_quantifier_free(rule_ref& r); void check_uninterpreted_free(rule_ref& r); void check_existential_tail(rule_ref& r); diff --git a/src/muz_qe/dl_util.h b/src/muz_qe/dl_util.h index 69be7e9ac..61f85a691 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz_qe/dl_util.h @@ -53,6 +53,7 @@ namespace datalog { BMC_ENGINE, QBMC_ENGINE, TAB_ENGINE, + DUALITY_ENGINE, LAST_ENGINE }; From feb5360999aa8cfdc56f521148d8d05791c70ceb Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 28 Apr 2013 16:29:55 -0700 Subject: [PATCH 016/509] integrating duality --- src/duality/duality.h | 7 +- src/duality/duality_rpfp.cpp | 6 + src/duality/duality_solver.cpp | 4 + src/duality/duality_wrapper.cpp | 31 +++ src/duality/duality_wrapper.h | 11 +- src/muz_qe/dl_context.cpp | 4 +- src/muz_qe/duality_dl_interface.cpp | 333 ++++++++++++++++++++++++++++ src/muz_qe/duality_dl_interface.h | 76 +++++++ src/muz_qe/fixedpoint_params.pyg | 6 + 9 files changed, 473 insertions(+), 5 deletions(-) create mode 100644 src/muz_qe/duality_dl_interface.cpp create mode 100644 src/muz_qe/duality_dl_interface.h diff --git a/src/duality/duality.h b/src/duality/duality.h index c75ecfa61..1017e0b60 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -171,6 +171,7 @@ namespace Duality { const std::vector &assumptions, const std::vector &theory ){} + virtual ~LogicSolver(){} }; /** This solver uses iZ3. */ @@ -205,8 +206,8 @@ namespace Duality { } #endif - iZ3LogicSolver(context c){ - ctx = ictx = new interpolating_context(c); + iZ3LogicSolver(context &c){ + ctx = ictx = &c; slvr = islvr = new interpolating_solver(*ictx); need_goals = false; islvr->SetWeakInterpolants(true); @@ -221,7 +222,7 @@ namespace Duality { #endif } ~iZ3LogicSolver(){ - delete ictx; + // delete ictx; delete islvr; } }; diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 6b176a574..ca6061520 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -590,7 +590,12 @@ namespace Duality { if (res == l_false) { DecodeTree(root, interpolant->getChildren()[0], persist); + delete interpolant; } + + delete tree; + if(goals) + delete goals; timer_stop("Solve"); return res; @@ -828,6 +833,7 @@ namespace Duality { return res; } + #ifdef Z3OPS static Z3_subterm_truth *stt; #endif diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index d5c6579d1..799b94c20 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -267,6 +267,8 @@ namespace Duality { // print_profile(std::cout); delete indset; delete heuristic; + delete unwinding; + delete reporter; return res; } @@ -1284,6 +1286,8 @@ 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; } diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index e5a8e31f4..b106709f1 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -161,6 +161,33 @@ expr context::make_quant(decl_kind op, const std::vector &bvs, const expr return cook(result.get()); } +expr context::make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body){ + if(_sorts.size() == 0) return body; + + + std::vector< ::symbol> names; + std::vector< ::sort *> types; + std::vector< ::expr *> bound_asts; + unsigned num_bound = _sorts.size(); + + for (unsigned i = 0; i < num_bound; ++i) { + names.push_back(_names[i]); + types.push_back(to_sort(_sorts[i].raw())); + } + expr_ref result(m()); + result = m().mk_quantifier( + op == Forall, + names.size(), &types[0], &names[0], to_expr(body.raw()), + 0, + ::symbol(), + ::symbol(), + 0, 0, + 0, 0 + ); + return cook(result.get()); +} + + decl_kind func_decl::get_decl_kind() const { return ctx().get_decl_kind(*this); } @@ -453,6 +480,10 @@ expr context::make_quant(decl_kind op, const std::vector &bvs, const expr for(unsigned i = 0; i < _interpolants.size(); i++) linearized_interpolants[i] = expr(ctx(),_interpolants[i]); + // since iz3interpolant returns interpolants with one ref count, we decrement here + for(unsigned i = 0; i < _interpolants.size(); i++) + m().dec_ref(_interpolants[i]); + unlinearize_interpolants(0,assumptions,linearized_interpolants,interpolant); interpolant->setTerm(ctx().bool_val(false)); } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 0afad2df9..20557e3e2 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -234,6 +234,7 @@ namespace Duality { expr make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2); expr make_quant(decl_kind op, const std::vector &bvs, const expr &body); + expr make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body); decl_kind get_decl_kind(const func_decl &t); @@ -771,7 +772,10 @@ namespace Duality { solver(context & c); solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; } solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver;} - ~solver() { } + ~solver() { + if(m_solver) + dealloc(m_solver); + } operator ::solver*() const { return m_solver; } solver & operator=(solver const & s) { m_ctx = s.m_ctx; @@ -1202,6 +1206,11 @@ namespace Duality { num = _num; } + ~TermTree(){ + for(unsigned i = 0; i < children.size(); i++) + delete children[i]; + } + private: expr term; std::vector children; diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 931a96ced..082b44815 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -1186,7 +1186,9 @@ namespace datalog { m_tab->display_certificate(out); return true; case DUALITY_ENGINE: - return false; + ensure_duality(); + m_duality->display_certificate(out); + return true; default: return false; } diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp new file mode 100644 index 000000000..6ccaaf52f --- /dev/null +++ b/src/muz_qe/duality_dl_interface.cpp @@ -0,0 +1,333 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + duality_dl_interface.cpp + +Abstract: + + SMT2 interface for Duality + +Author: + + Krystof Hoder (t-khoder) 2011-9-22. + Modified by Ken McMIllan (kenmcmil) 2013-4-18. + +Revision History: + +--*/ + +#include "dl_cmds.h" +#include "dl_context.h" +#include "dl_mk_coi_filter.h" +#include "dl_mk_interp_tail_simplifier.h" +#include "dl_mk_subsumption_checker.h" +#include "dl_mk_rule_inliner.h" +#include "dl_rule.h" +#include "dl_rule_transformer.h" +#include "dl_mk_extract_quantifiers.h" +#include "smt2parser.h" +#include "duality_dl_interface.h" +#include "dl_rule_set.h" +#include "dl_mk_slice.h" +#include "dl_mk_unfold.h" +#include "dl_mk_coalesce.h" +#include "expr_abstract.h" +#include "model_smt2_pp.h" + +#include "duality.h" + +using namespace Duality; + +namespace Duality { + + enum DualityStatus {StatusModel, StatusRefutation, StatusUnknown, StatusNull}; + + class duality_data { + public: + context ctx; + RPFP::LogicSolver *ls; + RPFP *rpfp; + + DualityStatus status; + std::vector clauses; + hash_map map; // edges to clauses + Solver::Counterexample cex; + + duality_data(ast_manager &_m) : ctx(_m,config(params_ref())) { + ls = 0; + rpfp = 0; + status = StatusNull; + } + ~duality_data(){ + if(rpfp) + dealloc(rpfp); + if(ls) + dealloc(ls); + if(cex.tree) + delete cex.tree; + } + }; + + +dl_interface::dl_interface(datalog::context& dl_ctx) : m_ctx(dl_ctx) + +{ + _d = alloc(duality_data,dl_ctx.get_manager()); + _d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx); + _d->rpfp = alloc(RPFP,_d->ls); +} + + +dl_interface::~dl_interface() { + dealloc(_d); +} + + +// +// Check if the new rules are weaker so that we can +// re-use existing context. +// +#if 0 +void dl_interface::check_reset() { + // TODO + datalog::rule_ref_vector const& new_rules = m_ctx.get_rules().get_rules(); + datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules(); + bool is_subsumed = !old_rules.empty(); + for (unsigned i = 0; is_subsumed && i < new_rules.size(); ++i) { + is_subsumed = false; + for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) { + if (m_ctx.check_subsumes(*old_rules[j], *new_rules[i])) { + is_subsumed = true; + } + } + if (!is_subsumed) { + TRACE("pdr", new_rules[i]->display(m_ctx, tout << "Fresh rule ");); + m_context->reset(); + } + } + m_old_rules.reset(); + m_old_rules.add_rules(new_rules.size(), new_rules.c_ptr()); +} +#endif + + + lbool dl_interface::query(::expr * query) { + // TODO: you can only call this once! + // we restore the initial state in the datalog context + m_ctx.ensure_opened(); + + expr_ref_vector rules(m_ctx.get_manager()); + svector< ::symbol> names; + + m_ctx.get_rules_as_formulas(rules, names); + + // get all the rules as clauses + std::vector &clauses = _d->clauses; + clauses.clear(); + for (unsigned i = 0; i < rules.size(); ++i) { + expr e(_d->ctx,rules[i].get()); + clauses.push_back(e); + } + + // turn the query into a clause + expr q(_d->ctx,query); + + std::vector b_sorts; + std::vector b_names; + if (q.is_quantifier() && !q.is_quantifier_forall()) { + int bound = q.get_quantifier_num_bound(); + for(int j = 0; j < bound; j++){ + b_sorts.push_back(q.get_quantifier_bound_sort(j)); + b_names.push_back(q.get_quantifier_bound_name(j)); + } + q = q.arg(0); + } + + expr qc = implies(q,_d->ctx.bool_val(false)); + qc = _d->ctx.make_quant(Exists,b_sorts,b_names,qc); + clauses.push_back(qc); + + // get the background axioms + unsigned num_asserts = m_ctx.get_num_assertions(); + for (unsigned i = 0; i < num_asserts; ++i) { + expr e(_d->ctx,m_ctx.get_assertion(i)); + _d->rpfp->AssertAxiom(e); + } + + // creates 1-1 map between clauses and rpfp edges + _d->rpfp->FromClauses(clauses); + + // populate the edge-to-clause map + for(unsigned i = 0; i < _d->rpfp->edges.size(); ++i) + _d->map[_d->rpfp->edges[i]] = i; + + // create a solver object + + Solver *rs = Solver::Create("duality", _d->rpfp); + + // set its options + IF_VERBOSE(1, rs->SetOption("report","1");); + rs->SetOption("full_expand",m_ctx.get_params().full_expand() ? "1" : "0"); + rs->SetOption("no_conj",m_ctx.get_params().no_conj() ? "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("stratified_inlining",m_ctx.get_params().stratified_inlining() ? "1" : "0"); + unsigned rb = m_ctx.get_params().recursion_bound(); + if(rb != UINT_MAX){ + std::ostringstream os; os << rb; + rs->SetOption("recursion_bound", os.str()); + } + + // Solve! + bool ans = rs->Solve(); + + // save the result and counterexample if there is one + _d->status = ans ? StatusModel : StatusRefutation; + _d->cex = rs->GetCounterexample(); + + dealloc(rs); + + // true means the RPFP problem is SAT, so the query is UNSAT + return ans ? l_false : l_true; +} + +expr_ref dl_interface::get_cover_delta(int level, ::func_decl* pred_orig) { + SASSERT(false); + return expr_ref(m_ctx.get_manager()); +} + + void dl_interface::add_cover(int level, ::func_decl* pred, ::expr* property) { + SASSERT(false); +} + + unsigned dl_interface::get_num_levels(::func_decl* pred) { + SASSERT(false); + return 0; +} + + void dl_interface::collect_statistics(::statistics& st) const { +} + +void dl_interface::reset_statistics() { +} + +static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexample &cex) { + context &ctx = d->dd()->ctx; + RPFP::Node &node = *cex.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); + } + } + + // print the label and the proved fact + + out << "(" << 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 << ")\n"; + + // print the substitution + + out << " (\n"; + RPFP::Edge *orig_edge = edge.map; + int orig_clause = d->dd()->map[orig_edge]; + expr &t = d->dd()->clauses[orig_clause]; + if (t.is_quantifier() && t.is_quantifier_forall()) { + int bound = t.get_quantifier_num_bound(); + std::vector sorts; + std::vector names; + hash_map subst; + for(int j = 0; j < bound; j++){ + 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"; + } + } + out << " )\n"; + + // reference the proofs of all the children, in syntactic order + // "true" means the child is not needed + + out << " ("; + for(unsigned i = 0; i < edge.Children.size(); i++){ + if(!cex.tree->Empty(edge.Children[i])) + out << " " << edge.Children[i]->number; + else + out << " true"; + } + out << " )"; + out << ")\n"; +} + + +void dl_interface::display_certificate(std::ostream& out) { + if(_d->status == StatusModel){ + ast_manager &m = m_ctx.get_manager(); + model_ref md = get_model(); + model_smt2_pp(out, m, *md.get(), 0); + } + else if(_d->status == StatusRefutation){ + out << "(\n"; + // negation of the query is the last clause -- prove it + print_proof(this,out,_d->cex); + out << ")\n"; + } +} + +expr_ref dl_interface::get_answer() { + SASSERT(false); + return expr_ref(m_ctx.get_manager()); +} + +void dl_interface::cancel() { +} + +void dl_interface::cleanup() { +} + +void dl_interface::updt_params() { +} + +model_ref dl_interface::get_model() { + ast_manager &m = m_ctx.get_manager(); + model_ref md(alloc(::model, m)); + std::vector &nodes = _d->rpfp->nodes; + expr_ref_vector conjs(m); + for (unsigned i = 0; i < nodes.size(); ++i) { + RPFP::Node *node = nodes[i]; + func_decl &pred = node->Name; + expr_ref prop(m); + prop = to_expr(node->Annotation.Formula); + std::vector ¶ms = node->Annotation.IndParams; + expr_ref q(m); + expr_ref_vector sig_vars(m); + for (unsigned j = 0; j < params.size(); ++j) + sig_vars.push_back(params[params.size()-j-1]); // TODO: why backwards? + expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q); + if (params.empty()) { + md->register_decl(pred, q); + } + else { + ::func_interp* fi = alloc(::func_interp, m, params.size()); + fi->set_else(q); + md->register_decl(pred, fi); + } + } + return md; +} + +proof_ref dl_interface::get_proof() { + return proof_ref(m_ctx.get_manager()); +} +} diff --git a/src/muz_qe/duality_dl_interface.h b/src/muz_qe/duality_dl_interface.h new file mode 100644 index 000000000..332c3f12c --- /dev/null +++ b/src/muz_qe/duality_dl_interface.h @@ -0,0 +1,76 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + duality_dl_interface.h + +Abstract: + + SMT2 interface for Duality + +Author: + + Krystof Hoder (t-khoder) 2011-9-22. + Modified by Ken McMIllan (kenmcmil) 2013-4-18. + +Revision History: + +--*/ + +#ifndef _DUALITY_DL_INTERFACE_H_ +#define _DUALITY_DL_INTERFACE_H_ + +#include "lbool.h" +#include "dl_rule.h" +#include "dl_rule_set.h" +#include "statistics.h" + +namespace datalog { + class context; +} + +namespace Duality { + + class duality_data; + + class dl_interface { + duality_data *_d; + datalog::context &m_ctx; + + public: + dl_interface(datalog::context& ctx); + ~dl_interface(); + + lbool query(expr* query); + + void cancel(); + + void cleanup(); + + void display_certificate(std::ostream& out); + + void collect_statistics(statistics& st) const; + + void reset_statistics(); + + expr_ref get_answer(); + + unsigned get_num_levels(func_decl* pred); + + expr_ref get_cover_delta(int level, func_decl* pred); + + void add_cover(int level, func_decl* pred, expr* property); + + void updt_params(); + + model_ref get_model(); + + proof_ref get_proof(); + + duality_data *dd(){return _d;} + }; +} + + +#endif diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index c2cfadd14..567a35216 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -58,6 +58,12 @@ def_module_params('fixedpoint', ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), ('print_statistics', BOOL, False, 'print statistics'), ('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), + ('full_expand', BOOL, False, 'DUALITY: Fully expand derivation trees'), + ('no_conj', BOOL, False, 'DUALITY: No forced covering (conjectures)'), + ('feasible_edges', BOOL, True, 'DUALITY: Don\'t expand definitley infeasible edges'), + ('use_underapprox', BOOL, False, 'DUALITY: Use underapproximations'), + ('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'), + ('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'), )) From e939dd2bc59eae7b0a2c6c696157b7f8b214ba8a Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 30 Apr 2013 13:07:49 -0700 Subject: [PATCH 017/509] still integrating duality --- src/ast/ast.cpp | 7 +++++++ src/duality/duality.h | 17 ++++++++++------- src/duality/duality_rpfp.cpp | 2 +- src/duality/duality_solver.cpp | 5 ++++- src/duality/duality_wrapper.cpp | 8 +++----- src/interp/iz3interp.cpp | 12 ++++++++++++ src/interp/iz3interp.h | 7 +++++++ src/muz_qe/dl_context.cpp | 21 ++++++++++++++++----- src/muz_qe/dl_context.h | 1 + src/muz_qe/duality_dl_interface.cpp | 10 +++++----- 10 files changed, 66 insertions(+), 24 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 797e118a7..8ecaed172 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -3152,4 +3152,11 @@ void scoped_mark::pop_scope(unsigned num_scopes) { } } +// Added by KLM for use in GDB + +// show an expr_ref on stdout + +void prexpr(expr_ref &e){ + std::cout << mk_pp(e.get(), e.get_manager()) << std::endl; +} diff --git a/src/duality/duality.h b/src/duality/duality.h index 1017e0b60..1ee9eca08 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -156,10 +156,11 @@ namespace Duality { virtual lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0 - ) = 0; + TermTree *&interpolants, + model &_model, + TermTree *goals = 0, + bool weak = false + ) = 0; /** Assert a background axiom. */ virtual void assert_axiom(const expr &axiom) = 0; @@ -181,11 +182,13 @@ namespace Duality { interpolating_solver *islvr; /** iZ3 solver */ lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0) + TermTree *&interpolants, + model &_model, + TermTree *goals = 0, + bool weak = false) { literals _labels; + islvr->SetWeakInterpolants(weak); return islvr->interpolate_tree(assumptions,interpolants,_model,_labels,true); } diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index ca6061520..78c2b6955 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -585,7 +585,7 @@ namespace Duality { // if (dualLabels != null) dualLabels.Dispose(); timer_start("interpolate_tree"); - lbool res = ls->interpolate_tree(tree, interpolant, dualModel,goals); + lbool res = ls->interpolate_tree(tree, interpolant, dualModel,goals,true); timer_stop("interpolate_tree"); if (res == l_false) { diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 799b94c20..bd02db976 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -294,7 +294,9 @@ namespace Duality { /** Return the counterexample */ virtual Counterexample GetCounterexample(){ - return cex; + Counterexample res = cex; + cex.tree = 0; // Cex now belongs to caller + return res; } // options @@ -879,6 +881,7 @@ namespace Duality { #endif if(_cex) *_cex = cex; else delete cex.tree; // delete the cex if not required + cex.tree = 0; node->Bound = save; // put back original bound timer_stop("ProveConjecture"); return false; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index b106709f1..17638335d 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -467,14 +467,12 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st if(res == unsat){ - // TODO -#if 0 + interpolation_options_struct opts; if(weak_mode) - Z3_set_interpolation_option(options,"weak","1"); -#endif + opts.set("weak","1"); ::ast *proof = m_solver->get_proof(); - iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,0); + iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts); std::vector linearized_interpolants(_interpolants.size()); for(unsigned i = 0; i < _interpolants.size(); i++) diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 6a9c54716..aac8648ba 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -323,6 +323,8 @@ void iz3interpolate(ast_manager &_m_manager, interpolation_options_struct * options) { iz3interp itp(_m_manager); + if(options) + options->apply(itp); std::vector _cnsts(cnsts.size()); std::vector _parents(parents.size()); std::vector _interps; @@ -348,6 +350,8 @@ void iz3interpolate(ast_manager &_m_manager, interpolation_options_struct * options) { iz3interp itp(_m_manager); + if(options) + options->apply(itp); std::vector _cnsts(cnsts.size()); std::vector _interps; for(unsigned i = 0; i < cnsts.size(); i++) @@ -369,6 +373,8 @@ lbool iz3interpolate(ast_manager &_m_manager, interpolation_options_struct * options) { iz3interp itp(_m_manager); + if(options) + options->apply(itp); iz3mgr::ast _tree = itp.cook(tree); std::vector _cnsts; itp.assert_conjuncts(s,_cnsts,_tree); @@ -391,6 +397,12 @@ lbool iz3interpolate(ast_manager &_m_manager, return res; } +void interpolation_options_struct::apply(iz3base &b){ + for(stl_ext::hash_map::iterator it = map.begin(), en = map.end(); + it != en; + ++it) + b.set_option((*it).first,(*it).second); +} diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h index 7174205f1..62f967c02 100644 --- a/src/interp/iz3interp.h +++ b/src/interp/iz3interp.h @@ -23,8 +23,15 @@ Revision History: #include "iz3hash.h" #include "solver.h" +class iz3base; + struct interpolation_options_struct { stl_ext::hash_map map; +public: + void set(const std::string &name, const std::string &value){ + map[name] = value; + } + void apply(iz3base &b); }; /** This object is thrown if a tree interpolation problem is mal-formed */ diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 082b44815..ef8168607 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -1013,13 +1013,15 @@ namespace datalog { } lbool context::query(expr* query) { - new_query(); - rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); - rule_ref r(m_rule_manager); - for (; it != end; ++it) { + if(get_engine() != DUALITY_ENGINE) { + new_query(); + rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); + rule_ref r(m_rule_manager); + for (; it != end; ++it) { r = *it; check_rule(r); - } + } + } switch(get_engine()) { case DATALOG_ENGINE: return rel_query(query); @@ -1259,6 +1261,15 @@ namespace datalog { } } + void context::get_raw_rule_formulas(expr_ref_vector& rules, svector& names){ + for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { + expr_ref r = bind_variables(m_rule_fmls[i].get(), true); + rules.push_back(r.get()); + // rules.push_back(m_rule_fmls[i].get()); + names.push_back(m_rule_names[i]); + } + } + void context::get_rules_as_formulas(expr_ref_vector& rules, svector& names) { expr_ref fml(m); datalog::rule_manager& rm = get_rule_manager(); diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index c9d0bec77..16295498b 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -238,6 +238,7 @@ namespace datalog { rule_set const & get_rules() { flush_add_rules(); return m_rule_set; } void get_rules_as_formulas(expr_ref_vector& fmls, svector& names); + void get_raw_rule_formulas(expr_ref_vector& fmls, svector& names); void add_fact(app * head); void add_fact(func_decl * pred, const relation_fact & fact); diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index 6ccaaf52f..ee1eca712 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -119,9 +119,9 @@ void dl_interface::check_reset() { m_ctx.ensure_opened(); expr_ref_vector rules(m_ctx.get_manager()); - svector< ::symbol> names; - - m_ctx.get_rules_as_formulas(rules, names); + svector< ::symbol> names; + // m_ctx.get_rules_as_formulas(rules, names); + m_ctx.get_raw_rule_formulas(rules, names); // get all the rules as clauses std::vector &clauses = _d->clauses; @@ -132,7 +132,7 @@ void dl_interface::check_reset() { } // turn the query into a clause - expr q(_d->ctx,query); + expr q(_d->ctx,m_ctx.bind_variables(query,false)); std::vector b_sorts; std::vector b_names; @@ -146,7 +146,7 @@ void dl_interface::check_reset() { } expr qc = implies(q,_d->ctx.bool_val(false)); - qc = _d->ctx.make_quant(Exists,b_sorts,b_names,qc); + qc = _d->ctx.make_quant(Forall,b_sorts,b_names,qc); clauses.push_back(qc); // get the background axioms From 2f8b7bfa189fec29de89ad35b4c103c1474e68a7 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 3 May 2013 17:29:13 -0700 Subject: [PATCH 018/509] adding labels to duality --- src/duality/duality.h | 16 ++++++++++++---- src/duality/duality_rpfp.cpp | 25 ++++++++++++++++--------- src/duality/duality_wrapper.cpp | 9 +++++++++ src/duality/duality_wrapper.h | 1 + src/muz_qe/duality_dl_interface.cpp | 4 +++- 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 1ee9eca08..e77fbe5c2 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -622,11 +622,20 @@ namespace Duality { */ + struct label_struct { + symbol name; + expr value; + bool pos; + label_struct(const symbol &s, const expr &e, bool b) + : name(s), value(e), pos(b) {} + }; + #ifdef WIN32 __declspec(dllexport) #endif - void FromClauses(const std::vector &clauses); + void FromClauses(const std::vector &clauses, + std::vector > &clause_labels); void FromFixpointContext(fixedpoint fp, std::vector &queries); @@ -707,10 +716,9 @@ namespace Duality { std::vector &res, std::vector &nodes); + Term RemoveLabelsRec(hash_map &memo, const Term &t, std::vector &lbls); - Term RemoveLabelsRec(hash_map &memo, const Term &t); - - Term RemoveLabels(const Term &t); + Term RemoveLabels(const Term &t, std::vector &lbls); Term GetAnnotation(Node *n); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 78c2b6955..38f00b4b0 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -1631,34 +1631,39 @@ namespace Duality { && t.num_args() == 0; } - RPFP::Term RPFP::RemoveLabelsRec(hash_map &memo, const Term &t){ + RPFP::Term RPFP::RemoveLabelsRec(hash_map &memo, const Term &t, + std::vector &lbls){ if(memo.find(t) != memo.end()) return memo[t]; Term res(ctx); if (t.is_app()){ func_decl f = t.decl(); - if(t.num_args() == 1 && f.name().str().substr(0,3) == "lbl"){ - res = RemoveLabelsRec(memo,t.arg(0)); + std::vector names; + bool pos; + if(t.is_label(pos,names)){ + res = RemoveLabelsRec(memo,t.arg(0),lbls); + for(unsigned i = 0; i < names.size(); i++) + lbls.push_back(label_struct(names[i],res,pos)); } else { int nargs = t.num_args(); std::vector args; for(int i = 0; i < nargs; i++) - args.push_back(RemoveLabelsRec(memo,t.arg(i))); + args.push_back(RemoveLabelsRec(memo,t.arg(i),lbls)); res = f(nargs,&args[0]); } } else if (t.is_quantifier()) - res = CloneQuantifier(t,RemoveLabelsRec(memo,t.body())); + res = CloneQuantifier(t,RemoveLabelsRec(memo,t.body(),lbls)); else res = t; memo[t] = res; return res; } - RPFP::Term RPFP::RemoveLabels(const Term &t){ + RPFP::Term RPFP::RemoveLabels(const Term &t, std::vector &lbls){ hash_map memo ; - return RemoveLabelsRec(memo,t); + return RemoveLabelsRec(memo,t,lbls); } Z3User::Term Z3User::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) @@ -1733,7 +1738,8 @@ namespace Duality { arg.decl().get_decl_kind() == Uninterpreted); } - void RPFP::FromClauses(const std::vector &unskolemized_clauses){ + void RPFP::FromClauses(const std::vector &unskolemized_clauses, + std::vector > &clause_labels){ hash_map pmap; func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); @@ -1800,6 +1806,7 @@ namespace Duality { // create the edges + clause_labels.resize(clauses.size()); for(unsigned i = 0; i < clauses.size(); i++){ Term clause = clauses[i]; Term body = clause.arg(0); @@ -1830,7 +1837,7 @@ namespace Duality { hash_map scan_memo; std::vector Children; body = ScanBody(scan_memo,body,pmap,Relparams,Children); - body = RemoveLabels(body); + body = RemoveLabels(body,clause_labels[i]); body = body.simplify(); // Create the edge diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 17638335d..0603b5a4a 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -545,6 +545,15 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st a.show(); } + bool expr::is_label (bool &pos,std::vector &names) const { + buffer< ::symbol> _names; + bool res = m().is_label(to_expr(raw()),pos,_names); + if(res) + for(unsigned i = 0; i < _names.size(); i++) + names.push_back(symbol(ctx(),_names[i])); + return res; + } + } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 20557e3e2..284d1c527 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -454,6 +454,7 @@ namespace Duality { bool is_app() const {return raw()->get_kind() == AST_APP;} bool is_quantifier() const {return raw()->get_kind() == AST_QUANTIFIER;} bool is_var() const {return raw()->get_kind() == AST_VAR;} + bool is_label (bool &pos,std::vector &names) const ; // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index ee1eca712..304e5773d 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -52,6 +52,7 @@ namespace Duality { DualityStatus status; std::vector clauses; + std::vector > clause_labels; hash_map map; // edges to clauses Solver::Counterexample cex; @@ -157,7 +158,7 @@ void dl_interface::check_reset() { } // creates 1-1 map between clauses and rpfp edges - _d->rpfp->FromClauses(clauses); + _d->rpfp->FromClauses(clauses,_d->clause_labels); // populate the edge-to-clause map for(unsigned i = 0; i < _d->rpfp->edges.size(); ++i) @@ -256,6 +257,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp } out << " )\n"; + // reference the proofs of all the children, in syntactic order // "true" means the child is not needed From 389c2018df2e3780896497a8b02b8579ea866634 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 3 May 2013 17:30:07 -0700 Subject: [PATCH 019/509] working on duality --- src/api/dotnet/Properties/AssemblyInfo.cs | 4 +- src/ast/ast.h | 4 +- src/duality/duality_profiling.cpp | 77 +---------------------- src/duality/duality_solver.cpp | 2 +- src/duality/duality_wrapper.cpp | 10 +++ src/duality/duality_wrapper.h | 6 +- src/muz_qe/duality_dl_interface.cpp | 15 +++-- 7 files changed, 29 insertions(+), 89 deletions(-) diff --git a/src/api/dotnet/Properties/AssemblyInfo.cs b/src/api/dotnet/Properties/AssemblyInfo.cs index 1cd0fe7b8..517349177 100644 --- a/src/api/dotnet/Properties/AssemblyInfo.cs +++ b/src/api/dotnet/Properties/AssemblyInfo.cs @@ -34,6 +34,6 @@ using System.Security.Permissions; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("4.2.0.0")] -[assembly: AssemblyVersion("4.3.2.0")] -[assembly: AssemblyFileVersion("4.3.2.0")] +[assembly: AssemblyVersion("4.3.2.0")] +[assembly: AssemblyFileVersion("4.3.2.0")] diff --git a/src/ast/ast.h b/src/ast/ast.h index 2753d0006..9c1bfcaf6 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -2295,8 +2295,8 @@ public: } void reset() { - ptr_buffer::iterator it = m_to_unmark.begin(); - ptr_buffer::iterator end = m_to_unmark.end(); + ptr_buffer<::ast>::iterator it = m_to_unmark.begin(); + ptr_buffer<::ast>::iterator end = m_to_unmark.end(); for (; it != end; ++it) { reset_mark(*it); } diff --git a/src/duality/duality_profiling.cpp b/src/duality/duality_profiling.cpp index b516e03ca..bec32c51a 100755 --- a/src/duality/duality_profiling.cpp +++ b/src/duality/duality_profiling.cpp @@ -25,82 +25,7 @@ Revision History: #include #include -#ifdef WIN32 - -// include "Windows.h" - - -#if 0 -typedef __int64 clock_t; - -static clock_t current_time(){ - LARGE_INTEGER lpPerformanceCount; - lpPerformanceCount.QuadPart = 0; - QueryPerformanceCounter(&lpPerformanceCount); - return lpPerformanceCount.QuadPart; -} - -static void output_time(std::ostream &os, clock_t time){ - LARGE_INTEGER lpFrequency; - lpFrequency.QuadPart = 1; - QueryPerformanceFrequency(&lpFrequency); - os << ((double)time)/lpFrequency.QuadPart; -} -#else - -typedef double clock_t; - -static clock_t current_time(){ - FILETIME lpCreationTime; - FILETIME lpExitTime; - FILETIME lpKernelTime; - FILETIME lpUserTime; - - GetProcessTimes( GetCurrentProcess(), - &lpCreationTime, &lpExitTime, &lpKernelTime, &lpUserTime ); - - - double usr_time = ((double) lpUserTime.dwLowDateTime)/10000000. + - ((double) lpUserTime.dwHighDateTime)/(10000000. * pow(2.0,32.0)); - return usr_time; -} - -static void output_time(std::ostream &os, clock_t time){ - os << time; -} - -#endif - -#else - -#include -#include -#include -#include - -static clock_t current_time(){ -#if 0 - struct tms buf; - times(&buf); - return buf.tms_utime; -#else - struct rusage r; - getrusage(RUSAGE_SELF, &r); - return 1000 * r.ru_utime.tv_sec + r.ru_utime.tv_usec / 1000; -#endif -} - -static void output_time(std::ostream &os, clock_t time){ -#if 0 - os << ((double)time)/sysconf(_SC_CLK_TCK); -#else - os << ((double)time)/1000; -#endif -} - -#endif - - +#include "duality_wrapper.h" namespace Duality { diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index bd02db976..88c9d3d3e 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1266,7 +1266,7 @@ namespace Duality { DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); bool res = dt.Derive(unwinding,node,UseUnderapprox); int end_decs = rpfp->CumulativeDecisions(); - std::cout << "decisions: " << (end_decs - start_decs) << std::endl; + // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; last_decisions = end_decs - start_decs; if(res){ cex.tree = dt.tree; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 17638335d..4548e3ca7 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -24,6 +24,7 @@ Revision History: #include "iz3interp.h" #include "statistics.h" #include "expr_abstract.h" +#include "stopwatch.h" namespace Duality { @@ -545,6 +546,15 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st a.show(); } + double current_time() + { + static stopwatch sw; + static bool started = false; + if(!started) + sw.start(); + return sw.get_seconds(); + } + } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 20557e3e2..2fc01e755 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -1281,8 +1281,10 @@ namespace Duality { m().inc_ref(a.raw()); return to_expr(a.raw()); } - - + + typedef double clock_t; + clock_t current_time(); + inline void output_time(std::ostream &os, clock_t time){os << time;} }; // to make Duality::ast hashable diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index ee1eca712..be776fa53 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -36,9 +36,12 @@ Revision History: #include "expr_abstract.h" #include "model_smt2_pp.h" +// template class symbol_table; + + #include "duality.h" -using namespace Duality; +// using namespace Duality; namespace Duality { @@ -230,7 +233,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp // print the label and the proved fact - out << "(" << node.number; + 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]); @@ -238,7 +241,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp // print the substitution - out << " (\n"; + out << " (subst\n"; RPFP::Edge *orig_edge = edge.map; int orig_clause = d->dd()->map[orig_edge]; expr &t = d->dd()->clauses[orig_clause]; @@ -259,10 +262,10 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp // reference the proofs of all the children, in syntactic order // "true" means the child is not needed - out << " ("; + out << " (ref "; for(unsigned i = 0; i < edge.Children.size(); i++){ if(!cex.tree->Empty(edge.Children[i])) - out << " " << edge.Children[i]->number; + out << " s!" << edge.Children[i]->number; else out << " true"; } @@ -278,7 +281,7 @@ void dl_interface::display_certificate(std::ostream& out) { model_smt2_pp(out, m, *md.get(), 0); } else if(_d->status == StatusRefutation){ - out << "(\n"; + out << "(derivation\n"; // negation of the query is the last clause -- prove it print_proof(this,out,_d->cex); out << ")\n"; From dc793907a586228de24e496e1eb6526bd35282fb Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 9 May 2013 13:31:17 -0700 Subject: [PATCH 020/509] added rule names to duality output --- src/muz_qe/duality_dl_interface.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index 1aee09f3f..c8a048a6a 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -240,6 +240,10 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp out << " " << cex.tree->Eval(&edge,edge.F.IndParams[i]); out << ")\n"; + // print the rule number + + out << " rule!" << node.Outgoing->map->number; + // print the substitution out << " (subst\n"; From 477754c3868dc01c385124828277f2aa2861ff7a Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 9 May 2013 14:24:22 -0700 Subject: [PATCH 021/509] fixed bug in label output in duality --- src/duality/duality.h | 4 ++-- src/duality/duality_rpfp.cpp | 4 ++++ src/muz_qe/duality_dl_interface.cpp | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index c6dfc906d..033cea660 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -672,6 +672,8 @@ namespace Duality { TermTree *GetGoalTree(Node *root); + int EvalTruth(hash_map &memo, Edge *e, const Term &f); + private: @@ -733,10 +735,8 @@ namespace Duality { Term ProjectFormula(std::vector &keep_vec, const Term &f); - public: int SubtermTruth(hash_map &memo, const Term &); - private: void ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, hash_set *done, bool truth, hash_set &dont_cares); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 38f00b4b0..13752401b 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -833,6 +833,10 @@ namespace Duality { return res; } + int RPFP::EvalTruth(hash_map &memo, Edge *e, const Term &f){ + Term tl = Localize(e, f); + return SubtermTruth(memo,tl); + } #ifdef Z3OPS static Z3_subterm_truth *stt; diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index c8a048a6a..e2ee25a2f 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -270,7 +270,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp hash_map memo; for(unsigned j = 0; j < labels.size(); j++){ RPFP::label_struct &lab = labels[j]; - int truth = cex.tree->SubtermTruth(memo,lab.value); + int truth = cex.tree->EvalTruth(memo,&edge,lab.value); if(truth == 1 && lab.pos || truth == 0 && !lab.pos) out << " " << lab.name; } From 65fbef4133689bee85583c1cccf0585e54f5d19b Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 10 May 2013 12:16:24 -0700 Subject: [PATCH 022/509] fix for compiler weirdness --- src/duality/duality_wrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index fd07173d9..bed347ef4 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -450,7 +450,7 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st ptr_vector< ::ast>_assumptions(size); for(int i = 0; i < size; i++) _assumptions[i] = linear_assumptions[i]; - ::vector _parents(parents.size()); + ::vector _parents; _parents.resize(parents.size()); for(unsigned i = 0; i < parents.size(); i++) _parents[i] = parents[i]; ptr_vector< ::ast> _theory(theory.size()); From 7905f48e887a36b5a9de9cd1f30c78a4585dcbcc Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 22 May 2013 13:23:37 -0700 Subject: [PATCH 023/509] status reporting issue --- src/duality/duality_solver.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 88c9d3d3e..40aa352c2 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -77,6 +77,7 @@ namespace Duality { virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){} virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} + virtual void Message(const std::string &msg){} virtual ~Reporter(){} }; @@ -1154,7 +1155,7 @@ namespace Duality { } #endif if(!full_scan && candidates.empty()){ - std::cout << "No candidates from updates. Trying full scan.\n"; + reporter->Message("No candidates from updates. Trying full scan."); GenCandidatesFromInductionFailure(true); } } @@ -2193,6 +2194,9 @@ namespace Duality { s << " " << children[i]->number; s << std::endl; } + virtual void Message(const std::string &msg){ + ev(); s << "msg " << msg << std::endl; + } }; From 9d611997b3057df4d72f4279ea8df54ccd8174f2 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 22 May 2013 15:18:50 -0700 Subject: [PATCH 024/509] fixing labels in duality --- src/duality/duality.h | 8 ++- src/duality/duality_rpfp.cpp | 101 ++++++++++++++++++++++++++-- src/duality/duality_wrapper.cpp | 11 +-- src/muz_qe/duality_dl_interface.cpp | 16 ++--- 4 files changed, 114 insertions(+), 22 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 033cea660..0a580a463 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -434,6 +434,7 @@ namespace Duality { hash_map relMap; hash_map varMap; Edge *map; + Term labeled; Edge(Node *_Parent, const Transformer &_F, const std::vector &_Children, RPFP *_owner, int _number) : F(_F), Parent(_Parent), Children(_Children), dual(expr(_owner->ctx)) { @@ -634,8 +635,7 @@ namespace Duality { #ifdef WIN32 __declspec(dllexport) #endif - void FromClauses(const std::vector &clauses, - std::vector > &clause_labels); + void FromClauses(const std::vector &clauses); void FromFixpointContext(fixedpoint fp, std::vector &queries); @@ -674,6 +674,10 @@ namespace Duality { int EvalTruth(hash_map &memo, Edge *e, const Term &f); + void GetLabels(Edge *e, std::vector &labels); + + int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); + private: diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 13752401b..334e0a056 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -838,6 +838,95 @@ namespace Duality { return SubtermTruth(memo,tl); } + /** Compute truth values of all boolean subterms in current model. + Assumes formulas has been simplified by Z3, so only boolean ops + ands and, or, not. Returns result in memo. + */ + + int RPFP::GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos){ + if(memo[labpos].find(f) != memo[labpos].end()){ + return memo[labpos][f]; + } + int res; + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + res = GetLabelsRec(memo,!f.arg(0) || f.arg(1), labels, labpos); + goto done; + } + if(k == And) { + res = 1; + for(int i = 0; i < nargs; i++){ + int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); + if(ar == 0){ + res = 0; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Or) { + res = 0; + for(int i = 0; i < nargs; i++){ + int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); + if(ar == 1){ + res = 1; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Not) { + int ar = GetLabelsRec(memo,f.arg(0), labels, !labpos); + res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); + goto done; + } + } + { + bool pos; std::vector names; + if(f.is_label(pos,names)){ + res = GetLabelsRec(memo,f.arg(0), labels, labpos); + if(pos == labpos && res == (pos ? 1 : 0)) + for(unsigned i = 0; i < names.size(); i++) + labels.push_back(names[i]); + goto done; + } + } + { + expr bv = dualModel.eval(f); + if(bv.is_app() && bv.decl().get_decl_kind() == Equal && + bv.arg(0).is_array()){ + bv = EvalArrayEquality(bv); + } + // Hack!!!! array equalities can occur negatively! + if(bv.is_app() && bv.decl().get_decl_kind() == Not && + bv.arg(0).decl().get_decl_kind() == Equal && + bv.arg(0).arg(0).is_array()){ + bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); + } + if(eq(bv,ctx.bool_val(true))) + res = 1; + else if(eq(bv,ctx.bool_val(false))) + res = 0; + else + res = 2; + } + done: + memo[labpos][f] = res; + return res; + } + + void RPFP::GetLabels(Edge *e, std::vector &labels){ + if(!e->map || e->map->labeled.null()) + return; + Term tl = Localize(e, e->map->labeled); + hash_map memo[2]; + GetLabelsRec(memo,tl,labels,true); + } + #ifdef Z3OPS static Z3_subterm_truth *stt; #endif @@ -1742,8 +1831,7 @@ namespace Duality { arg.decl().get_decl_kind() == Uninterpreted); } - void RPFP::FromClauses(const std::vector &unskolemized_clauses, - std::vector > &clause_labels){ + void RPFP::FromClauses(const std::vector &unskolemized_clauses){ hash_map pmap; func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); @@ -1810,7 +1898,6 @@ namespace Duality { // create the edges - clause_labels.resize(clauses.size()); for(unsigned i = 0; i < clauses.size(); i++){ Term clause = clauses[i]; Term body = clause.arg(0); @@ -1841,13 +1928,15 @@ namespace Duality { hash_map scan_memo; std::vector Children; body = ScanBody(scan_memo,body,pmap,Relparams,Children); - body = RemoveLabels(body,clause_labels[i]); + Term labeled = body; + std::vector lbls; // TODO: throw this away for now + body = RemoveLabels(body,lbls); body = body.simplify(); // Create the edge Transformer T = CreateTransformer(Relparams,Indparams,body); - // Edge *edge = - CreateEdge(Parent,T,Children); + Edge *edge = CreateEdge(Parent,T,Children); + edge->labeled = labeled;; // remember for label extraction // edges.push_back(edge); } } diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index bed347ef4..9ac3ec53e 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -25,6 +25,7 @@ Revision History: #include "statistics.h" #include "expr_abstract.h" #include "stopwatch.h" +#include "model_smt2_pp.h" namespace Duality { @@ -534,18 +535,20 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st std::cout << mk_pp(raw(), m()) << std::endl; } -#if 0 void model::show() const { - std::cout << Z3_model_to_string(ctx(), m_model) << std::endl; + model_smt2_pp(std::cout, m(), *m_model, 0); + std::cout << std::endl; } - Z3_ast ast_to_track = 0; -#endif void include_ast_show(ast &a){ a.show(); } + void include_model_show(model &a){ + a.show(); + } + bool expr::is_label (bool &pos,std::vector &names) const { buffer< ::symbol> _names; bool res = m().is_label(to_expr(raw()),pos,_names); diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index e2ee25a2f..e57ba5826 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -161,7 +161,7 @@ void dl_interface::check_reset() { } // creates 1-1 map between clauses and rpfp edges - _d->rpfp->FromClauses(clauses,_d->clause_labels); + _d->rpfp->FromClauses(clauses); // populate the edge-to-clause map for(unsigned i = 0; i < _d->rpfp->edges.size(); ++i) @@ -265,16 +265,12 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp out << " )\n"; out << " (labels"; - std::vector &labels = d->dd()->clause_labels[orig_clause]; - { - hash_map memo; - for(unsigned j = 0; j < labels.size(); j++){ - RPFP::label_struct &lab = labels[j]; - int truth = cex.tree->EvalTruth(memo,&edge,lab.value); - if(truth == 1 && lab.pos || truth == 0 && !lab.pos) - out << " " << lab.name; - } + std::vector labels; + cex.tree->GetLabels(&edge,labels); + for(unsigned j = 0; j < labels.size(); j++){ + out << " " << labels[j]; } + out << " )\n"; // reference the proofs of all the children, in syntactic order From 058c8d2083d9bc75cd052a62d86e15c038f1f8cf Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 22 May 2013 15:42:25 -0700 Subject: [PATCH 025/509] fixing labels in duality --- src/duality/duality_rpfp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 334e0a056..e46a52908 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -852,7 +852,7 @@ namespace Duality { int nargs = f.num_args(); decl_kind k = f.decl().get_decl_kind(); if(k == Implies){ - res = GetLabelsRec(memo,!f.arg(0) || f.arg(1), labels, labpos); + res = GetLabelsRec(memo,f.arg(1) || !f.arg(0), labels, labpos); goto done; } if(k == And) { @@ -946,7 +946,7 @@ namespace Duality { int nargs = f.num_args(); decl_kind k = f.decl().get_decl_kind(); if(k == Implies){ - ImplicantRed(memo,!f.arg(0) || f.arg(1),lits,done,truth,dont_cares); + ImplicantRed(memo,f.arg(1) || !f.arg(0) ,lits,done,truth,dont_cares); goto done; } if(k == Iff){ From b27abc501ea31586337ce65cca54865533e3792c Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 27 May 2013 19:22:19 -0700 Subject: [PATCH 026/509] set proof mode by default to avoid crash on pop if we set it later in duality --- src/ast/ast.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 8ecaed172..da3c80f06 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1305,6 +1305,10 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): } void ast_manager::init() { + // TODO: the following is a HACK to enable proofs in the old smt solver + // When we stop using that solver, this hack can be removed + m_proof_mode = PGM_FINE; + m_int_real_coercions = true; m_debug_ref_count = false; m_fresh_id = 0; From ee4b9d46f114a4b7ccdd2881f8b4b186b8fcc26e Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 27 May 2013 19:22:47 -0700 Subject: [PATCH 027/509] fix labels bug in duality --- src/duality/duality.h | 4 +- src/duality/duality_rpfp.cpp | 71 ++++++++++++++++++++++++++++- src/duality/duality_wrapper.cpp | 3 -- src/muz_qe/duality_dl_interface.cpp | 3 +- 4 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 0a580a463..ef16696ec 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -676,7 +676,7 @@ namespace Duality { void GetLabels(Edge *e, std::vector &labels); - int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); + // int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); private: @@ -767,6 +767,8 @@ namespace Duality { Term ModelValueAsConstraint(const Term &t); + void GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, + hash_set *done, bool truth); }; /** RPFP solver base class. */ diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index e46a52908..77a007c82 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -809,6 +809,13 @@ namespace Duality { goto done; } } + { + bool pos; std::vector names; + if(f.is_label(pos,names)){ + res = SubtermTruth(memo,f.arg(0)); + goto done; + } + } { expr bv = dualModel.eval(f); if(bv.is_app() && bv.decl().get_decl_kind() == Equal && @@ -843,6 +850,7 @@ namespace Duality { ands and, or, not. Returns result in memo. */ +#if 0 int RPFP::GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos){ if(memo[labpos].find(f) != memo[labpos].end()){ return memo[labpos][f]; @@ -918,13 +926,72 @@ namespace Duality { memo[labpos][f] = res; return res; } +#endif + + void RPFP::GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, + hash_set *done, bool truth){ + if(done[truth].find(f) != done[truth].end()) + return; /* already processed */ + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + GetLabelsRec(memo,f.arg(1) || !f.arg(0) ,labels,done,truth); + goto done; + } + if(k == Iff){ + int b = SubtermTruth(memo,f.arg(0)); + if(b == 2) + throw "disaster in GetLabelsRec"; + GetLabelsRec(memo,f.arg(1),labels,done,truth ? b : !b); + goto done; + } + if(truth ? k == And : k == Or) { + for(int i = 0; i < nargs; i++) + GetLabelsRec(memo,f.arg(i),labels,done,truth); + goto done; + } + if(truth ? k == Or : k == And) { + for(int i = 0; i < nargs; i++){ + Term a = f.arg(i); + timer_start("SubtermTruth"); + int b = SubtermTruth(memo,a); + timer_stop("SubtermTruth"); + if(truth ? (b == 1) : (b == 0)){ + GetLabelsRec(memo,a,labels,done,truth); + goto done; + } + } + /* Unreachable! */ + throw "error in RPFP::GetLabelsRec"; + goto done; + } + else if(k == Not) { + GetLabelsRec(memo,f.arg(0),labels,done,!truth); + goto done; + } + else { + bool pos; std::vector names; + if(f.is_label(pos,names)){ + GetLabelsRec(memo,f.arg(0), labels, done, truth); + if(pos == truth) + for(unsigned i = 0; i < names.size(); i++) + labels.push_back(names[i]); + goto done; + } + } + } + done: + done[truth].insert(f); + } void RPFP::GetLabels(Edge *e, std::vector &labels){ if(!e->map || e->map->labeled.null()) return; Term tl = Localize(e, e->map->labeled); - hash_map memo[2]; - GetLabelsRec(memo,tl,labels,true); + hash_map memo; + hash_set done[2]; + GetLabelsRec(memo,tl,labels,done,true); } #ifdef Z3OPS diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 9ac3ec53e..bfdc382bf 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -30,9 +30,6 @@ Revision History: namespace Duality { solver::solver(Duality::context& c) : object(c), the_model(c) { - // TODO: the following is a HACK to enable proofs in the old smt solver - // When we stop using that solver, this hack can be removed - m().toggle_proof_mode(PGM_FINE); params_ref p; p.set_bool("proof", true); // this is currently useless p.set_bool("model", true); diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index e57ba5826..e49b89775 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -117,7 +117,8 @@ void dl_interface::check_reset() { #endif - lbool dl_interface::query(::expr * query) { +lbool dl_interface::query(::expr * query) { + // TODO: you can only call this once! // we restore the initial state in the datalog context m_ctx.ensure_opened(); From dfae0c5109a1885309e374ca5d6fc1e3f2e02839 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 29 May 2013 16:40:47 -0700 Subject: [PATCH 028/509] output background model in duality counterexamples --- src/duality/duality.h | 2 ++ src/duality/duality_rpfp.cpp | 2 ++ src/duality/duality_wrapper.h | 8 ++++---- src/muz_qe/duality_dl_interface.cpp | 26 ++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index ef16696ec..343eeeb72 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -257,7 +257,9 @@ namespace Duality { }; + public: model dualModel; + private: literals dualLabels; std::list stack; std::vector axioms; // only saved here for printing purposes diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 77a007c82..623697880 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -248,6 +248,8 @@ namespace Duality { RPFP::Term RPFP::Localize(Edge *e, const Term &t){ timer_start("Localize"); hash_map memo; + if(e->F.IndParams.size() > 0 && e->varMap.empty()) + SetEdgeMaps(e); // TODO: why is this needed? Term res = LocalizeRec(e,memo,t); timer_stop("Localize"); return res; diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 2fa46ba5a..9477ab0fa 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -688,10 +688,10 @@ namespace Duality { void show() const; - unsigned num_consts() const; - unsigned num_funcs() const; - func_decl get_const_decl(unsigned i) const; - func_decl get_func_decl(unsigned i) const; + unsigned num_consts() const {return m_model.get()->get_num_constants();} + unsigned num_funcs() const {return m_model.get()->get_num_functions();} + func_decl get_const_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_constant(i));} + func_decl get_func_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_function(i));} unsigned size() const; func_decl operator[](unsigned i) const; diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index e49b89775..5922a8afd 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -35,6 +35,7 @@ Revision History: #include "dl_mk_coalesce.h" #include "expr_abstract.h" #include "model_smt2_pp.h" +#include "model_v2_pp.h" // template class symbol_table; @@ -218,6 +219,8 @@ expr_ref dl_interface::get_cover_delta(int level, ::func_decl* pred_orig) { void dl_interface::reset_statistics() { } +static hash_set *local_func_decls; + static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexample &cex) { context &ctx = d->dd()->ctx; RPFP::Node &node = *cex.root; @@ -261,6 +264,8 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp 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); + (*local_func_decls).insert(local_skolem.decl()); } } out << " )\n"; @@ -298,8 +303,29 @@ void dl_interface::display_certificate(std::ostream& out) { else if(_d->status == StatusRefutation){ out << "(derivation\n"; // negation of the query is the last clause -- prove it + hash_set locals; + local_func_decls = &locals; print_proof(this,out,_d->cex); out << ")\n"; + out << "(model \n"; + ::model mod(m_ctx.get_manager()); + model orig_model = _d->cex.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()){ + expr thing = orig_model.get_const_interp(cnst); + mod.register_decl(to_func_decl(cnst.raw()),to_expr(thing.raw())); + } + } + for(unsigned i = 0; i < orig_model.num_funcs(); i++){ + func_decl cnst = orig_model.get_func_decl(i); + if(locals.find(cnst) == locals.end()){ + func_interp thing = orig_model.get_func_interp(cnst); + mod.register_decl(to_func_decl(cnst.raw()),thing); + } + } + model_v2_pp(out,mod); + out << ")\n"; } } From ca381589665c3888a9e4c35849826b755036249a Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 31 May 2013 17:52:51 -0700 Subject: [PATCH 029/509] fix bug in getting decision count in duality --- src/duality/duality_wrapper.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index bfdc382bf..fcb637a84 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -296,6 +296,7 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st st.display(ss); std::string stats = ss.str(); int pos = stats.find("decisions:"); + if(pos < 0) return 0; // for some reason, decisions are not reported if there are none pos += 10; int end = stats.find('\n',pos); std::string val = stats.substr(pos,end-pos); From 9890b3bb5c2b81f26c9099577cb4bc939b1a9f47 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 31 May 2013 18:00:50 -0700 Subject: [PATCH 030/509] changing model format in duality to support boogie --- src/muz_qe/duality_dl_interface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index 5922a8afd..eb455ffe7 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -307,7 +307,7 @@ void dl_interface::display_certificate(std::ostream& out) { local_func_decls = &locals; print_proof(this,out,_d->cex); out << ")\n"; - out << "(model \n"; + out << "(model \n\""; ::model mod(m_ctx.get_manager()); model orig_model = _d->cex.tree->dualModel; for(unsigned i = 0; i < orig_model.num_consts(); i++){ @@ -325,7 +325,7 @@ void dl_interface::display_certificate(std::ostream& out) { } } model_v2_pp(out,mod); - out << ")\n"; + out << "\")\n"; } } From 7c32df93a4627f78c6be0dba6d6817967ce28c4d Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 3 Jun 2013 18:17:41 +0100 Subject: [PATCH 031/509] SLS tactic: compilation fixes Signed-off-by: Christoph M. Wintersteiger --- src/tactic/sls/sls_tactic.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index f49ce8422..69e1c8bc3 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -1370,7 +1370,7 @@ class sls_tactic : public tactic { void updt_params(params_ref const & p) { m_produce_models = p.get_bool("produce_models", false); - m_max_restarts = p.get_uint("sls_restarts", -1); + m_max_restarts = p.get_uint("sls_restarts", (unsigned)-1); m_tracker.set_random_seed(p.get_uint("random_seed", 0)); m_plateau_limit = p.get_uint("plateau_limit", 100); } @@ -1612,7 +1612,7 @@ class sls_tactic : public tactic { lbool search(goal_ref const & g) { lbool res = l_undef; double score = 0.0, old_score = 0.0; - unsigned new_const = -1, new_bit = 0; + unsigned new_const = (unsigned)-1, new_bit = 0; mpz new_value; move_type move; @@ -1631,7 +1631,7 @@ class sls_tactic : public tactic { checkpoint(); old_score = score; - new_const = -1; + new_const = (unsigned)-1; ptr_vector & to_evaluate = m_tracker.get_unsat_constants(g); From 093fe945bc3e57d5d8c1f9f4afdf597549ef8ab2 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 3 Jun 2013 18:19:45 +0100 Subject: [PATCH 032/509] FPA: min/max/fma bugfixes + partial quantifier support Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 223 ++++++++++++++++++++-------- src/tactic/fpa/fpa2bv_rewriter.h | 16 +- 2 files changed, 165 insertions(+), 74 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 363cf1df8..02aff67c6 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1022,13 +1022,15 @@ void fpa2bv_converter::mk_min(func_decl * f, unsigned num, expr * const * args, split(x, x_sgn, x_sig, x_exp); split(y, y_sgn, y_sig, y_exp); - expr_ref c1(m), c2(m), y_is_nan(m), x_is_nzero(m), y_is_zero(m), c2_and(m); - mk_is_nan(x, c1); - mk_is_nan(y, y_is_nan); - mk_is_nzero(x, x_is_nzero); + expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), c1_and(m); + mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); - m_simp.mk_and(x_is_nzero, y_is_zero, c2_and); - m_simp.mk_or(y_is_nan, c2_and, c2); + m_simp.mk_and(x_is_zero, y_is_zero, c1_and); + mk_is_nan(x, x_is_nan); + m_simp.mk_or(x_is_nan, c1_and, c1); + + mk_is_nan(y, y_is_nan); + c2 = y_is_nan; expr_ref c3(m); mk_float_lt(f, num, args, c3); @@ -1063,13 +1065,15 @@ void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, split(x, x_sgn, x_sig, x_exp); split(y, y_sgn, y_sig, y_exp); - expr_ref c1(m), c2(m), y_is_nan(m), y_is_nzero(m), x_is_zero(m), xy_is_zero(m); - mk_is_nan(x, c1); - mk_is_nan(y, y_is_nan); - mk_is_nzero(y, y_is_nzero); + expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), y_is_zero(m), x_is_zero(m), c1_and(m); + mk_is_zero(y, y_is_zero); mk_is_zero(x, x_is_zero); - m_simp.mk_and(y_is_nzero, x_is_zero, xy_is_zero); - m_simp.mk_or(y_is_nan, xy_is_zero, c2); + m_simp.mk_and(y_is_zero, x_is_zero, c1_and); + mk_is_nan(x, x_is_nan); + m_simp.mk_or(x_is_nan, c1_and, c1); + + mk_is_nan(y, y_is_nan); + c2 = y_is_nan; expr_ref c3(m); mk_float_gt(f, num, args, c3); @@ -1111,20 +1115,23 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar mk_minus_inf(f, ninf); mk_plus_inf(f, pinf); - expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); - expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); - expr_ref z_is_nan(m), z_is_zero(m), z_is_pos(m), z_is_inf(m); + expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); + expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); + expr_ref z_is_nan(m), z_is_zero(m), z_is_pos(m), z_is_neg(m), z_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); + mk_is_neg(x, x_is_neg); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); + mk_is_neg(y, y_is_neg); mk_is_inf(y, y_is_inf); mk_is_nan(z, z_is_nan); mk_is_zero(z, z_is_zero); mk_is_pos(z, z_is_pos); + mk_is_neg(z, z_is_neg); mk_is_inf(z, z_is_inf); dbg_decouple("fpa2bv_fma_x_is_nan", x_is_nan); @@ -1140,42 +1147,56 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar dbg_decouple("fpa2bv_fma_z_is_pos", z_is_pos); dbg_decouple("fpa2bv_fma_z_is_inf", z_is_inf); - expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); - expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); - // (x is NaN) || (y is NaN) -> NaN - m_simp.mk_or(x_is_nan, y_is_nan, c1); + expr_ref inf_xor(m), inf_cond(m); + m_simp.mk_xor(x_is_neg, y_is_neg, inf_xor); + m_simp.mk_xor(inf_xor, z_is_neg, inf_xor); + m_simp.mk_and(z_is_inf, inf_xor, inf_cond); + + // (x is NaN) || (y is NaN) || (z is Nan) -> NaN + m_simp.mk_or(x_is_nan, y_is_nan, z_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is 0) then NaN else inf with y's sign. mk_is_pinf(x, c2); - expr_ref y_sgn_inf(m); + expr_ref y_sgn_inf(m), inf_or(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); - mk_ite(y_is_zero, nan, y_sgn_inf, v2); + m_simp.mk_or(y_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is 0) then NaN else inf with x's sign. mk_is_pinf(y, c3); expr_ref x_sgn_inf(m); mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); - mk_ite(x_is_zero, nan, x_sgn_inf, v3); + m_simp.mk_or(x_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, x_sgn_inf, v3); // (x is -oo) -> if (y is 0) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); - mk_ite(y_is_zero, nan, neg_y_sgn_inf, v4); + m_simp.mk_or(y_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is 0) then NaN else inf with -x's sign. mk_is_ninf(y, c5); expr_ref neg_x_sgn_inf(m); mk_ite(x_is_pos, ninf, pinf, neg_x_sgn_inf); - mk_ite(x_is_zero, nan, neg_x_sgn_inf, v5); + m_simp.mk_or(x_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, neg_x_sgn_inf, v5); + + // z is +-INF -> Z. + mk_is_inf(z, c6); + v6 = z; // (x is 0) || (y is 0) -> x but with sign = x.sign ^ y.sign - m_simp.mk_or(x_is_zero, y_is_zero, c6); + m_simp.mk_or(x_is_zero, y_is_zero, c7); expr_ref sign_xor(m); m_simp.mk_xor(x_is_pos, y_is_pos, sign_xor); - mk_ite(sign_xor, nzero, pzero, v6); + mk_ite(sign_xor, nzero, pzero, v7); + // else comes the fused multiplication. unsigned ebits = m_util.get_ebits(f->get_range()); @@ -1190,54 +1211,57 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar expr_ref c_sgn(m), c_sig(m), c_exp(m), c_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); - unpack(z, c_sgn, c_sig, c_exp, c_lz, false); + unpack(z, c_sgn, c_sig, c_exp, c_lz, true); - expr_ref a_lz_ext(m), b_lz_ext(m); + expr_ref a_lz_ext(m), b_lz_ext(m), c_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); + c_lz_ext = m_bv_util.mk_zero_extend(2, c_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); - expr_ref a_exp_ext(m), b_exp_ext(m); + expr_ref a_exp_ext(m), b_exp_ext(m), c_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); + c_exp_ext = m_bv_util.mk_sign_extend(2, c_exp); expr_ref mul_sgn(m), mul_sig(m), mul_exp(m); expr * signs[2] = { a_sgn, b_sgn }; - mul_sgn = m_bv_util.mk_bv_xor(2, signs); + mul_sgn = m_bv_util.mk_bv_xor(2, signs); dbg_decouple("fpa2bv_fma_mul_sgn", mul_sgn); - mul_exp = m_bv_util.mk_bv_sub( - m_bv_util.mk_bv_add(a_exp_ext, b_exp_ext), - m_bv_util.mk_bv_add(a_lz_ext, b_lz_ext)); + mul_exp = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + dbg_decouple("fpa2bv_fma_mul_exp", mul_exp); mul_sig = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); - dbg_decouple("fpa2bv_fma_mul_sig", mul_sig); SASSERT(m_bv_util.get_bv_size(mul_sig) == 2*sbits); + SASSERT(m_bv_util.get_bv_size(mul_exp) == ebits + 2); - // The result in `product' represents a number of the form 1.*** (unpacked) - // (product = mul_sgn/mul_sig/mul_exp and c_sgn/c_sig/c_exp is unpacked w/o normalization). + // The product has the form [-1][0].[2*sbits - 2]. + + // Extend c + c_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, sbits-1))); - // extend c. - c_sig = m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, sbits)); - c_exp = m_bv_util.mk_sign_extend(2, c_exp); + SASSERT(m_bv_util.get_bv_size(mul_sig) == 2 * sbits); + SASSERT(m_bv_util.get_bv_size(c_sig) == 2 * sbits); expr_ref swap_cond(m); - swap_cond = m_bv_util.mk_sle(mul_exp, c_exp); + swap_cond = m_bv_util.mk_sle(mul_exp, c_exp_ext); SASSERT(is_well_sorted(m, swap_cond)); expr_ref e_sgn(m), e_sig(m), e_exp(m), f_sgn(m), f_sig(m), f_exp(m); m_simp.mk_ite(swap_cond, c_sgn, mul_sgn, e_sgn); m_simp.mk_ite(swap_cond, c_sig, mul_sig, e_sig); // has 2 * sbits - m_simp.mk_ite(swap_cond, c_exp, mul_exp, e_exp); // has ebits + 2 + m_simp.mk_ite(swap_cond, c_exp_ext, mul_exp, e_exp); // has ebits + 2 m_simp.mk_ite(swap_cond, mul_sgn, c_sgn, f_sgn); m_simp.mk_ite(swap_cond, mul_sig, c_sig, f_sig); // has 2 * sbits - m_simp.mk_ite(swap_cond, mul_exp, c_exp, f_exp); // has ebits + 2 + m_simp.mk_ite(swap_cond, mul_exp, c_exp_ext, f_exp); // has ebits + 2 SASSERT(is_well_sorted(m, e_sgn)); SASSERT(is_well_sorted(m, e_sig)); @@ -1247,26 +1271,94 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar SASSERT(is_well_sorted(m, f_exp)); expr_ref res_sgn(m), res_sig(m), res_exp(m); - add_core(2 * sbits, ebits + 2, rm, - e_sgn, e_sig, e_exp, f_sgn, f_sig, f_exp, - res_sgn, res_sig, res_exp); + + expr_ref exp_delta(m); + exp_delta = m_bv_util.mk_bv_sub(e_exp, f_exp); + dbg_decouple("fpa2bv_fma_add_exp_delta", exp_delta); - // Note: res_sig is now 2 * sbits + 4, i.e., `sbits' too much, which should go into a sticky bit. - unsigned sig_size = m_bv_util.get_bv_size(res_sig); - SASSERT(sig_size == (2*sbits+4)); + // cap the delta + expr_ref cap(m), cap_le_delta(m); + cap = m_bv_util.mk_numeral(sbits+3, ebits+2); + cap_le_delta = m_bv_util.mk_ule(exp_delta, cap); + m_simp.mk_ite(cap_le_delta, cap, exp_delta, exp_delta); + SASSERT(m_bv_util.get_bv_size(exp_delta) == ebits+2); + dbg_decouple("fpa2bv_fma_add_exp_delta_capped", exp_delta); + + // Alignment shift with sticky bit computation. + expr_ref big_f_sig(m); + big_f_sig = m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits+4)); + SASSERT(is_well_sorted(m, big_f_sig)); - // Note: res_exp is 2 bits too wide. - unsigned exp_size = m_bv_util.get_bv_size(res_exp); - SASSERT(exp_size == ebits+4); - res_exp = m_bv_util.mk_extract(ebits+1, 0, res_exp); + expr_ref shifted_big(m), shifted_f_sig(m), sticky_raw(m); + shifted_big = m_bv_util.mk_bv_lshr(big_f_sig, m_bv_util.mk_concat(m_bv_util.mk_numeral(0, (2*(sbits+4))-(ebits+2)), exp_delta)); + shifted_f_sig = m_bv_util.mk_extract((2*(sbits+4)-1), (sbits+4), shifted_big); + SASSERT(is_well_sorted(m, shifted_f_sig)); - expr_ref sticky(m); - sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sbits, 0, res_sig)); - res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(2*sbits+3, sbits+1, res_sig), sticky); + sticky_raw = m_bv_util.mk_extract(sbits+3, 0, shifted_big); + expr_ref sticky(m), sticky_eq(m), nil_sbit4(m), one_sbit4(m); + nil_sbit4 = m_bv_util.mk_numeral(0, sbits+4); + one_sbit4 = m_bv_util.mk_numeral(1, sbits+4); + m_simp.mk_eq(sticky_raw, nil_sbit4, sticky_eq); + m_simp.mk_ite(sticky_eq, nil_sbit4, one_sbit4, sticky); + SASSERT(is_well_sorted(m, sticky)); + + expr * or_args[2] = { shifted_f_sig, sticky }; + shifted_f_sig = m_bv_util.mk_bv_or(2, or_args); + SASSERT(is_well_sorted(m, shifted_f_sig)); - sig_size = m_bv_util.get_bv_size(res_sig); - SASSERT(sig_size == sbits+4); + expr_ref eq_sgn(m); + m_simp.mk_eq(e_sgn, f_sgn, eq_sgn); + + // two extra bits for catching the overflow. + e_sig = m_bv_util.mk_zero_extend(2, e_sig); + shifted_f_sig = m_bv_util.mk_zero_extend(2, shifted_f_sig); + SASSERT(m_bv_util.get_bv_size(e_sig) == sbits+6); + SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == sbits+6); + + dbg_decouple("fpa2bv_fma_add_e_sig", e_sig); + dbg_decouple("fpa2bv_fma_add_shifted_f_sig", shifted_f_sig); + + expr_ref sum(m); + m_simp.mk_ite(eq_sgn, + m_bv_util.mk_bv_add(e_sig, shifted_f_sig), // ADD LZ + m_bv_util.mk_bv_sub(e_sig, shifted_f_sig), + sum); + + SASSERT(is_well_sorted(m, sum)); + + dbg_decouple("fpa2bv_fma_add_sum", sum); + + expr_ref sign_bv(m), n_sum(m); + sign_bv = m_bv_util.mk_extract(sbits+4, sbits+4, sum); + n_sum = m_bv_util.mk_bv_neg(sum); + + dbg_decouple("fpa2bv_fma_add_sign_bv", sign_bv); + dbg_decouple("fpa2bv_fma_add_n_sum", n_sum); + + family_id bvfid = m_bv_util.get_fid(); + + expr_ref res_sgn_c1(m), res_sgn_c2(m), res_sgn_c3(m); + expr_ref not_e_sgn(m), not_f_sgn(m), not_sign_bv(m); + not_e_sgn = m_bv_util.mk_bv_not(e_sgn); + not_f_sgn = m_bv_util.mk_bv_not(f_sgn); + not_sign_bv = m_bv_util.mk_bv_not(sign_bv); + res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_e_sgn, e_sgn, sign_bv); + res_sgn_c2 = m.mk_app(bvfid, OP_BAND, e_sgn, not_f_sgn, not_sign_bv); + res_sgn_c3 = m.mk_app(bvfid, OP_BAND, e_sgn, f_sgn); + expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; + res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); + + expr_ref res_sig_eq(m), sig_abs(m), one_1(m); + one_1 = m_bv_util.mk_numeral(1, 1); + m_simp.mk_eq(sign_bv, one_1, res_sig_eq); + m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); + + dbg_decouple("fpa2bv_fma_add_sig_abs", sig_abs); + + res_sig = m_bv_util.mk_extract(sbits+3, 0, sig_abs); + res_exp = m_bv_util.mk_bv_sub(e_exp, c_lz_ext); + expr_ref is_zero_sig(m), nil_sbits4(m); nil_sbits4 = m_bv_util.mk_numeral(0, sbits+4); m_simp.mk_eq(res_sig, nil_sbits4, is_zero_sig); @@ -1281,10 +1373,11 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar expr_ref rounded(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); - mk_ite(is_zero_sig, zero_case, rounded, v7); + mk_ite(is_zero_sig, zero_case, rounded, v8); // And finally, we tie them together. - mk_ite(c6, v6, v7, result); + mk_ite(c7, v7, v8, result); + mk_ite(c6, v6, result, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); @@ -1293,7 +1386,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar SASSERT(is_well_sorted(m, result)); - TRACE("fpa2bv_mul", tout << "MUL = " << mk_ismt2_pp(result, m) << std::endl; ); + TRACE("fpa2bv_fma_", tout << "FMA = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -2571,9 +2664,13 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { sz = bv_mdl->get_num_functions(); for (unsigned i = 0; i < sz; i++) { - func_decl * c = bv_mdl->get_function(i); - if (!seen.contains(c)) - float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); + func_decl * f = bv_mdl->get_function(i); + if (!seen.contains(f)) + { + TRACE("fpa2bv_mc", tout << "Keeping: " << mk_ismt2_pp(f, m) << std::endl; ); + func_interp * val = bv_mdl->get_func_interp(f); + float_mdl->register_decl(f, val); + } } sz = bv_mdl->get_num_uninterpreted_sorts(); diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index 3398874f5..d8a141b62 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -29,9 +29,7 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; expr_ref_vector m_out; fpa2bv_converter & m_conv; - sort_ref_vector m_bindings; - expr_ref_vector m_mappings; - + sort_ref_vector m_bindings; unsigned long long m_max_memory; unsigned m_max_steps; @@ -42,8 +40,7 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { m_manager(m), m_out(m), m_conv(c), - m_bindings(m), - m_mappings(m) { + m_bindings(m) { updt_params(p); // We need to make sure that the mananger has the BV plugin loaded. symbol s_bv("bv"); @@ -177,7 +174,6 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { new_bindings.push_back(q->get_decl_sort(i)); SASSERT(new_bindings.size() == q->get_num_decls()); m_bindings.append(new_bindings); - m_mappings.resize(m_bindings.size(), 0); } return true; } @@ -215,8 +211,7 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); result_pr = 0; - m_bindings.shrink(old_sz); - m_mappings.shrink(old_sz); + m_bindings.shrink(old_sz); TRACE("fpa2bv", tout << "reduce_quantifier[" << old_q->get_depth() << "]: " << mk_ismt2_pp(old_q->get_expr(), m()) << std::endl << " new body: " << mk_ismt2_pp(new_body, m()) << std::endl; @@ -243,10 +238,9 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { new_exp); } else - new_exp = m().mk_var(t->get_idx(), s); - m_mappings[inx] = new_exp; + new_exp = m().mk_var(t->get_idx(), s); - result = m_mappings[inx].get(); + result = new_exp; result_pr = 0; TRACE("fpa2bv", tout << "reduce_var: " << mk_ismt2_pp(t, m()) << " -> " << mk_ismt2_pp(result, m()) << std::endl;); return true; From 418f148ecf4e8c405ddd844f665f077018af1c9e Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 4 Jun 2013 18:22:54 -0700 Subject: [PATCH 033/509] working on incremental stratified inlining in duality --- src/duality/duality.h | 2 +- src/duality/duality_solver.cpp | 32 +++++++++++++++++++++-------- src/muz_qe/dl_context.cpp | 1 + src/muz_qe/duality_dl_interface.cpp | 29 +++++++++++++++++++++----- 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 343eeeb72..f514e045f 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -803,7 +803,7 @@ namespace Duality { is chiefly useful for abstraction refinement, when we want to solve a series of similar problems. */ - virtual void LearnFrom(Solver *old_solver) = 0; + virtual void LearnFrom(Counterexample &old_cex) = 0; virtual ~Solver(){} diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 40aa352c2..e53ead302 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -160,6 +160,9 @@ namespace Duality { Heuristic(RPFP *_rpfp){ rpfp = _rpfp; } + + virtual ~Heuristic(){} + virtual void Update(RPFP::Node *node){ scores[node].updates++; } @@ -247,6 +250,7 @@ namespace Duality { heuristic = !cex.tree ? (Heuristic *)(new LocalHeuristic(rpfp)) : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); #endif + cex.tree = 0; // heuristic now owns it unwinding = new RPFP(rpfp->ls); unwinding->HornClauses = rpfp->HornClauses; indset = new Covering(this); @@ -254,8 +258,10 @@ namespace Duality { CreateEdgesByChildMap(); CreateLeaves(); #ifndef TOP_DOWN - if(FeasibleEdges)NullaryCandidates(); - else InstantiateAllEdges(); + if(!StratifiedInlining){ + if(FeasibleEdges)NullaryCandidates(); + else InstantiateAllEdges(); + } #else for(unsigned i = 0; i < leaves.size(); i++) if(!SatisfyUpperBound(leaves[i])) @@ -289,8 +295,8 @@ namespace Duality { } #endif - virtual void LearnFrom(Solver *old_solver){ - cex = old_solver->GetCounterexample(); + virtual void LearnFrom(Counterexample &old_cex){ + cex = old_cex; } /** Return the counterexample */ @@ -778,8 +784,14 @@ namespace Duality { std::vector nchs(chs.size()); for(unsigned i = 0; i < chs.size(); i++){ Node *child = chs[i]; - if(TopoSort[child] < TopoSort[node->map]) - nchs[i] = LeafMap[child]; + if(TopoSort[child] < TopoSort[node->map]){ + Node *leaf = LeafMap[child]; + nchs[i] = leaf; + if(unexpanded.find(leaf) != unexpanded.end()){ + unexpanded.erase(leaf); + insts_of_node[child].push_back(leaf); + } + } else { if(StratifiedLeafMap.find(child) == StratifiedLeafMap.end()){ RPFP::Node *nchild = CreateNodeInstance(child,StratifiedLeafCount--); @@ -1450,7 +1462,7 @@ namespace Duality { #ifdef EFFORT_BOUNDED_STRAT start_decs = tree->CumulativeDecisions(); #endif - // while(ExpandSomeNodes(true)); // do high-priority expansions + while(ExpandSomeNodes(true)); // do high-priority expansions while (true) { #ifndef WITH_CHILDREN @@ -1999,13 +2011,17 @@ namespace Duality { class ReplayHeuristic : public Heuristic { - Counterexample &old_cex; + Counterexample old_cex; public: ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) : Heuristic(_rpfp), old_cex(_old_cex) { } + ~ReplayHeuristic(){ + delete old_cex.tree; + } + // Maps nodes of derivation tree into old cex hash_map cex_map; diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index ef8168607..c171b66e4 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -198,6 +198,7 @@ namespace datalog { void context::push() { m_trail.push_scope(); m_trail.push(restore_rules(m_rule_set)); + m_trail.push(restore_vec_size_trail(m_rule_fmls)); m_trail.push(restore_vec_size_trail(m_background)); } diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index eb455ffe7..e53d79409 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -79,14 +79,13 @@ namespace Duality { dl_interface::dl_interface(datalog::context& dl_ctx) : m_ctx(dl_ctx) { - _d = alloc(duality_data,dl_ctx.get_manager()); - _d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx); - _d->rpfp = alloc(RPFP,_d->ls); + _d = 0; } dl_interface::~dl_interface() { - dealloc(_d); + if(_d) + dealloc(_d); } @@ -120,10 +119,22 @@ void dl_interface::check_reset() { lbool dl_interface::query(::expr * query) { - // TODO: you can only call this once! // we restore the initial state in the datalog context 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; + + // make a new problem and solver + _d = alloc(duality_data,m_ctx.get_manager()); + _d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx); + _d->rpfp = alloc(RPFP,_d->ls); + + + expr_ref_vector rules(m_ctx.get_manager()); svector< ::symbol> names; // m_ctx.get_rules_as_formulas(rules, names); @@ -173,6 +184,8 @@ lbool dl_interface::query(::expr * query) { Solver *rs = Solver::Create("duality", _d->rpfp); + rs->LearnFrom(old_cex); // new solver gets hints from old cex + // set its options IF_VERBOSE(1, rs->SetOption("report","1");); rs->SetOption("full_expand",m_ctx.get_params().full_expand() ? "1" : "0"); @@ -193,6 +206,12 @@ lbool dl_interface::query(::expr * query) { _d->status = ans ? StatusModel : StatusRefutation; _d->cex = rs->GetCounterexample(); + if(old_data){ + old_data->cex.tree = 0; // we own it now + dealloc(old_data); + } + + dealloc(rs); // true means the RPFP problem is SAT, so the query is UNSAT From c3eae9bf2ad9b67c6794def031b0293a10e06132 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 5 Jun 2013 17:02:13 -0700 Subject: [PATCH 034/509] working on incremental stratified inlining in duality --- src/duality/duality_solver.cpp | 31 +++++++++++++++++++++++++---- src/muz_qe/dl_cmds.cpp | 4 ++-- src/muz_qe/duality_dl_interface.cpp | 3 ++- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index e53ead302..40c82eac8 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -200,6 +200,9 @@ namespace Duality { best.insert(*it); } #endif + + /** Called when done expanding a tree */ + virtual void Done() {} }; @@ -749,7 +752,8 @@ namespace Duality { DoTopoSort(); for(unsigned i = 0; i < leaves.size(); i++){ Node *node = leaves[i]; - if(!SatisfyUpperBound(node)) + bool res = SatisfyUpperBound(node); + if(!res) return false; } // don't leave any dangling nodes! @@ -1429,6 +1433,7 @@ namespace Duality { tree->AssertNode(top); // assert the negation of the top-level spec timer_start("Build"); bool res = Build(); + heuristic->Done(); timer_stop("Build"); timer_start("Pop"); tree->Pop(1); @@ -2019,12 +2024,20 @@ namespace Duality { } ~ReplayHeuristic(){ - delete old_cex.tree; + if(old_cex.tree) + delete old_cex.tree; } // Maps nodes of derivation tree into old cex hash_map cex_map; + void Done() { + cex_map.clear(); + if(old_cex.tree) + delete old_cex.tree; + old_cex.tree = 0; // only replay once! + } + void ShowNodeAndChildren(Node *n){ std::cout << n->Name.name() << ": "; std::vector &chs = n->Outgoing->Children; @@ -2033,8 +2046,18 @@ namespace Duality { std::cout << std::endl; } + // HACK: When matching relation names, we drop suffixes used to + // make the names unique between runs. For compatibility + // with boggie, we drop suffixes beginning with @@ + std::string BaseName(const std::string &name){ + int pos = name.find("@@"); + if(pos >= 1) + return name.substr(0,pos); + return name; + } + virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority){ - if(!high_priority){ + if(!high_priority || !old_cex.tree){ Heuristic::ChooseExpand(choices,best,false); return; } @@ -2053,7 +2076,7 @@ namespace Duality { if(old_parent && old_parent->Outgoing){ std::vector &old_chs = old_parent->Outgoing->Children; for(unsigned i = 0, j=0; i < chs.size(); i++){ - if(j < old_chs.size() && chs[i]->Name.name() == old_chs[j]->Name.name()) + if(j < old_chs.size() && BaseName(chs[i]->Name.name().str()) == BaseName(old_chs[j]->Name.name().str())) cex_map[chs[i]] = old_chs[j++]; else { std::cout << "unmatched child: " << chs[i]->Name.name() << std::endl; diff --git a/src/muz_qe/dl_cmds.cpp b/src/muz_qe/dl_cmds.cpp index c88e7346e..ade4b633d 100644 --- a/src/muz_qe/dl_cmds.cpp +++ b/src/muz_qe/dl_cmds.cpp @@ -476,10 +476,10 @@ static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_c ctx.insert(alloc(dl_query_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_var_cmd, dl_ctx)); -#ifndef _EXTERNAL_RELEASE + // #ifndef _EXTERNAL_RELEASE ctx.insert(alloc(dl_push_cmd, dl_ctx)); // not exposed to keep command-extensions simple. ctx.insert(alloc(dl_pop_cmd, dl_ctx)); -#endif + // #endif } void install_dl_cmds(cmd_context & ctx) { diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index e53d79409..fd26a2a2b 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -340,7 +340,8 @@ void dl_interface::display_certificate(std::ostream& out) { func_decl cnst = orig_model.get_func_decl(i); if(locals.find(cnst) == locals.end()){ func_interp thing = orig_model.get_func_interp(cnst); - mod.register_decl(to_func_decl(cnst.raw()),thing); + ::func_interp *thing_raw = thing; + mod.register_decl(to_func_decl(cnst.raw()),thing_raw->copy()); } } model_v2_pp(out,mod); From 97a7ae1589c0ec2c2f50004a927b26455c74de06 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 5 Jun 2013 18:01:05 -0700 Subject: [PATCH 035/509] add profiling option --- src/api/dotnet/Properties/AssemblyInfo.cs | 4 ++-- src/duality/duality_solver.cpp | 15 +++++++++++++-- src/muz_qe/duality_dl_interface.cpp | 6 ++++++ src/muz_qe/fixedpoint_params.pyg | 1 + 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/api/dotnet/Properties/AssemblyInfo.cs b/src/api/dotnet/Properties/AssemblyInfo.cs index 517349177..1cd0fe7b8 100644 --- a/src/api/dotnet/Properties/AssemblyInfo.cs +++ b/src/api/dotnet/Properties/AssemblyInfo.cs @@ -34,6 +34,6 @@ using System.Security.Permissions; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("4.2.0.0")] -[assembly: AssemblyVersion("4.3.2.0")] -[assembly: AssemblyFileVersion("4.3.2.0")] +[assembly: AssemblyVersion("4.3.2.0")] +[assembly: AssemblyFileVersion("4.3.2.0")] diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 40c82eac8..142d8e457 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -684,6 +684,7 @@ namespace Duality { bool CandidateFeasible(const Candidate &cand){ if(!FeasibleEdges) return true; + timer_start("CandidateFeasible"); RPFP *checker = new RPFP(rpfp->ls); // std::cout << "Checking feasibility of extension " << cand.edge->Parent->number << std::endl; checker->Push(); @@ -691,8 +692,10 @@ namespace Duality { Node *root = checker->CloneNode(cand.edge->Parent); #ifdef BOUNDED for(unsigned i = 0; i < cand.Children.size(); i++) - if(NodePastRecursionBound(cand.Children[i])) + if(NodePastRecursionBound(cand.Children[i])){ + timer_stop("CandidateFeasible"); return false; + } #endif #ifdef NEW_CAND_SEL GenNodeSolutionFromIndSet(cand.edge->Parent,root->Bound); @@ -710,6 +713,7 @@ namespace Duality { if(!res)reporter->Reject(cand.edge,cand.Children); checker->Pop(1); delete checker; + timer_stop("CandidateFeasible"); return res; } @@ -749,12 +753,15 @@ namespace Duality { int StratifiedLeafCount; bool DoStratifiedInlining(){ + timer_start("StratifiedInlining"); DoTopoSort(); for(unsigned i = 0; i < leaves.size(); i++){ Node *node = leaves[i]; bool res = SatisfyUpperBound(node); - if(!res) + if(!res){ + timer_stop("StratifiedInlining"); return false; + } } // don't leave any dangling nodes! #ifndef EFFORT_BOUNDED_STRAT @@ -762,6 +769,7 @@ namespace Duality { if(!leaves[i]->Outgoing) MakeLeaf(leaves[i],true); #endif + timer_stop("StratifiedInlining"); return true; } @@ -1333,6 +1341,7 @@ namespace Duality { /** Extend the unwinding, keeping it solved. */ bool Extend(Candidate &cand){ + timer_start("Extend"); Node *node = CreateNodeInstance(cand.edge->Parent); CreateEdgeInstance(cand.edge,node,cand.Children); UpdateBackEdges(node); @@ -1344,11 +1353,13 @@ namespace Duality { ExpandUnderapproxNodes(cex.tree, cex.root); #endif if(UseUnderapprox) BuildFullCex(node); + timer_stop("Extend"); return res; } #ifdef EARLY_EXPAND TryExpandNode(node); #endif + timer_stop("Extend"); return res; } diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index fd26a2a2b..e59baa4c5 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -41,6 +41,7 @@ Revision History: #include "duality.h" +#include "duality_profiling.h" // using namespace Duality; @@ -202,6 +203,11 @@ lbool dl_interface::query(::expr * query) { // Solve! bool ans = rs->Solve(); + // profile! + + if(m_ctx.get_params().profile()) + print_profile(std::cout); + // save the result and counterexample if there is one _d->status = ans ? StatusModel : StatusRefutation; _d->cex = rs->GetCounterexample(); diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index 567a35216..fcd280f25 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -64,6 +64,7 @@ def_module_params('fixedpoint', ('use_underapprox', BOOL, False, 'DUALITY: Use underapproximations'), ('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'), ('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'), + ('profile', BOOL, False, 'DUALITY: profile run time'), )) From de7a675afaf7c73125a716d74e64cbad0f06d743 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 5 Jun 2013 18:02:07 -0700 Subject: [PATCH 036/509] a mistake --- src/api/dotnet/Properties/AssemblyInfo.cs | 4 ++-- src/muz_qe/fixedpoint_params.pyg | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/api/dotnet/Properties/AssemblyInfo.cs b/src/api/dotnet/Properties/AssemblyInfo.cs index 517349177..1cd0fe7b8 100644 --- a/src/api/dotnet/Properties/AssemblyInfo.cs +++ b/src/api/dotnet/Properties/AssemblyInfo.cs @@ -34,6 +34,6 @@ using System.Security.Permissions; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("4.2.0.0")] -[assembly: AssemblyVersion("4.3.2.0")] -[assembly: AssemblyFileVersion("4.3.2.0")] +[assembly: AssemblyVersion("4.3.2.0")] +[assembly: AssemblyFileVersion("4.3.2.0")] diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index 567a35216..fcd280f25 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -64,6 +64,7 @@ def_module_params('fixedpoint', ('use_underapprox', BOOL, False, 'DUALITY: Use underapproximations'), ('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'), ('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'), + ('profile', BOOL, False, 'DUALITY: profile run time'), )) From f4f1c63abb4a6fd0df908e376b0ab014bce3d9da Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 6 Jun 2013 13:20:43 -0700 Subject: [PATCH 037/509] Fix issue https://z3.codeplex.com/workitem/38 Signed-off-by: Leonardo de Moura --- src/sat/sat_clause_use_list.h | 20 +++++++++++++++++--- src/sat/sat_simplifier.cpp | 27 +++++++++++++++++---------- src/sat/sat_simplifier.h | 4 ++-- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/sat/sat_clause_use_list.h b/src/sat/sat_clause_use_list.h index ccf4b4a5b..0859439d5 100644 --- a/src/sat/sat_clause_use_list.h +++ b/src/sat/sat_clause_use_list.h @@ -20,6 +20,7 @@ Revision History: #define _SAT_CLAUSE_USE_LIST_H_ #include"sat_types.h" +#include"trace.h" namespace sat { @@ -35,6 +36,7 @@ namespace sat { #endif public: clause_use_list() { + STRACE("clause_use_list_bug", tout << "[cul_created] " << this << "\n";); #ifdef LAZY_USE_LIST m_size = 0; #endif @@ -51,22 +53,33 @@ namespace sat { bool empty() const { return size() == 0; } void insert(clause & c) { - SASSERT(!m_clauses.contains(&c)); SASSERT(!c.was_removed()); + STRACE("clause_use_list_bug", tout << "[cul_insert] " << this << " " << &c << "\n";); + SASSERT(!m_clauses.contains(&c)); + SASSERT(!c.was_removed()); m_clauses.push_back(&c); #ifdef LAZY_USE_LIST m_size++; #endif } + void erase_not_removed(clause & c) { + STRACE("clause_use_list_bug", tout << "[cul_erase_not_removed] " << this << " " << &c << "\n";); #ifdef LAZY_USE_LIST - SASSERT(m_clauses.contains(&c)); SASSERT(!c.was_removed()); m_clauses.erase(&c); m_size--; + SASSERT(m_clauses.contains(&c)); + SASSERT(!c.was_removed()); + m_clauses.erase(&c); + m_size--; #else m_clauses.erase(&c); #endif } + void erase(clause & c) { + STRACE("clause_use_list_bug", tout << "[cul_erase] " << this << " " << &c << "\n";); #ifdef LAZY_USE_LIST - SASSERT(m_clauses.contains(&c)); SASSERT(c.was_removed()); m_size--; + SASSERT(m_clauses.contains(&c)); + SASSERT(c.was_removed()); + m_size--; #else m_clauses.erase(&c); #endif @@ -80,6 +93,7 @@ namespace sat { } bool check_invariant() const; + // iterate & compress class iterator { clause_vector & m_clauses; diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 623c73758..51006b2c9 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -146,8 +146,11 @@ namespace sat { m_need_cleanup = false; m_use_list.init(s.num_vars()); init_visited(); - if (learned) + bool learned_in_use_lists = false; + if (learned) { register_clauses(s.m_learned); + learned_in_use_lists = true; + } register_clauses(s.m_clauses); if (!learned && (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls)) @@ -179,7 +182,7 @@ namespace sat { if (!m_need_cleanup) { if (vars_eliminated) { // must remove learned clauses with eliminated variables - cleanup_clauses(s.m_learned, true, true); + cleanup_clauses(s.m_learned, true, true, learned_in_use_lists); } CASSERT("sat_solver", s.check_invariant()); TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); @@ -187,8 +190,8 @@ namespace sat { return; } cleanup_watches(); - cleanup_clauses(s.m_learned, true, vars_eliminated); - cleanup_clauses(s.m_clauses, false, vars_eliminated); + cleanup_clauses(s.m_learned, true, vars_eliminated, learned_in_use_lists); + cleanup_clauses(s.m_clauses, false, vars_eliminated, true); TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); CASSERT("sat_solver", s.check_invariant()); free_memory(); @@ -221,7 +224,7 @@ namespace sat { } } - void simplifier::cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated) { + void simplifier::cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists) { clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = cs.end(); @@ -245,7 +248,7 @@ namespace sat { } } - if (cleanup_clause(c)) { + if (cleanup_clause(c, in_use_lists)) { s.del_clause(c); continue; } @@ -516,7 +519,7 @@ namespace sat { Return true if the clause is satisfied */ - bool simplifier::cleanup_clause(clause & c) { + bool simplifier::cleanup_clause(clause & c, bool in_use_list) { bool r = false; unsigned sz = c.size(); unsigned j = 0; @@ -529,7 +532,11 @@ namespace sat { break; case l_false: m_need_cleanup = true; - m_use_list.get(l).erase_not_removed(c); + if (in_use_list && !c.frozen()) { + // Remark: if in_use_list is false, then the given clause was not added to the use lists. + // Remark: frozen clauses are not added to the use lists. + m_use_list.get(l).erase_not_removed(c); + } break; case l_true: r = true; @@ -611,7 +618,7 @@ namespace sat { clause_use_list & occurs = m_use_list.get(l); occurs.erase_not_removed(c); m_sub_counter -= occurs.size()/2; - if (cleanup_clause(c)) { + if (cleanup_clause(c, true /* clause is in the use lists */)) { // clause was satisfied TRACE("elim_lit", tout << "clause was satisfied\n";); remove_clause(c); @@ -805,7 +812,7 @@ namespace sat { m_sub_counter--; TRACE("subsumption", tout << "next: " << c << "\n";); if (s.m_trail.size() > m_last_sub_trail_sz) { - if (cleanup_clause(c)) { + if (cleanup_clause(c, true /* clause is in the use_lists */)) { remove_clause(c); continue; } diff --git a/src/sat/sat_simplifier.h b/src/sat/sat_simplifier.h index 44e2f0dee..96d346598 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -125,7 +125,7 @@ namespace sat { void collect_subsumed0(clause const & c1, clause_vector & out); void back_subsumption0(clause & c1); - bool cleanup_clause(clause & c); + bool cleanup_clause(clause & c, bool in_use_list); bool cleanup_clause(literal_vector & c); void propagate_unit(literal l); void elim_lit(clause & c, literal l); @@ -136,7 +136,7 @@ namespace sat { void subsume(); void cleanup_watches(); - void cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated); + void cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists); bool is_external(bool_var v) const; bool was_eliminated(bool_var v) const; From 2b59f2ecc2b0cb7621c8558cd635a2b930b71097 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 6 Jun 2013 18:29:29 -0700 Subject: [PATCH 038/509] Fix issue https://z3.codeplex.com/workitem/37 Signed-off-by: Leonardo de Moura --- src/tactic/core/ctx_simplify_tactic.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 2c2afab6f..71f4771a9 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -21,7 +21,7 @@ Notes: #include"goal_num_occurs.h" #include"cooperate.h" #include"ast_ll_pp.h" -#include"ast_smt2_pp.h" +#include"ast_pp.h" struct ctx_simplify_tactic::imp { struct cached_result { @@ -105,6 +105,7 @@ struct ctx_simplify_tactic::imp { } bool shared(expr * t) const { + TRACE("ctx_simplify_tactic_bug", tout << mk_pp(t, m) << "\n";); return t->get_ref_count() > 1 && m_occs.get_num_occs(t) > 1; } @@ -242,12 +243,13 @@ struct ctx_simplify_tactic::imp { } void assert_expr(expr * t, bool sign) { + expr * p = t; if (m.is_not(t)) { t = to_app(t)->get_arg(0); sign = !sign; } bool mk_scope = true; - if (shared(t)) { + if (shared(t) || shared(p)) { push(); mk_scope = false; assert_eq_core(t, sign ? m.mk_false() : m.mk_true()); @@ -457,7 +459,7 @@ struct ctx_simplify_tactic::imp { if (visit.is_marked(s)) { continue; } - visit.mark(s, true); + visit.mark(s, true); ++sz; for (unsigned i = 0; is_app(s) && i < to_app(s)->get_num_args(); ++i) { todo.push_back(to_app(s)->get_arg(i)); From e5c720de29f146f3380b7069b3e102a044f3a295 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 7 Jun 2013 17:36:34 +0100 Subject: [PATCH 039/509] FPA: bugfix for abs Signed-off-by: Christoph M. Wintersteiger --- src/ast/float_decl_plugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/float_decl_plugin.h b/src/ast/float_decl_plugin.h index 4ec17addf..5c18e1241 100644 --- a/src/ast/float_decl_plugin.h +++ b/src/ast/float_decl_plugin.h @@ -223,7 +223,7 @@ public: app * mk_rem(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_REM, arg1, arg2); } app * mk_max(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_MAX, arg1, arg2); } app * mk_min(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_MIN, arg1, arg2); } - app * mk_abs(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_ABS, arg1, arg2); } + app * mk_abs(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_ABS, arg1); } app * mk_sqrt(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_SQRT, arg1, arg2); } app * mk_round(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_ROUND_TO_INTEGRAL, arg1, arg2); } app * mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4) { From 123d3ec3a765ff57b449526f49808efef3a9f36b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 7 Jun 2013 17:55:29 +0100 Subject: [PATCH 040/509] New FPA operators added. Signed-off-by: Christoph M. Wintersteiger --- src/ast/float_decl_plugin.cpp | 11 ++++++++++- src/ast/float_decl_plugin.h | 8 +++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index 2a090fc39..21d4d6a86 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -255,7 +255,10 @@ func_decl * float_decl_plugin::mk_unary_rel_decl(decl_kind k, unsigned num_param case OP_FLOAT_IS_ZERO: name = "isZero"; break; case OP_FLOAT_IS_NZERO: name = "isNZero"; break; case OP_FLOAT_IS_PZERO: name = "isPZero"; break; - case OP_FLOAT_IS_SIGN_MINUS: name = "isSignMinus"; break; + case OP_FLOAT_IS_SIGN_MINUS: name = "isSignMinus"; break; + case OP_FLOAT_IS_INF: name = "isInfinite"; break; + case OP_FLOAT_IS_NORMAL: name = "isNormal"; break; + case OP_FLOAT_IS_SUBNORMAL: name = "isSubnormal"; break; default: UNREACHABLE(); break; @@ -415,6 +418,9 @@ func_decl * float_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_FLOAT_IS_NZERO: case OP_FLOAT_IS_PZERO: case OP_FLOAT_IS_SIGN_MINUS: + case OP_FLOAT_IS_INF: + case OP_FLOAT_IS_NORMAL: + case OP_FLOAT_IS_SUBNORMAL: return mk_unary_rel_decl(k, num_parameters, parameters, arity, domain, range); case OP_FLOAT_ABS: case OP_FLOAT_UMINUS: @@ -473,9 +479,12 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("<=", OP_FLOAT_LE)); op_names.push_back(builtin_name(">=", OP_FLOAT_GE)); + op_names.push_back(builtin_name("isInfinite", OP_FLOAT_IS_INF)); op_names.push_back(builtin_name("isZero", OP_FLOAT_IS_ZERO)); op_names.push_back(builtin_name("isNZero", OP_FLOAT_IS_NZERO)); op_names.push_back(builtin_name("isPZero", OP_FLOAT_IS_PZERO)); + op_names.push_back(builtin_name("isNormal", OP_FLOAT_IS_NORMAL)); + op_names.push_back(builtin_name("isSubnormal", OP_FLOAT_IS_SUBNORMAL)); op_names.push_back(builtin_name("isSignMinus", OP_FLOAT_IS_SIGN_MINUS)); op_names.push_back(builtin_name("min", OP_FLOAT_MIN)); diff --git a/src/ast/float_decl_plugin.h b/src/ast/float_decl_plugin.h index 5c18e1241..fd632a8a1 100644 --- a/src/ast/float_decl_plugin.h +++ b/src/ast/float_decl_plugin.h @@ -61,9 +61,12 @@ enum float_op_kind { OP_FLOAT_GT, OP_FLOAT_LE, OP_FLOAT_GE, + OP_FLOAT_IS_INF, OP_FLOAT_IS_ZERO, - OP_FLOAT_IS_NZERO, + OP_FLOAT_IS_NORMAL, + OP_FLOAT_IS_SUBNORMAL, OP_FLOAT_IS_PZERO, + OP_FLOAT_IS_NZERO, OP_FLOAT_IS_SIGN_MINUS, OP_TO_FLOAT, @@ -237,7 +240,10 @@ public: app * mk_le(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_LE, arg1, arg2); } app * mk_ge(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_GE, arg1, arg2); } + app * mk_is_inf(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_INF, arg1); } app * mk_is_zero(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_ZERO, arg1); } + app * mk_is_normal(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_NORMAL, arg1); } + app * mk_is_subnormal(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_SUBNORMAL, arg1); } app * mk_is_nzero(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_NZERO, arg1); } app * mk_is_pzero(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_PZERO, arg1); } app * mk_is_sign_minus(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_SIGN_MINUS, arg1); } From d7639557d2867e8cade627237fc686ff76e59580 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 7 Jun 2013 18:03:46 +0100 Subject: [PATCH 041/509] FPA: added rewriting and fpa2bv conversion rules for new operations. Signed-off-by: Christoph M. Wintersteiger --- src/ast/rewriter/float_rewriter.cpp | 33 +++++++++++++++++++++++++++++ src/ast/rewriter/float_rewriter.h | 3 +++ src/tactic/fpa/fpa2bv_converter.cpp | 15 +++++++++++++ src/tactic/fpa/fpa2bv_converter.h | 3 +++ src/tactic/fpa/fpa2bv_rewriter.h | 3 +++ 5 files changed, 57 insertions(+) diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index b43f07b65..bfcfbcbfc 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -58,6 +58,9 @@ br_status float_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c case OP_FLOAT_IS_ZERO: SASSERT(num_args == 1); st = mk_is_zero(args[0], result); break; case OP_FLOAT_IS_NZERO: SASSERT(num_args == 1); st = mk_is_nzero(args[0], result); break; case OP_FLOAT_IS_PZERO: SASSERT(num_args == 1); st = mk_is_pzero(args[0], result); break; + case OP_FLOAT_IS_INF: SASSERT(num_args == 1); st = mk_is_inf(args[0], result); break; + case OP_FLOAT_IS_NORMAL: SASSERT(num_args == 1); st = mk_is_normal(args[0], result); break; + case OP_FLOAT_IS_SUBNORMAL: SASSERT(num_args == 1); st = mk_is_subnormal(args[0], result); break; case OP_FLOAT_IS_SIGN_MINUS: SASSERT(num_args == 1); st = mk_is_sign_minus(args[0], result); break; case OP_TO_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(args[0], result); break; } @@ -428,6 +431,36 @@ br_status float_rewriter::mk_is_pzero(expr * arg1, expr_ref & result) { return BR_FAILED; } +br_status float_rewriter::mk_is_inf(expr * arg1, expr_ref & result) { + scoped_mpf v(m_util.fm()); + if (m_util.is_value(arg1, v)) { + result = (m_util.fm().is_inf(v)) ? m().mk_true() : m().mk_false(); + return BR_DONE; + } + + return BR_FAILED; +} + +br_status float_rewriter::mk_is_normal(expr * arg1, expr_ref & result) { + scoped_mpf v(m_util.fm()); + if (m_util.is_value(arg1, v)) { + result = (m_util.fm().is_normal(v)) ? m().mk_true() : m().mk_false(); + return BR_DONE; + } + + return BR_FAILED; +} + +br_status float_rewriter::mk_is_subnormal(expr * arg1, expr_ref & result) { + scoped_mpf v(m_util.fm()); + if (m_util.is_value(arg1, v)) { + result = (m_util.fm().is_denormal(v)) ? m().mk_true() : m().mk_false(); + return BR_DONE; + } + + return BR_FAILED; +} + br_status float_rewriter::mk_is_sign_minus(expr * arg1, expr_ref & result) { scoped_mpf v(m_util.fm()); if (m_util.is_value(arg1, v)) { diff --git a/src/ast/rewriter/float_rewriter.h b/src/ast/rewriter/float_rewriter.h index 7c86a5bc3..6968d96cf 100644 --- a/src/ast/rewriter/float_rewriter.h +++ b/src/ast/rewriter/float_rewriter.h @@ -66,6 +66,9 @@ public: br_status mk_is_zero(expr * arg1, expr_ref & result); br_status mk_is_nzero(expr * arg1, expr_ref & result); br_status mk_is_pzero(expr * arg1, expr_ref & result); + br_status mk_is_inf(expr * arg1, expr_ref & result); + br_status mk_is_normal(expr * arg1, expr_ref & result); + br_status mk_is_subnormal(expr * arg1, expr_ref & result); br_status mk_is_sign_minus(expr * arg1, expr_ref & result); br_status mk_to_ieee_bv(expr * arg1, expr_ref & result); diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 02aff67c6..d95709813 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1621,6 +1621,21 @@ void fpa2bv_converter::mk_is_pzero(func_decl * f, unsigned num, expr * const * a m_simp.mk_and(a0_is_pos, a0_is_zero, result); } +void fpa2bv_converter::mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 1); + mk_is_inf(args[0], result); +} + +void fpa2bv_converter::mk_is_normal(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 1); + mk_is_normal(args[0], result); +} + +void fpa2bv_converter::mk_is_subnormal(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 1); + mk_is_denormal(args[0], result); +} + void fpa2bv_converter::mk_is_sign_minus(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_neg(args[0], result); diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/tactic/fpa/fpa2bv_converter.h index 2a68342f8..9f9c74c38 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/tactic/fpa/fpa2bv_converter.h @@ -116,6 +116,9 @@ public: void mk_is_nzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_pzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_sign_minus(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_is_normal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_is_subnormal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_float(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index d8a141b62..59d068626 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -132,6 +132,9 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { case OP_FLOAT_IS_ZERO: m_conv.mk_is_zero(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_NZERO: m_conv.mk_is_nzero(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_PZERO: m_conv.mk_is_pzero(f, num, args, result); return BR_DONE; + case OP_FLOAT_IS_INF: m_conv.mk_is_inf(f, num, args, result); return BR_DONE; + case OP_FLOAT_IS_NORMAL: m_conv.mk_is_normal(f, num, args, result); return BR_DONE; + case OP_FLOAT_IS_SUBNORMAL: m_conv.mk_is_subnormal(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_SIGN_MINUS: m_conv.mk_is_sign_minus(f, num, args, result); return BR_DONE; case OP_TO_FLOAT: m_conv.mk_to_float(f, num, args, result); return BR_DONE; case OP_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE; From 455618bb2b1564eb026b0984c2fe022150efcf02 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 7 Jun 2013 18:34:31 +0100 Subject: [PATCH 042/509] FPA: added is_nan Signed-off-by: Christoph M. Wintersteiger --- src/ast/float_decl_plugin.cpp | 3 +++ src/ast/float_decl_plugin.h | 2 ++ src/ast/rewriter/float_rewriter.cpp | 11 +++++++++++ src/ast/rewriter/float_rewriter.h | 1 + src/tactic/fpa/fpa2bv_converter.cpp | 5 +++++ src/tactic/fpa/fpa2bv_converter.h | 1 + src/tactic/fpa/fpa2bv_rewriter.h | 1 + 7 files changed, 24 insertions(+) diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index 21d4d6a86..4aab6ab32 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -256,6 +256,7 @@ func_decl * float_decl_plugin::mk_unary_rel_decl(decl_kind k, unsigned num_param case OP_FLOAT_IS_NZERO: name = "isNZero"; break; case OP_FLOAT_IS_PZERO: name = "isPZero"; break; case OP_FLOAT_IS_SIGN_MINUS: name = "isSignMinus"; break; + case OP_FLOAT_IS_NAN: name = "isNaN"; break; case OP_FLOAT_IS_INF: name = "isInfinite"; break; case OP_FLOAT_IS_NORMAL: name = "isNormal"; break; case OP_FLOAT_IS_SUBNORMAL: name = "isSubnormal"; break; @@ -418,6 +419,7 @@ func_decl * float_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_FLOAT_IS_NZERO: case OP_FLOAT_IS_PZERO: case OP_FLOAT_IS_SIGN_MINUS: + case OP_FLOAT_IS_NAN: case OP_FLOAT_IS_INF: case OP_FLOAT_IS_NORMAL: case OP_FLOAT_IS_SUBNORMAL: @@ -479,6 +481,7 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("<=", OP_FLOAT_LE)); op_names.push_back(builtin_name(">=", OP_FLOAT_GE)); + op_names.push_back(builtin_name("isNaN", OP_FLOAT_IS_NAN)); op_names.push_back(builtin_name("isInfinite", OP_FLOAT_IS_INF)); op_names.push_back(builtin_name("isZero", OP_FLOAT_IS_ZERO)); op_names.push_back(builtin_name("isNZero", OP_FLOAT_IS_NZERO)); diff --git a/src/ast/float_decl_plugin.h b/src/ast/float_decl_plugin.h index fd632a8a1..f1b60a91b 100644 --- a/src/ast/float_decl_plugin.h +++ b/src/ast/float_decl_plugin.h @@ -61,6 +61,7 @@ enum float_op_kind { OP_FLOAT_GT, OP_FLOAT_LE, OP_FLOAT_GE, + OP_FLOAT_IS_NAN, OP_FLOAT_IS_INF, OP_FLOAT_IS_ZERO, OP_FLOAT_IS_NORMAL, @@ -240,6 +241,7 @@ public: app * mk_le(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_LE, arg1, arg2); } app * mk_ge(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_GE, arg1, arg2); } + app * mk_is_nan(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_NAN, arg1); } app * mk_is_inf(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_INF, arg1); } app * mk_is_zero(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_ZERO, arg1); } app * mk_is_normal(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_NORMAL, arg1); } diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index bfcfbcbfc..015bce0ed 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -58,6 +58,7 @@ br_status float_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c case OP_FLOAT_IS_ZERO: SASSERT(num_args == 1); st = mk_is_zero(args[0], result); break; case OP_FLOAT_IS_NZERO: SASSERT(num_args == 1); st = mk_is_nzero(args[0], result); break; case OP_FLOAT_IS_PZERO: SASSERT(num_args == 1); st = mk_is_pzero(args[0], result); break; + case OP_FLOAT_IS_NAN: SASSERT(num_args == 1); st = mk_is_nan(args[0], result); break; case OP_FLOAT_IS_INF: SASSERT(num_args == 1); st = mk_is_inf(args[0], result); break; case OP_FLOAT_IS_NORMAL: SASSERT(num_args == 1); st = mk_is_normal(args[0], result); break; case OP_FLOAT_IS_SUBNORMAL: SASSERT(num_args == 1); st = mk_is_subnormal(args[0], result); break; @@ -431,6 +432,16 @@ br_status float_rewriter::mk_is_pzero(expr * arg1, expr_ref & result) { return BR_FAILED; } +br_status float_rewriter::mk_is_nan(expr * arg1, expr_ref & result) { + scoped_mpf v(m_util.fm()); + if (m_util.is_value(arg1, v)) { + result = (m_util.fm().is_nan(v)) ? m().mk_true() : m().mk_false(); + return BR_DONE; + } + + return BR_FAILED; +} + br_status float_rewriter::mk_is_inf(expr * arg1, expr_ref & result) { scoped_mpf v(m_util.fm()); if (m_util.is_value(arg1, v)) { diff --git a/src/ast/rewriter/float_rewriter.h b/src/ast/rewriter/float_rewriter.h index 6968d96cf..4a0879d4b 100644 --- a/src/ast/rewriter/float_rewriter.h +++ b/src/ast/rewriter/float_rewriter.h @@ -66,6 +66,7 @@ public: br_status mk_is_zero(expr * arg1, expr_ref & result); br_status mk_is_nzero(expr * arg1, expr_ref & result); br_status mk_is_pzero(expr * arg1, expr_ref & result); + br_status mk_is_nan(expr * arg1, expr_ref & result); br_status mk_is_inf(expr * arg1, expr_ref & result); br_status mk_is_normal(expr * arg1, expr_ref & result); br_status mk_is_subnormal(expr * arg1, expr_ref & result); diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index d95709813..dd40e8453 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1621,6 +1621,11 @@ void fpa2bv_converter::mk_is_pzero(func_decl * f, unsigned num, expr * const * a m_simp.mk_and(a0_is_pos, a0_is_zero, result); } +void fpa2bv_converter::mk_is_nan(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 1); + mk_is_nan(args[0], result); +} + void fpa2bv_converter::mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_inf(args[0], result); diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/tactic/fpa/fpa2bv_converter.h index 9f9c74c38..821618bae 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/tactic/fpa/fpa2bv_converter.h @@ -116,6 +116,7 @@ public: void mk_is_nzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_pzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_sign_minus(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_is_nan(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_normal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_subnormal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index 59d068626..24e275c0d 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -132,6 +132,7 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { case OP_FLOAT_IS_ZERO: m_conv.mk_is_zero(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_NZERO: m_conv.mk_is_nzero(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_PZERO: m_conv.mk_is_pzero(f, num, args, result); return BR_DONE; + case OP_FLOAT_IS_NAN: m_conv.mk_is_nan(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_INF: m_conv.mk_is_inf(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_NORMAL: m_conv.mk_is_normal(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_SUBNORMAL: m_conv.mk_is_subnormal(f, num, args, result); return BR_DONE; From 40fe1f6e999b5fb902c035c005b4af73e727dcb0 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 7 Jun 2013 11:50:01 -0700 Subject: [PATCH 043/509] adjusting stratified inlining in duality --- src/duality/duality_solver.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 142d8e457..4519efd7a 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -42,7 +42,7 @@ Revision History: #define NEW_EXPAND #define EARLY_EXPAND // #define TOP_DOWN -#define EFFORT_BOUNDED_STRAT +// #define EFFORT_BOUNDED_STRAT #define SKIP_UNDERAPPROX_NODES @@ -793,6 +793,18 @@ namespace Duality { #endif Edge *edge = node->map->Outgoing; std::vector &chs = edge->Children; + + // make sure we don't create a covered node in this process! + + for(unsigned i = 0; i < chs.size(); i++){ + Node *child = chs[i]; + if(TopoSort[child] < TopoSort[node->map]){ + Node *leaf = LeafMap[child]; + if(!indset->Contains(leaf)) + return node->Outgoing; + } + } + std::vector nchs(chs.size()); for(unsigned i = 0; i < chs.size(); i++){ Node *child = chs[i]; @@ -808,6 +820,7 @@ namespace Duality { if(StratifiedLeafMap.find(child) == StratifiedLeafMap.end()){ RPFP::Node *nchild = CreateNodeInstance(child,StratifiedLeafCount--); MakeLeaf(nchild); + nchild->Annotation.SetEmpty(); StratifiedLeafMap[child] = nchild; indset->SetDominated(nchild); } From adb1f95e0a35b421a186435461f8f67405b1dec5 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 7 Jun 2013 11:51:22 -0700 Subject: [PATCH 044/509] small fixes in duality --- src/api/dotnet/Properties/AssemblyInfo.cs | 4 ++-- src/duality/duality_solver.cpp | 2 +- src/duality/duality_wrapper.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/api/dotnet/Properties/AssemblyInfo.cs b/src/api/dotnet/Properties/AssemblyInfo.cs index 1cd0fe7b8..517349177 100644 --- a/src/api/dotnet/Properties/AssemblyInfo.cs +++ b/src/api/dotnet/Properties/AssemblyInfo.cs @@ -34,6 +34,6 @@ using System.Security.Permissions; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("4.2.0.0")] -[assembly: AssemblyVersion("4.3.2.0")] -[assembly: AssemblyFileVersion("4.3.2.0")] +[assembly: AssemblyVersion("4.3.2.0")] +[assembly: AssemblyFileVersion("4.3.2.0")] diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 142d8e457..f743e541c 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -787,7 +787,7 @@ namespace Duality { Edge *e = unwinding->CreateLowerBoundEdge(node); // node->Annotation = save; insts_of_node[node->map].push_back(node); - std::cout << "made leaf: " << node->number << std::endl; + // std::cout << "made leaf: " << node->number << std::endl; return e; } #endif diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index fcb637a84..737773e0b 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -560,9 +560,11 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st { static stopwatch sw; static bool started = false; - if(!started) + if(!started){ sw.start(); - return sw.get_seconds(); + started = true; + } + return sw.get_current_seconds(); } } From c21cd6ffa5e477849f8a68ac330d9558886a31f9 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 7 Jun 2013 16:16:56 -0700 Subject: [PATCH 045/509] fixed model completion problem in duality --- src/duality/duality_wrapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 9477ab0fa..4a6d32695 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -679,7 +679,7 @@ namespace Duality { return *this; } - expr eval(expr const & n, bool model_completion=false) const { + expr eval(expr const & n, bool model_completion=true) const { ::model * _m = m_model.get(); expr_ref result(ctx().m()); _m->eval(n, result, model_completion); From 0210156bf0c59844a65aacb4957ce49cd65e3761 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 10 Jun 2013 10:51:56 -0400 Subject: [PATCH 046/509] add convex interior generalizer Signed-off-by: Nikolaj Bjorner --- src/muz_qe/fixedpoint_params.pyg | 1 + src/muz_qe/pdr_context.cpp | 3 + src/muz_qe/pdr_generalizers.cpp | 171 +++++++++++++++++++++++++++++++ src/muz_qe/pdr_generalizers.h | 17 +++ 4 files changed, 192 insertions(+) diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index d38c3c9b0..c2996dd8f 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -48,6 +48,7 @@ def_module_params('fixedpoint', ('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"), ('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"), ('use_arith_inductive_generalizer', BOOL, False, "PDR: generalize lemmas using arithmetic heuristics for induction strengthening"), + ('use_convex_hull_generalizer', BOOL, False, "PDR: generalize using convex hulls of lemmas"), ('cache_mode', UINT, 0, "PDR: use no (0), symbolic (1) or explicit cache (2) for model search"), ('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when " "checking for reachability (not only during cube weakening)"), diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index 76f744325..1c49e0c83 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -1572,6 +1572,9 @@ namespace pdr { } } + if (m_params.use_convex_hull_generalizer()) { + m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this)); + } if (!use_mc && m_params.use_inductive_generalizer()) { m_core_generalizers.push_back(alloc(core_bool_inductive_generalizer, *this, 0)); } diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index 1b8ea22f9..800a21eaf 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -147,6 +147,177 @@ namespace pdr { } + core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx): + core_generalizer(ctx), + m(ctx.get_manager()), + a(m), + m_sigma(m), + m_trail(m) { + m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); + m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); + } + + void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { + manager& pm = n.pt().get_pdr_manager(); + expr_ref_vector conv1(m), conv2(m), core1(m), core2(m), eqs(m); + if (core.empty()) { + return; + } + if (!m_left.contains(n.pt().head())) { + expr_ref left(m), right(m); + m_left.insert(n.pt().head(), 0); + unsigned sz = n.pt().sig_size(); + for (unsigned i = 0; i < sz; ++i) { + func_decl* fn0 = n.pt().sig(i); + sort* srt = fn0->get_range(); + if (a.is_int_real(srt)) { + func_decl* fn1 = pm.o2n(fn0, 0); + left = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); + right = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); + m_left.insert(fn1, left); + m_right.insert(fn1, right); + m_trail.push_back(left); + m_trail.push_back(right); + } + } + } + unsigned sz = n.pt().sig_size(); + for (unsigned i = 0; i < sz; ++i) { + expr* left, *right; + func_decl* fn0 = n.pt().sig(i); + func_decl* fn1 = pm.o2n(fn0, 0); + if (m_left.find(fn1, left) && m_right.find(fn1, right)) { + eqs.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(left, right))); + } + } + if (!mk_convex(core, 0, conv1)) { + IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core), m) << "\n";); + return; + } + conv1.append(eqs); + conv1.push_back(a.mk_gt(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); + conv1.push_back(a.mk_gt(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); + conv1.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(m_sigma[0].get(), m_sigma[1].get()))); + expr_ref fml = n.pt().get_formulas(n.level(), false); + expr_ref_vector fmls(m); + datalog::flatten_and(fml, fmls); + for (unsigned i = 0; i < fmls.size(); ++i) { + fml = m.mk_not(fmls[i].get()); + core2.reset(); + datalog::flatten_and(fml, core2); + if (!mk_convex(core2, 1, conv2)) { + IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core2), m) << "\n";); + continue; + } + conv2.append(conv1); + expr_ref state = pm.mk_and(conv2); + TRACE("pdr", tout << "Check:\n" << mk_pp(state, m) << "\n"; + tout << "New formula:\n" << mk_pp(pm.mk_and(core), m) << "\n"; + tout << "Old formula:\n" << mk_pp(fml, m) << "\n"; + + ); + model_node nd(0, state, n.pt(), n.level()); + if (l_false == n.pt().is_reachable(nd, &conv2, uses_level)) { + TRACE("pdr", + tout << mk_pp(state, m) << "\n"; + tout << "Generalized to:\n" << mk_pp(pm.mk_and(conv2), m) << "\n";); + IF_VERBOSE(0, + verbose_stream() << mk_pp(state, m) << "\n"; + verbose_stream() << "Generalized to:\n" << mk_pp(pm.mk_and(conv2), m) << "\n";); + core.reset(); + core.append(conv2); + } + } + } + + bool core_convex_hull_generalizer::mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv) { + conv.reset(); + for (unsigned i = 0; i < core.size(); ++i) { + mk_convex(core[i], index, conv); + } + return !conv.empty(); + } + + void core_convex_hull_generalizer::mk_convex(expr* fml, unsigned index, expr_ref_vector& conv) { + expr_ref result(m), r1(m), r2(m); + expr* e1, *e2; + bool is_not = m.is_not(fml, fml); + if (a.is_le(fml, e1, e2) && mk_convex(e1, index, false, r1) && mk_convex(e2, index, false, r2)) { + result = a.mk_le(r1, r2); + } + else if (a.is_ge(fml, e1, e2) && mk_convex(e1, index, false, r1) && mk_convex(e2, index, false, r2)) { + result = a.mk_ge(r1, r2); + } + else if (a.is_gt(fml, e1, e2) && mk_convex(e1, index, false, r1) && mk_convex(e2, index, false, r2)) { + result = a.mk_gt(r1, r2); + } + else if (a.is_lt(fml, e1, e2) && mk_convex(e1, index, false, r1) && mk_convex(e2, index, false, r2)) { + result = a.mk_lt(r1, r2); + } + else if (m.is_eq(fml, e1, e2) && a.is_int_real(e1) && mk_convex(e1, index, false, r1) && mk_convex(e2, index, false, r2)) { + result = m.mk_eq(r1, r2); + } + else { + TRACE("pdr", tout << "Did not handle " << mk_pp(fml, m) << "\n";); + return; + } + if (is_not) { + result = m.mk_not(result); + } + conv.push_back(result); + } + + + bool core_convex_hull_generalizer::translate(func_decl* f, unsigned index, expr_ref& result) { + expr* tmp; + if (index == 0 && m_left.find(f, tmp)) { + result = tmp; + return true; + } + if (index == 1 && m_right.find(f, tmp)) { + result = tmp; + return true; + } + return false; + } + + + bool core_convex_hull_generalizer::mk_convex(expr* term, unsigned index, bool is_mul, expr_ref& result) { + if (!is_app(term)) { + return false; + } + app* app = to_app(term); + expr* e1, *e2; + expr_ref r1(m), r2(m); + if (translate(app->get_decl(), index, result)) { + return true; + } + if (a.is_add(term, e1, e2) && mk_convex(e1, index, is_mul, r1) && mk_convex(e2, index, is_mul, r2)) { + result = a.mk_add(r1, r2); + return true; + } + if (a.is_sub(term, e1, e2) && mk_convex(e1, index, is_mul, r1) && mk_convex(e2, index, is_mul, r2)) { + result = a.mk_sub(r1, r2); + return true; + } + if (a.is_mul(term, e1, e2) && mk_convex(e1, index, true, r1) && mk_convex(e2, index, true, r2)) { + result = a.mk_mul(r1, r2); + return true; + } + if (a.is_numeral(term)) { + if (is_mul) { + result = term; + } + else { + result = a.mk_mul(m_sigma[index].get(), term); + } + return true; + } + IF_VERBOSE(0, verbose_stream() << "Not handled: " << mk_pp(term, m) << "\n";); + return false; + } + + // --------------------------------- // core_arith_inductive_generalizer // NB. this is trying out some ideas for generalization in diff --git a/src/muz_qe/pdr_generalizers.h b/src/muz_qe/pdr_generalizers.h index 03bd89c4d..a4be9d1fa 100644 --- a/src/muz_qe/pdr_generalizers.h +++ b/src/muz_qe/pdr_generalizers.h @@ -73,6 +73,23 @@ namespace pdr { virtual void collect_statistics(statistics& st) const; }; + class core_convex_hull_generalizer : public core_generalizer { + ast_manager& m; + arith_util a; + expr_ref_vector m_sigma; + expr_ref_vector m_trail; + obj_map m_left; + obj_map m_right; + bool mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv); + void mk_convex(expr* fml, unsigned index, expr_ref_vector& conv); + bool mk_convex(expr* term, unsigned index, bool is_mul, expr_ref& result); + bool translate(func_decl* fn, unsigned index, expr_ref& result); + public: + core_convex_hull_generalizer(context& ctx); + virtual ~core_convex_hull_generalizer() {} + virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); + }; + class core_multi_generalizer : public core_generalizer { core_bool_inductive_generalizer m_gen; public: From 30a4627a1eab8e49418c39a030472e8a477805e3 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 10 Jun 2013 14:46:15 -0700 Subject: [PATCH 047/509] fixed problem with nullary background constants in duality --- src/duality/duality.h | 34 ++++++++++++++++++++++++++++++---- src/duality/duality_rpfp.cpp | 16 ++++++++++++---- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index f514e045f..f7ef4eb7a 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -79,10 +79,6 @@ namespace Duality { int CumulativeDecisions(); - Term SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t); - - Term SubstBound(hash_map &subst, const Term &t); - private: void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); @@ -162,6 +158,12 @@ namespace Duality { bool weak = false ) = 0; + /** Declare a constant in the background theory. */ + virtual void declare_constant(const func_decl &f) = 0; + + /** Is this a background constant? */ + virtual bool is_constant(const func_decl &f) = 0; + /** Assert a background axiom. */ virtual void assert_axiom(const expr &axiom) = 0; @@ -224,10 +226,24 @@ namespace Duality { islvr->write_interpolation_problem(file_name,assumptions,theory); #endif } + + /** Declare a constant in the background theory. */ + virtual void declare_constant(const func_decl &f){ + bckg.insert(f); + } + + /** Is this a background constant? */ + virtual bool is_constant(const func_decl &f){ + return bckg.find(f) != bckg.end(); + } + ~iZ3LogicSolver(){ // delete ictx; delete islvr; } + private: + hash_set bckg; + }; #if 0 @@ -478,6 +494,10 @@ namespace Duality { void AssertNode(Node *n); + /** Declare a constant in the background theory. */ + + void DeclareConstant(const FuncDecl &f); + /** Assert a background axiom. Background axioms can be used to provide the * theory of auxilliary functions or relations. All symbols appearing in * background axioms are considered global, and may appear in both transformer @@ -771,6 +791,12 @@ namespace Duality { void GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, hash_set *done, bool truth); + + Term SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t); + + Term SubstBound(hash_map &subst, const Term &t); + + }; /** RPFP solver base class. */ diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 623697880..71a5d47e0 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -205,7 +205,7 @@ namespace Duality { if(rit != e->relMap.end()) res = RedDualRela(e,args,(rit->second)); else { - if (args.size() == 0 && f.get_decl_kind() == Uninterpreted) + if (args.size() == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)) { res = HideVariable(t,e->number); } @@ -520,6 +520,12 @@ namespace Duality { } } + /** Declare a constant in the background theory. */ + + void RPFP::DeclareConstant(const FuncDecl &f){ + ls->declare_constant(f); + } + /** Assert a background axiom. Background axioms can be used to provide the * theory of auxilliary functions or relations. All symbols appearing in * background axioms are considered global, and may appear in both transformer @@ -1828,7 +1834,7 @@ namespace Duality { return RemoveLabelsRec(memo,t,lbls); } - Z3User::Term Z3User::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) + RPFP::Term RPFP::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) { std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo[level].insert(foo); @@ -1839,6 +1845,8 @@ namespace Duality { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); + if(nargs == 0) + 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))); res = f(args.size(),&args[0]); @@ -1858,7 +1866,7 @@ namespace Duality { return res; } - Z3User::Term Z3User::SubstBound(hash_map &subst, const Term &t){ + RPFP::Term RPFP::SubstBound(hash_map &subst, const Term &t){ hash_map > memo; return SubstBoundRec(memo, subst, 0, t); } @@ -2037,7 +2045,7 @@ namespace Duality { int nargs = t.num_args(); for(int i = 0; i < nargs; i++) WriteEdgeVars(e, memo, t.arg(i),s); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ + if (nargs == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)){ Term rename = HideVariable(t,e->number); Term value = dualModel.eval(rename); s << " (= " << t << " " << value << ")\n"; From 6184c5fdbcad3ad666f8220cdebcae4601b03bd1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Jun 2013 15:29:22 -0400 Subject: [PATCH 048/509] reorder attibutes to match initialization order Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_instruction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz_qe/dl_instruction.cpp index 6b16968c2..637d9a17f 100644 --- a/src/muz_qe/dl_instruction.cpp +++ b/src/muz_qe/dl_instruction.cpp @@ -528,9 +528,9 @@ namespace datalog { class instr_filter_interpreted_and_project : public instruction { reg_idx m_src; - reg_idx m_res; app_ref m_cond; unsigned_vector m_cols; + reg_idx m_res; public: instr_filter_interpreted_and_project(reg_idx src, app_ref & condition, unsigned col_cnt, const unsigned * removed_cols, reg_idx result) From 2c8b314a15ba1bfc79e508b0266df1405782656b Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 13 Jun 2013 13:34:20 -0700 Subject: [PATCH 049/509] Fix issue https://z3.codeplex.com/workitem/48 Signed-off-by: Leonardo de Moura --- src/sat/sat_simplifier.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 623c73758..47c4b6150 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -625,7 +625,8 @@ namespace sat { case 1: TRACE("elim_lit", tout << "clause became unit: " << c[0] << "\n";); propagate_unit(c[0]); - remove_clause(c); + // propagate_unit will delete c. + // remove_clause(c); return; case 2: TRACE("elim_lit", tout << "clause became binary: " << c[0] << " " << c[1] << "\n";); @@ -816,7 +817,8 @@ namespace sat { } if (sz == 1) { propagate_unit(c[0]); - remove_clause(c); + // propagate_unit will delete c. + // remove_clause(c); continue; } if (sz == 2) { From 40b1137b30da4914c233001a704928662567f60d Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 13 Jun 2013 13:45:14 -0700 Subject: [PATCH 050/509] Fix issue https://z3.codeplex.com/workitem/47 Signed-off-by: Leonardo de Moura --- src/sat/sat_asymm_branch.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sat/sat_asymm_branch.cpp b/src/sat/sat_asymm_branch.cpp index 81d631e4e..b8ac520b2 100644 --- a/src/sat/sat_asymm_branch.cpp +++ b/src/sat/sat_asymm_branch.cpp @@ -72,11 +72,14 @@ namespace sat { int limit = -static_cast(m_asymm_branch_limit); std::stable_sort(s.m_clauses.begin(), s.m_clauses.end(), clause_size_lt()); m_counter -= s.m_clauses.size(); + SASSERT(s.m_qhead == s.m_trail.size()); clause_vector::iterator it = s.m_clauses.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = s.m_clauses.end(); try { for (; it != end; ++it) { + if (s.inconsistent()) + break; SASSERT(s.m_qhead == s.m_trail.size()); if (m_counter < limit || s.inconsistent()) { *it2 = *it; @@ -111,6 +114,7 @@ namespace sat { bool asymm_branch::process(clause & c) { TRACE("asymm_branch_detail", tout << "processing: " << c << "\n";); SASSERT(s.scope_lvl() == 0); + SASSERT(s.m_qhead == s.m_trail.size()); #ifdef Z3DEBUG unsigned trail_sz = s.m_trail.size(); #endif @@ -143,6 +147,7 @@ namespace sat { SASSERT(!s.inconsistent()); SASSERT(s.scope_lvl() == 0); SASSERT(trail_sz == s.m_trail.size()); + SASSERT(s.m_qhead == s.m_trail.size()); if (i == sz - 1) { // clause size can't be reduced. s.attach_clause(c); @@ -181,15 +186,18 @@ namespace sat { s.assign(c[0], justification()); s.del_clause(c); s.propagate_core(false); + SASSERT(s.inconsistent() || s.m_qhead == s.m_trail.size()); return false; // check_missed_propagation() may fail, since m_clauses is not in a consistent state. case 2: SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); s.mk_bin_clause(c[0], c[1], false); s.del_clause(c); + SASSERT(s.m_qhead == s.m_trail.size()); return false; default: c.shrink(new_sz); s.attach_clause(c); + SASSERT(s.m_qhead == s.m_trail.size()); return true; } } From 1a26c9726bafca21cdba95f7a9f709ec087f43a0 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 14 Jun 2013 13:15:48 +0100 Subject: [PATCH 051/509] .NET API: bugfix Signed-off-by: Christoph M. Wintersteiger --- src/api/dotnet/Solver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index 274f8dc4a..555a2466f 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -210,7 +210,7 @@ namespace Microsoft.Z3 public Status Check(params Expr[] assumptions) { Z3_lbool r; - if (assumptions == null) + if (assumptions == null || assumptions.Length == 0) r = (Z3_lbool)Native.Z3_solver_check(Context.nCtx, NativeObject); else r = (Z3_lbool)Native.Z3_solver_check_assumptions(Context.nCtx, NativeObject, (uint)assumptions.Length, AST.ArrayToNative(assumptions)); From 76c59cb85c0637c32989f40a85c8c5364afb3e8f Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 14 Jun 2013 17:22:25 +0100 Subject: [PATCH 052/509] MPF conversion bugfix. Signed-off-by: Christoph M. Wintersteiger --- src/ast/rewriter/float_rewriter.cpp | 12 ++++++++---- src/util/mpf.cpp | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index 015bce0ed..367fa3a4c 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -83,17 +83,21 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c rational q; mpf q_mpf; if (m_util.au().is_numeral(args[1], q)) { + TRACE("fp_rewriter", tout << "q: " << q << std::endl; ); mpf v; m_util.fm().set(v, ebits, sbits, rm, q.to_mpq()); result = m_util.mk_value(v); - m_util.fm().del(v); + m_util.fm().del(v); + TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } - else if (m_util.is_value(args[1], q_mpf)) { + else if (m_util.is_value(args[1], q_mpf)) { + TRACE("fp_rewriter", tout << "q: " << m_util.fm().to_string(q_mpf) << std::endl; ); mpf v; m_util.fm().set(v, ebits, sbits, rm, q_mpf); result = m_util.mk_value(v); m_util.fm().del(v); + TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } else @@ -117,11 +121,11 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c return BR_FAILED; TRACE("fp_rewriter", tout << "q: " << q << ", e: " << e << "\n";); - mpf v; m_util.fm().set(v, ebits, sbits, rm, q.to_mpq(), e.to_mpq().numerator()); result = m_util.mk_value(v); - m_util.fm().del(v); + m_util.fm().del(v); + TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } else { diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index def9f4fd5..735d21c99 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -367,7 +367,7 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode o.ebits = ebits; o.sbits = sbits; - signed ds = sbits - x.sbits + 4; // plus rounding bits + signed ds = sbits - x.sbits + 3; // plus rounding bits if (ds > 0) { m_mpz_manager.mul2k(o.significand, ds); From 92c1b2597869f1bb65cdf68559b45f02127bbb0f Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 14 Jun 2013 20:14:00 +0100 Subject: [PATCH 053/509] FPA: bugfix for float to float conversion (subnormal numbers). Thanks to Gabriele Paganelli for reporting this bug! Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index dd40e8453..53ac5a58f 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1698,6 +1698,10 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); + dbg_decouple("fpa2bv_to_float_x_sig", sig); + dbg_decouple("fpa2bv_to_float_x_exp", exp); + dbg_decouple("fpa2bv_to_float_lz", lz); + expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = sgn; @@ -1710,10 +1714,13 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == from_sbits); SASSERT(m_bv_util.get_bv_size(exp) == from_ebits); + SASSERT(m_bv_util.get_bv_size(lz) == from_ebits); - if (from_sbits < (to_sbits + 3)) + if (from_sbits < (to_sbits + 3)) + { // make sure that sig has at least to_sbits + 3 res_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, to_sbits+3-from_sbits)); + } else if (from_sbits > (to_sbits + 3)) { // collapse the extra bits into a sticky bit. @@ -1734,7 +1741,9 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a exponent_overflow = m.mk_false(); if (from_ebits < (to_ebits + 2)) - res_exp = m_bv_util.mk_sign_extend(to_ebits+2-from_ebits, exp); + { + res_exp = m_bv_util.mk_sign_extend(to_ebits-from_ebits+2, exp); + } else if (from_ebits > (to_ebits + 2)) { expr_ref high(m), low(m), lows(m), high_red_or(m), high_red_and(m), h_or_eq(m), h_and_eq(m); @@ -1747,7 +1756,7 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a high_red_and = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, high.get()); zero1 = m_bv_util.mk_numeral(0, 1); - m_simp.mk_eq(high_red_and, one1, h_and_eq); + m_simp.mk_eq(high_red_and, one1, h_and_eq); m_simp.mk_eq(high_red_or, zero1, h_or_eq); m_simp.mk_eq(lows, zero1, s_is_zero); m_simp.mk_eq(lows, one1, s_is_one); @@ -1763,10 +1772,18 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a else res_exp = exp; + // subtract lz for subnormal numbers. + expr_ref lz_ext(m); + lz_ext = m_bv_util.mk_zero_extend(to_ebits-from_ebits+2, lz); + res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits+2); + dbg_decouple("fpa2bv_to_float_res_sig", res_sig); + dbg_decouple("fpa2bv_to_float_res_exp", res_exp); + expr_ref rounded(m); round(s, rm, res_sgn, res_sig, res_exp, rounded); + expr_ref is_neg(m), sig_inf(m); m_simp.mk_eq(sgn, one1, is_neg); @@ -1781,7 +1798,7 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); - mk_ite(c1, v1, result, result); + mk_ite(c1, v1, result, result); } else { // .. other than that, we only support rationals for asFloat @@ -1826,7 +1843,7 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a m_util.fm().del(v); } - SASSERT(is_well_sorted(m, result)); + SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -2161,7 +2178,7 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef _DEBUG - return; + // CMW: This works only for quantifier-free formulas. expr_ref new_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); From ecceb0accc455fcb0ee6eaf95ad33a91e2783b0c Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 14 Jun 2013 20:16:02 +0100 Subject: [PATCH 054/509] FPA: debug output disabled. Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 53ac5a58f..0913fbbcf 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -2178,7 +2178,7 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef _DEBUG - + return; // CMW: This works only for quantifier-free formulas. expr_ref new_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); From 886128c98996e5217e8d34ea13c8f48552c7dacd Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 14 Jun 2013 16:33:51 -0700 Subject: [PATCH 055/509] hooked up array.weak and array.extension params --- src/smt/params/smt_params.cpp | 2 ++ src/smt/params/smt_params_helper.pyg | 5 ++++- src/smt/params/theory_array_params.h | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index 519302c19..f1c407dc7 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -40,6 +40,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_arith_pivot_strategy = ARITH_PIVOT_GREATEST_ERROR; else if (_p.get_bool("arith.least_error_pivot", false)) m_arith_pivot_strategy = ARITH_PIVOT_LEAST_ERROR; + theory_array_params::updt_params(_p); } void smt_params::updt_params(params_ref const & p) { @@ -47,6 +48,7 @@ void smt_params::updt_params(params_ref const & p) { qi_params::updt_params(p); theory_arith_params::updt_params(p); theory_bv_params::updt_params(p); + // theory_array_params::updt_params(p); updt_local_params(p); } diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 43dd1b586..33c9a11ec 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -40,4 +40,7 @@ def_module_params(module_name='smt', ('arith.propagation_mode', UINT, 2, '0 - no propagation, 1 - propagate existing literals, 2 - refine bounds'), ('arith.branch_cut_ratio', UINT, 2, 'branch/cut ratio for linear integer arithmetic'), ('arith.int_eq_branch', BOOL, False, 'branching using derived integer equations'), - ('arith.ignore_int', BOOL, False, 'treat integer variables as real'))) + ('arith.ignore_int', BOOL, False, 'treat integer variables as real'), + ('array.weak', BOOL, False, 'weak array theory'), + ('array.extensional', BOOL, True, 'extensional array theory') + )) diff --git a/src/smt/params/theory_array_params.h b/src/smt/params/theory_array_params.h index c1ae8ce32..a022bcadf 100644 --- a/src/smt/params/theory_array_params.h +++ b/src/smt/params/theory_array_params.h @@ -51,6 +51,9 @@ struct theory_array_params : public array_simplifier_params { m_array_lazy_ieq_delay(10) { } + + void updt_params(params_ref const & _p); + #if 0 void register_params(ini_params & p) { p.register_int_param("array_solver", 0, 3, reinterpret_cast(m_array_mode), "0 - no array, 1 - simple, 2 - model based, 3 - full"); From a78564145b683e9d5b800f6821d5e1a440a1e114 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 14 Jun 2013 16:46:13 -0700 Subject: [PATCH 056/509] hooked up array.weak and array.extension params --- src/smt/params/theory_array_params.cpp | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/smt/params/theory_array_params.cpp diff --git a/src/smt/params/theory_array_params.cpp b/src/smt/params/theory_array_params.cpp new file mode 100644 index 000000000..e3c8b2448 --- /dev/null +++ b/src/smt/params/theory_array_params.cpp @@ -0,0 +1,28 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + theory_array_params.cpp + +Abstract: + + + +Author: + + Leonardo de Moura (leonardo) 2008-05-06. + +Revision History: + +--*/ +#include"theory_array_params.h" +#include"smt_params_helper.hpp" + +void theory_array_params::updt_params(params_ref const & _p) { + smt_params_helper p(_p); + m_array_weak = p.array_weak(); + m_array_extensional = p.array_extensional(); +} + + From 64acd9cac05ccb307da4cd73314a40f446340524 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 17 Jun 2013 18:04:23 -0700 Subject: [PATCH 057/509] fixed some bugs with quantifiers in rule bodies --- src/duality/duality_rpfp.cpp | 12 ++++++++++-- src/duality/duality_wrapper.cpp | 24 ++++++++++++++++++++++++ src/duality/duality_wrapper.h | 4 ++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 71a5d47e0..330799796 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -217,8 +217,12 @@ namespace Duality { } else if (t.is_quantifier()) { + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = LocalizeRec(e,memo,pats[i]); Term body = LocalizeRec(e,memo,t.body()); - res = CloneQuantifier(t,body); + res = clone_quantifier(t, body, pats); } else res = t; return res; @@ -1853,7 +1857,11 @@ namespace Duality { } else if (t.is_quantifier()){ int bound = t.get_quantifier_num_bound(); - res = CloneQuantifier(t,SubstBoundRec(memo, subst, level + bound, t.body())); + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = SubstBoundRec(memo, subst, level + bound, pats[i]); + res = clone_quantifier(t, SubstBoundRec(memo, subst, level + bound, t.body()), pats); } else if (t.is_var()) { int idx = t.get_index_value(); diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index fcb637a84..67768d3c8 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -326,6 +326,26 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st return q.ctx().cook(q.m().update_quantifier(to_quantifier(q.raw()), to_expr(b.raw()))); } + expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns){ + quantifier *thing = to_quantifier(q.raw()); + bool is_forall = thing->is_forall(); + unsigned num_patterns = patterns.size(); + std::vector< ::expr *> _patterns(num_patterns); + for(unsigned i = 0; i < num_patterns; i++) + _patterns[i] = to_expr(patterns[i].raw()); + return q.ctx().cook(q.m().update_quantifier(thing, is_forall, num_patterns, &_patterns[0], to_expr(b.raw()))); + } + + void expr::get_patterns(std::vector &pats) const { + quantifier *thing = to_quantifier(raw()); + unsigned num_patterns = thing->get_num_patterns(); + :: expr * const *it = thing->get_patterns(); + pats.resize(num_patterns); + for(unsigned i = 0; i < num_patterns; i++) + pats[i] = expr(ctx(),it[i]); + } + + func_decl context::fresh_func_decl(char const * prefix, const std::vector &domain, sort const & range){ std::vector < ::sort * > _domain(domain.size()); for(unsigned i = 0; i < domain.size(); i++) @@ -547,6 +567,10 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st a.show(); } + void show_ast(::ast *a, ast_manager &m) { + std::cout << mk_pp(a, m) << std::endl; + } + bool expr::is_label (bool &pos,std::vector &names) const { buffer< ::symbol> _names; bool res = m().is_label(to_expr(raw()),pos,_names); diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 4a6d32695..18fcc3e9f 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -556,11 +556,15 @@ namespace Duality { friend expr clone_quantifier(const expr &, const expr &); + friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); + friend std::ostream & operator<<(std::ostream & out, expr const & m){ m.ctx().print_expr(out,m); return out; } + void get_patterns(std::vector &pats) const ; + unsigned get_quantifier_num_bound() const { return to_quantifier(raw())->get_num_decls(); } From 0f13ec6e42912c6380ce8fdb5f0c8ef1370e40b7 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 18 Jun 2013 12:28:20 -0700 Subject: [PATCH 058/509] adding timeout to duality --- src/duality/duality.h | 14 ++++++++++++++ src/duality/duality_solver.cpp | 4 ++++ src/duality/duality_wrapper.cpp | 1 + src/duality/duality_wrapper.h | 17 +++++++++++++++-- src/muz_qe/dl_context.cpp | 1 + src/muz_qe/duality_dl_interface.cpp | 10 +++++++++- 6 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index f7ef4eb7a..166e8ef0d 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -174,6 +174,10 @@ namespace Duality { const std::vector &assumptions, const std::vector &theory ){} + + /** Cancel, throw Canceled object if possible. */ + virtual void cancel(){ } + virtual ~LogicSolver(){} }; @@ -225,8 +229,11 @@ namespace Duality { #if 0 islvr->write_interpolation_problem(file_name,assumptions,theory); #endif + } + void cancel(){islvr->cancel();} + /** Declare a constant in the background theory. */ virtual void declare_constant(const func_decl &f){ bckg.insert(f); @@ -835,6 +842,13 @@ namespace Duality { static Solver *Create(const std::string &solver_class, RPFP *rpfp); + /** This can be called asynchrnously to cause Solve to throw a + Canceled exception at some time in the future. + */ + virtual void Cancel() = 0; + /** Object thrown on cancellation */ + struct Canceled {}; + }; } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 4519efd7a..897c88c30 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -282,6 +282,10 @@ namespace Duality { return res; } + void Cancel(){ + // TODO + } + #if 0 virtual void Restart(RPFP *_rpfp){ rpfp = _rpfp; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 67768d3c8..567c81820 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -36,6 +36,7 @@ namespace Duality { p.set_bool("unsat_core", true); scoped_ptr sf = mk_smt_solver_factory(); m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); + canceled = false; } expr context::constant(const std::string &name, const sort &ty){ diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 18fcc3e9f..1048734f7 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -773,10 +773,11 @@ namespace Duality { protected: ::solver *m_solver; model the_model; + bool canceled; public: solver(context & c); - solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; } - solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver;} + solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} + solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver; canceled = false;} ~solver() { if(m_solver) dealloc(m_solver); @@ -788,12 +789,18 @@ namespace Duality { the_model = s.the_model; return *this; } + struct cancel_exception {}; + void checkpoint(){ + if(canceled) + throw(cancel_exception()); + } // void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } void push() { m_solver->push(); } void pop(unsigned n = 1) { m_solver->pop(n); } // void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } void add(expr const & e) { m_solver->assert_expr(e); } check_result check() { + checkpoint(); lbool r = m_solver->check_sat(0,0); model_ref m; m_solver->get_model(m); @@ -808,6 +815,7 @@ namespace Duality { return res; } check_result check(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { + checkpoint(); std::vector< ::expr *> _assumptions(n); for (unsigned i = 0; i < n; i++) { _assumptions[i] = to_expr(assumptions[i]); @@ -854,6 +862,11 @@ namespace Duality { int get_num_decisions(); + void cancel(){ + canceled = true; + if(m_solver) + m_solver->cancel(); + } }; #if 0 diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index c171b66e4..70610f04c 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -937,6 +937,7 @@ namespace datalog { if (m_bmc.get()) m_bmc->cancel(); if (m_rel.get()) m_rel->cancel(); if (m_tab.get()) m_tab->cancel(); + if (m_duality.get()) m_duality->cancel(); } void context::cleanup() { diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index e59baa4c5..f0a5c5b6b 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -201,7 +201,13 @@ lbool dl_interface::query(::expr * query) { } // Solve! - bool ans = rs->Solve(); + bool ans; + try { + ans = rs->Solve(); + } + catch (Duality::solver::cancel_exception &exn){ + throw default_exception("duality canceled"); + } // profile! @@ -361,6 +367,8 @@ expr_ref dl_interface::get_answer() { } void dl_interface::cancel() { + if(_d && _d->ls) + _d->ls->cancel(); } void dl_interface::cleanup() { From cd485f03dd7118a9990013779febb8432f39bf66 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 20 Jun 2013 17:02:15 -0700 Subject: [PATCH 059/509] Add trace msg Signed-off-by: Leonardo de Moura --- src/api/z3_replayer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index 079516145..acdb10bf6 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -26,6 +26,7 @@ Notes: void register_z3_replayer_cmds(z3_replayer & in); void throw_invalid_reference() { + TRACE("z3_replayer", tout << "invalid argument reference\n";); throw z3_replayer_exception("invalid argument reference"); } From 185f125f7a3d356192df272bfb2339ad36d3cdf9 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 20 Jun 2013 17:48:43 -0700 Subject: [PATCH 060/509] Fix problem reported at http://stackoverflow.com/questions/17215640/getting-concrete-values-from-a-model-containing-array-ext Signed-off-by: Leonardo de Moura --- src/smt/smt_model_finder.cpp | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index f3d0ca3bb..c4a48d9f8 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -94,7 +94,7 @@ namespace smt { } obj_map const & get_elems() const { return m_elems; } - + void insert(expr * n, unsigned generation) { if (m_elems.contains(n)) return; @@ -102,6 +102,14 @@ namespace smt { m_elems.insert(n, generation); SASSERT(!m_manager.is_model_value(n)); } + + void remove(expr * n) { + // We can only remove n if it is in m_elems, AND m_inv was not initialized yet. + SASSERT(m_elems.contains(n)); + SASSERT(m_inv.empty()); + m_elems.erase(n); + m_manager.dec_ref(n); + } void display(std::ostream & out) const { obj_map::iterator it = m_elems.begin(); @@ -525,6 +533,30 @@ namespace smt { } } + // For each instantiation_set, reemove entries that do not evaluate to values. + void cleanup_instantiation_sets() { + ptr_vector to_delete; + ptr_vector::const_iterator it = m_nodes.begin(); + ptr_vector::const_iterator end = m_nodes.end(); + for (; it != end; ++it) { + node * curr = *it; + if (curr->is_root()) { + instantiation_set * s = curr->get_instantiation_set(); + to_delete.reset(); + obj_map const & elems = s->get_elems(); + for (obj_map::iterator it = elems.begin(); it != elems.end(); it++) { + expr * n = it->m_key; + expr * n_val = eval(n, true); + if (!m_manager.is_value(n_val)) + to_delete.push_back(n); + } + for (ptr_vector::iterator it = to_delete.begin(); it != to_delete.end(); it++) { + s->remove(*it); + } + } + } + } + void display_nodes(std::ostream & out) const { display_key2node(out, m_uvars); display_A_f_is(out); @@ -545,6 +577,7 @@ namespace smt { r = 0; else r = tmp; + TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n----->\n" << mk_pp(r, m_manager) << "\n";); m_eval_cache.insert(n, r); m_eval_cache_range.push_back(r); return r; @@ -1047,6 +1080,7 @@ namespace smt { public: void fix_model(expr_ref_vector & new_constraints) { + cleanup_instantiation_sets(); m_new_constraints = &new_constraints; func_decl_set partial_funcs; collect_partial_funcs(partial_funcs); From a60b53bfd8dff26011e8bfd7bbca7801bf645dbf Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 20 Jun 2013 17:52:20 -0700 Subject: [PATCH 061/509] Fix compilation errors/warnings when using GCC Signed-off-by: Leonardo de Moura --- src/ast/rewriter/float_rewriter.cpp | 6 +++--- src/tactic/fpa/fpa2bv_rewriter.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index 367fa3a4c..0a4c3fc4e 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -88,7 +88,7 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c m_util.fm().set(v, ebits, sbits, rm, q.to_mpq()); result = m_util.mk_value(v); m_util.fm().del(v); - TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); + // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } else if (m_util.is_value(args[1], q_mpf)) { @@ -97,7 +97,7 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c m_util.fm().set(v, ebits, sbits, rm, q_mpf); result = m_util.mk_value(v); m_util.fm().del(v); - TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); + // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } else @@ -125,7 +125,7 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c m_util.fm().set(v, ebits, sbits, rm, q.to_mpq(), e.to_mpq().numerator()); result = m_util.mk_value(v); m_util.fm().del(v); - TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); + // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } else { diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index 24e275c0d..d737683a8 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -226,7 +226,7 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { if (t->get_idx() >= m_bindings.size()) return false; - unsigned inx = m_bindings.size() - t->get_idx() - 1; + // unsigned inx = m_bindings.size() - t->get_idx() - 1; expr_ref new_exp(m()); sort * s = t->get_sort(); From 42898f327671aa71e259a2966d78c45432a7b18a Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 21 Jun 2013 10:31:11 -0700 Subject: [PATCH 062/509] Fix bug reported by Florian Signed-off-by: Leonardo de Moura --- src/nlsat/nlsat_explain.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index be60fb052..50466c276 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -993,8 +993,22 @@ namespace nlsat { } return; } - else if (s == -1 && !is_even) { - atom_sign = -atom_sign; + else { + // We have shown the current factor is a constant MODULO the sign of the leading coefficient (of the equation used to rewrite the factor). + if (!info.m_lc_const) { + // If the leading coefficient is not a constant, we must store this information as an extra assumption. + if (d % 2 == 0 || // d is even + is_even || // rewriting a factor of even degree, sign flip doesn't matter + _a->get_kind() == atom::EQ) { // rewriting an equation, sign flip doesn't matter + info.add_lc_diseq(); + } + else { + info.add_lc_ineq(); + } + } + if (s == -1 && !is_even) { + atom_sign = -atom_sign; + } } } else { From 0b6250253a847ae6ba5c99240e8502879f7a1436 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 21 Jun 2013 21:16:03 +0100 Subject: [PATCH 063/509] FPA2BV: added sqrt function (Currently, there are a few corner cases where it doesn't round correctly.) Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 137 +++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 0913fbbcf..d085ce277 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1390,7 +1390,142 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar } void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - NOT_IMPLEMENTED_YET(); + SASSERT(num == 2); + + expr_ref rm(m), x(m); + rm = args[0]; + x = args[1]; + + expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); + mk_nan(f, nan); + mk_nzero(f, nzero); + mk_pzero(f, pzero); + mk_minus_inf(f, ninf); + mk_plus_inf(f, pinf); + + expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); + mk_is_nan(x, x_is_nan); + mk_is_zero(x, x_is_zero); + mk_is_pos(x, x_is_pos); + mk_is_inf(x, x_is_inf); + + expr_ref zero1(m), one1(m); + zero1 = m_bv_util.mk_numeral(0, 1); + one1 = m_bv_util.mk_numeral(1, 1); + + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); + + // (x is NaN) -> NaN + c1 = x_is_nan; + v1 = x; + + // (x is +oo) -> +oo + mk_is_pinf(x, c2); + v2 = x; + + // (x is +-0) -> +-0 + mk_is_zero(x, c3); + v3 = x; + + // (x < 0) -> NaN + mk_is_neg(x, c4); + v4 = nan; + + // else comes the actual square root. + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + SASSERT(ebits <= sbits); + + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + + dbg_decouple("fpa2bv_sqrt_sig", a_sig); + dbg_decouple("fpa2bv_sqrt_exp", a_exp); + + SASSERT(m_bv_util.get_bv_size(a_sig) == sbits); + SASSERT(m_bv_util.get_bv_size(a_exp) == ebits); + + expr_ref res_sgn(m), res_sig(m), res_exp(m); + + res_sgn = zero1; + + expr_ref real_exp(m); + real_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(1, a_exp), m_bv_util.mk_zero_extend(1, a_lz)); + res_exp = m_bv_util.mk_sign_extend(2, m_bv_util.mk_extract(ebits, 1, real_exp)); + + expr_ref e_is_odd(m); + e_is_odd = m.mk_eq(m_bv_util.mk_extract(0, 0, real_exp), one1); + + dbg_decouple("fpa2bv_sqrt_e_is_odd", e_is_odd); + dbg_decouple("fpa2bv_sqrt_real_exp", real_exp); + + expr_ref sig_prime(m); + m_simp.mk_ite(e_is_odd, m_bv_util.mk_concat(a_sig, zero1), + m_bv_util.mk_concat(zero1, a_sig), + sig_prime); + SASSERT(m_bv_util.get_bv_size(sig_prime) == sbits+1); + dbg_decouple("fpa2bv_sqrt_sig_prime", sig_prime); + + // This is algorithm 10.2 in the Handbook of Floating-Point Arithmetic + expr_ref Q(m), R(m), S(m), T(m); + + const mpz & p2 = fu().fm().m_powers2(sbits-1); + Q = m_bv_util.mk_numeral(p2, sbits+2); + R = m_bv_util.mk_bv_sub(m_bv_util.mk_concat(zero1, sig_prime), Q); + S = Q; + + for (unsigned i = 0; i < sbits; i++) { + dbg_decouple("fpa2bv_sqrt_Q", Q); + dbg_decouple("fpa2bv_sqrt_R", R); + + S = m_bv_util.mk_concat(zero1, m_bv_util.mk_extract(sbits+1, 1, S)); + + expr_ref twoQ_plus_S(m); + twoQ_plus_S = m_bv_util.mk_bv_add(m_bv_util.mk_concat(Q, zero1), m_bv_util.mk_concat(zero1, S)); + T = m_bv_util.mk_bv_sub(m_bv_util.mk_concat(R, zero1), twoQ_plus_S); + + dbg_decouple("fpa2bv_sqrt_T", T); + + SASSERT(m_bv_util.get_bv_size(Q) == sbits + 2); + SASSERT(m_bv_util.get_bv_size(R) == sbits + 2); + SASSERT(m_bv_util.get_bv_size(S) == sbits + 2); + SASSERT(m_bv_util.get_bv_size(T) == sbits + 3); + + expr_ref t_lt_0(m); + m_simp.mk_eq(m_bv_util.mk_extract(sbits+2, sbits+2, T), one1, t_lt_0); + + m_simp.mk_ite(t_lt_0, Q, + m_bv_util.mk_bv_add(Q, S), + Q); + m_simp.mk_ite(t_lt_0, m_bv_util.mk_concat(m_bv_util.mk_extract(sbits, 0, R), zero1), + m_bv_util.mk_extract(sbits+1, 0, T), + R); + } + + expr_ref rest(m), last(m), q_is_odd(m), rest_ext(m); + last = m_bv_util.mk_extract(0, 0, Q); + rest = m_bv_util.mk_extract(sbits, 1, Q); + m_simp.mk_eq(last, one1, q_is_odd); + dbg_decouple("fpa2bv_sqrt_q_is_odd", q_is_odd); + rest_ext = m_bv_util.mk_concat(rest, m_bv_util.mk_numeral(0, 4)); + m_simp.mk_ite(q_is_odd, m_bv_util.mk_bv_add(rest_ext, m_bv_util.mk_numeral(8, sbits+4)), + rest_ext, + res_sig); + + SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); + + expr_ref rounded(m); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); + v5 = rounded; + + // And finally, we tie them together. + mk_ite(c4, v4, v5, result); + mk_ite(c3, v3, result, result); + mk_ite(c2, v2, result, result); + mk_ite(c1, v1, result, result); + + SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { From 5b5a474b5443b9d7a6e5ca4ad0a61e8cc87166f1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 21 Jun 2013 16:23:37 -0700 Subject: [PATCH 064/509] experiment with point-based generalization method Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_generalizers.cpp | 129 +++++++++++++++++++++++++------- src/muz_qe/pdr_generalizers.h | 4 + 2 files changed, 105 insertions(+), 28 deletions(-) diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index 800a21eaf..5e777bf7e 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -156,40 +156,19 @@ namespace pdr { m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); } - + void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { + method2(n, core, uses_level); + } + + // use the entire region as starting point for generalization. + void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector& core, bool& uses_level) { manager& pm = n.pt().get_pdr_manager(); expr_ref_vector conv1(m), conv2(m), core1(m), core2(m), eqs(m); if (core.empty()) { return; } - if (!m_left.contains(n.pt().head())) { - expr_ref left(m), right(m); - m_left.insert(n.pt().head(), 0); - unsigned sz = n.pt().sig_size(); - for (unsigned i = 0; i < sz; ++i) { - func_decl* fn0 = n.pt().sig(i); - sort* srt = fn0->get_range(); - if (a.is_int_real(srt)) { - func_decl* fn1 = pm.o2n(fn0, 0); - left = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); - right = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); - m_left.insert(fn1, left); - m_right.insert(fn1, right); - m_trail.push_back(left); - m_trail.push_back(right); - } - } - } - unsigned sz = n.pt().sig_size(); - for (unsigned i = 0; i < sz; ++i) { - expr* left, *right; - func_decl* fn0 = n.pt().sig(i); - func_decl* fn1 = pm.o2n(fn0, 0); - if (m_left.find(fn1, left) && m_right.find(fn1, right)) { - eqs.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(left, right))); - } - } + add_variables(n, eqs); if (!mk_convex(core, 0, conv1)) { IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core), m) << "\n";); return; @@ -230,6 +209,100 @@ namespace pdr { } } + // take as starting point two points from different regions. + void core_convex_hull_generalizer::method2(model_node& n, expr_ref_vector& core, bool& uses_level) { + expr_ref_vector conv1(m), conv2(m), core1(m), core2(m); + if (core.empty()) { + return; + } + manager& pm = n.pt().get_pdr_manager(); + smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); + expr_ref goal(pm.mk_and(core)); + ctx.assert_expr(goal); + lbool r = ctx.check(); + if (r != l_true) { + IF_VERBOSE(0, verbose_stream() << "unexpected result from satisfiability check\n";); + return; + } + add_variables(n, conv1); + model_ref mdl; + ctx.get_model(mdl); + + unsigned sz = n.pt().sig_size(); + for (unsigned i = 0; i < sz; ++i) { + expr_ref_vector constr(m); + expr* left, *right; + func_decl* fn0 = n.pt().sig(i); + func_decl* fn1 = pm.o2n(fn0, 0); + if (m_left.find(fn1, left) && m_right.find(fn1, right)) { + expr_ref val(m); + mdl->eval(fn1, val); + if (val) { + conv1.push_back(m.mk_eq(left, val)); + constr.push_back(m.mk_eq(right, val)); + } + } + expr_ref new_model = pm.mk_and(constr); + m_trail.push_back(new_model); + m_trail.push_back(goal); + m_models.insert(goal, new_model); + } + conv1.push_back(a.mk_gt(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); + conv1.push_back(a.mk_gt(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); + conv1.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(m_sigma[0].get(), m_sigma[1].get()))); + + obj_map::iterator it = m_models.begin(), end = m_models.end(); + for (; it != end; ++it) { + if (it->m_key == goal) { + continue; + } + conv1.push_back(it->m_value); + expr_ref state = pm.mk_and(conv1); + TRACE("pdr", tout << "Try:\n" << mk_pp(state, m) << "\n";); + model_node nd(0, state, n.pt(), n.level()); + if (l_false == n.pt().is_reachable(nd, &conv2, uses_level)) { + IF_VERBOSE(0, + verbose_stream() << mk_pp(state, m) << "\n"; + verbose_stream() << "Generalized to:\n" << mk_pp(pm.mk_and(conv2), m) << "\n";); + core.reset(); + core.append(conv2); + return; + } + conv1.pop_back(); + } + } + + void core_convex_hull_generalizer::add_variables(model_node& n, expr_ref_vector& eqs) { + manager& pm = n.pt().get_pdr_manager(); + if (!m_left.contains(n.pt().head())) { + expr_ref left(m), right(m); + m_left.insert(n.pt().head(), 0); + unsigned sz = n.pt().sig_size(); + for (unsigned i = 0; i < sz; ++i) { + func_decl* fn0 = n.pt().sig(i); + sort* srt = fn0->get_range(); + if (a.is_int_real(srt)) { + func_decl* fn1 = pm.o2n(fn0, 0); + left = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); + right = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); + m_left.insert(fn1, left); + m_right.insert(fn1, right); + m_trail.push_back(left); + m_trail.push_back(right); + } + } + } + unsigned sz = n.pt().sig_size(); + for (unsigned i = 0; i < sz; ++i) { + expr* left, *right; + func_decl* fn0 = n.pt().sig(i); + func_decl* fn1 = pm.o2n(fn0, 0); + if (m_left.find(fn1, left) && m_right.find(fn1, right)) { + eqs.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(left, right))); + } + } + } + bool core_convex_hull_generalizer::mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv) { conv.reset(); for (unsigned i = 0; i < core.size(); ++i) { diff --git a/src/muz_qe/pdr_generalizers.h b/src/muz_qe/pdr_generalizers.h index a4be9d1fa..0aee94c16 100644 --- a/src/muz_qe/pdr_generalizers.h +++ b/src/muz_qe/pdr_generalizers.h @@ -80,10 +80,14 @@ namespace pdr { expr_ref_vector m_trail; obj_map m_left; obj_map m_right; + obj_map m_models; bool mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv); void mk_convex(expr* fml, unsigned index, expr_ref_vector& conv); bool mk_convex(expr* term, unsigned index, bool is_mul, expr_ref& result); bool translate(func_decl* fn, unsigned index, expr_ref& result); + void method1(model_node& n, expr_ref_vector& core, bool& uses_level); + void method2(model_node& n, expr_ref_vector& core, bool& uses_level); + void add_variables(model_node& n, expr_ref_vector& eqs); public: core_convex_hull_generalizer(context& ctx); virtual ~core_convex_hull_generalizer() {} From 13206f2fe723346cb002b33863750d8a6cacc465 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 24 Jun 2013 13:29:04 +0100 Subject: [PATCH 065/509] FPA: FMA bugfixes. Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 79 +++++++++++++++-------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index d085ce277..041d42da1 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1270,6 +1270,11 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar SASSERT(is_well_sorted(m, f_sig)); SASSERT(is_well_sorted(m, f_exp)); + SASSERT(m_bv_util.get_bv_size(e_sig) == 2 * sbits); + SASSERT(m_bv_util.get_bv_size(f_sig) == 2 * sbits); + SASSERT(m_bv_util.get_bv_size(e_exp) == ebits + 2); + SASSERT(m_bv_util.get_bv_size(f_exp) == ebits + 2); + expr_ref res_sgn(m), res_sig(m), res_exp(m); expr_ref exp_delta(m); @@ -1278,43 +1283,39 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar // cap the delta expr_ref cap(m), cap_le_delta(m); - cap = m_bv_util.mk_numeral(sbits+3, ebits+2); - cap_le_delta = m_bv_util.mk_ule(exp_delta, cap); + cap = m_bv_util.mk_numeral(2*sbits, ebits+2); + cap_le_delta = m_bv_util.mk_ule(cap, exp_delta); m_simp.mk_ite(cap_le_delta, cap, exp_delta, exp_delta); SASSERT(m_bv_util.get_bv_size(exp_delta) == ebits+2); + SASSERT(is_well_sorted(m, exp_delta)); dbg_decouple("fpa2bv_fma_add_exp_delta_capped", exp_delta); - // Alignment shift with sticky bit computation. - expr_ref big_f_sig(m); - big_f_sig = m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits+4)); - SASSERT(is_well_sorted(m, big_f_sig)); - + // Alignment shift with sticky bit computation. expr_ref shifted_big(m), shifted_f_sig(m), sticky_raw(m); - shifted_big = m_bv_util.mk_bv_lshr(big_f_sig, m_bv_util.mk_concat(m_bv_util.mk_numeral(0, (2*(sbits+4))-(ebits+2)), exp_delta)); - shifted_f_sig = m_bv_util.mk_extract((2*(sbits+4)-1), (sbits+4), shifted_big); - SASSERT(is_well_sorted(m, shifted_f_sig)); - - sticky_raw = m_bv_util.mk_extract(sbits+3, 0, shifted_big); + shifted_big = m_bv_util.mk_bv_lshr(f_sig, m_bv_util.mk_zero_extend((2*sbits)-(ebits+2), exp_delta)); + shifted_f_sig = m_bv_util.mk_zero_extend(sbits, m_bv_util.mk_extract(2*sbits-1, sbits, shifted_big)); + sticky_raw = m_bv_util.mk_extract(sbits-1, 0, shifted_big); + SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2 * sbits); + SASSERT(is_well_sorted(m, shifted_f_sig)); + expr_ref sticky(m), sticky_eq(m), nil_sbit4(m), one_sbit4(m); - nil_sbit4 = m_bv_util.mk_numeral(0, sbits+4); - one_sbit4 = m_bv_util.mk_numeral(1, sbits+4); - m_simp.mk_eq(sticky_raw, nil_sbit4, sticky_eq); - m_simp.mk_ite(sticky_eq, nil_sbit4, one_sbit4, sticky); + sticky = m_bv_util.mk_zero_extend(2*sbits-1, m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sticky_raw)); SASSERT(is_well_sorted(m, sticky)); expr * or_args[2] = { shifted_f_sig, sticky }; shifted_f_sig = m_bv_util.mk_bv_or(2, or_args); SASSERT(is_well_sorted(m, shifted_f_sig)); - expr_ref eq_sgn(m); - m_simp.mk_eq(e_sgn, f_sgn, eq_sgn); - - // two extra bits for catching the overflow. + // Significant addition. + // Two extra bits for catching the overflow. e_sig = m_bv_util.mk_zero_extend(2, e_sig); shifted_f_sig = m_bv_util.mk_zero_extend(2, shifted_f_sig); - SASSERT(m_bv_util.get_bv_size(e_sig) == sbits+6); - SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == sbits+6); + expr_ref eq_sgn(m); + m_simp.mk_eq(e_sgn, f_sgn, eq_sgn); + + SASSERT(m_bv_util.get_bv_size(e_sig) == 2*sbits + 2); + SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2*sbits + 2); dbg_decouple("fpa2bv_fma_add_e_sig", e_sig); dbg_decouple("fpa2bv_fma_add_shifted_f_sig", shifted_f_sig); @@ -1330,14 +1331,22 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar dbg_decouple("fpa2bv_fma_add_sum", sum); expr_ref sign_bv(m), n_sum(m); - sign_bv = m_bv_util.mk_extract(sbits+4, sbits+4, sum); + sign_bv = m_bv_util.mk_extract(2*sbits+1, 2*sbits+1, sum); n_sum = m_bv_util.mk_bv_neg(sum); + expr_ref res_sig_eq(m), sig_abs(m), one_1(m); + one_1 = m_bv_util.mk_numeral(1, 1); + m_simp.mk_eq(sign_bv, one_1, res_sig_eq); + m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); dbg_decouple("fpa2bv_fma_add_sign_bv", sign_bv); dbg_decouple("fpa2bv_fma_add_n_sum", n_sum); - - family_id bvfid = m_bv_util.get_fid(); + dbg_decouple("fpa2bv_fma_add_sig_abs", sig_abs); + res_exp = m_bv_util.mk_bv_sub(e_exp, c_lz_ext); + + // Result could overflow into 4.xxx ... + + family_id bvfid = m_bv_util.get_fid(); expr_ref res_sgn_c1(m), res_sgn_c2(m), res_sgn_c3(m); expr_ref not_e_sgn(m), not_f_sgn(m), not_sign_bv(m); not_e_sgn = m_bv_util.mk_bv_not(e_sgn); @@ -1348,21 +1357,15 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar res_sgn_c3 = m.mk_app(bvfid, OP_BAND, e_sgn, f_sgn); expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); - - expr_ref res_sig_eq(m), sig_abs(m), one_1(m); - one_1 = m_bv_util.mk_numeral(1, 1); - m_simp.mk_eq(sign_bv, one_1, res_sig_eq); - m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); - - dbg_decouple("fpa2bv_fma_add_sig_abs", sig_abs); - - res_sig = m_bv_util.mk_extract(sbits+3, 0, sig_abs); - res_exp = m_bv_util.mk_bv_sub(e_exp, c_lz_ext); + + sticky_raw = m_bv_util.mk_extract(sbits-5, 0, sig_abs); + sticky = m_bv_util.mk_zero_extend(sbits+7, m.mk_app(bvfid, OP_BREDOR, sticky_raw)); + res_sig = m_bv_util.mk_extract(2*sbits-1, sbits-4, sig_abs); + SASSERT(m_bv_util.get_bv_size(res_sig) == sbits+4); expr_ref is_zero_sig(m), nil_sbits4(m); nil_sbits4 = m_bv_util.mk_numeral(0, sbits+4); m_simp.mk_eq(res_sig, nil_sbits4, is_zero_sig); - SASSERT(is_well_sorted(m, is_zero_sig)); dbg_decouple("fpa2bv_fma_is_zero_sig", is_zero_sig); @@ -1511,7 +1514,7 @@ void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, rest_ext = m_bv_util.mk_concat(rest, m_bv_util.mk_numeral(0, 4)); m_simp.mk_ite(q_is_odd, m_bv_util.mk_bv_add(rest_ext, m_bv_util.mk_numeral(8, sbits+4)), rest_ext, - res_sig); + res_sig); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); @@ -2313,7 +2316,7 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef _DEBUG - return; + //return; // CMW: This works only for quantifier-free formulas. expr_ref new_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); From 9581055f973cb9980cf2a369f3667253ad549a79 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 24 Jun 2013 13:30:36 +0100 Subject: [PATCH 066/509] FPA: debug output disabled Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 041d42da1..d8e39e9fb 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -2316,7 +2316,7 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef _DEBUG - //return; + return; // CMW: This works only for quantifier-free formulas. expr_ref new_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); From 127402c10b661585721367a2d20d827518688802 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 24 Jun 2013 16:33:09 +0100 Subject: [PATCH 067/509] FPA: fpa2bv fma bugfix Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index d8e39e9fb..cb1048325 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1113,7 +1113,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar mk_nzero(f, nzero); mk_pzero(f, pzero); mk_minus_inf(f, ninf); - mk_plus_inf(f, pinf); + mk_plus_inf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); @@ -1227,6 +1227,13 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); c_exp_ext = m_bv_util.mk_sign_extend(2, c_exp); + dbg_decouple("fpa2bv_fma_a_sig", a_sig_ext); + dbg_decouple("fpa2bv_fma_b_sig", b_sig_ext); + dbg_decouple("fpa2bv_fma_c_sig", c_sig); + dbg_decouple("fpa2bv_fma_a_exp", a_exp_ext); + dbg_decouple("fpa2bv_fma_b_exp", b_exp_ext); + dbg_decouple("fpa2bv_fma_c_exp", c_exp_ext); + expr_ref mul_sgn(m), mul_sig(m), mul_exp(m); expr * signs[2] = { a_sgn, b_sgn }; @@ -1254,6 +1261,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar expr_ref swap_cond(m); swap_cond = m_bv_util.mk_sle(mul_exp, c_exp_ext); SASSERT(is_well_sorted(m, swap_cond)); + dbg_decouple("fpa2bv_fma_swap_cond", swap_cond); expr_ref e_sgn(m), e_sig(m), e_exp(m), f_sgn(m), f_sig(m), f_exp(m); m_simp.mk_ite(swap_cond, c_sgn, mul_sgn, e_sgn); @@ -1292,15 +1300,19 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar // Alignment shift with sticky bit computation. expr_ref shifted_big(m), shifted_f_sig(m), sticky_raw(m); - shifted_big = m_bv_util.mk_bv_lshr(f_sig, m_bv_util.mk_zero_extend((2*sbits)-(ebits+2), exp_delta)); - shifted_f_sig = m_bv_util.mk_zero_extend(sbits, m_bv_util.mk_extract(2*sbits-1, sbits, shifted_big)); + shifted_big = m_bv_util.mk_bv_lshr( + m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), + m_bv_util.mk_zero_extend((3*sbits)-(ebits+2), exp_delta)); + shifted_f_sig = m_bv_util.mk_zero_extend(sbits, m_bv_util.mk_extract(3*sbits-1, 2*sbits, shifted_big)); sticky_raw = m_bv_util.mk_extract(sbits-1, 0, shifted_big); SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2 * sbits); SASSERT(is_well_sorted(m, shifted_f_sig)); - expr_ref sticky(m), sticky_eq(m), nil_sbit4(m), one_sbit4(m); + expr_ref sticky(m); sticky = m_bv_util.mk_zero_extend(2*sbits-1, m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sticky_raw)); SASSERT(is_well_sorted(m, sticky)); + dbg_decouple("fpa2bv_fma_f_sig_sticky_raw", sticky_raw); + dbg_decouple("fpa2bv_fma_f_sig_sticky", sticky); expr * or_args[2] = { shifted_f_sig, sticky }; shifted_f_sig = m_bv_util.mk_bv_or(2, or_args); From 205520ed6cd3b79925bffbfcb6eae000c018c025 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 24 Jun 2013 15:34:42 -0700 Subject: [PATCH 068/509] Move AssemblyInfo.cs AssemblyInfo. Update mk_util.py to generate AssemblyInfo.cs instead of modifying it. Signed-off-by: Leonardo de Moura --- scripts/mk_util.py | 44 ++++++------------- .../{AssemblyInfo.cs => AssemblyInfo} | 0 2 files changed, 13 insertions(+), 31 deletions(-) rename src/api/dotnet/Properties/{AssemblyInfo.cs => AssemblyInfo} (100%) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 06120e2c4..38213a88b 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1785,7 +1785,7 @@ def update_version(): raise MKException("set_version(major, minor, build, revision) must be used before invoking update_version()") if not ONLY_MAKEFILES: mk_version_dot_h(major, minor, build, revision) - update_all_assembly_infos(major, minor, build, revision) + mk_all_assembly_infos(major, minor, build, revision) mk_def_files() # Update files with the version number @@ -1800,49 +1800,32 @@ def mk_version_dot_h(major, minor, build, revision): if VERBOSE: print("Generated '%s'" % os.path.join(c.src_dir, 'version.h')) -# Update version number in AssemblyInfo.cs files -def update_all_assembly_infos(major, minor, build, revision): +# Generate AssemblyInfo.cs files with the right version numbers by using AssemblyInfo files as a template +def mk_all_assembly_infos(major, minor, build, revision): for c in get_components(): if c.has_assembly_info(): - assembly = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo.cs') + assembly = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo') if os.path.exists(assembly): # It is a CS file - update_assembly_info_version(assembly, - major, minor, build, revision, False) + mk_assembly_info_version(assembly, major, minor, build, revision) else: - assembly = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo.cs') - if os.path.exists(assembly): - # It is a cpp file - update_assembly_info_version(assembly, - major, minor, build, revision, True) - else: - raise MKException("Failed to find assembly info file at '%s'" % os.path.join(c.src_dir, c.assembly_info_dir)) + raise MKException("Failed to find assembly info file 'AssemblyInfo' at '%s'" % os.path.join(c.src_dir, c.assembly_info_dir)) -# Update version number in the given AssemblyInfo.cs files -def update_assembly_info_version(assemblyinfo, major, minor, build, revision, is_cpp=False): - if is_cpp: - ver_pat = re.compile('[assembly:AssemblyVersionAttribute\("[\.\d]*"\) *') - fver_pat = re.compile('[assembly:AssemblyFileVersionAttribute\("[\.\d]*"\) *') - else: - ver_pat = re.compile('[assembly: AssemblyVersion\("[\.\d]*"\) *') - fver_pat = re.compile('[assembly: AssemblyFileVersion\("[\.\d]*"\) *') +# Generate version number in the given 'AssemblyInfo.cs' file using 'AssemblyInfo' as a template. +def mk_assembly_info_version(assemblyinfo, major, minor, build, revision): + ver_pat = re.compile('[assembly: AssemblyVersion\("[\.\d]*"\) *') + fver_pat = re.compile('[assembly: AssemblyFileVersion\("[\.\d]*"\) *') fin = open(assemblyinfo, 'r') - tmp = '%s.new' % assemblyinfo + tmp = '%s.cs' % assemblyinfo fout = open(tmp, 'w') num_updates = 0 for line in fin: if ver_pat.match(line): - if is_cpp: - fout.write('[assembly:AssemblyVersionAttribute("%s.%s.%s.%s")];\n' % (major, minor, build, revision)) - else: - fout.write('[assembly: AssemblyVersion("%s.%s.%s.%s")]\n' % (major, minor, build, revision)) + fout.write('[assembly: AssemblyVersion("%s.%s.%s.%s")]\n' % (major, minor, build, revision)) num_updates = num_updates + 1 elif fver_pat.match(line): - if is_cpp: - fout.write('[assembly:AssemblyFileVersionAttribute("%s.%s.%s.%s")];\n' % (major, minor, build, revision)) - else: - fout.write('[assembly: AssemblyFileVersion("%s.%s.%s.%s")]\n' % (major, minor, build, revision)) + fout.write('[assembly: AssemblyFileVersion("%s.%s.%s.%s")]\n' % (major, minor, build, revision)) num_updates = num_updates + 1 else: fout.write(line) @@ -1851,7 +1834,6 @@ def update_assembly_info_version(assemblyinfo, major, minor, build, revision, is assert num_updates == 2, "unexpected number of version number updates" fin.close() fout.close() - shutil.move(tmp, assemblyinfo) if VERBOSE: print("Updated '%s'" % assemblyinfo) diff --git a/src/api/dotnet/Properties/AssemblyInfo.cs b/src/api/dotnet/Properties/AssemblyInfo similarity index 100% rename from src/api/dotnet/Properties/AssemblyInfo.cs rename to src/api/dotnet/Properties/AssemblyInfo From efb6b2453e55203900b21731ada58d7a42c8cfe6 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 24 Jun 2013 15:34:42 -0700 Subject: [PATCH 069/509] Move AssemblyInfo.cs AssemblyInfo. Update mk_util.py to generate AssemblyInfo.cs instead of modifying it. Signed-off-by: Leonardo de Moura --- scripts/mk_util.py | 44 ++++------- .../{AssemblyInfo.cs => AssemblyInfo} | 74 +++++++++---------- 2 files changed, 50 insertions(+), 68 deletions(-) rename src/api/dotnet/Properties/{AssemblyInfo.cs => AssemblyInfo} (97%) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 8116da3f5..4a042d37f 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1822,7 +1822,7 @@ def update_version(): raise MKException("set_version(major, minor, build, revision) must be used before invoking update_version()") if not ONLY_MAKEFILES: mk_version_dot_h(major, minor, build, revision) - update_all_assembly_infos(major, minor, build, revision) + mk_all_assembly_infos(major, minor, build, revision) mk_def_files() # Update files with the version number @@ -1837,49 +1837,32 @@ def mk_version_dot_h(major, minor, build, revision): if VERBOSE: print("Generated '%s'" % os.path.join(c.src_dir, 'version.h')) -# Update version number in AssemblyInfo.cs files -def update_all_assembly_infos(major, minor, build, revision): +# Generate AssemblyInfo.cs files with the right version numbers by using AssemblyInfo files as a template +def mk_all_assembly_infos(major, minor, build, revision): for c in get_components(): if c.has_assembly_info(): - assembly = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo.cs') + assembly = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo') if os.path.exists(assembly): # It is a CS file - update_assembly_info_version(assembly, - major, minor, build, revision, False) + mk_assembly_info_version(assembly, major, minor, build, revision) else: - assembly = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo.cs') - if os.path.exists(assembly): - # It is a cpp file - update_assembly_info_version(assembly, - major, minor, build, revision, True) - else: - raise MKException("Failed to find assembly info file at '%s'" % os.path.join(c.src_dir, c.assembly_info_dir)) + raise MKException("Failed to find assembly info file 'AssemblyInfo' at '%s'" % os.path.join(c.src_dir, c.assembly_info_dir)) -# Update version number in the given AssemblyInfo.cs files -def update_assembly_info_version(assemblyinfo, major, minor, build, revision, is_cpp=False): - if is_cpp: - ver_pat = re.compile('[assembly:AssemblyVersionAttribute\("[\.\d]*"\) *') - fver_pat = re.compile('[assembly:AssemblyFileVersionAttribute\("[\.\d]*"\) *') - else: - ver_pat = re.compile('[assembly: AssemblyVersion\("[\.\d]*"\) *') - fver_pat = re.compile('[assembly: AssemblyFileVersion\("[\.\d]*"\) *') +# Generate version number in the given 'AssemblyInfo.cs' file using 'AssemblyInfo' as a template. +def mk_assembly_info_version(assemblyinfo, major, minor, build, revision): + ver_pat = re.compile('[assembly: AssemblyVersion\("[\.\d]*"\) *') + fver_pat = re.compile('[assembly: AssemblyFileVersion\("[\.\d]*"\) *') fin = open(assemblyinfo, 'r') - tmp = '%s.new' % assemblyinfo + tmp = '%s.cs' % assemblyinfo fout = open(tmp, 'w') num_updates = 0 for line in fin: if ver_pat.match(line): - if is_cpp: - fout.write('[assembly:AssemblyVersionAttribute("%s.%s.%s.%s")];\n' % (major, minor, build, revision)) - else: - fout.write('[assembly: AssemblyVersion("%s.%s.%s.%s")]\n' % (major, minor, build, revision)) + fout.write('[assembly: AssemblyVersion("%s.%s.%s.%s")]\n' % (major, minor, build, revision)) num_updates = num_updates + 1 elif fver_pat.match(line): - if is_cpp: - fout.write('[assembly:AssemblyFileVersionAttribute("%s.%s.%s.%s")];\n' % (major, minor, build, revision)) - else: - fout.write('[assembly: AssemblyFileVersion("%s.%s.%s.%s")]\n' % (major, minor, build, revision)) + fout.write('[assembly: AssemblyFileVersion("%s.%s.%s.%s")]\n' % (major, minor, build, revision)) num_updates = num_updates + 1 else: fout.write(line) @@ -1888,7 +1871,6 @@ def update_assembly_info_version(assemblyinfo, major, minor, build, revision, is assert num_updates == 2, "unexpected number of version number updates" fin.close() fout.close() - shutil.move(tmp, assemblyinfo) if VERBOSE: print("Updated '%s'" % assemblyinfo) diff --git a/src/api/dotnet/Properties/AssemblyInfo.cs b/src/api/dotnet/Properties/AssemblyInfo similarity index 97% rename from src/api/dotnet/Properties/AssemblyInfo.cs rename to src/api/dotnet/Properties/AssemblyInfo index 1cd0fe7b8..1ac6cb520 100644 --- a/src/api/dotnet/Properties/AssemblyInfo.cs +++ b/src/api/dotnet/Properties/AssemblyInfo @@ -1,39 +1,39 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security.Permissions; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Z3 .NET Interface")] -[assembly: AssemblyDescription(".NET Interface to the Z3 Theorem Prover")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft Corporation")] -[assembly: AssemblyProduct("Z3")] -[assembly: AssemblyCopyright("Copyright © Microsoft Corporation 2006")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4853ed71-2078-40f4-8117-bc46646bce0e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("4.2.0.0")] +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Permissions; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Z3 .NET Interface")] +[assembly: AssemblyDescription(".NET Interface to the Z3 Theorem Prover")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyProduct("Z3")] +[assembly: AssemblyCopyright("Copyright © Microsoft Corporation 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4853ed71-2078-40f4-8117-bc46646bce0e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("4.2.0.0")] [assembly: AssemblyVersion("4.3.2.0")] [assembly: AssemblyFileVersion("4.3.2.0")] - + From 74792eeec41a5cae57fc1dd94a9235ad581a5051 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 25 Jun 2013 15:06:13 +0100 Subject: [PATCH 070/509] FPA: compilation bugfixes --- src/tactic/fpa/fpa2bv_converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index d8e39e9fb..b527af780 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1299,7 +1299,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar SASSERT(is_well_sorted(m, shifted_f_sig)); expr_ref sticky(m), sticky_eq(m), nil_sbit4(m), one_sbit4(m); - sticky = m_bv_util.mk_zero_extend(2*sbits-1, m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sticky_raw)); + sticky = m_bv_util.mk_zero_extend(2*sbits-1, m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sticky_raw.get())); SASSERT(is_well_sorted(m, sticky)); expr * or_args[2] = { shifted_f_sig, sticky }; @@ -1359,7 +1359,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); sticky_raw = m_bv_util.mk_extract(sbits-5, 0, sig_abs); - sticky = m_bv_util.mk_zero_extend(sbits+7, m.mk_app(bvfid, OP_BREDOR, sticky_raw)); + sticky = m_bv_util.mk_zero_extend(sbits+7, m.mk_app(bvfid, OP_BREDOR, sticky_raw.get())); res_sig = m_bv_util.mk_extract(2*sbits-1, sbits-4, sig_abs); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits+4); From 67aaec872a9d27859312790c9cc36bf72d4464c0 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 25 Jun 2013 18:27:53 +0100 Subject: [PATCH 071/509] Java API: status bugfix. Thanks to user Bauna for reporting this issue (#50). Signed-off-by: Christoph M. Wintersteiger --- src/api/java/Status.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/java/Status.java b/src/api/java/Status.java index 117618426..e37631070 100644 --- a/src/api/java/Status.java +++ b/src/api/java/Status.java @@ -12,7 +12,7 @@ package com.microsoft.z3; public enum Status { // / Used to signify an unsatisfiable status. - UNSATISFIABLE(1), + UNSATISFIABLE(-1), // / Used to signify an unknown status. UNKNOWN(0), From 324dc5869da434d84d8d09cf360d36e260456896 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Jun 2013 13:07:28 -0500 Subject: [PATCH 072/509] fix substitution bug in qe, working on boogie trace Signed-off-by: Nikolaj Bjorner --- src/api/api_datalog.cpp | 7 +- src/api/z3_api.h | 6 +- src/ast/rewriter/expr_safe_replace.cpp | 7 + src/ast/rewriter/expr_safe_replace.h | 2 + src/muz_qe/clp_context.cpp | 1 + src/muz_qe/clp_context.h | 17 +- src/muz_qe/dl_bmc_engine.cpp | 1 + src/muz_qe/dl_bmc_engine.h | 2 +- src/muz_qe/dl_boogie_proof.cpp | 297 ++++++++++++++++++ src/muz_qe/dl_boogie_proof.h | 87 ++++++ src/muz_qe/dl_cmds.cpp | 4 +- src/muz_qe/dl_compiler.cpp | 8 +- src/muz_qe/dl_context.cpp | 331 ++++++--------------- src/muz_qe/dl_context.h | 35 +-- src/muz_qe/dl_instruction.cpp | 2 +- src/muz_qe/dl_mk_backwards.cpp | 2 +- src/muz_qe/dl_mk_explanations.cpp | 6 +- src/muz_qe/dl_mk_karr_invariants.cpp | 10 +- src/muz_qe/dl_mk_karr_invariants.h | 1 + src/muz_qe/dl_mk_magic_sets.cpp | 2 +- src/muz_qe/dl_mk_partial_equiv.cpp | 2 +- src/muz_qe/dl_mk_rule_inliner.cpp | 5 +- src/muz_qe/dl_mk_similarity_compressor.cpp | 2 +- src/muz_qe/dl_mk_simple_joins.cpp | 8 +- src/muz_qe/dl_mk_subsumption_checker.cpp | 6 +- src/muz_qe/dl_mk_unbound_compressor.cpp | 6 +- src/muz_qe/dl_relation_manager.cpp | 62 +++- src/muz_qe/dl_relation_manager.h | 1 + src/muz_qe/dl_util.h | 38 +++ src/muz_qe/fixedpoint_params.pyg | 2 + src/muz_qe/pdr_context.cpp | 29 +- src/muz_qe/pdr_dl_interface.cpp | 1 + src/muz_qe/pdr_dl_interface.h | 29 +- src/muz_qe/proof_utils.cpp | 2 + src/muz_qe/proof_utils.h | 1 + src/muz_qe/qe_arith_plugin.cpp | 39 ++- src/muz_qe/qe_array_plugin.cpp | 12 +- src/muz_qe/qe_bool_plugin.cpp | 16 +- src/muz_qe/qe_bv_plugin.cpp | 10 +- src/muz_qe/qe_datatype_plugin.cpp | 24 +- src/muz_qe/qe_dl_plugin.cpp | 14 +- src/muz_qe/rel_context.cpp | 3 +- src/muz_qe/rel_context.h | 9 +- src/muz_qe/tab_context.cpp | 1 + src/muz_qe/tab_context.h | 17 +- src/shell/datalog_frontend.cpp | 12 +- src/smt/theory_array_base.cpp | 4 +- 47 files changed, 769 insertions(+), 414 deletions(-) create mode 100644 src/muz_qe/dl_boogie_proof.cpp create mode 100644 src/muz_qe/dl_boogie_proof.h diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index f6eadfea0..bf4752645 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -48,8 +48,11 @@ namespace api { if (!m.has_plugin(name)) { m.register_plugin(name, alloc(datalog::dl_decl_plugin)); } - datalog::relation_manager& r = m_context.get_rel_context().get_rmanager(); - r.register_plugin(alloc(datalog::external_relation_plugin, *this, r)); + datalog::rel_context* rel = m_context.get_rel_context(); + if (rel) { + datalog::relation_manager& r = rel->get_rmanager(); + r.register_plugin(alloc(datalog::external_relation_plugin, *this, r)); + } } void fixedpoint_context::reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 6d59cf650..458e2f53f 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -778,11 +778,11 @@ typedef enum } or \nicebox{ - (=> (and ln+1 ln+2 .. ln+m) l0) + (=> (and l1 l2 .. ln) l0) } or in the most general (ground) form: \nicebox{ - (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln-1)) + (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln)) } In other words we use the following (Prolog style) convention for Horn implications: @@ -798,7 +798,7 @@ typedef enum general non-ground form is: \nicebox{ - (forall (vars) (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln-1))) + (forall (vars) (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln))) } The hyper-resolution rule takes a sequence of parameters. diff --git a/src/ast/rewriter/expr_safe_replace.cpp b/src/ast/rewriter/expr_safe_replace.cpp index 1ec810414..a4886ad1a 100644 --- a/src/ast/rewriter/expr_safe_replace.cpp +++ b/src/ast/rewriter/expr_safe_replace.cpp @@ -106,3 +106,10 @@ void expr_safe_replace::reset() { m_dst.reset(); m_subst.reset(); } + +void expr_safe_replace::apply_substitution(expr* s, expr* def, expr_ref& t) { + reset(); + insert(s, def); + (*this)(t, t); + reset(); +} diff --git a/src/ast/rewriter/expr_safe_replace.h b/src/ast/rewriter/expr_safe_replace.h index b6131906a..ad626d18f 100644 --- a/src/ast/rewriter/expr_safe_replace.h +++ b/src/ast/rewriter/expr_safe_replace.h @@ -39,6 +39,8 @@ public: void operator()(expr* src, expr_ref& e); + void apply_substitution(expr* s, expr* def, expr_ref& t); + void reset(); }; diff --git a/src/muz_qe/clp_context.cpp b/src/muz_qe/clp_context.cpp index 94a956eb9..3a3908f59 100644 --- a/src/muz_qe/clp_context.cpp +++ b/src/muz_qe/clp_context.cpp @@ -205,6 +205,7 @@ namespace datalog { }; clp::clp(context& ctx): + engine_base(ctx.get_manager(), "clp"), m_imp(alloc(imp, ctx)) { } clp::~clp() { diff --git a/src/muz_qe/clp_context.h b/src/muz_qe/clp_context.h index 635891205..5184b474d 100644 --- a/src/muz_qe/clp_context.h +++ b/src/muz_qe/clp_context.h @@ -22,23 +22,24 @@ Revision History: #include "ast.h" #include "lbool.h" #include "statistics.h" +#include "dl_util.h" namespace datalog { class context; - class clp { + class clp : public datalog::engine_base { class imp; imp* m_imp; public: clp(context& ctx); ~clp(); - lbool query(expr* query); - void cancel(); - void cleanup(); - void reset_statistics(); - void collect_statistics(statistics& st) const; - void display_certificate(std::ostream& out) const; - expr_ref get_answer(); + virtual lbool query(expr* query); + virtual void cancel(); + virtual void cleanup(); + virtual void reset_statistics(); + virtual void collect_statistics(statistics& st) const; + virtual void display_certificate(std::ostream& out) const; + virtual expr_ref get_answer(); }; }; diff --git a/src/muz_qe/dl_bmc_engine.cpp b/src/muz_qe/dl_bmc_engine.cpp index 8e9fb510e..65a60cdeb 100644 --- a/src/muz_qe/dl_bmc_engine.cpp +++ b/src/muz_qe/dl_bmc_engine.cpp @@ -1416,6 +1416,7 @@ namespace datalog { }; bmc::bmc(context& ctx): + engine_base(ctx.get_manager(), "bmc"), m_ctx(ctx), m(ctx.get_manager()), m_solver(m, m_fparams), diff --git a/src/muz_qe/dl_bmc_engine.h b/src/muz_qe/dl_bmc_engine.h index 5911f5f72..e20987002 100644 --- a/src/muz_qe/dl_bmc_engine.h +++ b/src/muz_qe/dl_bmc_engine.h @@ -30,7 +30,7 @@ Revision History: namespace datalog { class context; - class bmc { + class bmc : public engine_base { context& m_ctx; ast_manager& m; smt_params m_fparams; diff --git a/src/muz_qe/dl_boogie_proof.cpp b/src/muz_qe/dl_boogie_proof.cpp new file mode 100644 index 000000000..8720b3544 --- /dev/null +++ b/src/muz_qe/dl_boogie_proof.cpp @@ -0,0 +1,297 @@ +/** + +Example from Boogie: + +(derivation +(step s!4 (main_loop_LoopHead true true) + rule!3 (subst + (= assertsPassed@@1 true) + (= assertsPassed@2@@0 true) + (= main_loop_LoopHead_assertsPassed true) + ) + (labels @950 +895 +668 +670 +893 +899 ) + (ref true )) +(step s!3 (main true false) + rule!1 (subst + (= assertsPassed true) + (= assertsPassed@0 true) + (= assertsPassed@2 false) + (= main_assertsPassed false) + ) + (labels @839 +763 +547 +546 +761 +544 +545 +si_fcall_805 +681 +768 ) + (ref s!4 )) +(step s!2 (main_SeqInstr true false) + rule!2 (subst + (= assertsPassed@@0 true) + (= assertsPassed@0@@0 false) + (= main_SeqInstr_assertsPassed false) + ) + (labels @890 +851 +572 +si_fcall_883 +853 ) + (ref s!3 )) +(step s!1 (@Fail!0) + rule!4 (subst + (= assertsPassed@@2 true) + (= main_SeqInstr_assertsPassed@@0 false) + ) + (labels ) + (ref s!2 )) +) +(model +"tickleBool -> { + true -> true + false -> true + else -> true +} +") +*/ + +#include "dl_boogie_proof.h" +#include "model_pp.h" +#include "proof_utils.h" +#include "ast_pp.h" +#include "dl_util.h" + +namespace datalog { + + /** + \brief push hyper-resolution steps upwards such that every use of + hyper-resolution uses a premise that is not derived from hyper-resolution. + + perform the following rewrite: + + hr(hr(p1,p2,p3,..),p4,p5) => hr(p1,hr(p2,p4),p3,..,p5) + + */ + + void mk_input_resolution(proof_ref& pr) { + ast_manager& m = pr.get_manager(); + proof_ref pr1(m); + proof_ref_vector premises1(m), premises2(m), premises(m); + expr_ref conclusion1(m), conclusion2(m), conclusion(m); + svector > positions1, positions2, positions; + vector substs1, substs2, substs; + + if (m.is_hyper_resolve(pr, premises1, conclusion1, positions1, substs1) && + m.is_hyper_resolve(premises1[0].get(), premises, conclusion2, positions, substs2)) { + for (unsigned i = 1; i < premises1.size(); ++i) { + pr1 = premises1[i].get(); + mk_input_resolution(pr1); + premises1[i] = pr1; + } + for (unsigned i = 0; i < premises.size(); ++i) { + pr1 = premises[i].get(); + mk_input_resolution(pr1); + premises[i] = pr1; + } + unsigned sz = premises.size(); + for (unsigned i = 1; i < sz; ++i) { + proof* premise = premises[i].get(); + expr_ref_vector literals(m); + expr* l1, *l2; + if (!m.is_implies(premise, l1, l2)) { + continue; + } + datalog::flatten_and(l1, literals); + positions2.reset(); + premises2.reset(); + premises2.push_back(premise); + substs2.reset(); + for (unsigned j = 0; j < literals.size(); ++j) { + expr* lit = literals[j].get(); + for (unsigned k = 1; k < premises1.size(); ++k) { + if (m.get_fact(premises1[k].get()) == lit) { + premises2.push_back(premises1[k].get()); + positions2.push_back(std::make_pair(j+1,0)); + substs2.push_back(expr_ref_vector(m)); + break; + } + } + } + premises[i] = m.mk_hyper_resolve(premises2.size(), premises2.c_ptr(), l2, positions2, substs2); + } + conclusion = conclusion1; + pr = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); + } + } + + void boogie_proof::set_proof(proof* p) { + std::cout << "set proof\n"; + m_proof = p; + proof_utils::push_instantiations_up(m_proof); + mk_input_resolution(m_proof); + std::cout << "proof set\n"; + } + + void boogie_proof::set_model(model* m) { + m_model = m; + } + + void boogie_proof::pp(std::ostream& out) { + if (m_proof) { + pp_proof(out); + } + if (m_model) { + model_pp(out, *m_model); + } + } + + void boogie_proof::pp_proof(std::ostream& out) { + vector steps; + ptr_vector rules; + rules.push_back(m_proof); + steps.push_back(step()); + obj_map index; + index.insert(m_proof, 0); + + for (unsigned j = 0; j < rules.size(); ++j) { + proof* p = rules[j]; + proof_ref_vector premises(m); + expr_ref conclusion(m); + svector > positions; + vector substs; + + expr* tmp; + steps[j].m_fact = m.get_fact(p); + m.is_implies(steps[j].m_fact, tmp, steps[j].m_fact); + get_subst(p, steps[j].m_subst); + get_labels(p, steps[j].m_labels); + + if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { + for (unsigned i = 1; i < premises.size(); ++i) { + proof* premise = premises[i].get(); + unsigned position = 0; + if (!index.find(premise, position)) { + position = rules.size(); + rules.push_back(premise); + steps.push_back(step()); + index.insert(premise, position); + } + steps[j].m_refs.push_back(position); + } + get_rule_name(premises[0].get(), steps[j].m_rule_name); + } + } + for (unsigned j = steps.size(); j > 0; ) { + --j; + step &s = steps[j]; + + // TBD + s.m_labels; + + // set references, compensate for reverse ordering. + for (unsigned i = 0; i < s.m_refs.size(); ++i) { + s.m_refs[i] = rules.size()-1-s.m_refs[i]; + } + } + steps.reverse(); + pp_steps(out, steps); + } + + /** + \brief extract the instantiation by searching for the first occurrence of a hyper-resolution + rule that produces an instance. + */ + void boogie_proof::get_subst(proof* p, subst& s) { + ptr_vector todo; + todo.push_back(p); + ast_mark visited; + std::cout << "get_subst\n" << mk_pp(p, m) << "\n"; + while (!todo.empty()) { + proof* p = todo.back(); + todo.pop_back(); + if (visited.is_marked(p)) { + continue; + } + visited.mark(p, true); + proof_ref_vector premises(m); + expr_ref conclusion(m); + svector > positions; + vector substs; + if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { + expr_ref_vector const& sub = substs[0]; + if (!sub.empty()) { + quantifier* q = to_quantifier(m.get_fact(premises[0].get())); + unsigned sz = sub.size(); + SASSERT(sz == q->get_num_decls()); + for (unsigned i = 0; i < sz; ++i) { + s.push_back(std::make_pair(q->get_decl_name(sz-1-i), sub[i])); + } + return; + } + } + unsigned sz = m.get_num_parents(p); + for (unsigned i = 0; i < sz; ++i) { + todo.push_back(m.get_parent(p, i)); + } + } + } + + void boogie_proof::get_rule_name(proof* p, symbol& n) { + + } + + void boogie_proof::get_labels(proof* p, labels& lbls) { + + } + + void boogie_proof::pp_steps(std::ostream& out, vector& steps) { + out << "(derivation\n"; + for (unsigned i = 0; i < steps.size(); ++i) { + pp_step(out, i, steps[i]); + } + out << ")\n"; + + } + + // step :: "(" "step" step-name fact rule-name subst labels premises ")" + void boogie_proof::pp_step(std::ostream& out, unsigned id, step& s) { + out << "(step\n"; + out << " s!" << id << " "; + pp_fact(out, s.m_fact); + out << " " << s.m_rule_name << "\n"; + pp_subst(out << " ", s.m_subst); + pp_labels(out << " ", s.m_labels); + pp_premises(out << " ", s.m_refs); + out << ")\n"; + } + + // fact :: "(" predicate theory-term* ")" + void boogie_proof::pp_fact(std::ostream& out, expr* fact) { + out << mk_pp(fact, m) << "\n"; + } + + // subst :: "(" "subst" assignment* ")" + void boogie_proof::pp_subst(std::ostream& out, subst& s) { + out << "(subst"; + for (unsigned i = 0; i < s.size(); ++i) { + pp_assignment(out, s[i].first, s[i].second); + } + out << ")\n"; + } + + // assignment :: "(" "=" variable theory-term ")" + void boogie_proof::pp_assignment(std::ostream& out, symbol const& v, expr* t) { + out << "\n (= " << v << " " << mk_pp(t, m) << ")"; + } + + + // labels :: "(" "labels" label* ")" + void boogie_proof::pp_labels(std::ostream& out, labels& lbls) { + out << "(labels"; + for (unsigned i = 0; i < lbls.size(); ++i) { + out << " " << lbls[i]; + } + out << ")\n"; + } + + // premises "(" "ref" step-name* ")" + void boogie_proof::pp_premises(std::ostream& out, refs& refs) { + out << "(ref"; + for (unsigned i = 0; i < refs.size(); ++i) { + out << " s!" << refs[i]; + } + out << ")\n"; + } + + +} diff --git a/src/muz_qe/dl_boogie_proof.h b/src/muz_qe/dl_boogie_proof.h new file mode 100644 index 000000000..6c8fbbae3 --- /dev/null +++ b/src/muz_qe/dl_boogie_proof.h @@ -0,0 +1,87 @@ +/** + +output :: derivation model + +derivation :: "(" "derivation" step* ")" + +step :: "(" "step" step-name fact rule-name subst labels premises ")" + +step-name :: identifier + +rule-name :: identifier + +fact :: "(" predicate theory-term* ")" + +subst :: "(" "subst" assignment* ")" + +assignment :: "(" "=" variable theory-term ")" + +labels :: "(" "labels" label* ")" + +premises :: "(" "ref" step-name* ")" + +model :: "(" "model" smtlib2-model ")" + +In each step the "fact" is derivable by hyper-resolution from the named +premises and the named rule, under the given substitution for the +universally quantified variables in the rule. The premises of each +step must have occurred previously in the step sequence. The last fact +is a nullary placeholder predicate representing satisfaction of the query +(its name is arbitrary). + +The labels list consists of all the positively labeled sub-formulas whose +truth is used in the proof, and all the negatively labeled formulas whose +negation is used. A theory-term is a ground term using only interpreted +constants of the background theories. + +The smtlib2-model gives an interpretation of the uninterpreted constants +in the background theory under which the derivation is valid. Currently +it is a quoted string in the old z3 model format, for compatibility with +Boogie, however, this should be changed to the new model format (using +define-fun) when Boogie supports this. + +*/ + +#include "ast.h" +#include "model.h" + +namespace datalog { + class boogie_proof { + typedef vector > subst; + typedef svector labels; + typedef unsigned_vector refs; + struct step { + symbol m_rule_name; + expr* m_fact; + subst m_subst; + labels m_labels; + refs m_refs; + }; + + ast_manager& m; + proof_ref m_proof; + model_ref m_model; + + void pp_proof(std::ostream& out); + void pp_steps(std::ostream& out, vector& steps); + void pp_step(std::ostream& out, unsigned i, step& s); + void pp_fact(std::ostream& out, expr* fact); + void pp_subst(std::ostream& out, subst& s); + void pp_assignment(std::ostream& out, symbol const& v, expr* t); + void pp_labels(std::ostream& out, labels& lbls); + void pp_premises(std::ostream& out, refs& refs); + + void get_subst(proof* p, subst& sub); + void get_rule_name(proof* p, symbol&); + void get_labels(proof* p, labels&); + + public: + boogie_proof(ast_manager& m): m(m), m_proof(m), m_model(0) {} + + void set_proof(proof* p); + + void set_model(model* m); + + void pp(std::ostream& out); + }; +} diff --git a/src/muz_qe/dl_cmds.cpp b/src/muz_qe/dl_cmds.cpp index b82225785..693105e6e 100644 --- a/src/muz_qe/dl_cmds.cpp +++ b/src/muz_qe/dl_cmds.cpp @@ -328,9 +328,7 @@ private: void print_certificate(cmd_context& ctx) { if (m_dl_ctx->get_params().print_certificate()) { datalog::context& dlctx = m_dl_ctx->dlctx(); - if (!dlctx.display_certificate(ctx.regular_stream())) { - throw cmd_exception("certificates are not supported for the selected engine"); - } + dlctx.display_certificate(ctx.regular_stream()); ctx.regular_stream() << "\n"; } } diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index 3f16d0dab..a5f8009e7 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -42,7 +42,7 @@ namespace datalog { return; } relation_signature sig; - m_context.get_rel_context().get_rmanager().from_predicate(pred, sig); + m_context.get_rel_context()->get_rmanager().from_predicate(pred, sig); reg_idx reg = get_fresh_register(sig); e->get_data().m_value=reg; @@ -606,7 +606,7 @@ namespace datalog { } SASSERT(is_app(e)); relation_sort arg_sort; - m_context.get_rel_context().get_rmanager().from_predicate(neg_pred, i, arg_sort); + m_context.get_rel_context()->get_rmanager().from_predicate(neg_pred, i, arg_sort); reg_idx new_reg; bool new_dealloc; make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), new_reg, new_dealloc, acc); @@ -1252,7 +1252,7 @@ namespace datalog { func_decl_set::iterator fdit = preds.begin(); func_decl_set::iterator fdend = preds.end(); for(; fdit!=fdend; ++fdit) { - if(!m_context.get_rel_context().get_rmanager().is_saturated(*fdit)) { + if(!m_context.get_rel_context()->get_rmanager().is_saturated(*fdit)) { return false; } } @@ -1337,7 +1337,7 @@ namespace datalog { acc.set_observer(0); - TRACE("dl", execution_code.display(m_context.get_rel_context(), tout);); + TRACE("dl", execution_code.display(*m_context.get_rel_context(), tout);); } diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index f8e572ab1..2627cbbec 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -45,6 +45,7 @@ Revision History: #include"dl_mk_bit_blast.h" #include"dl_mk_array_blast.h" #include"dl_mk_karr_invariants.h" +#include"dl_mk_magic_symbolic.h" #include"dl_mk_quantifier_abstraction.h" #include"dl_mk_quantifier_instantiation.h" #include"datatype_decl_plugin.h" @@ -238,11 +239,13 @@ namespace datalog { m_rule_fmls(m), m_background(m), m_mc(0), + m_rel(0), + m_engine(0), m_closed(false), m_saturation_was_run(false), m_last_status(OK), m_last_answer(m), - m_engine(LAST_ENGINE), + m_engine_type(LAST_ENGINE), m_cancel(false) { } @@ -260,8 +263,7 @@ namespace datalog { m_preds.reset(); m_preds_by_name.reset(); reset_dealloc_values(m_sorts); - m_pdr = 0; - m_bmc = 0; + m_engine = 0; m_rel = 0; } @@ -432,8 +434,7 @@ namespace datalog { void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { - if (relation_name_cnt > 0) { - ensure_rel(); + if (m_rel && relation_name_cnt > 0) { m_rel->set_predicate_representation(pred, relation_name_cnt, relation_names); } } @@ -445,7 +446,7 @@ namespace datalog { register_predicate(new_pred, true); - if (m_rel.get()) { + if (m_rel) { m_rel->inherit_predicate_kind(new_pred, orig_pred); } return new_pred; @@ -542,64 +543,18 @@ namespace datalog { } unsigned context::get_num_levels(func_decl* pred) { - switch(get_engine()) { - case DATALOG_ENGINE: - throw default_exception("get_num_levels is not supported for datalog engine"); - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - return m_pdr->get_num_levels(pred); - case BMC_ENGINE: - case QBMC_ENGINE: - throw default_exception("get_num_levels is not supported for bmc"); - case TAB_ENGINE: - throw default_exception("get_num_levels is not supported for tab"); - case CLP_ENGINE: - throw default_exception("get_num_levels is not supported for clp"); - default: - throw default_exception("unknown engine"); - } + ensure_engine(); + return m_engine->get_num_levels(pred); } expr_ref context::get_cover_delta(int level, func_decl* pred) { - switch(get_engine()) { - case DATALOG_ENGINE: - throw default_exception("operation is not supported for datalog engine"); - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - return m_pdr->get_cover_delta(level, pred); - case BMC_ENGINE: - case QBMC_ENGINE: - throw default_exception("operation is not supported for BMC engine"); - case TAB_ENGINE: - throw default_exception("operation is not supported for TAB engine"); - case CLP_ENGINE: - throw default_exception("operation is not supported for CLP engine"); - default: - throw default_exception("unknown engine"); - } + ensure_engine(); + return m_engine->get_cover_delta(level, pred); } void context::add_cover(int level, func_decl* pred, expr* property) { - switch(get_engine()) { - case DATALOG_ENGINE: - throw default_exception("operation is not supported for datalog engine"); - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - m_pdr->add_cover(level, pred, property); - break; - case BMC_ENGINE: - case QBMC_ENGINE: - throw default_exception("operation is not supported for BMC engine"); - case TAB_ENGINE: - throw default_exception("operation is not supported for TAB engine"); - case CLP_ENGINE: - throw default_exception("operation is not supported for CLP engine"); - default: - throw default_exception("unknown engine"); - } + ensure_engine(); + m_engine->add_cover(level, pred, property); } void context::check_uninterpreted_free(rule_ref& r) { @@ -743,7 +698,7 @@ namespace datalog { void context::add_fact(func_decl * pred, const relation_fact & fact) { if (get_engine() == DATALOG_ENGINE) { - ensure_rel(); + ensure_engine(); m_rel->add_fact(pred, fact); } else { @@ -769,7 +724,7 @@ namespace datalog { void context::add_table_fact(func_decl * pred, const table_fact & fact) { if (get_engine() == DATALOG_ENGINE) { - ensure_rel(); + ensure_engine(); m_rel->add_fact(pred, fact); } else { @@ -907,6 +862,9 @@ namespace datalog { m_transf.register_plugin(alloc(datalog::mk_bit_blast, *this, 35000)); m_transf.register_plugin(alloc(datalog::mk_array_blast, *this, 36000)); m_transf.register_plugin(alloc(datalog::mk_karr_invariants, *this, 36010)); + if (get_params().magic()) { + m_transf.register_plugin(alloc(datalog::mk_magic_symbolic, *this, 36020)); + } transform_rules(m_transf); } @@ -917,7 +875,7 @@ namespace datalog { void context::updt_params(params_ref const& p) { m_params_ref.copy(p); - if (m_pdr.get()) m_pdr->updt_params(); + if (m_engine.get()) m_engine->updt_params(); } expr_ref context::get_background_assertion() { @@ -940,45 +898,39 @@ namespace datalog { m_cancel = true; m_last_status = CANCELED; m_transf.cancel(); - if (m_pdr.get()) m_pdr->cancel(); - if (m_bmc.get()) m_bmc->cancel(); - if (m_tab.get()) m_tab->cancel(); - if (m_rel.get()) m_rel->set_cancel(true); + if (m_engine) m_engine->cancel(); } void context::cleanup() { m_cancel = false; m_last_status = OK; - if (m_pdr.get()) m_pdr->cleanup(); - if (m_bmc.get()) m_bmc->cleanup(); - if (m_tab.get()) m_tab->cleanup(); - if (m_rel.get()) m_rel->set_cancel(false); + if (m_engine) m_engine->cleanup(); } class context::engine_type_proc { ast_manager& m; arith_util a; datatype_util dt; - DL_ENGINE m_engine; + DL_ENGINE m_engine_type; public: - engine_type_proc(ast_manager& m): m(m), a(m), dt(m), m_engine(DATALOG_ENGINE) {} + engine_type_proc(ast_manager& m): m(m), a(m), dt(m), m_engine_type(DATALOG_ENGINE) {} - DL_ENGINE get_engine() const { return m_engine; } + DL_ENGINE get_engine() const { return m_engine_type; } void operator()(expr* e) { if (is_quantifier(e)) { - m_engine = QPDR_ENGINE; + m_engine_type = QPDR_ENGINE; } - else if (m_engine != QPDR_ENGINE) { + else if (m_engine_type != QPDR_ENGINE) { if (a.is_int_real(e)) { - m_engine = PDR_ENGINE; + m_engine_type = PDR_ENGINE; } else if (is_var(e) && m.is_bool(e)) { - m_engine = PDR_ENGINE; + m_engine_type = PDR_ENGINE; } else if (dt.is_datatype(m.get_sort(e))) { - m_engine = PDR_ENGINE; + m_engine_type = PDR_ENGINE; } } } @@ -988,46 +940,46 @@ namespace datalog { symbol e = m_params.engine(); if (e == symbol("datalog")) { - m_engine = DATALOG_ENGINE; + m_engine_type = DATALOG_ENGINE; } else if (e == symbol("pdr")) { - m_engine = PDR_ENGINE; + m_engine_type = PDR_ENGINE; } else if (e == symbol("qpdr")) { - m_engine = QPDR_ENGINE; + m_engine_type = QPDR_ENGINE; } else if (e == symbol("bmc")) { - m_engine = BMC_ENGINE; + m_engine_type = BMC_ENGINE; } else if (e == symbol("qbmc")) { - m_engine = QBMC_ENGINE; + m_engine_type = QBMC_ENGINE; } else if (e == symbol("tab")) { - m_engine = TAB_ENGINE; + m_engine_type = TAB_ENGINE; } else if (e == symbol("clp")) { - m_engine = CLP_ENGINE; + m_engine_type = CLP_ENGINE; } - if (m_engine == LAST_ENGINE) { + if (m_engine_type == LAST_ENGINE) { expr_fast_mark1 mark; engine_type_proc proc(m); - m_engine = DATALOG_ENGINE; - for (unsigned i = 0; m_engine == DATALOG_ENGINE && i < m_rule_set.get_num_rules(); ++i) { + m_engine_type = DATALOG_ENGINE; + for (unsigned i = 0; m_engine_type == DATALOG_ENGINE && i < m_rule_set.get_num_rules(); ++i) { rule * r = m_rule_set.get_rule(i); quick_for_each_expr(proc, mark, r->get_head()); for (unsigned j = 0; j < r->get_tail_size(); ++j) { quick_for_each_expr(proc, mark, r->get_tail(j)); } - m_engine = proc.get_engine(); + m_engine_type = proc.get_engine(); } - for (unsigned i = m_rule_fmls_head; m_engine == DATALOG_ENGINE && i < m_rule_fmls.size(); ++i) { + for (unsigned i = m_rule_fmls_head; m_engine_type == DATALOG_ENGINE && i < m_rule_fmls.size(); ++i) { expr* fml = m_rule_fmls[i].get(); while (is_quantifier(fml)) { fml = to_quantifier(fml)->get_expr(); } quick_for_each_expr(proc, mark, fml); - m_engine = proc.get_engine(); + m_engine_type = proc.get_engine(); } } } @@ -1038,170 +990,78 @@ namespace datalog { m_last_answer = 0; switch (get_engine()) { case DATALOG_ENGINE: - flush_add_rules(); - return rel_query(query); case PDR_ENGINE: case QPDR_ENGINE: - flush_add_rules(); - return pdr_query(query); case BMC_ENGINE: case QBMC_ENGINE: - flush_add_rules(); - return bmc_query(query); case TAB_ENGINE: - flush_add_rules(); - return tab_query(query); case CLP_ENGINE: flush_add_rules(); - return clp_query(query); + break; default: UNREACHABLE(); - return rel_query(query); } + ensure_engine(); + return m_engine->query(query); } model_ref context::get_model() { - switch(get_engine()) { - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - return m_pdr->get_model(); - default: - return model_ref(alloc(model, m)); - } + ensure_engine(); + return m_engine->get_model(); } proof_ref context::get_proof() { - switch(get_engine()) { - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - return m_pdr->get_proof(); - default: - return proof_ref(m.mk_asserted(m.mk_true()), m); - } + ensure_engine(); + return m_engine->get_proof(); } - void context::ensure_pdr() { - if (!m_pdr.get()) { - m_pdr = alloc(pdr::dl_interface, *this); + void context::ensure_engine() { + if (!m_engine.get()) { + switch (get_engine()) { + case PDR_ENGINE: + case QPDR_ENGINE: + m_engine = alloc(pdr::dl_interface, *this); + break; + case DATALOG_ENGINE: + m_rel = alloc(rel_context, *this); + m_engine = m_rel; + break; + case BMC_ENGINE: + case QBMC_ENGINE: + m_engine = alloc(bmc, *this); + break; + case TAB_ENGINE: + m_engine = alloc(tab, *this); + break; + case CLP_ENGINE: + m_engine = alloc(clp, *this); + break; + } } } - lbool context::pdr_query(expr* query) { - ensure_pdr(); - return m_pdr->query(query); - } - - void context::ensure_bmc() { - if (!m_bmc.get()) { - m_bmc = alloc(bmc, *this); + lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { + ensure_engine(); + if (m_rel) { + return m_rel->query(num_rels, rels); + } + else { + return l_undef; } } - - lbool context::bmc_query(expr* query) { - ensure_bmc(); - return m_bmc->query(query); - } - - void context::ensure_tab() { - if (!m_tab.get()) { - m_tab = alloc(tab, *this); - } - } - - void context::ensure_clp() { - if (!m_clp.get()) { - m_clp = alloc(clp, *this); - } - } - - lbool context::tab_query(expr* query) { - ensure_tab(); - return m_tab->query(query); - } - - lbool context::clp_query(expr* query) { - ensure_clp(); - return m_clp->query(query); - } - - void context::ensure_rel() { - if (!m_rel.get()) { - m_rel = alloc(rel_context, *this); - } - } - - lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { - ensure_rel(); - return m_rel->query(num_rels, rels); - } - - lbool context::rel_query(expr* query) { - ensure_rel(); - return m_rel->query(query); - } - - + expr* context::get_answer_as_formula() { if (m_last_answer) { return m_last_answer.get(); } - switch(get_engine()) { - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - m_last_answer = m_pdr->get_answer(); - return m_last_answer.get(); - case BMC_ENGINE: - case QBMC_ENGINE: - ensure_bmc(); - m_last_answer = m_bmc->get_answer(); - return m_last_answer.get(); - case DATALOG_ENGINE: - ensure_rel(); - m_last_answer = m_rel->get_last_answer(); - return m_last_answer.get(); - case TAB_ENGINE: - ensure_tab(); - m_last_answer = m_tab->get_answer(); - return m_last_answer.get(); - case CLP_ENGINE: - ensure_clp(); - m_last_answer = m_clp->get_answer(); - return m_last_answer.get(); - default: - UNREACHABLE(); - } - m_last_answer = m.mk_false(); + ensure_engine(); + m_last_answer = m_engine->get_answer(); return m_last_answer.get(); } - bool context::display_certificate(std::ostream& out) { - switch(get_engine()) { - case DATALOG_ENGINE: - return false; - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - m_pdr->display_certificate(out); - return true; - case BMC_ENGINE: - case QBMC_ENGINE: - ensure_bmc(); - m_bmc->display_certificate(out); - return true; - case TAB_ENGINE: - ensure_tab(); - m_tab->display_certificate(out); - return true; - case CLP_ENGINE: - ensure_clp(); - m_clp->display_certificate(out); - return true; - default: - return false; - } + void context::display_certificate(std::ostream& out) { + ensure_engine(); + m_engine->display_certificate(out); } void context::display_profile(std::ostream& out) const { @@ -1219,26 +1079,14 @@ namespace datalog { } void context::reset_statistics() { - if (m_pdr) { - m_pdr->reset_statistics(); - } - if (m_bmc) { - m_bmc->reset_statistics(); - } - if (m_tab) { - m_tab->reset_statistics(); + if (m_engine) { + m_engine->reset_statistics(); } } void context::collect_statistics(statistics& st) const { - if (m_pdr) { - m_pdr->collect_statistics(st); - } - if (m_bmc) { - m_bmc->collect_statistics(st); - } - if (m_tab) { - m_tab->collect_statistics(st); + if (m_engine) { + m_engine->collect_statistics(st); } } @@ -1246,8 +1094,7 @@ namespace datalog { execution_result context::get_status() { return m_last_status; } bool context::result_contains_fact(relation_fact const& f) { - ensure_rel(); - return m_rel->result_contains_fact(f); + return m_rel && m_rel->result_contains_fact(f); } // NB: algebraic data-types declarations will not be printed. diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index e9abf7f23..c54f7d591 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -121,17 +121,14 @@ namespace datalog { model_converter_ref m_mc; proof_converter_ref m_pc; - scoped_ptr m_pdr; - scoped_ptr m_bmc; - scoped_ptr m_rel; - scoped_ptr m_tab; - scoped_ptr m_clp; + rel_context* m_rel; + scoped_ptr m_engine; bool m_closed; bool m_saturation_was_run; execution_result m_last_status; expr_ref m_last_answer; - DL_ENGINE m_engine; + DL_ENGINE m_engine_type; volatile bool m_cancel; @@ -161,7 +158,7 @@ namespace datalog { rule_manager & get_rule_manager() { return m_rule_manager; } smt_params & get_fparams() const { return m_fparams; } fixedpoint_params const& get_params() const { return m_params; } - DL_ENGINE get_engine() { configure_engine(); return m_engine; } + DL_ENGINE get_engine() { configure_engine(); return m_engine_type; } th_rewriter& get_rewriter() { return m_rewriter; } var_subst & get_var_subst() { return m_var_subst; } dl_decl_util & get_decl_util() { return m_decl_util; } @@ -454,14 +451,14 @@ namespace datalog { /** \brief Display a certificate for reachability and/or unreachability. */ - bool display_certificate(std::ostream& out); + void display_certificate(std::ostream& out); /** \brief query result if it contains fact. */ bool result_contains_fact(relation_fact const& f); - rel_context& get_rel_context() { ensure_rel(); return *m_rel; } + rel_context* get_rel_context() { ensure_engine(); return m_rel; } private: @@ -473,25 +470,7 @@ namespace datalog { void flush_add_rules(); - void ensure_pdr(); - - void ensure_bmc(); - - void ensure_tab(); - - void ensure_clp(); - - void ensure_rel(); - - lbool rel_query(expr* query); - - lbool pdr_query(expr* query); - - lbool bmc_query(expr* query); - - lbool tab_query(expr* query); - - lbool clp_query(expr* query); + void ensure_engine(); void check_quantifier_free(rule_ref& r); void check_uninterpreted_free(rule_ref& r); diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz_qe/dl_instruction.cpp index 637d9a17f..df4736b8e 100644 --- a/src/muz_qe/dl_instruction.cpp +++ b/src/muz_qe/dl_instruction.cpp @@ -59,7 +59,7 @@ namespace datalog { } rel_context& execution_context::get_rel_context() { - return m_context.get_rel_context(); + return *m_context.get_rel_context(); } struct compare_size_proc { diff --git a/src/muz_qe/dl_mk_backwards.cpp b/src/muz_qe/dl_mk_backwards.cpp index b1d8b7d36..771de0dc3 100644 --- a/src/muz_qe/dl_mk_backwards.cpp +++ b/src/muz_qe/dl_mk_backwards.cpp @@ -48,7 +48,7 @@ namespace datalog { neg.reset(); rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); - unsigned tsz = r.get_tail_size(); + unsigned tsz = r.get_tail_size(); if (!source.is_output_predicate(r.get_decl())) { tail.push_back(r.get_head()); neg.push_back(false); diff --git a/src/muz_qe/dl_mk_explanations.cpp b/src/muz_qe/dl_mk_explanations.cpp index 004b1823d..253bbbec7 100644 --- a/src/muz_qe/dl_mk_explanations.cpp +++ b/src/muz_qe/dl_mk_explanations.cpp @@ -604,7 +604,7 @@ namespace datalog { m_e_sort = m_decl_util.mk_rule_sort(); m_pinned.push_back(m_e_sort); - relation_manager & rmgr = ctx.get_rel_context().get_rmanager(); + relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); symbol er_symbol = explanation_relation_plugin::get_name(m_relation_level); m_er_plugin = static_cast(rmgr.get_relation_plugin(er_symbol)); if (!m_er_plugin) { @@ -637,7 +637,7 @@ namespace datalog { void mk_explanations::assign_rel_level_kind(func_decl * e_decl, func_decl * orig) { SASSERT(m_relation_level); - relation_manager & rmgr = m_context.get_rel_context().get_rmanager(); + relation_manager & rmgr = m_context.get_rel_context()->get_rmanager(); unsigned sz = e_decl->get_arity(); relation_signature sig; rmgr.from_predicate(e_decl, sig); @@ -871,7 +871,7 @@ namespace datalog { return 0; } rule_set * res = alloc(rule_set, m_context); - transform_facts(m_context.get_rel_context().get_rmanager(), source, *res); + transform_facts(m_context.get_rel_context()->get_rmanager(), source, *res); transform_rules(source, *res); return res; } diff --git a/src/muz_qe/dl_mk_karr_invariants.cpp b/src/muz_qe/dl_mk_karr_invariants.cpp index 143a38636..4987a7e3d 100644 --- a/src/muz_qe/dl_mk_karr_invariants.cpp +++ b/src/muz_qe/dl_mk_karr_invariants.cpp @@ -49,7 +49,8 @@ namespace datalog { rm(ctx.get_rule_manager()), m_inner_ctx(m, ctx.get_fparams()), a(m), - m_pinned(m) { + m_pinned(m), + m_cancel(false) { params_ref params; params.set_sym("default_relation", symbol("karr_relation")); params.set_sym("engine", symbol("datalog")); @@ -189,6 +190,7 @@ namespace datalog { }; void mk_karr_invariants::cancel() { + m_cancel = true; m_inner_ctx.cancel(); } @@ -211,6 +213,10 @@ namespace datalog { get_invariants(*src_loop); + if (m_cancel) { + return 0; + } + // figure out whether to update same rules as used for saturation. scoped_ptr rev_source = bwd(*src_loop); get_invariants(*rev_source); @@ -225,7 +231,7 @@ namespace datalog { void mk_karr_invariants::get_invariants(rule_set const& src) { m_inner_ctx.reset(); - rel_context& rctx = m_inner_ctx.get_rel_context(); + rel_context& rctx = *m_inner_ctx.get_rel_context(); ptr_vector heads; func_decl_set const& predicates = m_ctx.get_predicates(); for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) { diff --git a/src/muz_qe/dl_mk_karr_invariants.h b/src/muz_qe/dl_mk_karr_invariants.h index ec554e284..3d993e60a 100644 --- a/src/muz_qe/dl_mk_karr_invariants.h +++ b/src/muz_qe/dl_mk_karr_invariants.h @@ -57,6 +57,7 @@ namespace datalog { arith_util a; obj_map m_fun2inv; ast_ref_vector m_pinned; + volatile bool m_cancel; void get_invariants(rule_set const& src); diff --git a/src/muz_qe/dl_mk_magic_sets.cpp b/src/muz_qe/dl_mk_magic_sets.cpp index f6f79f348..24d9d01cb 100644 --- a/src/muz_qe/dl_mk_magic_sets.cpp +++ b/src/muz_qe/dl_mk_magic_sets.cpp @@ -362,7 +362,7 @@ namespace datalog { rule * r = *it; transform_rule(task.m_adornment, r, *result); } - if (!m_context.get_rel_context().get_relation(task.m_pred).empty()) { + if (!m_context.get_rel_context()->get_relation(task.m_pred).empty()) { //we need a rule to copy facts that are already in a relation into the adorned //relation (since out intentional predicates can have facts, not only rules) create_transfer_rule(task, *result); diff --git a/src/muz_qe/dl_mk_partial_equiv.cpp b/src/muz_qe/dl_mk_partial_equiv.cpp index 5ba1e71dd..4d1a1e860 100644 --- a/src/muz_qe/dl_mk_partial_equiv.cpp +++ b/src/muz_qe/dl_mk_partial_equiv.cpp @@ -97,7 +97,7 @@ namespace datalog { return 0; } - relation_manager & rm = m_context.get_rel_context().get_rmanager(); + relation_manager & rm = m_context.get_rel_context()->get_rmanager(); rule_set::decl2rules::iterator it = source.begin_grouped_rules(); rule_set::decl2rules::iterator end = source.end_grouped_rules(); diff --git a/src/muz_qe/dl_mk_rule_inliner.cpp b/src/muz_qe/dl_mk_rule_inliner.cpp index 4afc1d323..5e0d6446b 100644 --- a/src/muz_qe/dl_mk_rule_inliner.cpp +++ b/src/muz_qe/dl_mk_rule_inliner.cpp @@ -206,7 +206,10 @@ namespace datalog { void mk_rule_inliner::count_pred_occurrences(rule_set const & orig) { - m_context.get_rel_context().get_rmanager().collect_non_empty_predicates(m_preds_with_facts); + rel_context* rel = m_context.get_rel_context(); + if (rel) { + rel->get_rmanager().collect_non_empty_predicates(m_preds_with_facts); + } rule_set::iterator rend = orig.end(); for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) { diff --git a/src/muz_qe/dl_mk_similarity_compressor.cpp b/src/muz_qe/dl_mk_similarity_compressor.cpp index b600811f0..d4f410130 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.cpp +++ b/src/muz_qe/dl_mk_similarity_compressor.cpp @@ -368,7 +368,7 @@ namespace datalog { collect_orphan_consts(*it, const_infos, val_fact); m_context.add_fact(aux_pred, val_fact); } - m_context.get_rel_context().get_rmanager().mark_saturated(aux_pred); + m_context.get_rel_context()->get_rmanager().mark_saturated(aux_pred); app * new_head = r->get_head(); ptr_vector new_tail; diff --git a/src/muz_qe/dl_mk_simple_joins.cpp b/src/muz_qe/dl_mk_simple_joins.cpp index 990125475..300ed0879 100644 --- a/src/muz_qe/dl_mk_simple_joins.cpp +++ b/src/muz_qe/dl_mk_simple_joins.cpp @@ -570,11 +570,15 @@ namespace datalog { cost estimate_size(app * t) const { func_decl * pred = t->get_decl(); unsigned n=pred->get_arity(); - relation_manager& rm = m_context.get_rel_context().get_rmanager(); + rel_context* rel = m_context.get_rel_context(); + if (!rel) { + return cost(1); + } + relation_manager& rm = rel->get_rmanager(); if( (m_context.saturation_was_run() && rm.try_get_relation(pred)) || rm.is_saturated(pred)) { SASSERT(rm.try_get_relation(pred)); //if it is saturated, it should exist - unsigned rel_size_int = m_context.get_rel_context().get_relation(pred).get_size_estimate_rows(); + unsigned rel_size_int = rel->get_relation(pred).get_size_estimate_rows(); if(rel_size_int!=0) { cost rel_size = static_cast(rel_size_int); cost curr_size = rel_size; diff --git a/src/muz_qe/dl_mk_subsumption_checker.cpp b/src/muz_qe/dl_mk_subsumption_checker.cpp index 8c9e7e69f..0d32a5c3b 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.cpp +++ b/src/muz_qe/dl_mk_subsumption_checker.cpp @@ -249,7 +249,11 @@ namespace datalog { } void mk_subsumption_checker::scan_for_relations_total_due_to_facts(rule_set const& source) { - relation_manager& rm = m_context.get_rel_context().get_rmanager(); + rel_context* rel = m_context.get_rel_context(); + if (!rel) { + return; + } + relation_manager& rm = rel->get_rmanager(); decl_set const& candidate_preds = m_context.get_predicates(); diff --git a/src/muz_qe/dl_mk_unbound_compressor.cpp b/src/muz_qe/dl_mk_unbound_compressor.cpp index 7eb6f829d..71d4d5479 100644 --- a/src/muz_qe/dl_mk_unbound_compressor.cpp +++ b/src/muz_qe/dl_mk_unbound_compressor.cpp @@ -334,8 +334,10 @@ namespace datalog { // TODO mc m_modified = false; - m_context.get_rel_context().get_rmanager().collect_non_empty_predicates(m_non_empty_rels); - + rel_context* rel = m_context.get_rel_context(); + if (rel) { + rel->get_rmanager().collect_non_empty_predicates(m_non_empty_rels); + } unsigned init_rule_cnt = source.get_num_rules(); SASSERT(m_rules.empty()); for (unsigned i=0; i m_filter; + scoped_ptr m_project; + unsigned_vector m_removed_cols; + public: + /** + This constructor should be used only if we know that the projection operation + exists for the result of the join. + */ + default_relation_filter_interpreted_and_project_fn( + relation_mutator_fn* filter, + unsigned removed_col_cnt, + const unsigned * removed_cols) + : m_filter(filter), + m_project(0), + m_removed_cols(removed_col_cnt, removed_cols) {} + + virtual relation_base * operator()(const relation_base & t) { + scoped_rel t1 = t.clone(); + (*m_filter)(*t1); + if( !m_project) { + relation_manager & rmgr = t1->get_plugin().get_manager(); + m_project = rmgr.mk_project_fn(*t1, m_removed_cols.size(), m_removed_cols.c_ptr()); + if (!m_project) { + throw default_exception("projection does not exist"); + } + } + return (*m_project)(*t1); + } + }; + + relation_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn( + const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols) { + + relation_transformer_fn* res = + t.get_plugin().mk_filter_interpreted_and_project_fn( + t, + condition, + removed_col_cnt, + removed_cols); + + if (!res) { + relation_mutator_fn* filter_fn = mk_filter_interpreted_fn(t, condition); + if (filter_fn) { + res = alloc(default_relation_filter_interpreted_and_project_fn, + filter_fn, + removed_col_cnt, + removed_cols); + } + } + return res; + } + class relation_manager::default_relation_join_project_fn : public relation_join_fn { scoped_ptr m_join; @@ -720,14 +774,6 @@ namespace datalog { return t.get_plugin().mk_filter_interpreted_fn(t, condition); } - relation_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn(const relation_base & t, - app * condition, - unsigned removed_col_cnt, - const unsigned * removed_cols) { - return t.get_plugin().mk_filter_interpreted_and_project_fn(t, condition, removed_col_cnt, removed_cols); - } - - class relation_manager::default_relation_select_equal_and_project_fn : public relation_transformer_fn { scoped_ptr m_filter; scoped_ptr m_project; diff --git a/src/muz_qe/dl_relation_manager.h b/src/muz_qe/dl_relation_manager.h index ccdba2783..9f12b4bb6 100644 --- a/src/muz_qe/dl_relation_manager.h +++ b/src/muz_qe/dl_relation_manager.h @@ -42,6 +42,7 @@ namespace datalog { class default_relation_join_project_fn; class default_relation_select_equal_and_project_fn; class default_relation_intersection_filter_fn; + class default_relation_filter_interpreted_and_project_fn; class auxiliary_table_transformer_fn; class auxiliary_table_filter_fn; diff --git a/src/muz_qe/dl_util.h b/src/muz_qe/dl_util.h index ea2def025..b92a33d1a 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz_qe/dl_util.h @@ -28,6 +28,8 @@ Revision History: #include"substitution.h" #include"fixedpoint_params.hpp" #include"ast_counter.h" +#include"statistics.h" +#include"lbool.h" namespace datalog { @@ -58,6 +60,42 @@ namespace datalog { LAST_ENGINE }; + class engine_base { + ast_manager& m; + std::string m_name; + public: + engine_base(ast_manager& m, char const* name): m(m), m_name(name) {} + virtual ~engine_base() {} + + virtual expr_ref get_answer() = 0; + virtual lbool query(expr* q) = 0; + + virtual void reset_statistics() {} + virtual void display_profile(std::ostream& out) const {} + virtual void collect_statistics(statistics& st) const {} + virtual unsigned get_num_levels(func_decl* pred) { + throw default_exception(std::string("get_num_levels is not supported for ") + m_name); + } + virtual expr_ref get_cover_delta(int level, func_decl* pred) { + throw default_exception(std::string("operation is not supported for ") + m_name); + } + virtual void add_cover(int level, func_decl* pred, expr* property) { + throw default_exception(std::string("operation is not supported for ") + m_name); + } + virtual void display_certificate(std::ostream& out) const { + throw default_exception(std::string("certificates are not supported for ") + m_name); + } + virtual model_ref get_model() { + return model_ref(alloc(model, m)); + } + virtual proof_ref get_proof() { + return proof_ref(m.mk_asserted(m.mk_true()), m); + } + virtual void updt_params() {} + virtual void cancel() {} + virtual void cleanup() {} + }; + struct std_string_hash_proc { unsigned operator()(const std::string & s) const { return string_hash(s.c_str(), static_cast(s.length()), 17); } diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index c2996dd8f..860dcb68e 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -10,6 +10,7 @@ def_module_params('fixedpoint', ('bit_blast', BOOL, False, '(PDR) bit-blast bit-vectors'), ('explanations_on_relation_level', BOOL, False, '(DATALOG) if true, explanations are generated as history of each relation, rather than per fact (generate_explanations must be set to true for this option to have any effect)'), ('magic_sets_for_queries', BOOL, False, "(DATALOG) magic set transformation will be used for queries"), + ('magic', BOOL, False, "perform symbolic magic set transformation"), ('unbound_compressor', BOOL, True, "auxiliary relations will be introduced to avoid unbound variables in rule heads"), ('similarity_compressor', BOOL, True, "(DATALOG) rules that differ only in values of constants will be merged into a single rule"), ('similarity_compressor_threshold', UINT, 11, "(DATALOG) if similarity_compressor is on, this value determines how many similar rules there must be in order for them to be merged"), @@ -60,6 +61,7 @@ def_module_params('fixedpoint', "by putting in half of the table columns, if it would have been empty otherwise"), ('print_answer', BOOL, False, 'print answer instance(s) to query'), ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), + ('print_boogie_certificate', BOOL, False, 'print certificate for reachability or non-reachability using a format understood by Boogie'), ('print_statistics', BOOL, False, 'print statistics'), ('use_utvpi', BOOL, True, 'PDR: Enable UTVPI strategy'), ('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index 1c49e0c83..6f81d93d8 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -44,6 +44,7 @@ Notes: #include "proof_checker.h" #include "smt_value_sort.h" #include "proof_utils.h" +#include "dl_boogie_proof.h" namespace pdr { @@ -1063,7 +1064,7 @@ namespace pdr { ast_manager& m = pt.get_manager(); datalog::context& dctx = ctx.get_context(); datalog::rule_manager& rm = dctx.get_rule_manager(); - datalog::rule_unifier unifier(dctx); + datalog::rule_unifier unif(dctx); datalog::dl_decl_util util(m); datalog::rule_ref r0(rm), r1(rm); obj_map cache; @@ -1072,7 +1073,7 @@ namespace pdr { proof_ref_vector trail(m); datalog::rule_ref_vector rules_trail(rm); proof* pr = 0; - unifier.set_normalize(false); + unif.set_normalize(true); todo.push_back(m_root); update_models(); while (!todo.empty()) { @@ -1134,14 +1135,14 @@ namespace pdr { substs.push_back(binding); // TODO base substitution. for (unsigned i = 1; i < rls.size(); ++i) { datalog::rule& src = *rls[i]; - bool unified = unifier.unify_rules(*reduced_rule, 0, src); + bool unified = unif.unify_rules(*reduced_rule, 0, src); if (!unified) { IF_VERBOSE(0, verbose_stream() << "Could not unify rules: "; reduced_rule->display(dctx, verbose_stream()); src.display(dctx, verbose_stream());); } - expr_ref_vector sub1 = unifier.get_rule_subst(*reduced_rule, true); + expr_ref_vector sub1 = unif.get_rule_subst(*reduced_rule, true); TRACE("pdr", for (unsigned k = 0; k < sub1.size(); ++k) { tout << mk_pp(sub1[k].get(), m) << " "; @@ -1160,8 +1161,8 @@ namespace pdr { } positions.push_back(std::make_pair(i,0)); - substs.push_back(unifier.get_rule_subst(src, false)); - VERIFY(unifier.apply(*reduced_rule.get(), 0, src, r3)); + substs.push_back(unif.get_rule_subst(src, false)); + VERIFY(unif.apply(*reduced_rule.get(), 0, src, r3)); reduced_rule = r3; } @@ -1620,6 +1621,12 @@ namespace pdr { IF_VERBOSE(1, verbose_stream() << "\n"; m_search.display(verbose_stream());); m_last_result = l_true; validate(); + + IF_VERBOSE(1, + if (m_params.print_boogie_certificate()) { + display_certificate(verbose_stream()); + }); + return l_true; } catch (inductive_exception) { @@ -2129,7 +2136,15 @@ namespace pdr { break; } case l_true: { - strm << mk_pp(mk_sat_answer(), m); + if (m_params.print_boogie_certificate()) { + datalog::boogie_proof bp(m); + bp.set_proof(get_proof()); + bp.set_model(0); + bp.pp(strm); + } + else { + strm << mk_pp(mk_sat_answer(), m); + } break; } case l_undef: { diff --git a/src/muz_qe/pdr_dl_interface.cpp b/src/muz_qe/pdr_dl_interface.cpp index 437c08f6a..05a13dfc7 100644 --- a/src/muz_qe/pdr_dl_interface.cpp +++ b/src/muz_qe/pdr_dl_interface.cpp @@ -37,6 +37,7 @@ Revision History: using namespace pdr; dl_interface::dl_interface(datalog::context& ctx) : + engine_base(ctx.get_manager(), "pdr"), m_ctx(ctx), m_pdr_rules(ctx), m_old_rules(ctx), diff --git a/src/muz_qe/pdr_dl_interface.h b/src/muz_qe/pdr_dl_interface.h index c4844f892..2075dff47 100644 --- a/src/muz_qe/pdr_dl_interface.h +++ b/src/muz_qe/pdr_dl_interface.h @@ -23,6 +23,7 @@ Revision History: #include "lbool.h" #include "dl_rule.h" #include "dl_rule_set.h" +#include "dl_util.h" #include "statistics.h" namespace datalog { @@ -33,7 +34,7 @@ namespace pdr { class context; - class dl_interface { + class dl_interface : public datalog::engine_base { datalog::context& m_ctx; datalog::rule_set m_pdr_rules; datalog::rule_set m_old_rules; @@ -47,31 +48,31 @@ namespace pdr { dl_interface(datalog::context& ctx); ~dl_interface(); - lbool query(expr* query); + virtual lbool query(expr* query); - void cancel(); + virtual void cancel(); - void cleanup(); + virtual void cleanup(); - void display_certificate(std::ostream& out) const; + virtual void display_certificate(std::ostream& out) const; - void collect_statistics(statistics& st) const; + virtual void collect_statistics(statistics& st) const; - void reset_statistics(); + virtual void reset_statistics(); - expr_ref get_answer(); + virtual expr_ref get_answer(); - unsigned get_num_levels(func_decl* pred); + virtual unsigned get_num_levels(func_decl* pred); - expr_ref get_cover_delta(int level, func_decl* pred); + virtual expr_ref get_cover_delta(int level, func_decl* pred); - void add_cover(int level, func_decl* pred, expr* property); + virtual void add_cover(int level, func_decl* pred, expr* property); - void updt_params(); + virtual void updt_params(); - model_ref get_model(); + virtual model_ref get_model(); - proof_ref get_proof(); + virtual proof_ref get_proof(); }; } diff --git a/src/muz_qe/proof_utils.cpp b/src/muz_qe/proof_utils.cpp index 36e721b5c..87ecf9985 100644 --- a/src/muz_qe/proof_utils.cpp +++ b/src/muz_qe/proof_utils.cpp @@ -608,3 +608,5 @@ void proof_utils::push_instantiations_up(proof_ref& pr) { push_instantiations_up_cl push(pr.get_manager()); push(pr); } + + diff --git a/src/muz_qe/proof_utils.h b/src/muz_qe/proof_utils.h index dc3cdc3ef..383b5c379 100644 --- a/src/muz_qe/proof_utils.h +++ b/src/muz_qe/proof_utils.h @@ -42,6 +42,7 @@ public: */ static void push_instantiations_up(proof_ref& pr); + }; #endif diff --git a/src/muz_qe/qe_arith_plugin.cpp b/src/muz_qe/qe_arith_plugin.cpp index 1baee51fa..7bf0978f6 100644 --- a/src/muz_qe/qe_arith_plugin.cpp +++ b/src/muz_qe/qe_arith_plugin.cpp @@ -20,7 +20,7 @@ Revision History: #include "qe.h" #include "ast_pp.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "bool_rewriter.h" #include "bv_decl_plugin.h" #include "arith_decl_plugin.h" @@ -93,7 +93,7 @@ namespace qe { expr_ref m_one_r; expr_ref m_tmp; public: - scoped_ptr m_replace; + expr_safe_replace m_replace; bool_rewriter m_bool_rewriter; arith_rewriter m_arith_rewriter; @@ -111,7 +111,7 @@ namespace qe { m_zero_r(m_arith.mk_numeral(numeral(0), false), m), m_one_r(m_arith.mk_numeral(numeral(1), false), m), m_tmp(m), - m_replace(mk_default_expr_replacer(m)), + m_replace(m), m_bool_rewriter(m), m_arith_rewriter(m) { } @@ -827,7 +827,7 @@ namespace qe { while (index <= up) { expr* n = mk_numeral(index); result = body; - m_replace->apply_substitution(x, n, result); + m_replace.apply_substitution(x, n, result); ors.push_back(result); ++index; } @@ -857,7 +857,7 @@ namespace qe { mk_flat_and(e1, body, result); app_ref z(m); mk_bounded_var(up, z_bv, z); - m_replace->apply_substitution(x, z, result); + m_replace.apply_substitution(x, z, result); } @@ -966,7 +966,7 @@ namespace qe { << mk_pp(e, m) << "\n"; ); expr_ref result(fml, m); - m_replace->apply_substitution(x, e, result); + m_replace.apply_substitution(x, e, result); simplify(result); TRACE("qe", tout << "singular solved:\n" @@ -1044,7 +1044,7 @@ namespace qe { tout << " = 0\n"; ); expr_ref result(fml, m); - m_replace->apply_substitution(x, p1, result); + m_replace.apply_substitution(x, p1, result); simplify(result); m_ctx.elim_var(index-1, result, p1); TRACE("qe", tout << "Reduced: " << mk_pp(result, m) << "\n";); @@ -2080,7 +2080,7 @@ public: app* atm = atoms[i]; t1 = m_util.mk_add(m_util.mk_mul(coeffs[i], z), terms[i]); m_util.mk_divides(divisors[i], t1, new_atom); - m_util.m_replace->apply_substitution(atm, new_atom.get(), result); + m_util.m_replace.apply_substitution(atm, new_atom.get(), result); m_ctx.add_constraint(false, mk_not(atm), new_atom); m_ctx.add_constraint(false, mk_not(new_atom), atm); @@ -2121,7 +2121,7 @@ public: m_util.simplify(mod_term2); m_ctx.add_constraint(false, m.mk_eq(mod_term2, m_util.mk_zero(mod_term2))); - m_util.m_replace->apply_substitution(atm, z1, result); + m_util.m_replace.apply_substitution(atm, z1, result); // // conjoin (coeff*z + rest - z1) mod k == 0 to result @@ -2153,7 +2153,7 @@ public: for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, is_lower)[i]; m_ctx.add_constraint(true, mk_not(e)); - m_util.m_replace->apply_substitution(e, m.mk_false(), result); + m_util.m_replace.apply_substitution(e, m.mk_false(), result); } } @@ -2162,7 +2162,7 @@ public: for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, !is_lower)[i]; m_ctx.add_constraint(true, e); - m_util.m_replace->apply_substitution(e, m.mk_true(), result); + m_util.m_replace.apply_substitution(e, m.mk_true(), result); } } @@ -2276,7 +2276,7 @@ public: else { m_ctx.add_constraint(true, e); } - m_util.m_replace->apply_substitution(atm, m.mk_true(), result); + m_util.m_replace.apply_substitution(atm, m.mk_true(), result); continue; } @@ -2293,7 +2293,7 @@ public: (same_strict && i < index); mk_bound(result_is_strict, is_lower, a, t, b, s, tmp); - m_util.m_replace->apply_substitution(e, tmp.get(), result); + m_util.m_replace.apply_substitution(e, tmp.get(), result); TRACE("qe", tout << (result_is_strict?"strict result":"non-strict result") << "\n"; @@ -2330,7 +2330,7 @@ public: s = x_t.mk_term(b, s); b = x_t.mk_coeff(b); m_util.mk_resolve(x, strict_resolve, a, t, b, s, tmp); - m_util.m_replace->apply_substitution(e, tmp.get(), result); + m_util.m_replace.apply_substitution(e, tmp.get(), result); m_ctx.add_constraint(true, mk_not(e), tmp); @@ -2398,7 +2398,7 @@ public: weights_t m_weights; th_rewriter m_rewriter; nlarith::util m_util; - scoped_ptr m_replacer; + expr_safe_replace m_replace; expr_ref_vector m_trail; factor_rewriter_star m_factor_rw; bool m_produce_models; @@ -2407,7 +2407,7 @@ public: qe_solver_plugin(m, m.mk_family_id("arith"), ctx), m_rewriter(m), m_util(m), - m_replacer(mk_default_expr_replacer(m)), + m_replace(m), m_trail(m), m_factor_rw(m), m_produce_models(produce_models) { @@ -2480,12 +2480,11 @@ public: SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < brs->size()); unsigned j = vl.get_unsigned(); - expr_substitution sub(m); + m_replace.reset(); for (unsigned i = 0; i < brs->preds().size(); ++i) { - sub.insert(to_app(brs->preds(i)), brs->subst(j)[i]); + m_replace.insert(brs->preds(i), brs->subst(j)[i]); } - m_replacer->set_substitution(&sub); - (*m_replacer)(fml); + m_replace(fml); expr_ref tmp(m.mk_and(brs->constraints(j), fml), m); m_factor_rw(tmp, fml); if (def) { diff --git a/src/muz_qe/qe_array_plugin.cpp b/src/muz_qe/qe_array_plugin.cpp index 106a42338..c9de1d745 100644 --- a/src/muz_qe/qe_array_plugin.cpp +++ b/src/muz_qe/qe_array_plugin.cpp @@ -1,7 +1,7 @@ #include "qe.h" #include "array_decl_plugin.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "ast_pp.h" #include "arith_decl_plugin.h" @@ -11,13 +11,13 @@ namespace qe { class array_plugin : public qe_solver_plugin { - scoped_ptr m_replace; + expr_safe_replace m_replace; public: array_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("array"), ctx), - m_replace(mk_default_expr_replacer(m)) + m_replace(m) { } @@ -123,7 +123,7 @@ namespace qe { if (m_ctx.is_var(a, idx) && !m_ctx.contains(idx)(rhs)) { expr_ref result(fml, m); - m_replace->apply_substitution(a, rhs, result); + m_replace.apply_substitution(a, rhs, result); m_ctx.elim_var(idx, result, rhs); return true; } @@ -175,7 +175,7 @@ namespace qe { tout << "eq: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n"; ); expr_ref result(fml, m); - m_replace->apply_substitution(A, store_B_i_t, result); + m_replace.apply_substitution(A, store_B_i_t, result); m_ctx.add_var(B); m_ctx.elim_var(idx, result, store_B_i_t); return true; @@ -248,7 +248,7 @@ namespace qe { tout << "eq: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n"; ); expr_ref result(fml, m); - m_replace->apply_substitution(A, store_t, result); + m_replace.apply_substitution(A, store_t, result); m_ctx.elim_var(idx, result, store_t); return true; } diff --git a/src/muz_qe/qe_bool_plugin.cpp b/src/muz_qe/qe_bool_plugin.cpp index 782644c28..39a46ae55 100644 --- a/src/muz_qe/qe_bool_plugin.cpp +++ b/src/muz_qe/qe_bool_plugin.cpp @@ -25,18 +25,18 @@ Notes: --*/ #include "qe.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "ast_pp.h" #include "model_evaluator.h" namespace qe { class bool_plugin : public qe_solver_plugin { - scoped_ptr m_replace; + expr_safe_replace m_replace; public: bool_plugin(i_solver_context& ctx, ast_manager& m): qe_solver_plugin(m, m.get_basic_family_id(), ctx), - m_replace(mk_default_expr_replacer(m)) + m_replace(m) {} virtual void assign(contains_app& x, expr* fml, rational const& vl) { @@ -51,7 +51,7 @@ namespace qe { virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { SASSERT(vl.is_one() || vl.is_zero()); expr* tf = (vl.is_one())?m.mk_true():m.mk_false(); - m_replace->apply_substitution(x.x(), tf, 0, fml); + m_replace.apply_substitution(x.x(), tf, fml); if (def) { *def = tf; } @@ -103,12 +103,12 @@ namespace qe { app* a = to_app(e); expr* e1; if (m_ctx.is_var(a, idx)) { - m_replace->apply_substitution(a, m.mk_true(), 0, fml); + m_replace.apply_substitution(a, m.mk_true(), fml); m_ctx.elim_var(idx, fml, m.mk_true()); return true; } else if (m.is_not(e, e1) && m_ctx.is_var(e1, idx)) { - m_replace->apply_substitution(to_app(e1), m.mk_false(), 0, fml); + m_replace.apply_substitution(to_app(e1), m.mk_false(), fml); m_ctx.elim_var(idx, fml, m.mk_false()); return true; } @@ -148,7 +148,7 @@ namespace qe { } // only occurrences of 'x' must be in positive atoms def = m.mk_true(); - m_replace->apply_substitution(x, def, 0, fml); + m_replace.apply_substitution(x, def, fml); return true; } else if (!p && n) { @@ -161,7 +161,7 @@ namespace qe { if (x != *it && contains_x(*it)) return false; } def = m.mk_false(); - m_replace->apply_substitution(x, def, 0, fml); + m_replace.apply_substitution(x, def, fml); return true; } else if (contains_x(fml)) { diff --git a/src/muz_qe/qe_bv_plugin.cpp b/src/muz_qe/qe_bv_plugin.cpp index cae567111..df1f8c619 100644 --- a/src/muz_qe/qe_bv_plugin.cpp +++ b/src/muz_qe/qe_bv_plugin.cpp @@ -21,19 +21,19 @@ Notes: --*/ #include "qe.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "bv_decl_plugin.h" #include "model_evaluator.h" namespace qe { class bv_plugin : public qe_solver_plugin { - scoped_ptr m_replace; - bv_util m_bv; + expr_safe_replace m_replace; + bv_util m_bv; public: bv_plugin(i_solver_context& ctx, ast_manager& m): qe_solver_plugin(m, m.mk_family_id("bv"), ctx), - m_replace(mk_default_expr_replacer(m)), + m_replace(m), m_bv(m) {} @@ -48,7 +48,7 @@ namespace qe { virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { app_ref c(m_bv.mk_numeral(vl, m_bv.get_bv_size(x.x())), m); - m_replace->apply_substitution(x.x(), c, 0, fml); + m_replace.apply_substitution(x.x(), c, fml); if (def) { *def = m_bv.mk_numeral(vl, m_bv.get_bv_size(x.x())); } diff --git a/src/muz_qe/qe_datatype_plugin.cpp b/src/muz_qe/qe_datatype_plugin.cpp index 0b51f26af..9b77de42a 100644 --- a/src/muz_qe/qe_datatype_plugin.cpp +++ b/src/muz_qe/qe_datatype_plugin.cpp @@ -95,7 +95,7 @@ #include "qe.h" #include "datatype_decl_plugin.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "obj_pair_hashtable.h" #include "for_each_expr.h" #include "ast_pp.h" @@ -415,7 +415,7 @@ namespace qe { typedef obj_pair_map subst_map; datatype_util m_datatype_util; - scoped_ptr m_replace; + expr_safe_replace m_replace; eqs_cache m_eqs_cache; subst_map m_subst_cache; ast_ref_vector m_trail; @@ -424,7 +424,7 @@ namespace qe { datatype_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("datatype"), ctx), m_datatype_util(m), - m_replace(mk_default_expr_replacer(m)), + m_replace(m), m_trail(m) { } @@ -518,7 +518,7 @@ namespace qe { subst_clos* sub = 0; if (m_subst_cache.find(x.x(), c, sub)) { - m_replace->apply_substitution(x.x(), sub->first, 0, fml); + m_replace.apply_substitution(x.x(), sub->first, fml); add_def(sub->first, def); for (unsigned i = 0; i < sub->second.size(); ++i) { m_ctx.add_var(sub->second[i]); @@ -541,7 +541,7 @@ namespace qe { m_trail.push_back(t); add_def(t, def); - m_replace->apply_substitution(x.x(), t, 0, fml); + m_replace.apply_substitution(x.x(), t, fml); sub->first = t; m_subst_cache.insert(x.x(), c, sub); } @@ -673,7 +673,7 @@ namespace qe { fml = m.mk_and(is_c, fml); app_ref fresh_x(m.mk_fresh_const("x", s), m); m_ctx.add_var(fresh_x); - m_replace->apply_substitution(x, fresh_x, 0, fml); + m_replace.apply_substitution(x, fresh_x, fml); add_def(fresh_x, def); TRACE("qe", tout << "Add recognizer " << mk_pp(is_c, m) << "\n";); return; @@ -697,33 +697,33 @@ namespace qe { for (unsigned i = 0; i < eqs.num_recognizers(); ++i) { app* rec = eqs.recognizer(i); if (rec->get_decl() == r) { - m_replace->apply_substitution(rec, m.mk_true(), fml); + m_replace.apply_substitution(rec, m.mk_true(), fml); } else { - m_replace->apply_substitution(rec, m.mk_false(), fml); + m_replace.apply_substitution(rec, m.mk_false(), fml); } } for (unsigned i = 0; i < eqs.num_unsat(); ++i) { - m_replace->apply_substitution(eqs.unsat_atom(i), m.mk_false(), fml); + m_replace.apply_substitution(eqs.unsat_atom(i), m.mk_false(), fml); } if (idx < eqs.num_eqs()) { expr* t = eqs.eq(idx); expr* c = eqs.eq_cond(idx); add_def(t, def); - m_replace->apply_substitution(x, t, fml); + m_replace.apply_substitution(x, t, fml); if (!m.is_true(c)) { fml = m.mk_and(c, fml); } } else { for (unsigned i = 0; i < eqs.num_eqs(); ++i) { - m_replace->apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); + m_replace.apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); } for (unsigned i = 0; i < eqs.num_neqs(); ++i) { - m_replace->apply_substitution(eqs.neq_atom(i), m.mk_false(), fml); + m_replace.apply_substitution(eqs.neq_atom(i), m.mk_false(), fml); } if (def) { sort* s = m.get_sort(x); diff --git a/src/muz_qe/qe_dl_plugin.cpp b/src/muz_qe/qe_dl_plugin.cpp index 61466795b..a93301b4f 100644 --- a/src/muz_qe/qe_dl_plugin.cpp +++ b/src/muz_qe/qe_dl_plugin.cpp @@ -1,6 +1,6 @@ #include "qe.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "dl_decl_plugin.h" #include "obj_pair_hashtable.h" #include "ast_pp.h" @@ -35,7 +35,7 @@ namespace qe { class dl_plugin : public qe_solver_plugin { typedef obj_pair_map eqs_cache; - scoped_ptr m_replace; + expr_safe_replace m_replace; datalog::dl_decl_util m_util; expr_ref_vector m_trail; eqs_cache m_eqs_cache; @@ -44,7 +44,7 @@ namespace qe { public: dl_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("datalog_relation"), ctx), - m_replace(mk_default_expr_replacer(m)), + m_replace(m), m_util(m), m_trail(m) { @@ -140,7 +140,7 @@ namespace qe { void subst_small_domain(contains_app & x,eq_atoms& eqs, unsigned v,expr_ref & fml) { expr_ref vl(m_util.mk_numeral(v, m.get_sort(x.x())), m); - m_replace->apply_substitution(x.x(), vl, fml); + m_replace.apply_substitution(x.x(), vl, fml); } // assumes that all disequalities can be satisfied. @@ -148,15 +148,15 @@ namespace qe { SASSERT(w <= eqs.num_eqs()); if (w < eqs.num_eqs()) { expr* e = eqs.eq(w); - m_replace->apply_substitution(x.x(), e, fml); + m_replace.apply_substitution(x.x(), e, fml); } else { for (unsigned i = 0; i < eqs.num_eqs(); ++i) { - m_replace->apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); + m_replace.apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); } for (unsigned i = 0; i < eqs.num_neqs(); ++i) { - m_replace->apply_substitution(eqs.neq_atom(i), m.mk_true(), fml); + m_replace.apply_substitution(eqs.neq_atom(i), m.mk_true(), fml); } } } diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index 7aade28e2..4ebcbf2fd 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -75,7 +75,8 @@ namespace datalog { }; rel_context::rel_context(context& ctx) - : m_context(ctx), + : engine_base(ctx.get_manager(), "datalog"), + m_context(ctx), m(ctx.get_manager()), m_rmanager(ctx), m_answer(m), diff --git a/src/muz_qe/rel_context.h b/src/muz_qe/rel_context.h index 7b4ee551c..057d1c15f 100644 --- a/src/muz_qe/rel_context.h +++ b/src/muz_qe/rel_context.h @@ -30,7 +30,7 @@ namespace datalog { class context; typedef vector > fact_vector; - class rel_context { + class rel_context : public engine_base { context& m_context; ast_manager& m; relation_manager m_rmanager; @@ -59,11 +59,11 @@ namespace datalog { context& get_context() const { return m_context; } relation_base & get_relation(func_decl * pred); relation_base * try_get_relation(func_decl * pred) const; - expr_ref get_last_answer() { return m_answer; } + virtual expr_ref get_answer() { return m_answer; } bool output_profile() const; - lbool query(expr* q); + virtual lbool query(expr* q); lbool query(unsigned num_rels, func_decl * const* rels); void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, @@ -73,6 +73,9 @@ namespace datalog { void set_cancel(bool f); + virtual void cancel() { set_cancel(true); } + virtual void cleanup() { set_cancel(false);} + /** \brief Restrict the set of used predicates to \c res. diff --git a/src/muz_qe/tab_context.cpp b/src/muz_qe/tab_context.cpp index 653f5f188..4f4c14cc7 100644 --- a/src/muz_qe/tab_context.cpp +++ b/src/muz_qe/tab_context.cpp @@ -1657,6 +1657,7 @@ namespace datalog { }; tab::tab(context& ctx): + datalog::engine_base(ctx.get_manager(),"tabulation"), m_imp(alloc(imp, ctx)) { } tab::~tab() { diff --git a/src/muz_qe/tab_context.h b/src/muz_qe/tab_context.h index f0a2eefed..c23ccb7d3 100644 --- a/src/muz_qe/tab_context.h +++ b/src/muz_qe/tab_context.h @@ -22,23 +22,24 @@ Revision History: #include "ast.h" #include "lbool.h" #include "statistics.h" +#include "dl_util.h" namespace datalog { class context; - class tab { + class tab : public engine_base { class imp; imp* m_imp; public: tab(context& ctx); ~tab(); - lbool query(expr* query); - void cancel(); - void cleanup(); - void reset_statistics(); - void collect_statistics(statistics& st) const; - void display_certificate(std::ostream& out) const; - expr_ref get_answer(); + virtual lbool query(expr* query); + virtual void cancel(); + virtual void cleanup(); + virtual void reset_statistics(); + virtual void collect_statistics(statistics& st) const; + virtual void display_certificate(std::ostream& out) const; + virtual expr_ref get_answer(); }; }; diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index efcab14ea..56f60bfa0 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -75,7 +75,7 @@ static void display_statistics( out << "--------------\n"; out << "instructions \n"; - code.display(ctx.get_rel_context(), out); + code.display(*ctx.get_rel_context(), out); out << "--------------\n"; out << "big relations \n"; @@ -83,7 +83,7 @@ static void display_statistics( } out << "--------------\n"; out << "relation sizes\n"; - ctx.get_rel_context().get_rmanager().display_relation_sizes(out); + ctx.get_rel_context()->get_rmanager().display_relation_sizes(out); if (verbose) { out << "--------------\n"; @@ -125,7 +125,7 @@ unsigned read_datalog(char const * file) { params.set_sym("engine", symbol("datalog")); datalog::context ctx(m, s_params, params); - datalog::relation_manager & rmgr = ctx.get_rel_context().get_rmanager(); + datalog::relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); datalog::relation_plugin & inner_plg = *rmgr.get_relation_plugin(symbol("tr_hashtable")); SASSERT(&inner_plg); rmgr.register_plugin(alloc(datalog::finite_product_relation_plugin, inner_plg, rmgr)); @@ -187,7 +187,7 @@ unsigned read_datalog(char const * file) { datalog::compiler::compile(ctx, ctx.get_rules(), rules_code, termination_code); - TRACE("dl_compiler", rules_code.display(ctx.get_rel_context(), tout);); + TRACE("dl_compiler", rules_code.display(*ctx.get_rel_context(), tout);); rules_code.make_annotations(ex_ctx); @@ -227,10 +227,10 @@ unsigned read_datalog(char const * file) { TRACE("dl_compiler", ctx.display(tout); - rules_code.display(ctx.get_rel_context(), tout);); + rules_code.display(*ctx.get_rel_context(), tout);); if (ctx.get_params().output_tuples()) { - ctx.get_rel_context().display_output_facts(ctx.get_rules(), std::cout); + ctx.get_rel_context()->display_output_facts(ctx.get_rules(), std::cout); } display_statistics( diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index c2c50567f..3f96a1d62 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -688,7 +688,7 @@ namespace smt { } } } - + void theory_array_base::propagate_select_to_store_parents(enode * r, enode * sel, svector & todo) { SASSERT(r->get_root() == r); SASSERT(is_select(sel)); @@ -880,7 +880,7 @@ namespace smt { } else { theory_var r = mg_find(v); - void * else_val = m_else_values[r]; + void * else_val = m_else_values[r]; // DISABLED. It seems wrong, since different nodes can share the same // else_val according to the mg class. // SASSERT(else_val == 0 || get_context().is_relevant(UNTAG(app*, else_val))); From 13262a0fc52f9d3919046fb3444ebea22a9be2a3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Jun 2013 13:13:47 -0500 Subject: [PATCH 073/509] missing files Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_magic_symbolic.cpp | 133 ++++++++++++++++++++++++++++ src/muz_qe/dl_mk_magic_symbolic.h | 40 +++++++++ 2 files changed, 173 insertions(+) create mode 100644 src/muz_qe/dl_mk_magic_symbolic.cpp create mode 100644 src/muz_qe/dl_mk_magic_symbolic.h diff --git a/src/muz_qe/dl_mk_magic_symbolic.cpp b/src/muz_qe/dl_mk_magic_symbolic.cpp new file mode 100644 index 000000000..2820ecaef --- /dev/null +++ b/src/muz_qe/dl_mk_magic_symbolic.cpp @@ -0,0 +1,133 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_magic_symbolic.cpp + +Abstract: + + Create Horn clauses for magic symbolic flow. + + Q(x) :- A(y), B(z), phi1(x,y,z). + Q(x) :- C(y), phi2(x,y). + A(x) :- C(y), phi3(x,y). + A(x) :- A(y), phi3(x,y). + B(x) :- C(y), A(z), phi4(x,y,z). + C(x) :- phi5(x). + + Transformed clauses: + + Q_ans(x) :- Q_query(x), A_ans(y), B_ans(z), phi1(x,y,z). + Q_ans(x) :- Q_query(x), C_ans(y), phi2(x,y). + Q_query(x) :- true. + + A_ans(x) :- A_query(x), C_ans(y), phi2(x,y) + A_ans(x) :- A_query(x), A_ans(y), phi3(x,y). + A_query(y) :- Q_query(x), phi1(x,y,z). + A_query(y) :- A_query(x), phi3(x,y). + A_query(z) :- B_query(x), C_ans(y), phi4(x,y,z). + + B_ans(x) :- B_query(x), C_ans(y), A_ans(z), phi4(x,y,z). + B_query(z) :- Q_query(x), A_ans(y), phi1(x,y,z). + + C_ans(x) :- C_query(x), phi5(x). + C_query(y) :- Q_query(x), phi2(x,y). + C_query(y) :- Q_query(x), phi3(x,y). + C_query(y) :- B_query(x), phi4(x,y,z). + +General scheme: + A(x) :- P1(x_1), ..., Pn(x_n), phi(x,x1,..,x_n). + + P(x) :- Prefix(x,y,z), A(z) ... + + A_ans(x) :- A_query(x), P_i_ans(x_i), phi(x,..). + A_query(z) :- P_query(x), Prefix_ans(x,y,z). + +Author: + + Nikolaj Bjorner (nbjorner) 2013-06-19 + +Revision History: + +--*/ + +#include"dl_mk_magic_symbolic.h" +#include"dl_context.h" + +namespace datalog { + + + mk_magic_symbolic::mk_magic_symbolic(context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx) { + } + + mk_magic_symbolic::~mk_magic_symbolic() { } + + rule_set * mk_magic_symbolic::operator()(rule_set const & source) { + if (!m_ctx.get_params().magic()) { + return 0; + } + context& ctx = source.get_context(); + rule_manager& rm = source.get_rule_manager(); + rule_set * result = alloc(rule_set, ctx); + unsigned sz = source.get_num_rules(); + rule_ref new_rule(rm); + app_ref_vector tail(m); + app_ref head(m); + svector neg; + for (unsigned i = 0; i < sz; ++i) { + rule & r = *source.get_rule(i); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + tail.reset(); + neg.reset(); + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + neg.push_back(false); + } + tail.push_back(mk_query(r.get_head())); + neg.push_back(false); + for (unsigned j = 0; j < utsz; ++j) { + tail.push_back(mk_ans(r.get_tail(j))); + neg.push_back(false); + } + new_rule = rm.mk(mk_ans(r.get_head()), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + if (source.is_output_predicate(r.get_decl())) { + result->set_output_predicate(new_rule->get_decl()); + } + + result->add_rule(new_rule); + for (unsigned j = 0; j < utsz; ++j) { + new_rule = rm.mk(mk_query(r.get_tail(j)), tail.size()-utsz+j, tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + } + + } + TRACE("dl", result->display(tout);); + return result; + } + + app_ref mk_magic_symbolic::mk_query(app* q) { + string_buffer<64> name; + func_decl* f = q->get_decl(); + name << f->get_name() << "!query"; + func_decl_ref g(m); + g = m.mk_func_decl(symbol(name.c_str()), f->get_arity(), f->get_domain(), f->get_range()); + m_ctx.register_predicate(g, false); + return app_ref(m.mk_app(g, q->get_num_args(), q->get_args()), m); + } + + app_ref mk_magic_symbolic::mk_ans(app* q) { + string_buffer<64> name; + func_decl* f = q->get_decl(); + func_decl_ref g(m); + name << f->get_name() << "!ans"; + g = m.mk_func_decl(symbol(name.c_str()), f->get_arity(), f->get_domain(), f->get_range()); + m_ctx.register_predicate(g, false); + return app_ref(m.mk_app(g, q->get_num_args(), q->get_args()), m); + } + +}; diff --git a/src/muz_qe/dl_mk_magic_symbolic.h b/src/muz_qe/dl_mk_magic_symbolic.h new file mode 100644 index 000000000..ce9ca1145 --- /dev/null +++ b/src/muz_qe/dl_mk_magic_symbolic.h @@ -0,0 +1,40 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_magic_symbolic.h + +Abstract: + + Create Horn clauses for magic symbolic transformation. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-06-19 + +Revision History: + +--*/ +#ifndef _DL_MK_MAGIC_SYMBOLIC_H_ +#define _DL_MK_MAGIC_SYMBOLIC_H_ + +#include"dl_rule_transformer.h" + +namespace datalog { + + class mk_magic_symbolic : public rule_transformer::plugin { + ast_manager& m; + context& m_ctx; + app_ref mk_ans(app* q); + app_ref mk_query(app* q); + public: + mk_magic_symbolic(context & ctx, unsigned priority = 33037); + ~mk_magic_symbolic(); + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_MAGIC_SYMBOLIC_H_ */ + From 7cc6ff0a4c8248204f4209f87f62acdc2c5a8443 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 25 Jun 2013 12:25:41 -0700 Subject: [PATCH 074/509] changed timeout behavior in duality --- src/muz_qe/duality_dl_interface.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index f0a5c5b6b..275d59f5d 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -367,8 +367,14 @@ expr_ref dl_interface::get_answer() { } void dl_interface::cancel() { +#if 0 if(_d && _d->ls) _d->ls->cancel(); +#else + // HACK: duality can't cancel at all times, we just exit here + std::cout << "(error \"duality canceled\")\nunknown\n"; + exit(0); +#endif } void dl_interface::cleanup() { From 0d2a7f922c82ec0963ee059d2471541a4a11ad48 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 26 Jun 2013 18:16:25 +0100 Subject: [PATCH 075/509] FPA: sqrt precision bugfixes Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 53 +++++++++++++++++------------ 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 64052359e..a4961f555 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1485,16 +1485,16 @@ void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, // This is algorithm 10.2 in the Handbook of Floating-Point Arithmetic expr_ref Q(m), R(m), S(m), T(m); - const mpz & p2 = fu().fm().m_powers2(sbits); - Q = m_bv_util.mk_numeral(p2, sbits+2); - R = m_bv_util.mk_bv_sub(m_bv_util.mk_concat(sig_prime, zero1), Q); + const mpz & p2 = fu().fm().m_powers2(sbits+3); + Q = m_bv_util.mk_numeral(p2, sbits+5); + R = m_bv_util.mk_bv_sub(m_bv_util.mk_concat(sig_prime, m_bv_util.mk_numeral(0, 4)), Q); S = Q; - for (unsigned i = 0; i <= sbits; i++) { + for (unsigned i = 0; i < sbits + 3; i++) { dbg_decouple("fpa2bv_sqrt_Q", Q); dbg_decouple("fpa2bv_sqrt_R", R); - S = m_bv_util.mk_concat(zero1, m_bv_util.mk_extract(sbits+1, 1, S)); + S = m_bv_util.mk_concat(zero1, m_bv_util.mk_extract(sbits+4, 1, S)); expr_ref twoQ_plus_S(m); twoQ_plus_S = m_bv_util.mk_bv_add(m_bv_util.mk_concat(Q, zero1), m_bv_util.mk_concat(zero1, S)); @@ -1502,31 +1502,40 @@ void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, dbg_decouple("fpa2bv_sqrt_T", T); - SASSERT(m_bv_util.get_bv_size(Q) == sbits + 2); - SASSERT(m_bv_util.get_bv_size(R) == sbits + 2); - SASSERT(m_bv_util.get_bv_size(S) == sbits + 2); - SASSERT(m_bv_util.get_bv_size(T) == sbits + 3); + SASSERT(m_bv_util.get_bv_size(Q) == sbits + 5); + SASSERT(m_bv_util.get_bv_size(R) == sbits + 5); + SASSERT(m_bv_util.get_bv_size(S) == sbits + 5); + SASSERT(m_bv_util.get_bv_size(T) == sbits + 6); expr_ref t_lt_0(m); - m_simp.mk_eq(m_bv_util.mk_extract(sbits+2, sbits+2, T), one1, t_lt_0); + m_simp.mk_eq(m_bv_util.mk_extract(sbits+5, sbits+5, T), one1, t_lt_0); - m_simp.mk_ite(t_lt_0, Q, - m_bv_util.mk_bv_add(Q, S), + expr * or_args[2] = { Q, S }; + + m_simp.mk_ite(t_lt_0, Q, + m_bv_util.mk_bv_or(2, or_args), Q); - m_simp.mk_ite(t_lt_0, m_bv_util.mk_concat(m_bv_util.mk_extract(sbits, 0, R), zero1), - m_bv_util.mk_extract(sbits+1, 0, T), + m_simp.mk_ite(t_lt_0, m_bv_util.mk_concat(m_bv_util.mk_extract(sbits+3, 0, R), zero1), + m_bv_util.mk_extract(sbits+4, 0, T), R); } - + + expr_ref is_exact(m); + m_simp.mk_eq(R, m_bv_util.mk_numeral(0, sbits+5), is_exact); + dbg_decouple("fpa2bv_sqrt_is_exact", is_exact); + expr_ref rest(m), last(m), q_is_odd(m), rest_ext(m); last = m_bv_util.mk_extract(0, 0, Q); - rest = m_bv_util.mk_extract(sbits, 1, Q); - m_simp.mk_eq(last, one1, q_is_odd); - dbg_decouple("fpa2bv_sqrt_q_is_odd", q_is_odd); - rest_ext = m_bv_util.mk_zero_extend(1, m_bv_util.mk_concat(rest, m_bv_util.mk_numeral(0, 3))); - m_simp.mk_ite(q_is_odd, m_bv_util.mk_bv_add(rest_ext, m_bv_util.mk_numeral(8, sbits+4)), - rest_ext, - res_sig); + rest = m_bv_util.mk_extract(sbits+3, 1, Q); + dbg_decouple("fpa2bv_sqrt_last", last); + dbg_decouple("fpa2bv_sqrt_rest", rest); + rest_ext = m_bv_util.mk_zero_extend(1, rest); + expr_ref sticky(m); + m_simp.mk_ite(is_exact, m_bv_util.mk_zero_extend(sbits+3, last), + m_bv_util.mk_numeral(1, sbits+4), + sticky); + expr * or_args[2] = { rest_ext, sticky }; + res_sig = m_bv_util.mk_bv_or(2, or_args); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); From 42b3a81ef622ca9fe460d5d59dd4cf7504593245 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 27 Jun 2013 16:08:25 +0100 Subject: [PATCH 076/509] FPA: precision bugfixes for FMA Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 55 +++++++++++++++++------------ 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index a4961f555..234c343b8 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1134,6 +1134,9 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar mk_is_neg(z, z_is_neg); mk_is_inf(z, z_is_inf); + expr_ref rm_is_to_neg(m); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + dbg_decouple("fpa2bv_fma_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_fma_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_fma_x_is_pos", x_is_pos); @@ -1187,31 +1190,28 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar m_simp.mk_or(x_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, neg_x_sgn_inf, v5); - // z is +-INF -> Z. + // z is +-INF -> z. mk_is_inf(z, c6); v6 = z; - // (x is 0) || (y is 0) -> x but with sign = x.sign ^ y.sign + // (x is 0) || (y is 0) -> z m_simp.mk_or(x_is_zero, y_is_zero, c7); - expr_ref sign_xor(m); - m_simp.mk_xor(x_is_pos, y_is_pos, sign_xor); - mk_ite(sign_xor, nzero, pzero, v7); + expr_ref ite_c(m); + m_simp.mk_and(z_is_zero, m.mk_not(rm_is_to_neg), ite_c); + mk_ite(ite_c, pzero, z, v7); // else comes the fused multiplication. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); - SASSERT(ebits <= sbits); - - expr_ref rm_is_to_neg(m); - mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + SASSERT(ebits <= sbits); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); expr_ref c_sgn(m), c_sig(m), c_exp(m), c_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); - unpack(z, c_sgn, c_sig, c_exp, c_lz, true); + unpack(z, c_sgn, c_sig, c_exp, c_lz, true); expr_ref a_lz_ext(m), b_lz_ext(m), c_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); @@ -1220,12 +1220,12 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); - b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); + b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); expr_ref a_exp_ext(m), b_exp_ext(m), c_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); - c_exp_ext = m_bv_util.mk_sign_extend(2, c_exp); + c_exp_ext = m_bv_util.mk_sign_extend(2, c_exp); dbg_decouple("fpa2bv_fma_a_sig", a_sig_ext); dbg_decouple("fpa2bv_fma_b_sig", b_sig_ext); @@ -1233,6 +1233,9 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar dbg_decouple("fpa2bv_fma_a_exp", a_exp_ext); dbg_decouple("fpa2bv_fma_b_exp", b_exp_ext); dbg_decouple("fpa2bv_fma_c_exp", c_exp_ext); + dbg_decouple("fpa2bv_fma_a_lz", a_lz_ext); + dbg_decouple("fpa2bv_fma_b_lz", b_lz_ext); + dbg_decouple("fpa2bv_fma_c_lz", c_lz_ext); expr_ref mul_sgn(m), mul_sig(m), mul_exp(m); expr * signs[2] = { a_sgn, b_sgn }; @@ -1254,6 +1257,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar // Extend c c_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, sbits-1))); + c_exp_ext = m_bv_util.mk_bv_sub(c_exp_ext, c_lz_ext); SASSERT(m_bv_util.get_bv_size(mul_sig) == 2 * sbits); SASSERT(m_bv_util.get_bv_size(c_sig) == 2 * sbits); @@ -1269,7 +1273,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar m_simp.mk_ite(swap_cond, c_exp_ext, mul_exp, e_exp); // has ebits + 2 m_simp.mk_ite(swap_cond, mul_sgn, c_sgn, f_sgn); m_simp.mk_ite(swap_cond, mul_sig, c_sig, f_sig); // has 2 * sbits - m_simp.mk_ite(swap_cond, mul_exp, c_exp_ext, f_exp); // has ebits + 2 + m_simp.mk_ite(swap_cond, mul_exp, c_exp_ext, f_exp); // has ebits + 2 SASSERT(is_well_sorted(m, e_sgn)); SASSERT(is_well_sorted(m, e_sig)); @@ -1303,7 +1307,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar shifted_big = m_bv_util.mk_bv_lshr( m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), m_bv_util.mk_zero_extend((3*sbits)-(ebits+2), exp_delta)); - shifted_f_sig = m_bv_util.mk_zero_extend(sbits, m_bv_util.mk_extract(3*sbits-1, 2*sbits, shifted_big)); + shifted_f_sig = m_bv_util.mk_extract(3*sbits-1, sbits, shifted_big); sticky_raw = m_bv_util.mk_extract(sbits-1, 0, shifted_big); SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2 * sbits); SASSERT(is_well_sorted(m, shifted_f_sig)); @@ -1334,7 +1338,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar expr_ref sum(m); m_simp.mk_ite(eq_sgn, - m_bv_util.mk_bv_add(e_sig, shifted_f_sig), // ADD LZ + m_bv_util.mk_bv_add(e_sig, shifted_f_sig), m_bv_util.mk_bv_sub(e_sig, shifted_f_sig), sum); @@ -1354,7 +1358,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar dbg_decouple("fpa2bv_fma_add_n_sum", n_sum); dbg_decouple("fpa2bv_fma_add_sig_abs", sig_abs); - res_exp = m_bv_util.mk_bv_sub(e_exp, c_lz_ext); + res_exp = e_exp; // Result could overflow into 4.xxx ... @@ -1371,8 +1375,11 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); sticky_raw = m_bv_util.mk_extract(sbits-5, 0, sig_abs); - sticky = m_bv_util.mk_zero_extend(sbits+7, m.mk_app(bvfid, OP_BREDOR, sticky_raw.get())); - res_sig = m_bv_util.mk_extract(2*sbits-1, sbits-4, sig_abs); + sticky = m_bv_util.mk_zero_extend(sbits+3, m.mk_app(bvfid, OP_BREDOR, sticky_raw.get())); + dbg_decouple("fpa2bv_fma_add_sum_sticky", sticky); + + expr * res_or_args[2] = { m_bv_util.mk_extract(2*sbits-1, sbits-4, sig_abs), sticky }; + res_sig = m_bv_util.mk_bv_or(2, res_or_args); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits+4); expr_ref is_zero_sig(m), nil_sbits4(m); @@ -2264,14 +2271,16 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref zero_e = m_bv_util.mk_numeral(0, ebits); if (normalize) { + expr_ref is_sig_zero(m), zero_s(m); + zero_s = m_bv_util.mk_numeral(0, sbits); + m_simp.mk_eq(zero_s, denormal_sig, is_sig_zero); + expr_ref lz_d(m); - mk_leading_zeros(denormal_sig, ebits, lz_d); - m_simp.mk_ite(is_normal, zero_e, lz_d, lz); + mk_leading_zeros(denormal_sig, ebits, lz_d); + m_simp.mk_ite(m.mk_or(is_normal, is_sig_zero), zero_e, lz_d, lz); dbg_decouple("fpa2bv_unpack_lz", lz); - expr_ref is_sig_zero(m), shift(m), zero_s(m); - zero_s = m_bv_util.mk_numeral(0, sbits); - m_simp.mk_eq(zero_s, denormal_sig, is_sig_zero); + expr_ref shift(m); m_simp.mk_ite(is_sig_zero, zero_e, lz, shift); dbg_decouple("fpa2bv_unpack_shift", shift); SASSERT(is_well_sorted(m, is_sig_zero)); From f238720b768ad9262dc7976c907672383195afe7 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 27 Jun 2013 09:19:23 -0700 Subject: [PATCH 077/509] Cherry-pick goodies from mcsat branch Signed-off-by: Leonardo de Moura --- src/ast/ast_util.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++ src/ast/ast_util.h | 31 ++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/src/ast/ast_util.cpp b/src/ast/ast_util.cpp index 8b0b3fa04..d77deca01 100644 --- a/src/ast/ast_util.cpp +++ b/src/ast/ast_util.cpp @@ -1,3 +1,21 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + ast_util.cpp + +Abstract: + + Helper functions + +Author: + + Leonardo de Moura (leonardo) 2007-06-08. + +Revision History: + +--*/ #include "ast_util.h" app * mk_list_assoc_app(ast_manager & m, func_decl * f, unsigned num_args, expr * const * args) { @@ -138,3 +156,38 @@ expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx) { SASSERT(m.is_or(cls)); return to_app(cls)->get_arg(idx); } + +expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args) { + if (num_args == 0) + return m.mk_true(); + else if (num_args == 1) + return args[0]; + else + return m.mk_and(num_args, args); +} + +expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args) { + if (num_args == 0) + return m.mk_false(); + else if (num_args == 1) + return args[0]; + else + return m.mk_or(num_args, args); +} + +expr * mk_not(ast_manager & m, expr * arg) { + expr * atom; + if (m.is_not(arg, atom)) + return atom; + else + return m.mk_not(arg); +} + +expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) { + expr_ref_buffer new_diseqs(m); + for (unsigned i = 0; i < num_args; i++) { + for (unsigned j = i + 1; j < num_args; j++) + new_diseqs.push_back(m.mk_not(m.mk_eq(args[i], args[j]))); + } + return mk_and(m, new_diseqs.size(), new_diseqs.c_ptr()); +} diff --git a/src/ast/ast_util.h b/src/ast/ast_util.h index c7f54da01..1e813b1b0 100644 --- a/src/ast/ast_util.h +++ b/src/ast/ast_util.h @@ -95,5 +95,36 @@ bool is_clause(ast_manager & m, expr * n); unsigned get_clause_num_literals(ast_manager & m, expr * cls); expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx); +// ----------------------------------- +// +// Goodies for creating Boolean expressions +// +// ----------------------------------- + +/** + Return (and args[0] ... args[num_args-1]) if num_args >= 2 + Return args[0] if num_args == 1 + Return true if num_args == 0 + */ +expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args); + +/** + Return (or args[0] ... args[num_args-1]) if num_args >= 2 + Return args[0] if num_args == 1 + Return false if num_args == 0 + */ +expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args); + +/** + Return a if arg = (not a) + Retur (not arg) otherwise + */ +expr * mk_not(ast_manager & m, expr * arg); + +/** + Return the expression (and (not (= args[0] args[1])) (not (= args[0] args[2])) ... (not (= args[num_args-2] args[num_args-1]))) +*/ +expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args); + #endif /* _AST_UTIL_H_ */ From 5b7201a911a0e5f0e4efabc143ebac4be6e41c13 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 27 Jun 2013 09:30:25 -0700 Subject: [PATCH 078/509] Fix minor problem Signed-off-by: Leonardo de Moura --- src/smt/tactic/ctx_solver_simplify_tactic.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.cpp b/src/smt/tactic/ctx_solver_simplify_tactic.cpp index 5668ca455..63c14f297 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.cpp +++ b/src/smt/tactic/ctx_solver_simplify_tactic.cpp @@ -23,7 +23,7 @@ Notes: #include"smt_kernel.h" #include"ast_pp.h" #include"mk_simplified_app.h" - +#include"ast_util.h" class ctx_solver_simplify_tactic : public tactic { ast_manager& m; @@ -104,7 +104,7 @@ protected: return; ptr_vector fmls; g.get_formulas(fmls); - fml = m.mk_and(fmls.size(), fmls.c_ptr()); + fml = mk_and(m, fmls.size(), fmls.c_ptr()); m_solver.push(); reduce(fml); m_solver.pop(1); @@ -119,7 +119,7 @@ protected: { m_solver.push(); expr_ref fml1(m); - fml1 = m.mk_and(fmls.size(), fmls.c_ptr()); + fml1 = mk_and(m, fmls.size(), fmls.c_ptr()); fml1 = m.mk_iff(fml, fml1); fml1 = m.mk_not(fml1); m_solver.assert_expr(fml1); From 619bd91ddb30c6b6283409be567336ee390e692d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 27 Jun 2013 11:59:40 -0500 Subject: [PATCH 079/509] fix bug in ctx-solver-simplify reported @ http://z3.codeplex.com/workitem/51 Signed-off-by: Nikolaj Bjorner --- src/smt/tactic/ctx_solver_simplify_tactic.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.cpp b/src/smt/tactic/ctx_solver_simplify_tactic.cpp index 5668ca455..626cb6d22 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.cpp +++ b/src/smt/tactic/ctx_solver_simplify_tactic.cpp @@ -218,7 +218,7 @@ protected: else if (m.is_bool(arg)) { res = local_simplify(a, n, id, i); TRACE("ctx_solver_simplify_tactic", - tout << "Already cached: " << path_r.first << " " << mk_pp(res, m) << "\n";); + tout << "Already cached: " << path_r.first << " " << mk_pp(arg, m) << " |-> " << mk_pp(res, m) << "\n";); args.push_back(res); } else { @@ -327,7 +327,7 @@ protected: tmp = m.mk_eq(result, n); m_solver.assert_expr(tmp); if (!simplify_bool(n2, result)) { - result = a; + result = a->get_arg(index); } m_solver.pop(1); return result; From 4d939c07a34f440f87be91a8882fda13a31bfaab Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 27 Jun 2013 11:28:38 -0700 Subject: [PATCH 080/509] fixed bug in range computation --- src/interp/iz3scopes.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h index 6e2261d0c..54cf89aba 100755 --- a/src/interp/iz3scopes.h +++ b/src/interp/iz3scopes.h @@ -98,8 +98,13 @@ class scopes { } void range_add(int i, range &n){ +#if 0 if(i < n.lo) n.lo = i; if(i > n.hi) n.hi = i; +#else + range rng; rng.lo = i; rng.hi = i; + n = range_lub(rng,n); +#endif } /** Choose an element of rng1 that is near to rng2 */ From ea127c8ab93ecadb2a07f7dbe1bd2aec78b84614 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 27 Jun 2013 12:24:18 -0700 Subject: [PATCH 081/509] some confusion about proof generation --- src/tactic/goal.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index 03a13aba5..51d7fedb0 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -255,7 +255,8 @@ void goal::get_formulas(ptr_vector & result) { } void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { - SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr))); + // KLM: don't know why this assertion is no longer true + // SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr))); if (m_inconsistent) return; if (proofs_enabled()) { From d8b31773b80965ae7bf7bf9e1766aef86f44b2ed Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 27 Jun 2013 17:27:36 -0700 Subject: [PATCH 082/509] some debugging stuff --- src/interp/iz3interp.cpp | 15 ++++++++++++++- src/interp/iz3translate_direct.cpp | 10 ++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index aac8648ba..ac6970231 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -190,6 +190,16 @@ public: return true; } + void test_secondary(const std::vector &cnsts, + const std::vector &parents, + std::vector &interps + ){ + int num = cnsts.size(); + iz3secondary *sp = iz3foci::create(this,num,(int *)(parents.empty()?0:&parents[0])); + int res = sp->interpolate(cnsts, interps); + if(res != 0) + throw "secondary failed"; + } void proof_to_interpolant(z3pf proof, const std::vector &cnsts, @@ -198,7 +208,10 @@ public: const std::vector &theory, interpolation_options_struct *options = 0 ){ - +#if 0 + test_secondary(cnsts,parents,interps); + return; +#endif profiling::timer_start("Interpolation prep"); // get rid of frames not used in proof diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index da05c6642..58ebb67f7 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -39,6 +39,7 @@ using namespace stl_ext; static int lemma_count = 0; +static int nll_lemma_count = 0; #define SHOW_LEMMA_COUNT -1 // One half of a resolution. We need this to distinguish @@ -764,7 +765,8 @@ public: #if 1 - // std::cout << "lemma: " << ++lemma_count << "\n"; + ++lemma_count; + // std::cout << "lemma: " << lemma_count << "\n"; if(lemma_count == SHOW_LEMMA_COUNT){ for(unsigned i = 0; i < lits.size(); i++) show_lit(lits[i]); @@ -1337,7 +1339,7 @@ public: return res; } -// #define NEW_EXTRACT_TH_LEMMA + //#define NEW_EXTRACT_TH_LEMMA void get_local_hyps(const ast &proof, std::set &res){ std::set hyps = get_hyps(proof); @@ -1372,7 +1374,7 @@ public: } } #ifdef NEW_EXTRACT_TH_LEMMA - bool lemma_nll = nargs > 1; + bool lemma_nll = nprems > 1; if(nll && !lemma_nll){ lemma_nll = false; // std::cout << "lemma count = " << nll_lemma_count << "\n"; @@ -1394,7 +1396,7 @@ public: try { res = extract_th_lemma_common(lits,nll,lemma_nll); } -#if 0 +#if 1 catch (const invalid_lemma &) { std::cout << "\n\nlemma: " << my_count; std::cout << "\n\nproof node: \n"; From 41f77ab57cadc2fba75cdffc025959e51ca939fb Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 27 Jun 2013 17:29:12 -0700 Subject: [PATCH 083/509] duality abort hack and debugging hacks --- src/interp/iz3translate_direct.cpp | 6 ++++-- src/muz_qe/duality_dl_interface.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index da05c6642..1b93e9828 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -764,7 +764,7 @@ public: #if 1 - // std::cout << "lemma: " << ++lemma_count << "\n"; + std::cout << "lemma: " << ++lemma_count << std::endl; if(lemma_count == SHOW_LEMMA_COUNT){ for(unsigned i = 0; i < lits.size(); i++) show_lit(lits[i]); @@ -786,6 +786,8 @@ public: int sat = secondary->interpolate(preds,itps); profiling::timer_stop("foci"); + std::cout << "lemma done" << std::endl; + // if sat, lemma isn't valid, something is wrong if(sat){ #if 1 @@ -1337,7 +1339,7 @@ public: return res; } -// #define NEW_EXTRACT_TH_LEMMA +#define NEW_EXTRACT_TH_LEMMA void get_local_hyps(const ast &proof, std::set &res){ std::set hyps = get_hyps(proof); diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz_qe/duality_dl_interface.cpp index 275d59f5d..016731e3a 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz_qe/duality_dl_interface.cpp @@ -373,7 +373,7 @@ void dl_interface::cancel() { #else // HACK: duality can't cancel at all times, we just exit here std::cout << "(error \"duality canceled\")\nunknown\n"; - exit(0); + abort(); #endif } From 4f72e1d5289b5afce868377a286e7f325f65e0f0 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 28 Jun 2013 12:14:14 +0100 Subject: [PATCH 084/509] FPA: avoid compiler warnings. Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 234c343b8..ff24d1ceb 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -2437,7 +2437,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & t = m_bv_util.mk_bv_sub(t, lz); t = m_bv_util.mk_bv_sub(t, m_bv_util.mk_sign_extend(2, e_min)); expr_ref TINY(m); - TINY = m_bv_util.mk_sle(t, m_bv_util.mk_numeral(-1, ebits+2)); + TINY = m_bv_util.mk_sle(t, m_bv_util.mk_numeral((unsigned)-1, ebits+2)); TRACE("fpa2bv_dbg", tout << "TINY = " << mk_ismt2_pp(TINY, m) << std::endl;); SASSERT(is_well_sorted(m, TINY)); @@ -2481,7 +2481,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & m_simp.mk_ite(sigma_le_cap, sigma_neg, sigma_cap, sigma_neg_capped); dbg_decouple("fpa2bv_rnd_sigma_neg", sigma_neg); dbg_decouple("fpa2bv_rnd_sigma_neg_capped", sigma_neg_capped); - sigma_lt_zero = m_bv_util.mk_sle(sigma, m_bv_util.mk_numeral(-1, sigma_size)); + sigma_lt_zero = m_bv_util.mk_sle(sigma, m_bv_util.mk_numeral((unsigned)-1, sigma_size)); dbg_decouple("fpa2bv_rnd_sigma_lt_zero", sigma_lt_zero); sig_ext = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, sig_size)); From d9941c0ccc871881d4bbc1625bfdc005d4618ae8 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 28 Jun 2013 19:21:27 -0700 Subject: [PATCH 085/509] Add code for rejecting bitvector constants of size 0 Signed-off-by: Leonardo de Moura --- src/ast/bv_decl_plugin.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 8b77244f9..f1c61619a 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -210,6 +210,10 @@ func_decl * bv_decl_plugin::mk_unary(ptr_vector & decls, decl_kind k, func_decl * bv_decl_plugin::mk_int2bv(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain) { + if (bv_size == 0) { + m_manager->raise_exception("bit-vector size must be greater than zero"); + } + force_ptr_array_size(m_int2bv, bv_size + 1); if (arity != 1) { @@ -415,6 +419,9 @@ func_decl * bv_decl_plugin::mk_num_decl(unsigned num_parameters, parameter const return 0; } unsigned bv_size = parameters[1].get_int(); + if (bv_size == 0) { + m_manager->raise_exception("bit-vector size must be greater than zero"); + } // TODO: sign an error if the parameters[0] is out of range, that is, it is a value not in [0, 2^{bv_size}) // This cannot be enforced now, since some Z3 modules try to generate these invalid numerals. // After SMT-COMP, I should find all offending modules. From 210bca8f456361f696152be909e33a4e8b58607f Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 2 Jul 2013 12:57:54 +0100 Subject: [PATCH 086/509] .NET Example: Sudoku example bugfix. Many thanks to Ilya Mironov for reporting this issue. Signed-off-by: Christoph M. Wintersteiger --- examples/dotnet/Program.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/dotnet/Program.cs b/examples/dotnet/Program.cs index 2085de296..4361cab96 100644 --- a/examples/dotnet/Program.cs +++ b/examples/dotnet/Program.cs @@ -474,7 +474,7 @@ namespace test_mapi cells_c[i] = new BoolExpr[9]; for (uint j = 0; j < 9; j++) cells_c[i][j] = ctx.MkAnd(ctx.MkLe(ctx.MkInt(1), X[i][j]), - ctx.MkLe(X[i][j], ctx.MkInt(9))); + ctx.MkLe(X[i][j], ctx.MkInt(9))); } // each row contains a digit at most once @@ -485,7 +485,13 @@ namespace test_mapi // each column contains a digit at most once BoolExpr[] cols_c = new BoolExpr[9]; for (uint j = 0; j < 9; j++) - cols_c[j] = ctx.MkDistinct(X[j]); + { + IntExpr[] column = new IntExpr[9]; + for (uint i = 0; i < 9; i++) + column[i] = X[i][j]; + + cols_c[j] = ctx.MkDistinct(column); + } // each 3x3 square contains a digit at most once BoolExpr[][] sq_c = new BoolExpr[3][]; @@ -2087,7 +2093,7 @@ namespace test_mapi { QuantifierExample3(ctx); QuantifierExample4(ctx); - } + } Log.Close(); if (Log.isOpen()) From ccb36f1ae7121a895c75500998a9d6e219108e0e Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Tue, 9 Jul 2013 08:22:17 -0700 Subject: [PATCH 087/509] Fix issue https://z3.codeplex.com/workitem/54 Signed-off-by: Leonardo de Moura --- src/api/c++/z3++.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index a255c2d97..6f745d620 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1305,7 +1305,7 @@ namespace z3 { expr as_expr() const { unsigned n = size(); if (n == 0) - return ctx().bool_val(false); + return ctx().bool_val(true); else if (n == 1) return operator[](0); else { From 784455d1fc4d7d734ab6ee35dd33bee48b176de4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 10 Jul 2013 17:20:44 +0300 Subject: [PATCH 088/509] detect approximate relations to return unknown, fix product relations, fix symbolic magic set transformation Signed-off-by: Nikolaj Bjorner --- src/api/api_datalog.cpp | 206 +++++++++++++++------------ src/api/api_datalog.h | 36 +---- src/muz_qe/dl_base.h | 3 +- src/muz_qe/dl_bound_relation.cpp | 5 +- src/muz_qe/dl_bound_relation.h | 2 + src/muz_qe/dl_cmds.cpp | 6 +- src/muz_qe/dl_context.cpp | 3 +- src/muz_qe/dl_context.h | 1 + src/muz_qe/dl_instruction.cpp | 7 +- src/muz_qe/dl_interval_relation.cpp | 3 +- src/muz_qe/dl_interval_relation.h | 1 + src/muz_qe/dl_mk_karr_invariants.cpp | 2 + src/muz_qe/dl_mk_karr_invariants.h | 1 + src/muz_qe/dl_mk_magic_symbolic.cpp | 4 +- src/muz_qe/dl_product_relation.cpp | 6 +- src/muz_qe/dl_product_relation.h | 10 ++ src/muz_qe/dl_relation_manager.cpp | 18 +-- src/muz_qe/rel_context.cpp | 23 ++- 18 files changed, 182 insertions(+), 155 deletions(-) diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index bf4752645..64b8b064e 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -29,107 +29,123 @@ Revision History: #include"dl_cmds.h" #include"cmd_context.h" #include"smt2parser.h" +#include"dl_context.h" +#include"dl_external_relation.h" +#include"dl_decl_plugin.h" namespace api { - - fixedpoint_context::fixedpoint_context(ast_manager& m, smt_params& p) : - m_state(0), - m_reduce_app(0), - m_reduce_assign(0), - m_context(m, p), - m_trail(m) {} - - - void fixedpoint_context::set_state(void* state) { - SASSERT(!m_state); - m_state = state; - symbol name("datalog_relation"); - ast_manager& m = m_context.get_manager(); - if (!m.has_plugin(name)) { - m.register_plugin(name, alloc(datalog::dl_decl_plugin)); - } - datalog::rel_context* rel = m_context.get_rel_context(); - if (rel) { - datalog::relation_manager& r = rel->get_rmanager(); - r.register_plugin(alloc(datalog::external_relation_plugin, *this, r)); - } - } - - void fixedpoint_context::reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { - expr* r = 0; - if (m_reduce_app) { - m_reduce_app(m_state, f, num_args, args, &r); - result = r; - m_trail.push_back(f); - for (unsigned i = 0; i < num_args; ++i) { - m_trail.push_back(args[i]); - } - m_trail.push_back(r); - } - // allow fallthrough. - if (r == 0) { + + class fixedpoint_context : public datalog::external_relation_context { + void * m_state; + reduce_app_callback_fptr m_reduce_app; + reduce_assign_callback_fptr m_reduce_assign; + datalog::context m_context; + ast_ref_vector m_trail; + public: + fixedpoint_context(ast_manager& m, smt_params& p): + m_state(0), + m_reduce_app(0), + m_reduce_assign(0), + m_context(m, p), + m_trail(m) {} + + virtual ~fixedpoint_context() {} + family_id get_family_id() const { return const_cast(m_context).get_decl_util().get_family_id(); } + void set_state(void* state) { + SASSERT(!m_state); + m_state = state; + symbol name("datalog_relation"); ast_manager& m = m_context.get_manager(); - result = m.mk_app(f, num_args, args); - } - } - - // overwrite terms passed in outs vector with values computed by function. - void fixedpoint_context::reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { - if (m_reduce_assign) { - m_trail.push_back(f); - for (unsigned i = 0; i < num_args; ++i) { - m_trail.push_back(args[i]); + if (!m.has_plugin(name)) { + m.register_plugin(name, alloc(datalog::dl_decl_plugin)); + } + datalog::rel_context* rel = m_context.get_rel_context(); + if (rel) { + datalog::relation_manager& r = rel->get_rmanager(); + r.register_plugin(alloc(datalog::external_relation_plugin, *this, r)); } - m_reduce_assign(m_state, f, num_args, args, num_out, outs); } - } - - - void fixedpoint_context::add_rule(expr* rule, symbol const& name) { - m_context.add_rule(rule, name); - } - - void fixedpoint_context::update_rule(expr* rule, symbol const& name) { - m_context.update_rule(rule, name); - } - - void fixedpoint_context::add_table_fact(func_decl* r, unsigned num_args, unsigned args[]) { - m_context.add_table_fact(r, num_args, args); - } - - unsigned fixedpoint_context::get_num_levels(func_decl* pred) { - return m_context.get_num_levels(pred); - } - - expr_ref fixedpoint_context::get_cover_delta(int level, func_decl* pred) { - return m_context.get_cover_delta(level, pred); - } - - void fixedpoint_context::add_cover(int level, func_decl* pred, expr* predicate) { - m_context.add_cover(level, pred, predicate); - } - - std::string fixedpoint_context::get_last_status() { - datalog::execution_result status = m_context.get_status(); - switch(status) { - case datalog::INPUT_ERROR: - return "input error"; - case datalog::OK: - return "ok"; - case datalog::TIMEOUT: - return "timeout"; - default: - UNREACHABLE(); - return "unknown"; + void set_reduce_app(reduce_app_callback_fptr f) { + m_reduce_app = f; } - } - - std::string fixedpoint_context::to_string(unsigned num_queries, expr*const* queries) { - std::stringstream str; - m_context.display_smt2(num_queries, queries, str); - return str.str(); - } - + void set_reduce_assign(reduce_assign_callback_fptr f) { + m_reduce_assign = f; + } + virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { + expr* r = 0; + if (m_reduce_app) { + m_reduce_app(m_state, f, num_args, args, &r); + result = r; + m_trail.push_back(f); + for (unsigned i = 0; i < num_args; ++i) { + m_trail.push_back(args[i]); + } + m_trail.push_back(r); + } + // allow fallthrough. + if (r == 0) { + ast_manager& m = m_context.get_manager(); + result = m.mk_app(f, num_args, args); + } + } + virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { + if (m_reduce_assign) { + m_trail.push_back(f); + for (unsigned i = 0; i < num_args; ++i) { + m_trail.push_back(args[i]); + } + m_reduce_assign(m_state, f, num_args, args, num_out, outs); + } + } + datalog::context& ctx() { return m_context; } + void add_rule(expr* rule, symbol const& name) { + m_context.add_rule(rule, name); + } + void update_rule(expr* rule, symbol const& name) { + m_context.update_rule(rule, name); + } + void add_table_fact(func_decl* r, unsigned num_args, unsigned args[]) { + m_context.add_table_fact(r, num_args, args); + } + std::string get_last_status() { + datalog::execution_result status = m_context.get_status(); + switch(status) { + case datalog::INPUT_ERROR: + return "input error"; + case datalog::OK: + return "ok"; + case datalog::TIMEOUT: + return "timeout"; + case datalog::APPROX: + return "approximated"; + default: + UNREACHABLE(); + return "unknown"; + } + } + std::string to_string(unsigned num_queries, expr*const* queries) { + std::stringstream str; + m_context.display_smt2(num_queries, queries, str); + return str.str(); + } + void cancel() { + m_context.cancel(); + } + void reset_cancel() { + m_context.reset_cancel(); + } + unsigned get_num_levels(func_decl* pred) { + return m_context.get_num_levels(pred); + } + expr_ref get_cover_delta(int level, func_decl* pred) { + return m_context.get_cover_delta(level, pred); + } + void add_cover(int level, func_decl* pred, expr* predicate) { + m_context.add_cover(level, pred, predicate); + } + void collect_param_descrs(param_descrs & p) { m_context.collect_params(p); } + void updt_params(params_ref const& p) { m_context.updt_params(p); } + }; }; extern "C" { diff --git a/src/api/api_datalog.h b/src/api/api_datalog.h index 51dbb72ec..f5bfada33 100644 --- a/src/api/api_datalog.h +++ b/src/api/api_datalog.h @@ -22,48 +22,14 @@ Revision History: #include"z3.h" #include"ast.h" #include"smt_params.h" -#include"dl_external_relation.h" -#include"dl_decl_plugin.h" #include"smt_kernel.h" #include"api_util.h" -#include"dl_context.h" typedef void (*reduce_app_callback_fptr)(void*, func_decl*, unsigned, expr*const*, expr**); typedef void (*reduce_assign_callback_fptr)(void*, func_decl*, unsigned, expr*const*, unsigned, expr*const*); namespace api { - - class fixedpoint_context : public datalog::external_relation_context { - void * m_state; - reduce_app_callback_fptr m_reduce_app; - reduce_assign_callback_fptr m_reduce_assign; - datalog::context m_context; - ast_ref_vector m_trail; - public: - fixedpoint_context(ast_manager& m, smt_params& p); - virtual ~fixedpoint_context() {} - family_id get_family_id() const { return const_cast(m_context).get_decl_util().get_family_id(); } - void set_state(void* state); - void set_reduce_app(reduce_app_callback_fptr f) { m_reduce_app = f; } - void set_reduce_assign(reduce_assign_callback_fptr f) { m_reduce_assign = f; } - virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result); - virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs); - datalog::context& ctx() { return m_context; } - void add_rule(expr* rule, symbol const& name); - void update_rule(expr* rule, symbol const& name); - void add_table_fact(func_decl* r, unsigned num_args, unsigned args[]); - std::string get_last_status(); - std::string to_string(unsigned num_queries, expr*const* queries); - void cancel() { m_context.cancel(); } - void reset_cancel() { m_context.reset_cancel(); } - - unsigned get_num_levels(func_decl* pred); - expr_ref get_cover_delta(int level, func_decl* pred); - void add_cover(int level, func_decl* pred, expr* predicate); - void collect_param_descrs(param_descrs & p) { m_context.collect_params(p); } - void updt_params(params_ref const& p) { m_context.updt_params(p); } - - }; + class fixedpoint_context; }; diff --git a/src/muz_qe/dl_base.h b/src/muz_qe/dl_base.h index 200ce2d83..f9078c2f2 100644 --- a/src/muz_qe/dl_base.h +++ b/src/muz_qe/dl_base.h @@ -776,7 +776,7 @@ namespace datalog { bool is_finite_product_relation() const { return m_special_type==ST_FINITE_PRODUCT_RELATION; } bool is_product_relation() const { return m_special_type==ST_PRODUCT_RELATION; } bool is_sieve_relation() const { return m_special_type==ST_SIEVE_RELATION; } - + /** \brief If true, the relation can contain only one or zero elements. @@ -805,6 +805,7 @@ namespace datalog { virtual void to_formula(expr_ref& fml) const = 0; bool from_table() const { return get_plugin().from_table(); } + virtual bool is_precise() const { return true; } }; typedef ptr_vector relation_vector; diff --git a/src/muz_qe/dl_bound_relation.cpp b/src/muz_qe/dl_bound_relation.cpp index 0c76e8e1e..182046c1e 100644 --- a/src/muz_qe/dl_bound_relation.cpp +++ b/src/muz_qe/dl_bound_relation.cpp @@ -677,7 +677,7 @@ namespace datalog { void bound_relation::display_index(unsigned i, uint_set2 const& src, std::ostream & out) const { uint_set::iterator it = src.lt.begin(), end = src.lt.end(); - out << i; + out << "#" << i; if (!src.lt.empty()) { out << " < "; for(; it != end; ++it) { @@ -691,6 +691,9 @@ namespace datalog { out << *it << " "; } } + if (src.lt.empty() && src.le.empty()) { + out << " < oo"; + } out << "\n"; } diff --git a/src/muz_qe/dl_bound_relation.h b/src/muz_qe/dl_bound_relation.h index 603717bd8..04479b3b6 100644 --- a/src/muz_qe/dl_bound_relation.h +++ b/src/muz_qe/dl_bound_relation.h @@ -69,6 +69,7 @@ namespace datalog { unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } + #if 0 virtual intersection_filter_fn * mk_filter_by_intersection_fn( const relation_base & t, @@ -138,6 +139,7 @@ namespace datalog { bool is_lt(unsigned i, unsigned j) const; + virtual bool is_precise() const { return false; } private: typedef uint_set2 T; diff --git a/src/muz_qe/dl_cmds.cpp b/src/muz_qe/dl_cmds.cpp index 693105e6e..dcdf3ebb4 100644 --- a/src/muz_qe/dl_cmds.cpp +++ b/src/muz_qe/dl_cmds.cpp @@ -260,7 +260,11 @@ public: case datalog::TIMEOUT: ctx.regular_stream() << "timeout\n"; break; - + + case datalog::APPROX: + ctx.regular_stream() << "approximated relations\n"; + break; + case datalog::OK: UNREACHABLE(); break; diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 2627cbbec..e44d10c78 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -434,7 +434,8 @@ namespace datalog { void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { - if (m_rel && relation_name_cnt > 0) { + ensure_engine(); + if (relation_name_cnt > 0 && m_rel) { m_rel->set_predicate_representation(pred, relation_name_cnt, relation_names); } } diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index c54f7d591..a3c7e583c 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -56,6 +56,7 @@ namespace datalog { TIMEOUT, MEMOUT, INPUT_ERROR, + APPROX, CANCELED }; diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz_qe/dl_instruction.cpp index df4736b8e..55327f55b 100644 --- a/src/muz_qe/dl_instruction.cpp +++ b/src/muz_qe/dl_instruction.cpp @@ -494,6 +494,7 @@ namespace datalog { relation_mutator_fn * fn; relation_base & r = *ctx.reg(m_reg); + TRACE("dl_verbose", r.display(tout <<"pre-filter-interpreted:\n");); if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_interpreted_fn(r, m_cond); if (!fn) { @@ -507,7 +508,9 @@ namespace datalog { if (ctx.eager_emptiness_checking() && r.empty()) { ctx.make_empty(m_reg); - } + } + TRACE("dl_verbose", r.display(tout <<"post-filter-interpreted:\n");); + return true; } virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { @@ -545,6 +548,7 @@ namespace datalog { relation_transformer_fn * fn; relation_base & reg = *ctx.reg(m_src); + TRACE("dl_verbose", reg.display(tout <<"pre-filter-interpreted-and-project:\n");); if (!find_fn(reg, fn)) { fn = reg.get_manager().mk_filter_interpreted_and_project_fn(reg, m_cond, m_cols.size(), m_cols.c_ptr()); if (!fn) { @@ -560,6 +564,7 @@ namespace datalog { if (ctx.eager_emptiness_checking() && ctx.reg(m_res)->empty()) { ctx.make_empty(m_res); } + TRACE("dl_verbose", reg.display(tout << "post-filter-interpreted-and-project:\n");); return true; } diff --git a/src/muz_qe/dl_interval_relation.cpp b/src/muz_qe/dl_interval_relation.cpp index 4c8171bc7..adc8cb760 100644 --- a/src/muz_qe/dl_interval_relation.cpp +++ b/src/muz_qe/dl_interval_relation.cpp @@ -48,7 +48,7 @@ namespace datalog { return alloc(interval_relation, *this, s, true); } - relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { return alloc(interval_relation, *this, s, false); } @@ -317,6 +317,7 @@ namespace datalog { interval_relation::interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty): vector_relation(p, s, is_empty, interval(p.dep())) { + TRACE("interval_relation", tout << s.size() << "\n";); } void interval_relation::add_fact(const relation_fact & f) { diff --git a/src/muz_qe/dl_interval_relation.h b/src/muz_qe/dl_interval_relation.h index 1a25f430f..685ef5c86 100644 --- a/src/muz_qe/dl_interval_relation.h +++ b/src/muz_qe/dl_interval_relation.h @@ -104,6 +104,7 @@ namespace datalog { interval_relation_plugin& get_plugin() const; void filter_interpreted(app* cond); + virtual bool is_precise() const { return false; } private: diff --git a/src/muz_qe/dl_mk_karr_invariants.cpp b/src/muz_qe/dl_mk_karr_invariants.cpp index 4987a7e3d..e4e5a1ff8 100644 --- a/src/muz_qe/dl_mk_karr_invariants.cpp +++ b/src/muz_qe/dl_mk_karr_invariants.cpp @@ -355,6 +355,8 @@ namespace datalog { return m_empty; } + virtual bool is_precise() const { return false; } + virtual void add_fact(const relation_fact & f) { SASSERT(m_empty); SASSERT(!m_basis_valid); diff --git a/src/muz_qe/dl_mk_karr_invariants.h b/src/muz_qe/dl_mk_karr_invariants.h index 3d993e60a..a86f726ef 100644 --- a/src/muz_qe/dl_mk_karr_invariants.h +++ b/src/muz_qe/dl_mk_karr_invariants.h @@ -123,6 +123,7 @@ namespace datalog { virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + private: bool dualizeI(matrix& dst, matrix const& src); void dualizeH(matrix& dst, matrix const& src); diff --git a/src/muz_qe/dl_mk_magic_symbolic.cpp b/src/muz_qe/dl_mk_magic_symbolic.cpp index 2820ecaef..a06a573ad 100644 --- a/src/muz_qe/dl_mk_magic_symbolic.cpp +++ b/src/muz_qe/dl_mk_magic_symbolic.cpp @@ -95,11 +95,13 @@ namespace datalog { neg.push_back(false); } new_rule = rm.mk(mk_ans(r.get_head()), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); if (source.is_output_predicate(r.get_decl())) { result->set_output_predicate(new_rule->get_decl()); + new_rule = rm.mk(mk_query(r.get_head()), 0, 0, 0, r.name(), true); + result->add_rule(new_rule); } - result->add_rule(new_rule); for (unsigned j = 0; j < utsz; ++j) { new_rule = rm.mk(mk_query(r.get_tail(j)), tail.size()-utsz+j, tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); diff --git a/src/muz_qe/dl_product_relation.cpp b/src/muz_qe/dl_product_relation.cpp index c10b5799a..48cd666e6 100644 --- a/src/muz_qe/dl_product_relation.cpp +++ b/src/muz_qe/dl_product_relation.cpp @@ -174,8 +174,7 @@ namespace datalog { relation_base * product_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { rel_spec spec; - relation_signature sig_empty; - m_spec_store.get_relation_spec(sig_empty, kind, spec); + m_spec_store.get_relation_spec(s, kind, spec); relation_vector inner_rels; unsigned rel_cnt = spec.size(); for(unsigned i=0; iis_precise()) { + return false; + } + } + return true; + } }; }; diff --git a/src/muz_qe/dl_relation_manager.cpp b/src/muz_qe/dl_relation_manager.cpp index 812531e67..457ef28c0 100644 --- a/src/muz_qe/dl_relation_manager.cpp +++ b/src/muz_qe/dl_relation_manager.cpp @@ -80,12 +80,6 @@ namespace datalog { if(m_pred_kinds.find(pred, res)) { return res; } - //This is commented out as the favourite relation might not be suitable for all - //signatures. In the cases where it is suitable, it will be used anyway if we - //now return null_family_id. - //else if (m_favourite_relation_plugin) { - // return m_favourite_relation_plugin->get_kind(); - //} else { return null_family_id; } @@ -115,7 +109,7 @@ namespace datalog { void relation_manager::store_relation(func_decl * pred, relation_base * rel) { SASSERT(rel); relation_map::entry * e = m_relations.insert_if_not_there2(pred, 0); - if(e->get_data().m_value) { + if (e->get_data().m_value) { e->get_data().m_value->deallocate(); } else { @@ -142,7 +136,7 @@ namespace datalog { relation_map::iterator rend = m_relations.end(); for(; rit!=rend; ++rit) { func_decl * pred = rit->m_key; - if(!preds.contains(pred)) { + if (!preds.contains(pred)) { to_remove.insert(pred); } } @@ -152,7 +146,7 @@ namespace datalog { for(; pit!=pend; ++pit) { func_decl * pred = *pit; relation_base * rel; - TRUSTME( m_relations.find(pred, rel) ); + VERIFY( m_relations.find(pred, rel) ); rel->deallocate(); m_relations.remove(pred); get_context().get_manager().dec_ref(pred); @@ -278,7 +272,7 @@ namespace datalog { SASSERT(kind>=0); SASSERT(kindget_num_args()==0); - TRUSTME(get_context().get_decl_util().is_numeral_ext(from, to)); + VERIFY(get_context().get_decl_util().is_numeral_ext(from, to)); } void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index 4ebcbf2fd..ce1b30b88 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -213,17 +213,25 @@ namespace datalog { expr_ref_vector ans(m); expr_ref e(m); bool some_non_empty = num_rels == 0; + bool is_approx = false; for (unsigned i = 0; i < num_rels; ++i) { relation_base& rel = get_relation(rels[i]); if (!rel.empty()) { some_non_empty = true; } + if (!rel.is_precise()) { + is_approx = true; + } rel.to_formula(e); ans.push_back(e); } SASSERT(!m_last_result_relation); if (some_non_empty) { m_answer = m.mk_and(ans.size(), ans.c_ptr()); + if (is_approx) { + res = l_undef; + m_context.set_status(APPROX); + } } else { m_answer = m.mk_false(); @@ -278,6 +286,10 @@ namespace datalog { } else { m_last_result_relation->to_formula(m_answer); + if (!m_last_result_relation->is_precise()) { + m_context.set_status(APPROX); + res = l_undef; + } } } @@ -365,8 +377,15 @@ namespace datalog { void rel_context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { - relation_manager & rmgr = get_rmanager(); + TRACE("dl", + tout << pred->get_name() << ": "; + for (unsigned i = 0; i < relation_name_cnt; ++i) { + tout << relation_names[i] << " "; + } + tout << "\n"; + ); + relation_manager & rmgr = get_rmanager(); family_id target_kind = null_family_id; switch (relation_name_cnt) { case 0: @@ -386,7 +405,7 @@ namespace datalog { } else { relation_signature rel_sig; - //rmgr.from_predicate(pred, rel_sig); + rmgr.from_predicate(pred, rel_sig); product_relation_plugin & prod_plugin = product_relation_plugin::get_plugin(rmgr); rel_kind = prod_plugin.get_relation_kind(rel_sig, rel_kinds); } From 6ce0e7cf2562111d6bd03eed12abe531a70f7db6 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 15 Jul 2013 12:22:01 +0100 Subject: [PATCH 089/509] .NET build changes to include /linkresource Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_util.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 38213a88b..59122af32 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1089,20 +1089,19 @@ class DotNetDLLComponent(Component): cs_fp_files.append(os.path.join(self.to_src_dir, self.assembly_info_dir, cs_file)) cs_files.append(os.path.join(self.assembly_info_dir, cs_file)) dllfile = '%s.dll' % self.dll_name - out.write('%s:' % dllfile) + out.write('%s: %s$(SO_EXT)' % (dllfile, get_component(Z3_DLL_COMPONENT).dll_name)) for cs_file in cs_fp_files: out.write(' ') out.write(cs_file) out.write('\n') - out.write(' cd %s && csc /noconfig /unsafe+ /nowarn:1701,1702 /nostdlib+ /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:mscorlib.dll /reference:System.Core.dll /reference:System.dll /reference:System.Numerics.dll /debug+ /debug:full /filealign:512 /optimize- /out:%s.dll /target:library' % (self.to_src_dir, self.dll_name)) + out.write(' csc /noconfig /unsafe+ /nowarn:1701,1702 /nostdlib+ /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:mscorlib.dll /reference:System.Core.dll /reference:System.dll /reference:System.Numerics.dll /debug+ /debug:full /filealign:512 /optimize- /linkresource:%s.dll /out:%s.dll /target:library' % (get_component(Z3_DLL_COMPONENT).dll_name, self.dll_name)) + if VS_X64: + out.write(' /platform:x64') + else: + out.write(' /platform:x86') for cs_file in cs_files: - out.write(' ') - out.write(cs_file) + out.write(' %s' % os.path.join(self.to_src_dir, cs_file)) out.write('\n') - # HACK - win_to_src_dir = self.to_src_dir.replace('/', '\\') - out.write(' move %s\n' % os.path.join(win_to_src_dir, dllfile)) - out.write(' move %s.pdb\n' % os.path.join(win_to_src_dir, self.dll_name)) out.write('%s: %s\n\n' % (self.name, dllfile)) return From f1d3a13b7f80ed84433c9890de3aa37484deff5e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 16 Jul 2013 11:46:29 +0400 Subject: [PATCH 090/509] add missing case handlers for internal bit-vector operators that leak during simplification Signed-off-by: Nikolaj Bjorner --- src/api/api_ast.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 6855f6209..293983c9a 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -1073,6 +1073,12 @@ extern "C" { case OP_BSMUL_NO_OVFL: case OP_BUMUL_NO_OVFL: case OP_BSMUL_NO_UDFL: + case OP_BSDIV_I: + case OP_BUDIV_I: + case OP_BSREM_I: + case OP_BUREM_I: + case OP_BSMOD_I: + return Z3_OP_UNINTERPRETED; default: UNREACHABLE(); From 0cd3c3364bf3ad7fbcfa67b09778d1ade3e6b6ff Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 16 Jul 2013 23:42:50 +0400 Subject: [PATCH 091/509] add control over Farkas parameter for Arie Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_context.cpp | 4 +++- src/muz_qe/pdr_context.cpp | 6 +++--- src/muz_qe/pdr_context.h | 2 ++ src/muz_qe/pdr_generalizers.cpp | 2 ++ src/muz_qe/pdr_prop_solver.cpp | 7 ++++++- src/muz_qe/pdr_prop_solver.h | 3 +++ 6 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index e44d10c78..34513cc76 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -434,7 +434,9 @@ namespace datalog { void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { - ensure_engine(); + if (relation_name_cnt > 0) { + ensure_engine(); + } if (relation_name_cnt > 0 && m_rel) { m_rel->set_predicate_representation(pred, relation_name_cnt, relation_names); } diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index 6f81d93d8..e2b679756 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -1556,13 +1556,13 @@ namespace pdr { if (use_mc) { m_core_generalizers.push_back(alloc(core_multi_generalizer, *this, 0)); } - if (m_params.use_farkas() && !classify.is_bool()) { + if (!classify.is_bool()) { m.toggle_proof_mode(PGM_FINE); m_fparams.m_arith_bound_prop = BP_NONE; m_fparams.m_arith_auto_config_simplex = true; m_fparams.m_arith_propagate_eqs = false; m_fparams.m_arith_eager_eq_axioms = false; - if (classify.is_dl()) { + if (classify.is_dl() && m_params.use_utvpi()) { m_fparams.m_arith_mode = AS_DIFF_LOGIC; m_fparams.m_arith_expand_eqs = true; } @@ -1571,7 +1571,6 @@ namespace pdr { m_fparams.m_arith_mode = AS_UTVPI; m_fparams.m_arith_expand_eqs = true; } - } if (m_params.use_convex_hull_generalizer()) { m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this)); @@ -1800,6 +1799,7 @@ namespace pdr { m_expanded_lvl = n.level(); } + n.pt().set_use_farkas(m_params.use_farkas()); if (n.pt().is_reachable(n.state())) { TRACE("pdr", tout << "reachable\n";); close_node(n); diff --git a/src/muz_qe/pdr_context.h b/src/muz_qe/pdr_context.h index 1785991c6..4c26fc1ac 100644 --- a/src/muz_qe/pdr_context.h +++ b/src/muz_qe/pdr_context.h @@ -166,6 +166,8 @@ namespace pdr { prop_solver& get_solver() { return m_solver; } + void set_use_farkas(bool f) { get_solver().set_use_farkas(f); } + }; diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index 5e777bf7e..e73dcdac8 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -196,6 +196,7 @@ namespace pdr { ); model_node nd(0, state, n.pt(), n.level()); + n.pt().set_use_farkas(true); if (l_false == n.pt().is_reachable(nd, &conv2, uses_level)) { TRACE("pdr", tout << mk_pp(state, m) << "\n"; @@ -260,6 +261,7 @@ namespace pdr { expr_ref state = pm.mk_and(conv1); TRACE("pdr", tout << "Try:\n" << mk_pp(state, m) << "\n";); model_node nd(0, state, n.pt(), n.level()); + n.pt().set_use_farkas(true); if (l_false == n.pt().is_reachable(nd, &conv2, uses_level)) { IF_VERBOSE(0, verbose_stream() << mk_pp(state, m) << "\n"; diff --git a/src/muz_qe/pdr_prop_solver.cpp b/src/muz_qe/pdr_prop_solver.cpp index e3cd0d9c5..c7f0bfbc3 100644 --- a/src/muz_qe/pdr_prop_solver.cpp +++ b/src/muz_qe/pdr_prop_solver.cpp @@ -237,6 +237,7 @@ namespace pdr { m_proxies(m), m_core(0), m_subset_based_core(false), + m_use_farkas(false), m_in_level(false) { m_ctx->assert_expr(m_pm.get_background()); @@ -328,7 +329,11 @@ namespace pdr { } } - if (result == l_false && m_core && m.proofs_enabled() && !m_subset_based_core) { + if (result == l_false && + m_core && + m.proofs_enabled() && + m_use_farkas && + !m_subset_based_core) { extract_theory_core(safe); } else if (result == l_false && m_core) { diff --git a/src/muz_qe/pdr_prop_solver.h b/src/muz_qe/pdr_prop_solver.h index 165a37845..7712573ee 100644 --- a/src/muz_qe/pdr_prop_solver.h +++ b/src/muz_qe/pdr_prop_solver.h @@ -50,6 +50,7 @@ namespace pdr { model_ref* m_model; bool m_subset_based_core; bool m_assumes_level; + bool m_use_farkas; func_decl_set m_aux_symbols; bool m_in_level; unsigned m_current_level; // set when m_in_level @@ -97,6 +98,8 @@ namespace pdr { ~scoped_level() { m_lev = false; } }; + void set_use_farkas(bool f) { m_use_farkas = f; } + void add_formula(expr * form); void add_level_formula(expr * form, unsigned level); From 327b2bbe9ce9e70f13c030b2d6f3c8eaf979020d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 17 Jul 2013 00:03:38 +0400 Subject: [PATCH 092/509] add control over Farkas parameter for Arie Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_context.cpp | 18 ++++++++++-------- src/muz_qe/pdr_generalizers.cpp | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index e2b679756..034bb236a 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -1562,14 +1562,16 @@ namespace pdr { m_fparams.m_arith_auto_config_simplex = true; m_fparams.m_arith_propagate_eqs = false; m_fparams.m_arith_eager_eq_axioms = false; - if (classify.is_dl() && m_params.use_utvpi()) { - m_fparams.m_arith_mode = AS_DIFF_LOGIC; - m_fparams.m_arith_expand_eqs = true; - } - else if (classify.is_utvpi() && m_params.use_utvpi()) { - IF_VERBOSE(1, verbose_stream() << "UTVPI\n";); - m_fparams.m_arith_mode = AS_UTVPI; - m_fparams.m_arith_expand_eqs = true; + if (m_params.use_utvpi() && !m_params.use_convex_hull_generalizer()) { + if (classify.is_dl()) { + m_fparams.m_arith_mode = AS_DIFF_LOGIC; + m_fparams.m_arith_expand_eqs = true; + } + else if (classify.is_utvpi()) { + IF_VERBOSE(1, verbose_stream() << "UTVPI\n";); + m_fparams.m_arith_mode = AS_UTVPI; + m_fparams.m_arith_expand_eqs = true; + } } } if (m_params.use_convex_hull_generalizer()) { diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index e73dcdac8..5e928ff45 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -158,7 +158,7 @@ namespace pdr { } void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - method2(n, core, uses_level); + method1(n, core, uses_level); } // use the entire region as starting point for generalization. From a7ed218636eca7f22478dcd4fbd8475e8151f681 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 7 Aug 2013 13:16:46 -0700 Subject: [PATCH 093/509] generalize ackerman reduction to work with nested terms Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_array_blast.cpp | 105 +++++++++++++++++++++++-------- src/muz_qe/dl_mk_array_blast.h | 15 ++++- 2 files changed, 92 insertions(+), 28 deletions(-) diff --git a/src/muz_qe/dl_mk_array_blast.cpp b/src/muz_qe/dl_mk_array_blast.cpp index c1f0912c0..2c7d3d5cd 100644 --- a/src/muz_qe/dl_mk_array_blast.cpp +++ b/src/muz_qe/dl_mk_array_blast.cpp @@ -18,7 +18,6 @@ Revision History: --*/ #include "dl_mk_array_blast.h" -#include "expr_safe_replace.h" namespace datalog { @@ -31,7 +30,9 @@ namespace datalog { a(m), rm(ctx.get_rule_manager()), m_rewriter(m, m_params), - m_simplifier(ctx) { + m_simplifier(ctx), + m_sub(m), + m_next_var(0) { m_params.set_bool("expand_select_store",true); m_rewriter.updt_params(m_params); } @@ -50,14 +51,63 @@ namespace datalog { } return false; } + + expr* mk_array_blast::get_select(expr* e) const { + while (a.is_select(e)) { + e = to_app(e)->get_arg(0); + } + return e; + } + + void mk_array_blast::get_select_args(expr* e, ptr_vector& args) const { + while (a.is_select(e)) { + app* ap = to_app(e); + for (unsigned i = 1; i < ap->get_num_args(); ++i) { + args.push_back(ap->get_arg(i)); + } + e = ap->get_arg(0); + } + } + + bool mk_array_blast::insert_def(rule const& r, app* e, var* v) { + // + // For the Ackermann reduction we would like the arrays + // to be variables, so that variables can be + // assumed to represent difference (alias) + // classes. Ehm., Soundness of this approach depends on + // if the arrays are finite domains... + // + + if (!is_var(get_select(e))) { + return false; + } + if (v) { + m_sub.insert(e, v); + m_defs.insert(e, to_var(v)); + } + else { + if (m_next_var == 0) { + ptr_vector vars; + r.get_vars(vars); + m_next_var = vars.size() + 1; + } + v = m.mk_var(m_next_var, m.get_sort(e)); + m_sub.insert(e, v); + m_defs.insert(e, v); + ++m_next_var; + } + return true; + } - bool mk_array_blast::ackermanize(expr_ref& body, expr_ref& head) { + bool mk_array_blast::ackermanize(rule const& r, expr_ref& body, expr_ref& head) { expr_ref_vector conjs(m); flatten_and(body, conjs); - defs_t defs; - expr_safe_replace sub(m); + m_defs.reset(); + m_sub.reset(); + m_next_var = 0; ptr_vector todo; todo.push_back(head); + unsigned next_var = 0; for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); expr* x, *y; @@ -66,22 +116,17 @@ namespace datalog { std::swap(x,y); } if (a.is_select(x) && is_var(y)) { - // - // For the Ackermann reduction we would like the arrays - // to be variables, so that variables can be - // assumed to represent difference (alias) - // classes. - // - if (!is_var(to_app(x)->get_arg(0))) { + if (!insert_def(r, to_app(x), to_var(y))) { return false; } - sub.insert(x, y); - defs.insert(to_app(x), to_var(y)); } } + if (a.is_select(e) && !insert_def(r, to_app(e), 0)) { + return false; + } todo.push_back(e); } - // now check that all occurrences of select have been covered. + // now make sure to cover all occurrences. ast_mark mark; while (!todo.empty()) { expr* e = todo.back(); @@ -97,22 +142,28 @@ namespace datalog { return false; } app* ap = to_app(e); - if (a.is_select(e) && !defs.contains(ap)) { - return false; + if (a.is_select(ap) && !m_defs.contains(ap)) { + if (!insert_def(r, ap, 0)) { + return false; + } + } + if (a.is_select(e)) { + get_select_args(e, todo); + continue; } for (unsigned i = 0; i < ap->get_num_args(); ++i) { todo.push_back(ap->get_arg(i)); } } - sub(body); - sub(head); + m_sub(body); + m_sub(head); conjs.reset(); // perform the Ackermann reduction by creating implications // i1 = i2 => val1 = val2 for each equality pair: // (= val1 (select a_i i1)) // (= val2 (select a_i i2)) - defs_t::iterator it1 = defs.begin(), end = defs.end(); + defs_t::iterator it1 = m_defs.begin(), end = m_defs.end(); for (; it1 != end; ++it1) { app* a1 = it1->m_key; var* v1 = it1->m_value; @@ -121,12 +172,15 @@ namespace datalog { for (; it2 != end; ++it2) { app* a2 = it2->m_key; var* v2 = it2->m_value; - if (a1->get_arg(0) != a2->get_arg(0)) { + if (get_select(a1) != get_select(a2)) { continue; } expr_ref_vector eqs(m); - for (unsigned j = 1; j < a1->get_num_args(); ++j) { - eqs.push_back(m.mk_eq(a1->get_arg(j), a2->get_arg(j))); + ptr_vector args1, args2; + get_select_args(a1, args1); + get_select_args(a2, args2); + for (unsigned j = 0; j < args1.size(); ++j) { + eqs.push_back(m.mk_eq(args1[j], args2[j])); } conjs.push_back(m.mk_implies(m.mk_and(eqs.size(), eqs.c_ptr()), m.mk_eq(v1, v2))); } @@ -179,15 +233,14 @@ namespace datalog { } } - expr_ref fml1(m), fml2(m), body(m), head(m); - r.to_formula(fml1); + expr_ref fml2(m), body(m), head(m); body = m.mk_and(new_conjs.size(), new_conjs.c_ptr()); head = r.get_head(); sub(body); m_rewriter(body); sub(head); m_rewriter(head); - change = ackermanize(body, head) || change; + change = ackermanize(r, body, head) || change; if (!inserted && !change) { rules.add_rule(&r); return false; diff --git a/src/muz_qe/dl_mk_array_blast.h b/src/muz_qe/dl_mk_array_blast.h index 21f2a0bf7..f4b685b7a 100644 --- a/src/muz_qe/dl_mk_array_blast.h +++ b/src/muz_qe/dl_mk_array_blast.h @@ -25,6 +25,7 @@ Revision History: #include"dl_mk_interp_tail_simplifier.h" #include "equiv_proof_converter.h" #include "array_decl_plugin.h" +#include "expr_safe_replace.h" namespace datalog { @@ -32,6 +33,8 @@ namespace datalog { \brief Blast occurrences of arrays in rules */ class mk_array_blast : public rule_transformer::plugin { + typedef obj_map defs_t; + context& m_ctx; ast_manager& m; array_util a; @@ -40,13 +43,21 @@ namespace datalog { th_rewriter m_rewriter; mk_interp_tail_simplifier m_simplifier; - typedef obj_map defs_t; + defs_t m_defs; + expr_safe_replace m_sub; + unsigned m_next_var; bool blast(rule& r, rule_set& new_rules); bool is_store_def(expr* e, expr*& x, expr*& y); - bool ackermanize(expr_ref& body, expr_ref& head); + bool ackermanize(rule const& r, expr_ref& body, expr_ref& head); + + expr* get_select(expr* e) const; + + void get_select_args(expr* e, ptr_vector& args) const; + + bool insert_def(rule const& r, app* e, var* v); public: /** From ec22156ae12754aa79e689be25bc060fb0951fe7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 7 Aug 2013 14:52:34 -0700 Subject: [PATCH 094/509] fix bug in get_answer reported by Anvesh Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_context.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index 034bb236a..de0041413 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -765,6 +765,7 @@ namespace pdr { ini_state = m.mk_and(ini_tags, pt().initial_state(), state()); model_ref mdl; pt().get_solver().set_model(&mdl); + TRACE("pdr", tout << mk_pp(ini_state, m) << "\n";); VERIFY(l_true == pt().get_solver().check_conjunction_as_assumptions(ini_state)); datalog::rule const& rl2 = pt().find_rule(*mdl); SASSERT(is_ini(rl2)); @@ -958,12 +959,14 @@ namespace pdr { */ void model_search::update_models() { obj_map models; + obj_map rules; ptr_vector todo; todo.push_back(m_root); while (!todo.empty()) { model_node* n = todo.back(); if (n->get_model_ptr()) { models.insert(n->state(), n->get_model_ptr()); + rules.insert(n->state(), n->get_rule()); } todo.pop_back(); todo.append(n->children().size(), n->children().c_ptr()); @@ -973,9 +976,13 @@ namespace pdr { while (!todo.empty()) { model_node* n = todo.back(); model* md = 0; + ast_manager& m = n->pt().get_manager(); if (!n->get_model_ptr() && models.find(n->state(), md)) { + TRACE("pdr", tout << mk_pp(n->state(), m) << "\n";); model_ref mr(md); n->set_model(mr); + datalog::rule const* rule = rules.find(n->state()); + n->set_rule(rule); } todo.pop_back(); todo.append(n->children().size(), n->children().c_ptr()); @@ -1037,10 +1044,6 @@ namespace pdr { } first = false; predicates.pop_back(); - for (unsigned i = 0; i < rule->get_uninterpreted_tail_size(); ++i) { - subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); - predicates.push_back(tmp); - } for (unsigned i = rule->get_uninterpreted_tail_size(); i < rule->get_tail_size(); ++i) { subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); dctx.get_rewriter()(tmp); @@ -1051,9 +1054,22 @@ namespace pdr { for (unsigned i = 0; i < constraints.size(); ++i) { max_var = std::max(vc.get_max_var(constraints[i].get()), max_var); } + if (n->children().empty()) { + // nodes whose states are repeated + // in the search tree do not have children. + continue; + } + + SASSERT(n->children().size() == rule->get_uninterpreted_tail_size()); + + for (unsigned i = 0; i < rule->get_uninterpreted_tail_size(); ++i) { + subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); + predicates.push_back(tmp); + } for (unsigned i = 0; i < predicates.size(); ++i) { max_var = std::max(vc.get_max_var(predicates[i].get()), max_var); } + children.append(n->children()); } return pm.mk_and(constraints); @@ -1230,6 +1246,7 @@ namespace pdr { m_expanded_lvl(0), m_cancel(false) { + enable_trace("pdr"); } context::~context() { From 5b9ec3dec7584b7212b8bc8991316e98b80a4811 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 7 Aug 2013 19:58:21 -0700 Subject: [PATCH 095/509] add scoped class for controlling Farkas generalization Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_context.cpp | 2 +- src/muz_qe/pdr_context.h | 11 +++++++++++ src/muz_qe/pdr_generalizers.cpp | 4 ++-- src/muz_qe/pdr_prop_solver.h | 1 + 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index 034bb236a..4454e2fbf 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -1801,7 +1801,7 @@ namespace pdr { m_expanded_lvl = n.level(); } - n.pt().set_use_farkas(m_params.use_farkas()); + pred_transformer::scoped_farkas sf (n.pt(), m_params.use_farkas()); if (n.pt().is_reachable(n.state())) { TRACE("pdr", tout << "reachable\n";); close_node(n); diff --git a/src/muz_qe/pdr_context.h b/src/muz_qe/pdr_context.h index 4c26fc1ac..3501ca7cd 100644 --- a/src/muz_qe/pdr_context.h +++ b/src/muz_qe/pdr_context.h @@ -165,8 +165,19 @@ namespace pdr { void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars); prop_solver& get_solver() { return m_solver; } + prop_solver const& get_solver() const { return m_solver; } void set_use_farkas(bool f) { get_solver().set_use_farkas(f); } + bool get_use_farkas() const { return get_solver().get_use_farkas(); } + class scoped_farkas { + bool m_old; + pred_transformer& m_p; + public: + scoped_farkas(pred_transformer& p, bool v): m_old(p.get_use_farkas()), m_p(p) { + p.set_use_farkas(v); + } + ~scoped_farkas() { m_p.set_use_farkas(m_old); } + }; }; diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index 5e928ff45..7321a1c03 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -196,7 +196,7 @@ namespace pdr { ); model_node nd(0, state, n.pt(), n.level()); - n.pt().set_use_farkas(true); + pred_transformer::scoped_farkas sf(n.pt(), true); if (l_false == n.pt().is_reachable(nd, &conv2, uses_level)) { TRACE("pdr", tout << mk_pp(state, m) << "\n"; @@ -261,7 +261,7 @@ namespace pdr { expr_ref state = pm.mk_and(conv1); TRACE("pdr", tout << "Try:\n" << mk_pp(state, m) << "\n";); model_node nd(0, state, n.pt(), n.level()); - n.pt().set_use_farkas(true); + pred_transformer::scoped_farkas sf(n.pt(), true); if (l_false == n.pt().is_reachable(nd, &conv2, uses_level)) { IF_VERBOSE(0, verbose_stream() << mk_pp(state, m) << "\n"; diff --git a/src/muz_qe/pdr_prop_solver.h b/src/muz_qe/pdr_prop_solver.h index 7712573ee..5a6b09360 100644 --- a/src/muz_qe/pdr_prop_solver.h +++ b/src/muz_qe/pdr_prop_solver.h @@ -99,6 +99,7 @@ namespace pdr { }; void set_use_farkas(bool f) { m_use_farkas = f; } + bool get_use_farkas() const { return m_use_farkas; } void add_formula(expr * form); void add_level_formula(expr * form, unsigned level); From 0595fe8ceccebd56909f416cc492c9db30cef2cd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 8 Aug 2013 09:36:31 -0700 Subject: [PATCH 096/509] remove tracing Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_context.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index de0041413..e6916050a 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -1246,7 +1246,6 @@ namespace pdr { m_expanded_lvl(0), m_cancel(false) { - enable_trace("pdr"); } context::~context() { From dc58bce052dc355c6f3f2aee25d9ffda7ea6ca10 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 8 Aug 2013 14:09:45 -0700 Subject: [PATCH 097/509] initial test for polynormalization Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_coi_filter.cpp | 98 +++++++++++++++++++++++-- src/muz_qe/dl_mk_coi_filter.h | 4 ++ src/test/dl_context.cpp | 2 +- src/test/dl_product_relation.cpp | 4 +- src/test/dl_query.cpp | 8 +-- src/test/dl_relation.cpp | 4 +- src/test/dl_table.cpp | 2 +- src/test/main.cpp | 1 + src/test/polynorm.cpp | 119 +++++++++++++++++++++++++++++++ 9 files changed, 227 insertions(+), 15 deletions(-) create mode 100644 src/test/polynorm.cpp diff --git a/src/muz_qe/dl_mk_coi_filter.cpp b/src/muz_qe/dl_mk_coi_filter.cpp index b253a0e20..308198ff7 100644 --- a/src/muz_qe/dl_mk_coi_filter.cpp +++ b/src/muz_qe/dl_mk_coi_filter.cpp @@ -16,6 +16,9 @@ Author: Revision History: + Andrey Rybalchenko (rybal) 2013-8-8 + Added bottom_up pruning. + --*/ @@ -32,12 +35,97 @@ namespace datalog { // // ----------------------------------- - - rule_set * mk_coi_filter::operator()(rule_set const & source) - { + rule_set * mk_coi_filter::operator()(rule_set const & source) { if (source.empty()) { return 0; } + scoped_ptr result = top_down(source); + return bottom_up(result?*result:source); + } + + rule_set * mk_coi_filter::bottom_up(rule_set const & source) { + decl_set all, reached; + ptr_vector todo; + rule_set::decl2rules body2rules; + // initialization for reachability + for (rule_set::iterator it = source.begin(); it != source.end(); ++it) { + rule * r = *it; + all.insert(r->get_decl()); + if (r->get_uninterpreted_tail_size() == 0) { + if (!reached.contains(r->get_decl())) { + reached.insert(r->get_decl()); + todo.insert(r->get_decl()); + } + } + else { + for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { + func_decl * d = r->get_tail(i)->get_decl(); + all.insert(d); + rule_set::decl2rules::obj_map_entry * e = body2rules.insert_if_not_there2(d, 0); + if (!e->get_data().m_value) { + e->get_data().m_value = alloc(ptr_vector); + } + e->get_data().m_value->push_back(r); + } + } + } + // reachability computation + while (!todo.empty()) { + func_decl * d = todo.back(); + todo.pop_back(); + ptr_vector * rules; + if (!body2rules.find(d, rules)) continue; + for (ptr_vector::iterator it = rules->begin(); it != rules->end(); ++it) { + rule * r = *it; + if (reached.contains(r->get_decl())) continue; + bool contained = true; + for (unsigned i = 0; contained && i < r->get_uninterpreted_tail_size(); ++i) { + contained = reached.contains(r->get_tail(i)->get_decl()); + } + if (!contained) continue; + reached.insert(r->get_decl()); + todo.insert(r->get_decl()); + } + } + + // eliminate each rule when some body predicate is not reached + scoped_ptr res = alloc(rule_set, m_context); + res->inherit_predicates(source); + for (rule_set::iterator it = source.begin(); it != source.end(); ++it) { + rule * r = *it; + + bool contained = true; + for (unsigned i = 0; contained && i < r->get_uninterpreted_tail_size(); ++i) { + contained = reached.contains(r->get_tail(i)->get_decl()); + } + if (contained) { + res->add_rule(r); + } + } + + if (res->get_num_rules() == source.get_num_rules()) { + return 0; + } + res->close(); + + // set to false each unreached predicate + if (m_context.get_model_converter()) { + extension_model_converter* mc0 = alloc(extension_model_converter, m); + for (decl_set::iterator it = all.begin(); it != all.end(); ++it) { + if (!reached.contains(*it)) { + mc0->insert(*it, m.mk_false()); + } + } + m_context.add_model_converter(mc0); + } + // clean up body2rules range resources + for (rule_set::decl2rules::iterator it = body2rules.begin(); it != body2rules.end(); ++it) { + dealloc(it->m_value); + } + return res.detach(); + } + + rule_set * mk_coi_filter::top_down(rule_set const & source) { decl_set interesting_preds; decl_set pruned_preds; @@ -60,7 +148,7 @@ namespace datalog { const rule_dependencies::item_set& cdeps = deps.get_deps(curr); rule_dependencies::item_set::iterator dend = cdeps.end(); - for (rule_dependencies::item_set::iterator it = cdeps.begin(); it!=dend; ++it) { + for (rule_dependencies::item_set::iterator it = cdeps.begin(); it != dend; ++it) { func_decl * dep_pred = *it; if (!interesting_preds.contains(dep_pred)) { interesting_preds.insert(dep_pred); @@ -73,7 +161,7 @@ namespace datalog { res->inherit_predicates(source); rule_set::iterator rend = source.end(); - for (rule_set::iterator rit = source.begin(); rit!=rend; ++rit) { + for (rule_set::iterator rit = source.begin(); rit != rend; ++rit) { rule * r = *rit; func_decl * pred = r->get_decl(); if (interesting_preds.contains(pred)) { diff --git a/src/muz_qe/dl_mk_coi_filter.h b/src/muz_qe/dl_mk_coi_filter.h index b02bed9ec..8ec7e80c4 100644 --- a/src/muz_qe/dl_mk_coi_filter.h +++ b/src/muz_qe/dl_mk_coi_filter.h @@ -32,6 +32,10 @@ namespace datalog { ast_manager & m; context & m_context; + + rule_set * bottom_up(rule_set const & source); + rule_set * top_down(rule_set const & source); + public: mk_coi_filter(context & ctx, unsigned priority=45000) : plugin(priority), diff --git a/src/test/dl_context.cpp b/src/test/dl_context.cpp index d5fcddb71..9e8a37974 100644 --- a/src/test/dl_context.cpp +++ b/src/test/dl_context.cpp @@ -60,7 +60,7 @@ void dl_context_saturate_file(params_ref & params, const char * f) { } dealloc(parser); std::cerr << "Saturating...\n"; - ctx.get_rel_context().saturate(); + ctx.get_rel_context()->saturate(); std::cerr << "Done\n"; } diff --git a/src/test/dl_product_relation.cpp b/src/test/dl_product_relation.cpp index ef43258ad..d58603be1 100644 --- a/src/test/dl_product_relation.cpp +++ b/src/test/dl_product_relation.cpp @@ -22,7 +22,7 @@ namespace datalog { void test_functional_columns(smt_params fparams, params_ref& params) { ast_manager m; context ctx(m, fparams); - rel_context& rctx = ctx.get_rel_context(); + rel_context& rctx = *ctx.get_rel_context(); ctx.updt_params(params); relation_manager & rmgr(rctx.get_rmanager()); @@ -127,7 +127,7 @@ namespace datalog { context ctx(m, fparams); ctx.updt_params(params); dl_decl_util dl_util(m); - relation_manager & rmgr = ctx.get_rel_context().get_rmanager(); + relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); relation_plugin & rel_plugin = *rmgr.get_relation_plugin(params.get_sym("default_relation", symbol("sparse"))); SASSERT(&rel_plugin); diff --git a/src/test/dl_query.cpp b/src/test/dl_query.cpp index d0abfbac0..17781d7bb 100644 --- a/src/test/dl_query.cpp +++ b/src/test/dl_query.cpp @@ -58,7 +58,7 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, TRUSTME( p->parse_file(problem_file) ); dealloc(p); } - relation_manager & rel_mgr_q = ctx_b.get_rel_context().get_rmanager(); + relation_manager & rel_mgr_q = ctx_b.get_rel_context()->get_rmanager(); decl_set out_preds = ctx_b.get_rules().get_output_predicates(); decl_set::iterator it = out_preds.begin(); @@ -69,10 +69,10 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, func_decl * pred_q = ctx_q.try_get_predicate_decl(symbol(pred_b->get_name().bare_str())); SASSERT(pred_q); - relation_base & rel_b = ctx_b.get_rel_context().get_relation(pred_b); + relation_base & rel_b = ctx_b.get_rel_context()->get_relation(pred_b); relation_signature sig_b = rel_b.get_signature(); - relation_signature sig_q = ctx_q.get_rel_context().get_relation(pred_q).get_signature(); + relation_signature sig_q = ctx_q.get_rel_context()->get_relation(pred_q).get_signature(); SASSERT(sig_b.size()==sig_q.size()); std::cerr << "Queries on random facts...\n"; @@ -211,7 +211,7 @@ void tst_dl_query() { TRUSTME( p->parse_file(problem_file) ); dealloc(p); } - ctx_base.get_rel_context().saturate(); + ctx_base.get_rel_context()->saturate(); for(unsigned use_restarts=0; use_restarts<=1; use_restarts++) { params.set_uint("initial_restart_timeout", use_restarts ? 100 : 0); diff --git a/src/test/dl_relation.cpp b/src/test/dl_relation.cpp index 5daf3dc9b..609dca3da 100644 --- a/src/test/dl_relation.cpp +++ b/src/test/dl_relation.cpp @@ -12,7 +12,7 @@ namespace datalog { ast_manager ast_m; context ctx(ast_m, params); arith_util autil(ast_m); - relation_manager & m = ctx.get_rel_context().get_rmanager(); + relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(interval_relation_plugin, m)); interval_relation_plugin& ip = dynamic_cast(*m.get_relation_plugin(symbol("interval_relation"))); SASSERT(&ip); @@ -115,7 +115,7 @@ namespace datalog { ast_manager ast_m; context ctx(ast_m, params); arith_util autil(ast_m); - relation_manager & m = ctx.get_rel_context().get_rmanager(); + relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(bound_relation_plugin, m)); bound_relation_plugin& br = dynamic_cast(*m.get_relation_plugin(symbol("bound_relation"))); SASSERT(&br); diff --git a/src/test/dl_table.cpp b/src/test/dl_table.cpp index 32a6f65e3..d7025ed48 100644 --- a/src/test/dl_table.cpp +++ b/src/test/dl_table.cpp @@ -19,7 +19,7 @@ static void test_table(mk_table_fn mk_table) { smt_params params; ast_manager ast_m; datalog::context ctx(ast_m, params); - datalog::relation_manager & m = ctx.get_rel_context().get_rmanager(); + datalog::relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(datalog::bitvector_table_plugin, m)); diff --git a/src/test/main.cpp b/src/test/main.cpp index c8c011674..b769b20fd 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -213,6 +213,7 @@ int main(int argc, char ** argv) { TST(dl_query); TST(quant_solve); TST(rcf); + TST(polynorm); } void initialize_mam() {} diff --git a/src/test/polynorm.cpp b/src/test/polynorm.cpp new file mode 100644 index 000000000..9853181ca --- /dev/null +++ b/src/test/polynorm.cpp @@ -0,0 +1,119 @@ +#include "th_rewriter.h" +#include "smt2parser.h" +#include "arith_decl_plugin.h" +#include "reg_decl_plugins.h" +#include "ast_pp.h" + + +static expr_ref parse_fml(ast_manager& m, char const* str) { + expr_ref result(m); + cmd_context ctx(false, &m); + ctx.set_ignore_check(true); + std::ostringstream buffer; + buffer << "(declare-const x Int)\n" + << "(declare-const y Int)\n" + << "(declare-const z Int)\n" + << "(declare-const a Int)\n" + << "(declare-const b Int)\n" + << "(assert " << str << ")\n"; + std::istringstream is(buffer.str()); + VERIFY(parse_smt2_commands(ctx, is)); + SASSERT(ctx.begin_assertions() != ctx.end_assertions()); + result = *ctx.begin_assertions(); + return result; +} + +static char const* example1 = "(= (+ (- (* x x) (* 2 y)) y) 0)"; +static char const* example2 = "(= (+ 4 3 (- (* x x) (* 2 y)) y) 0)"; + + +// ast +/// sort : ast +/// func_decl : ast +/// expr : ast +/// app : expr +/// quantifier : expr +/// var : expr +/// + +static expr_ref mk_mul(arith_util& arith, unsigned num_args, expr* const* args) { + ast_manager& m = arith.get_manager(); + expr_ref result(m); + switch (num_args) { + case 0: + UNREACHABLE(); + break; + case 1: + result = args[0]; + break; + default: + result = arith.mk_mul(num_args, args); + break; + } + return result; +} + +static void nf(expr_ref& term) { + ast_manager& m = term.get_manager(); + expr *e1, *e2; + + th_rewriter rw(m); + arith_util arith(m); + + VERIFY(m.is_eq(term, e1, e2)); + term = e1; + + rw(term); + + std::cout << mk_pp(term, m) << "\n"; + std::cout << arith.is_add(term) << "\n"; + + expr_ref_vector factors(m); + vector coefficients; + rational coefficient(0); + + if (arith.is_add(term)) { + factors.append(to_app(term)->get_num_args(), to_app(term)->get_args()); + } + else { + factors.push_back(term); + } + for (unsigned i = 0; i < factors.size(); ++i) { + expr* f = factors[i].get(); + rational r; + if (arith.is_mul(f) && arith.is_numeral(to_app(f)->get_arg(0), r)) { + coefficients.push_back(r); + factors[i] = mk_mul(arith, to_app(f)->get_num_args()-1, to_app(f)->get_args()+1); + } + else if (arith.is_numeral(f, r)) { + factors[i] = factors.back(); + factors.pop_back(); + SASSERT(coefficient.is_zero()); + SASSERT(!r.is_zero()); + coefficient = r; + --i; // repeat examining 'i' + } + else { + coefficients.push_back(rational(1)); + } + } + + std::cout << coefficient << "\n"; + for (unsigned i = 0; i < factors.size(); ++i) { + std::cout << mk_pp(factors[i].get(), m) << " * " << coefficients[i] << "\n"; + } +} + +void tst_polynorm() { + ast_manager m; + reg_decl_plugins(m); + expr_ref fml(m); + + fml = parse_fml(m, example2); + + std::cout << mk_pp(fml, m) << "\n"; + + nf(fml); + + +} From a0d79c8dd766c4781aeaf1389efa362118ed75b8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 8 Aug 2013 15:01:35 -0700 Subject: [PATCH 098/509] fix coi filter to consider 0 cases Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_coi_filter.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/muz_qe/dl_mk_coi_filter.cpp b/src/muz_qe/dl_mk_coi_filter.cpp index 308198ff7..88ec67607 100644 --- a/src/muz_qe/dl_mk_coi_filter.cpp +++ b/src/muz_qe/dl_mk_coi_filter.cpp @@ -36,11 +36,16 @@ namespace datalog { // ----------------------------------- rule_set * mk_coi_filter::operator()(rule_set const & source) { + TRACE("dl", tout << "Hello";); if (source.empty()) { return 0; } - scoped_ptr result = top_down(source); - return bottom_up(result?*result:source); + scoped_ptr result1 = top_down(source); + scoped_ptr result2 = bottom_up(result1?*result1:source); + if (!result2) { + result2 = result1; + } + return result2.detach(); } rule_set * mk_coi_filter::bottom_up(rule_set const & source) { @@ -102,11 +107,13 @@ namespace datalog { res->add_rule(r); } } - if (res->get_num_rules() == source.get_num_rules()) { - return 0; - } - res->close(); + TRACE("dl", tout << "No transformation\n";); + res = 0; + } + else { + res->close(); + } // set to false each unreached predicate if (m_context.get_model_converter()) { @@ -122,6 +129,7 @@ namespace datalog { for (rule_set::decl2rules::iterator it = body2rules.begin(); it != body2rules.end(); ++it) { dealloc(it->m_value); } + CTRACE("dl", 0 != res, res->display(tout);); return res.detach(); } @@ -173,6 +181,7 @@ namespace datalog { } if (res->get_num_rules() == source.get_num_rules()) { + TRACE("dl", tout << "No transformation\n";); res = 0; } @@ -185,7 +194,7 @@ namespace datalog { } m_context.add_model_converter(mc0); } - + CTRACE("dl", 0 != res, res->display(tout);); return res.detach(); } From af700e88cf580b33fc7f179f1863c3e81e8f74d7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 8 Aug 2013 15:02:18 -0700 Subject: [PATCH 099/509] fix coi filter to consider 0 cases Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_coi_filter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/muz_qe/dl_mk_coi_filter.cpp b/src/muz_qe/dl_mk_coi_filter.cpp index 88ec67607..7bbb14353 100644 --- a/src/muz_qe/dl_mk_coi_filter.cpp +++ b/src/muz_qe/dl_mk_coi_filter.cpp @@ -36,7 +36,6 @@ namespace datalog { // ----------------------------------- rule_set * mk_coi_filter::operator()(rule_set const & source) { - TRACE("dl", tout << "Hello";); if (source.empty()) { return 0; } From 30df2837fbff02f1edf785b861f60dfbc80d8815 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 8 Aug 2013 15:38:13 -0700 Subject: [PATCH 100/509] fix build warnings Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_boogie_proof.cpp | 2 +- src/muz_qe/dl_compiler.cpp | 1 - src/muz_qe/dl_context.cpp | 3 +++ src/muz_qe/dl_mk_array_blast.cpp | 1 - 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/muz_qe/dl_boogie_proof.cpp b/src/muz_qe/dl_boogie_proof.cpp index 8720b3544..e14512973 100644 --- a/src/muz_qe/dl_boogie_proof.cpp +++ b/src/muz_qe/dl_boogie_proof.cpp @@ -176,7 +176,7 @@ namespace datalog { step &s = steps[j]; // TBD - s.m_labels; + // s.m_labels; // set references, compensate for reverse ordering. for (unsigned i = 0; i < s.m_refs.size(); ++i) { diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index a5f8009e7..7a2b47c78 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -1004,7 +1004,6 @@ namespace datalog { void compiler::detect_chains(const func_decl_set & preds, func_decl_vector & ordered_preds, func_decl_set & global_deltas) { - typedef obj_map pred2pred; SASSERT(ordered_preds.empty()); SASSERT(global_deltas.empty()); diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 34513cc76..d8a50668b 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -686,6 +686,7 @@ namespace datalog { check_existential_tail(r); check_positive_predicates(r); break; + case LAST_ENGINE: default: UNREACHABLE(); break; @@ -1039,6 +1040,8 @@ namespace datalog { case CLP_ENGINE: m_engine = alloc(clp, *this); break; + case LAST_ENGINE: + UNREACHABLE(); } } } diff --git a/src/muz_qe/dl_mk_array_blast.cpp b/src/muz_qe/dl_mk_array_blast.cpp index 2c7d3d5cd..9f057a148 100644 --- a/src/muz_qe/dl_mk_array_blast.cpp +++ b/src/muz_qe/dl_mk_array_blast.cpp @@ -107,7 +107,6 @@ namespace datalog { m_next_var = 0; ptr_vector todo; todo.push_back(head); - unsigned next_var = 0; for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); expr* x, *y; From cb06ce295e888c55f34c1f36951081a8c7bc5b42 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 9 Aug 2013 09:00:40 -0700 Subject: [PATCH 101/509] add comments to generalizer code Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_generalizers.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index 7321a1c03..c094b6c7f 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -162,6 +162,18 @@ namespace pdr { } // use the entire region as starting point for generalization. + // + // Constraints: + // add_variables: y = y1 + y2 + // core: Ay <= b -> conv1: A*y1 <= b*sigma1 + // sigma1 > 0 + // sigma2 > 0 + // 1 = sigma1 + sigma2 + // A'y <= b' -> conv2: A'*y2 <= b'*sigma2 + // + // If Constraints & Transition(y0, y) is unsat, then + // update with new core. + // void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector& core, bool& uses_level) { manager& pm = n.pt().get_pdr_manager(); expr_ref_vector conv1(m), conv2(m), core1(m), core2(m), eqs(m); From 3b64265c27266bbf69ca62037c89be4f7487dc83 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 9 Aug 2013 09:15:04 -0700 Subject: [PATCH 102/509] remove duplicated definition of is_store and is_select Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 29 ++------------------- src/ast/simplifier/bv_simplifier_plugin.cpp | 2 +- src/muz_qe/qe_lite.cpp | 9 ++++++- 3 files changed, 11 insertions(+), 29 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index bbd267292..739a9e61a 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -3856,32 +3856,6 @@ def is_array(a): """ return isinstance(a, ArrayRef) -def is_select(a): - """Return `True` if `a` is a Z3 array select. - - >>> a = Array('a', IntSort(), IntSort()) - >>> is_select(a) - False - >>> i = Int('i') - >>> is_select(a[i]) - True - """ - return is_app_of(a, Z3_OP_SELECT) - -def is_store(a): - """Return `True` if `a` is a Z3 array store. - - >>> a = Array('a', IntSort(), IntSort()) - >>> is_store(a) - False - >>> i = Int('i') - >>> is_store(a[i]) - False - >>> is_store(Store(a, i, i + 1)) - True - """ - return is_app_of(a, Z3_OP_STORE) - def is_const_array(a): """Return `True` if `a` is a Z3 constant array. @@ -4072,7 +4046,8 @@ def is_select(a): >>> a = Array('a', IntSort(), IntSort()) >>> is_select(a) False - >>> is_select(a[0]) + >>> i = Int('i') + >>> is_select(a[i]) True """ return is_app_of(a, Z3_OP_SELECT) diff --git a/src/ast/simplifier/bv_simplifier_plugin.cpp b/src/ast/simplifier/bv_simplifier_plugin.cpp index 8ee353a76..45fee07e4 100644 --- a/src/ast/simplifier/bv_simplifier_plugin.cpp +++ b/src/ast/simplifier/bv_simplifier_plugin.cpp @@ -179,7 +179,7 @@ bool bv_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const } SASSERT(result.get()); - TRACE("bv_simplifier", + TRACE("bv_simplifier", tout << mk_pp(f, m_manager) << "\n"; for (unsigned i = 0; i < num_args; ++i) { tout << mk_pp(args[i], m_manager) << " "; diff --git a/src/muz_qe/qe_lite.cpp b/src/muz_qe/qe_lite.cpp index f840f19d6..01537b574 100644 --- a/src/muz_qe/qe_lite.cpp +++ b/src/muz_qe/qe_lite.cpp @@ -799,9 +799,15 @@ namespace ar { } /** - Ex A. A[x] = t & Phi where x \not\in A, t. + Ex A. A[x] = t & Phi where x \not\in A, t. A \not\in t, x => Ex A. Phi[store(A,x,t)] + + Perhaps also: + Ex A. store(A,y,z)[x] = t & Phi where x \not\in A, t, y, z, A \not\in y z, t + => + Ex A, v . (x = y => z = t) & Phi[store(store(A,x,t),y,v)] + */ bool solve_select(expr_ref_vector& conjs, unsigned i, expr* e1, expr* e2) { @@ -827,6 +833,7 @@ namespace ar { expr_safe_replace rep(m); rep.insert(A, B); expr_ref tmp(m); + std::cout << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n"; for (unsigned j = 0; j < conjs.size(); ++j) { if (i == j) { conjs[j] = m.mk_true(); From d94f1b3fd634a3dd4c2c9edfba4b93a8cbd4e098 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 10 Aug 2013 10:50:03 -0700 Subject: [PATCH 103/509] add normalizer of monomial coefficients Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_util.cpp | 149 ++++++++++++++++++++++++++++++++++++ src/muz_qe/pdr_util.h | 11 +++ src/test/arith_rewriter.cpp | 42 ++++++++++ 3 files changed, 202 insertions(+) diff --git a/src/muz_qe/pdr_util.cpp b/src/muz_qe/pdr_util.cpp index 9711cffc2..169eeec72 100644 --- a/src/muz_qe/pdr_util.cpp +++ b/src/muz_qe/pdr_util.cpp @@ -42,6 +42,10 @@ Notes: #include "arith_decl_plugin.h" #include "expr_replacer.h" #include "model_smt2_pp.h" +#include "poly_rewriter.h" +#include "poly_rewriter_def.h" +#include "arith_rewriter.h" + namespace pdr { @@ -1278,6 +1282,151 @@ namespace pdr { return test.is_dl(); } + class arith_normalizer : public poly_rewriter { + ast_manager& m; + arith_util m_util; + enum op_kind { LE, GE, EQ }; + public: + arith_normalizer(ast_manager& m, params_ref const& p = params_ref()): poly_rewriter(m, p), m(m), m_util(m) {} + + br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { + br_status st = BR_FAILED; + if (m.is_eq(f)) { + SASSERT(num_args == 2); return mk_eq_core(args[0], args[1], result); + } + + if (f->get_family_id() != get_fid()) { + return st; + } + switch (f->get_decl_kind()) { + case OP_NUM: st = BR_FAILED; break; + case OP_IRRATIONAL_ALGEBRAIC_NUM: st = BR_FAILED; break; + case OP_LE: SASSERT(num_args == 2); st = mk_le_core(args[0], args[1], result); break; + case OP_GE: SASSERT(num_args == 2); st = mk_ge_core(args[0], args[1], result); break; + case OP_LT: SASSERT(num_args == 2); st = mk_lt_core(args[0], args[1], result); break; + case OP_GT: SASSERT(num_args == 2); st = mk_gt_core(args[0], args[1], result); break; + default: st = BR_FAILED; break; + } + return st; + } + + private: + + br_status mk_eq_core(expr* arg1, expr* arg2, expr_ref& result) { + return mk_le_ge_eq_core(arg1, arg2, EQ, result); + } + br_status mk_le_core(expr* arg1, expr* arg2, expr_ref& result) { + return mk_le_ge_eq_core(arg1, arg2, LE, result); + } + br_status mk_ge_core(expr* arg1, expr* arg2, expr_ref& result) { + return mk_le_ge_eq_core(arg1, arg2, GE, result); + } + br_status mk_lt_core(expr* arg1, expr* arg2, expr_ref& result) { + result = m.mk_not(m_util.mk_ge(arg1, arg2)); + return BR_REWRITE2; + } + br_status mk_gt_core(expr* arg1, expr* arg2, expr_ref& result) { + result = m.mk_not(m_util.mk_le(arg1, arg2)); + return BR_REWRITE2; + } + + br_status mk_le_ge_eq_core(expr* arg1, expr* arg2, op_kind kind, expr_ref& result) { + if (m_util.is_real(arg1)) { + numeral g(0); + get_coeffs(arg1, g); + get_coeffs(arg2, g); + if (!g.is_one() && !g.is_zero()) { + SASSERT(g.is_pos()); + expr_ref new_arg1 = rdiv_polynomial(arg1, g); + expr_ref new_arg2 = rdiv_polynomial(arg2, g); + switch(kind) { + case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_DONE; + case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_DONE; + case EQ: result = m_util.mk_eq(new_arg1, new_arg2); return BR_DONE; + } + } + } + return BR_FAILED; + } + + void update_coeff(numeral const& r, numeral& g) { + if (g.is_zero() || abs(r) < g) { + g = abs(r); + } + } + + void get_coeffs(expr* e, numeral& g) { + rational r; + unsigned sz; + expr* const* args = get_monomials(e, sz); + for (unsigned i = 0; i < sz; ++i) { + expr* arg = args[i]; + if (!m_util.is_numeral(arg, r)) { + get_power_product(arg, r); + } + update_coeff(r, g); + } + } + + expr_ref rdiv_polynomial(expr* e, numeral const& g) { + rational r; + SASSERT(g.is_pos()); + SASSERT(!g.is_one()); + expr_ref_vector monomes(m); + unsigned sz; + expr* const* args = get_monomials(e, sz); + for (unsigned i = 0; i < sz; ++i) { + expr* arg = args[i]; + if (m_util.is_numeral(arg, r)) { + monomes.push_back(m_util.mk_numeral(r/g, false)); + } + else { + expr* p = get_power_product(arg, r); + r /= g; + if (r.is_one()) { + monomes.push_back(p); + } + else { + monomes.push_back(m_util.mk_mul(m_util.mk_numeral(r, false), p)); + } + } + } + expr_ref result(m); + mk_add(monomes.size(), monomes.c_ptr(), result); + return result; + } + + }; + + + struct arith_normalizer_cfg: public default_rewriter_cfg { + arith_normalizer m_r; + bool rewrite_patterns() const { return false; } + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + return m_r.mk_app_core(f, num, args, result); + } + arith_normalizer_cfg(ast_manager & m, params_ref const & p):m_r(m,p) {} + }; + + class arith_normalizer_star : public rewriter_tpl { + arith_normalizer_cfg m_cfg; + public: + arith_normalizer_star(ast_manager & m, params_ref const & p): + rewriter_tpl(m, false, m_cfg), + m_cfg(m, p) {} + }; + + + void normalize_arithmetic(expr_ref& t) { + ast_manager& m = t.get_manager(); + datalog::scoped_no_proof _sp(m); + params_ref p; + arith_normalizer_star rw(m, p); + expr_ref tmp(m); + rw(t, tmp); + t = tmp; + } + } template class rewriter_tpl; diff --git a/src/muz_qe/pdr_util.h b/src/muz_qe/pdr_util.h index 5f2d22b76..67be6751b 100644 --- a/src/muz_qe/pdr_util.h +++ b/src/muz_qe/pdr_util.h @@ -142,6 +142,7 @@ namespace pdr { Assumption: the model satisfies the conjunctions. */ void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml); + /** @@ -149,6 +150,16 @@ namespace pdr { */ void hoist_non_bool_if(expr_ref& fml); + + /** + \brief normalize coefficients in polynomials so that least coefficient is 1. + */ + void normalize_arithmetic(expr_ref& t); + + + /** + \brief determine if formulas belong to difference logic or UTVPI fragment. + */ bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); diff --git a/src/test/arith_rewriter.cpp b/src/test/arith_rewriter.cpp index 0933e9d11..9eb9e559b 100644 --- a/src/test/arith_rewriter.cpp +++ b/src/test/arith_rewriter.cpp @@ -2,6 +2,33 @@ #include "bv_decl_plugin.h" #include "ast_pp.h" #include "reg_decl_plugins.h" +#include "th_rewriter.h" +#include "model.h" +#include "pdr_util.h" +#include "smt2parser.h" + + +static expr_ref parse_fml(ast_manager& m, char const* str) { + expr_ref result(m); + cmd_context ctx(false, &m); + ctx.set_ignore_check(true); + std::ostringstream buffer; + buffer << "(declare-const x Real)\n" + << "(declare-const y Real)\n" + << "(declare-const z Real)\n" + << "(declare-const a Real)\n" + << "(declare-const b Real)\n" + << "(assert " << str << ")\n"; + std::istringstream is(buffer.str()); + VERIFY(parse_smt2_commands(ctx, is)); + SASSERT(ctx.begin_assertions() != ctx.end_assertions()); + result = *ctx.begin_assertions(); + return result; +} + +static char const* example1 = "(<= (+ (* 1.3 x y) (* 2.3 y y) (* (- 1.1 x x))) 2.2)"; +static char const* example2 = "(= (+ 4 3 (- (* 3 x x) (* 5 y)) y) 0)"; + void tst_arith_rewriter() { ast_manager m; @@ -14,4 +41,19 @@ void tst_arith_rewriter() { expr* args[2] = { t1, t2 }; ar.mk_mul(2, args, result); std::cout << mk_pp(result, m) << "\n"; + + + th_rewriter rw(m); + expr_ref fml = parse_fml(m, example1); + rw(fml); + std::cout << mk_pp(fml, m) << "\n"; + pdr::normalize_arithmetic(fml); + std::cout << mk_pp(fml, m) << "\n"; + + + fml = parse_fml(m, example2); + rw(fml); + std::cout << mk_pp(fml, m) << "\n"; + pdr::normalize_arithmetic(fml); + std::cout << mk_pp(fml, m) << "\n"; } From e7f458101c5ccfc5bb64449cc5eb928bd3cc6674 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 10 Aug 2013 10:53:46 -0700 Subject: [PATCH 104/509] add normalizer of monomial coefficients Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz_qe/pdr_util.cpp b/src/muz_qe/pdr_util.cpp index 169eeec72..3d93f8104 100644 --- a/src/muz_qe/pdr_util.cpp +++ b/src/muz_qe/pdr_util.cpp @@ -1430,6 +1430,6 @@ namespace pdr { } template class rewriter_tpl; - +template class rewriter_tpl; From a20656de35f664b922c1f5f47212656abeec03b5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 10 Aug 2013 10:57:25 -0700 Subject: [PATCH 105/509] fix unused variable warning in unit test Signed-off-by: Nikolaj Bjorner --- src/test/polynorm.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/polynorm.cpp b/src/test/polynorm.cpp index 9853181ca..092ac022a 100644 --- a/src/test/polynorm.cpp +++ b/src/test/polynorm.cpp @@ -109,10 +109,12 @@ void tst_polynorm() { reg_decl_plugins(m); expr_ref fml(m); - fml = parse_fml(m, example2); - + fml = parse_fml(m, example1); std::cout << mk_pp(fml, m) << "\n"; + nf(fml); + fml = parse_fml(m, example2); + std::cout << mk_pp(fml, m) << "\n"; nf(fml); From 1c3f715e26ad80346e9ce4128e042d27279f05e0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 10 Aug 2013 12:21:49 -0700 Subject: [PATCH 106/509] switch between convex and interior hull, add multiple cores Signed-off-by: Nikolaj Bjorner --- src/muz_qe/fixedpoint_params.pyg | 3 +- src/muz_qe/pdr_context.cpp | 11 +++-- src/muz_qe/pdr_generalizers.cpp | 80 +++++++++++++++++++++++++------- src/muz_qe/pdr_generalizers.h | 8 +++- 4 files changed, 80 insertions(+), 22 deletions(-) diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index 860dcb68e..555c44df2 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -49,7 +49,8 @@ def_module_params('fixedpoint', ('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"), ('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"), ('use_arith_inductive_generalizer', BOOL, False, "PDR: generalize lemmas using arithmetic heuristics for induction strengthening"), - ('use_convex_hull_generalizer', BOOL, False, "PDR: generalize using convex hulls of lemmas"), + ('use_convex_closure_generalizer', BOOL, False, "PDR: generalize using convex closures of lemmas"), + ('use_convex_interior_generalizer', BOOL, False, "PDR: generalize using convex interiors of lemmas"), ('cache_mode', UINT, 0, "PDR: use no (0), symbolic (1) or explicit cache (2) for model search"), ('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when " "checking for reachability (not only during cube weakening)"), diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index 0121e49b8..fd86a4402 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -1578,7 +1578,9 @@ namespace pdr { m_fparams.m_arith_auto_config_simplex = true; m_fparams.m_arith_propagate_eqs = false; m_fparams.m_arith_eager_eq_axioms = false; - if (m_params.use_utvpi() && !m_params.use_convex_hull_generalizer()) { + if (m_params.use_utvpi() && + !m_params.use_convex_closure_generalizer() && + !m_params.use_convex_interior_generalizer()) { if (classify.is_dl()) { m_fparams.m_arith_mode = AS_DIFF_LOGIC; m_fparams.m_arith_expand_eqs = true; @@ -1590,8 +1592,11 @@ namespace pdr { } } } - if (m_params.use_convex_hull_generalizer()) { - m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this)); + if (m_params.use_convex_closure_generalizer()) { + m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this, true)); + } + if (m_params.use_convex_interior_generalizer()) { + m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this, false)); } if (!use_mc && m_params.use_inductive_generalizer()) { m_core_generalizers.push_back(alloc(core_bool_inductive_generalizer, *this, 0)); diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index c094b6c7f..2c49917be 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -147,18 +147,23 @@ namespace pdr { } - core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx): + core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx, bool is_closure): core_generalizer(ctx), m(ctx.get_manager()), a(m), m_sigma(m), - m_trail(m) { + m_trail(m), + m_is_closure(is_closure) { m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); } + void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { + method1(n, core, uses_level, new_cores); + } + void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - method1(n, core, uses_level); + UNREACHABLE(); } // use the entire region as starting point for generalization. @@ -174,20 +179,27 @@ namespace pdr { // If Constraints & Transition(y0, y) is unsat, then // update with new core. // - void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector& core, bool& uses_level) { + void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { manager& pm = n.pt().get_pdr_manager(); expr_ref_vector conv1(m), conv2(m), core1(m), core2(m), eqs(m); if (core.empty()) { return; } + new_cores.push_back(std::make_pair(core, uses_level)); add_variables(n, eqs); if (!mk_convex(core, 0, conv1)) { IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core), m) << "\n";); return; } conv1.append(eqs); - conv1.push_back(a.mk_gt(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); - conv1.push_back(a.mk_gt(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); + if (m_is_closure) { + conv1.push_back(a.mk_gt(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); + conv1.push_back(a.mk_gt(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); + } + else { + conv1.push_back(a.mk_ge(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); + conv1.push_back(a.mk_ge(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); + } conv1.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(m_sigma[0].get(), m_sigma[1].get()))); expr_ref fml = n.pt().get_formulas(n.level(), false); expr_ref_vector fmls(m); @@ -202,22 +214,23 @@ namespace pdr { } conv2.append(conv1); expr_ref state = pm.mk_and(conv2); - TRACE("pdr", tout << "Check:\n" << mk_pp(state, m) << "\n"; - tout << "New formula:\n" << mk_pp(pm.mk_and(core), m) << "\n"; - tout << "Old formula:\n" << mk_pp(fml, m) << "\n"; - + TRACE("pdr", + tout << "Check states:\n" << mk_pp(state, m) << "\n"; + tout << "Old states:\n" << mk_pp(fml, m) << "\n"; ); model_node nd(0, state, n.pt(), n.level()); pred_transformer::scoped_farkas sf(n.pt(), true); - if (l_false == n.pt().is_reachable(nd, &conv2, uses_level)) { + bool uses_level1 = uses_level; + if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) { + new_cores.push_back(std::make_pair(conv2, uses_level1)); + + expr_ref state1 = pm.mk_and(conv2); TRACE("pdr", tout << mk_pp(state, m) << "\n"; - tout << "Generalized to:\n" << mk_pp(pm.mk_and(conv2), m) << "\n";); + tout << "Generalized to:\n" << mk_pp(state1, m) << "\n";); IF_VERBOSE(0, verbose_stream() << mk_pp(state, m) << "\n"; - verbose_stream() << "Generalized to:\n" << mk_pp(pm.mk_and(conv2), m) << "\n";); - core.reset(); - core.append(conv2); + verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";); } } } @@ -317,12 +330,47 @@ namespace pdr { } } + expr_ref core_convex_hull_generalizer::mk_closure(expr* e) { + expr* e0, *e1, *e2; + expr_ref result(m); + if (a.is_lt(e, e1, e2)) { + result = a.mk_le(e1, e2); + } + else if (a.is_gt(e, e1, e2)) { + result = a.mk_ge(e1, e2); + } + else if (m.is_not(e, e0) && a.is_ge(e0, e1, e2)) { + result = a.mk_le(e1, e2); + } + else if (m.is_not(e, e0) && a.is_le(e0, e1, e2)) { + result = a.mk_ge(e1, e2); + } + else if (a.is_ge(e) || a.is_le(e) || m.is_eq(e) || + (m.is_not(e, e0) && (a.is_gt(e0) || a.is_lt(e0)))) { + result = e; + } + else { + IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";); + } + return result; + } + + bool core_convex_hull_generalizer::mk_closure(expr_ref_vector& conj) { + for (unsigned i = 0; i < conj.size(); ++i) { + conj[i] = mk_closure(conj[i].get()); + if (!conj[i].get()) { + return false; + } + } + return true; + } + bool core_convex_hull_generalizer::mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv) { conv.reset(); for (unsigned i = 0; i < core.size(); ++i) { mk_convex(core[i], index, conv); } - return !conv.empty(); + return !conv.empty() && mk_closure(conv); } void core_convex_hull_generalizer::mk_convex(expr* fml, unsigned index, expr_ref_vector& conv) { diff --git a/src/muz_qe/pdr_generalizers.h b/src/muz_qe/pdr_generalizers.h index 0aee94c16..ece1f51f1 100644 --- a/src/muz_qe/pdr_generalizers.h +++ b/src/muz_qe/pdr_generalizers.h @@ -81,16 +81,20 @@ namespace pdr { obj_map m_left; obj_map m_right; obj_map m_models; + bool m_is_closure; + expr_ref mk_closure(expr* e); + bool mk_closure(expr_ref_vector& conj); bool mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv); void mk_convex(expr* fml, unsigned index, expr_ref_vector& conv); bool mk_convex(expr* term, unsigned index, bool is_mul, expr_ref& result); bool translate(func_decl* fn, unsigned index, expr_ref& result); - void method1(model_node& n, expr_ref_vector& core, bool& uses_level); + void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); void method2(model_node& n, expr_ref_vector& core, bool& uses_level); void add_variables(model_node& n, expr_ref_vector& eqs); public: - core_convex_hull_generalizer(context& ctx); + core_convex_hull_generalizer(context& ctx, bool is_closure); virtual ~core_convex_hull_generalizer() {} + virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); }; From 0f83e7a251a4d1f094d3abba2c76e6c7512e032f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 10 Aug 2013 12:23:34 -0700 Subject: [PATCH 107/509] fix bug masked by default configuration Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/arith_rewriter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h index bce59657a..d08e6f13a 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -118,7 +118,7 @@ public: void mk_eq(expr * arg1, expr * arg2, expr_ref & result) { if (mk_eq_core(arg1, arg2, result) == BR_FAILED) - result = m_util.mk_le(arg1, arg2); + result = m_util.mk_eq(arg1, arg2); } void mk_le(expr * arg1, expr * arg2, expr_ref & result) { if (mk_le_core(arg1, arg2, result) == BR_FAILED) From 6a820adfed83a4db4cc520f877deec4d73c8dfc9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 11 Aug 2013 09:43:17 -0700 Subject: [PATCH 108/509] fix logic for adding cores Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_generalizers.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index 2c49917be..bf1693ce3 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -182,12 +182,14 @@ namespace pdr { void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { manager& pm = n.pt().get_pdr_manager(); expr_ref_vector conv1(m), conv2(m), core1(m), core2(m), eqs(m); + unsigned orig_size = new_cores.size(); if (core.empty()) { + new_cores.push_back(std::make_pair(core, uses_level)); return; } - new_cores.push_back(std::make_pair(core, uses_level)); add_variables(n, eqs); if (!mk_convex(core, 0, conv1)) { + new_cores.push_back(std::make_pair(core, uses_level)); IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core), m) << "\n";); return; } @@ -233,6 +235,10 @@ namespace pdr { verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";); } } + if (!m_is_closure || new_cores.empty()) { + new_cores.push_back(std::make_pair(core, uses_level)); + } + } // take as starting point two points from different regions. From 661fe7eec9b4db7f86681953248a242d24754992 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 11 Aug 2013 19:10:46 -0700 Subject: [PATCH 109/509] add missing detach in coi_filter Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_coi_filter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz_qe/dl_mk_coi_filter.cpp b/src/muz_qe/dl_mk_coi_filter.cpp index 7bbb14353..a0fc845bf 100644 --- a/src/muz_qe/dl_mk_coi_filter.cpp +++ b/src/muz_qe/dl_mk_coi_filter.cpp @@ -42,7 +42,7 @@ namespace datalog { scoped_ptr result1 = top_down(source); scoped_ptr result2 = bottom_up(result1?*result1:source); if (!result2) { - result2 = result1; + result2 = result1.detach(); } return result2.detach(); } From 0fd94a033f86f2c8c3750234cabda95d93cad719 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 12 Aug 2013 09:52:10 -0700 Subject: [PATCH 110/509] change non-null test Signed-off-by: Nikolaj Bjorner --- src/api/api_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index cf179332a..05e1ca675 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -139,7 +139,7 @@ namespace api { if (m_interruptable) (*m_interruptable)(); m().set_cancel(true); - if (m_rcf_manager.get() == 0) + if (m_rcf_manager.get() != 0) m_rcf_manager->set_cancel(true); } } From 6c5f7741b2d100d325228db1de5d1960fc20ea0f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 14 Aug 2013 11:55:23 -0700 Subject: [PATCH 111/509] more on polynorm Signed-off-by: Nikolaj Bjorner --- src/smt/theory_dl.cpp | 2 +- src/test/polynorm.cpp | 109 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_dl.cpp b/src/smt/theory_dl.cpp index 9c3489aec..4ef1bd8e0 100644 --- a/src/smt/theory_dl.cpp +++ b/src/smt/theory_dl.cpp @@ -91,7 +91,7 @@ namespace smt { rational val; if (ctx.e_internalized(rep_of) && th_bv && th_bv->get_fixed_value(rep_of.get(), val)) { - result = m_th.u().mk_numeral(val.get_int64(), s); + result = m_th.u().mk_numeral(val.get_int64(), s); } else { result = m_th.u().mk_numeral(0, s); diff --git a/src/test/polynorm.cpp b/src/test/polynorm.cpp index 092ac022a..3593e98ce 100644 --- a/src/test/polynorm.cpp +++ b/src/test/polynorm.cpp @@ -2,6 +2,7 @@ #include "smt2parser.h" #include "arith_decl_plugin.h" #include "reg_decl_plugins.h" +#include "arith_rewriter.h" #include "ast_pp.h" @@ -27,6 +28,114 @@ static char const* example1 = "(= (+ (- (* x x) (* 2 y)) y) 0)"; static char const* example2 = "(= (+ 4 3 (- (* x x) (* 2 y)) y) 0)"; +class poly_nf { + expr_ref m_coefficient; + expr_ref_vector m_coefficients; + expr_ref_vector m_factors; +public: + poly_nf(ast_manager& m): + m_coefficient(m), + m_coefficients(m), + m_factors(m) {} + + expr_ref& coefficient() { return m_coefficient; } + expr_ref_vector& coefficients() { return m_coefficients; } + expr_ref_vector& factors() { return m_factors; } + + void reset() { + m_coefficient.reset(); + m_coefficients.reset(); + m_factors.reset(); + } + +}; + +class polynorm { + ast_manager& m; + arith_util m_arith; + arith_rewriter m_arith_rw; + th_rewriter m_rw; +public: + polynorm(ast_manager& m): m(m), m_arith(m), m_arith_rw(m), m_rw(m) {} + +private: + expr_ref_vector mk_fresh_constants(unsigned num, sort* s) { + expr_ref_vector result(m); + for (unsigned i = 0; i < num; ++i) { + result.push_back(m.mk_fresh_const("fresh", s)); + } + return result; + } + + expr_ref_vector mk_fresh_reals(unsigned num) { + return mk_fresh_constants(num, m_arith.mk_real()); + } + + expr_ref mk_mul(unsigned num_args, expr* const* args) { + expr_ref result(m); + m_arith_rw.mk_mul(num_args, args, result); + return result; + } + + void nf(expr_ref& term, obj_hashtable& constants, poly_nf& poly) { + + expr_ref_vector& factors = poly.factors(); + expr_ref_vector& coefficients = poly.coefficients(); + expr_ref& coefficient = poly.coefficient(); + + m_rw(term); + + if (m_arith.is_add(term)) { + factors.append(to_app(term)->get_num_args(), to_app(term)->get_args()); + } + else { + factors.push_back(term); + } + for (unsigned i = 0; i < factors.size(); ++i) { + expr* f = factors[i].get(); + unsigned num_args = 1; + expr* const* args = &f; + if (m_arith.is_mul(f)) { + num_args = to_app(f)->get_num_args(); + args = to_app(f)->get_args(); + } + for (unsigned j = 0; j < num_args; ++j) { + if (m_arith.is_numeral(args[j]) || constants.contains(args[j])) { + //consts.push_back(args[j]); + } + else { + // vars.push_back(args[j]); + } + // deal with the relevant corner cases. + } +#if 0 + rational r; + if (m_arith.is_mul(f) && m_arith.is_numeral(to_app(f)->get_arg(0), r)) { + coefficients.push_back(r); + factors[i] = mk_mul(to_app(f)->get_num_args()-1, to_app(f)->get_args()+1); + } + else if (m_arith.is_numeral(f, r)) { + factors[i] = factors.back(); + factors.pop_back(); + SASSERT(coefficient.is_zero()); + SASSERT(!r.is_zero()); + coefficient = r; + --i; // repeat examining 'i' + } + else { + coefficients.push_back(rational(1)); + } +#endif + } + + TRACE("polynorm", + tout << mk_pp(coefficient, m) << "\n"; + for (unsigned i = 0; i < factors.size(); ++i) { + tout << mk_pp(factors[i].get(), m) << " * " << mk_pp(coefficients[i].get(), m) << "\n"; + }); + } +}; + // ast /// sort : ast /// func_decl : ast From 04678d96282e7ef62346b5b0877201b3708115a3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 14 Aug 2013 17:56:07 -0700 Subject: [PATCH 112/509] improve error message when sorts are non-numeric Signed-off-by: Nikolaj Bjorner --- src/ast/dl_decl_plugin.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index 4f0c9a75d..6d0823a24 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -657,7 +657,9 @@ namespace datalog { SASSERT(value == 1); return m.mk_true(); } - m.raise_exception("unrecognized sort"); + std::stringstream strm; + strm << "sort '" << mk_pp(s, m) << "' is not recognized as a sort that contains numeric values.\nUse Bool, BitVec, Int, Real, or a Finite domain sort"; + m.raise_exception(strm.str().c_str()); return 0; } From 01d59d2c9164d1070d895c9db7f9c78f576e169f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 15 Aug 2013 18:36:27 -0700 Subject: [PATCH 113/509] fix bugs reported by Arie Gurfinkel Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_context.cpp | 5 +++++ src/muz_qe/pdr_generalizers.cpp | 11 ++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index fd86a4402..ff40be4f3 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -1881,6 +1881,11 @@ namespace pdr { // predicate transformer (or some unfolding of it). // lbool context::expand_state(model_node& n, expr_ref_vector& result, bool& uses_level) { + TRACE("pdr", + tout << "expand_state: " << n.pt().head()->get_name(); + tout << " level: " << n.level() << "\n"; + tout << mk_pp(n.state(), m) << "\n";); + return n.pt().is_reachable(n, &result, uses_level); } diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index bf1693ce3..28226dffa 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -195,13 +195,14 @@ namespace pdr { } conv1.append(eqs); if (m_is_closure) { - conv1.push_back(a.mk_gt(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); - conv1.push_back(a.mk_gt(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); - } - else { conv1.push_back(a.mk_ge(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); conv1.push_back(a.mk_ge(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); } + else { + // is interior: + conv1.push_back(a.mk_gt(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); + conv1.push_back(a.mk_gt(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); + } conv1.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(m_sigma[0].get(), m_sigma[1].get()))); expr_ref fml = n.pt().get_formulas(n.level(), false); expr_ref_vector fmls(m); @@ -235,7 +236,7 @@ namespace pdr { verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";); } } - if (!m_is_closure || new_cores.empty()) { + if (!m_is_closure || new_cores.size() == orig_size) { new_cores.push_back(std::make_pair(core, uses_level)); } From 7bbabcdf6dadcc133044f7b36e0647583ec63796 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 16 Aug 2013 14:47:48 -0700 Subject: [PATCH 114/509] updated documentation for finite domain sizes Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/Context.cs | 8 ++++++++ src/api/z3_api.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 68cca046e..ae8b233d1 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -303,6 +303,9 @@ namespace Microsoft.Z3 /// /// Create a new finite domain sort. + /// The name used to identify the sort + /// The size of the sort + /// The result is a sort /// public FiniteDomainSort MkFiniteDomainSort(Symbol name, ulong size) { @@ -315,6 +318,11 @@ namespace Microsoft.Z3 /// /// Create a new finite domain sort. + /// The name used to identify the sort + /// The size of the sort + /// The result is a sort + /// Elements of the sort are created using , + /// and the elements range from 0 to size-1. /// public FiniteDomainSort MkFiniteDomainSort(string name, ulong size) { diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 458e2f53f..2b8c74e65 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1721,6 +1721,8 @@ extern "C" { To create constants that belong to the finite domain, use the APIs for creating numerals and pass a numeric constant together with the sort returned by this call. + The numeric constant should be between 0 and the less + than the size of the domain. \sa Z3_get_finite_domain_sort_size From 07bb534d659fc52997157f77029c8f70a3cb91d3 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 16 Aug 2013 18:38:24 -0700 Subject: [PATCH 115/509] some duality fixes --- src/duality/duality_rpfp.cpp | 5 +- src/duality/duality_solver.cpp | 4 +- src/duality/duality_wrapper.h | 18 +- src/interp/iz3mgr.cpp | 208 +++++- src/interp/iz3mgr.h | 158 ++++- src/interp/iz3proof_itp.cpp | 789 ++++++++++++++++++++++ src/interp/iz3proof_itp.h | 130 ++++ src/interp/iz3translate.cpp | 1164 ++++++++++++++++++++++++++++++++ src/interp/iz3translate.h | 5 + 9 files changed, 2461 insertions(+), 20 deletions(-) create mode 100644 src/interp/iz3proof_itp.cpp create mode 100644 src/interp/iz3proof_itp.h create mode 100755 src/interp/iz3translate.cpp diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 330799796..1c2927da8 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -1636,7 +1636,8 @@ namespace Duality { dont_cares.insert(b); resolve_ite_memo.clear(); timer_start("UnderapproxFormula"); - Term eu = UnderapproxFormula(root->Outgoing->dual,dont_cares); + Term dual = root->Outgoing->dual.null() ? ctx.bool_val(true) : root->Outgoing->dual; + Term eu = UnderapproxFormula(dual,dont_cares); timer_stop("UnderapproxFormula"); /* combine with children */ chu.push_back(eu); @@ -1944,6 +1945,8 @@ namespace Duality { for(unsigned i = 0; i < clauses.size(); i++){ Term &clause = clauses[i]; + if(clause.is_app() && clause.decl().get_decl_kind() == Uninterpreted) + clause = implies(ctx.bool_val(true),clause); if(!canonical_clause(clause)) clause = implies((!clause).simplify(),ctx.bool_val(false)); Term head = clause.arg(1); diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 897c88c30..f716f04fe 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -916,7 +916,7 @@ namespace Duality { return true; } #ifdef UNDERAPPROX_NODES - if(0 && last_decisions > 5000){ + if(UseUnderapprox && last_decisions > 500){ std::cout << "making an underapprox\n"; ExpandNodeFromCoverFail(node); } @@ -1642,7 +1642,7 @@ namespace Duality { std::set old_choices; void ExpansionChoices(std::set &best, bool high_priority){ - if(!underapprox || constrained){ + if(!underapprox || constrained || high_priority){ ExpansionChoicesFull(best, high_priority); return; } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 1048734f7..21ed45479 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -515,39 +515,39 @@ namespace Duality { } friend expr operator+(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_ADD,a,b)); + return a.ctx().make(Plus,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_ADD,a,b)); } friend expr operator*(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_MUL,a,b)); + return a.ctx().make(Times,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_MUL,a,b)); } friend expr operator/(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DIV,a,b)); + return a.ctx().make(Div,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DIV,a,b)); } friend expr operator-(expr const & a) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_UMINUS,a)); + return a.ctx().make(Uminus,a); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_UMINUS,a)); } friend expr operator-(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_SUB,a,b)); + return a.ctx().make(Sub,a,b); // expr(a.ctx(),a.m().mk_app(a.ctx().m_arith_fid,OP_SUB,a,b)); } friend expr operator<=(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LE,a,b)); + return a.ctx().make(Leq,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LE,a,b)); } friend expr operator>=(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GE,a,b)); + return a.ctx().make(Geq,a,b); //expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GE,a,b)); } friend expr operator<(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LT,a,b)); + return a.ctx().make(Lt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LT,a,b)); } friend expr operator>(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GT,a,b)); + return a.ctx().make(Gt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GT,a,b)); } expr simplify() const; diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 807c38227..7a787fdea 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -26,6 +26,7 @@ Revision History: #include #include "expr_abstract.h" +#include "params.h" #ifndef WIN32 @@ -145,19 +146,19 @@ iz3mgr::ast iz3mgr::make(symb sym){ return make(sym,0,0); } -iz3mgr::ast iz3mgr::make(symb sym, ast &arg0){ +iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0){ raw_ast *a = arg0.raw(); return make(sym,1,&a); } -iz3mgr::ast iz3mgr::make(symb sym, ast &arg0, ast &arg1){ +iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0, const ast &arg1){ raw_ast *args[2]; args[0] = arg0.raw(); args[1] = arg1.raw(); return make(sym,2,args); } -iz3mgr::ast iz3mgr::make(symb sym, ast &arg0, ast &arg1, ast &arg2){ +iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0, const ast &arg1, const ast &arg2){ raw_ast *args[3]; args[0] = arg0.raw(); args[1] = arg1.raw(); @@ -199,7 +200,7 @@ iz3mgr::ast iz3mgr::make_quant(opr op, const std::vector &bvs, ast &body){ // FIXME replace this with existing Z3 functionality -iz3mgr::ast iz3mgr::clone(ast &t, const std::vector &_args){ +iz3mgr::ast iz3mgr::clone(const ast &t, const std::vector &_args){ if(_args.size() == 0) return t; @@ -242,6 +243,10 @@ void iz3mgr::show(ast t){ std::cout << mk_pp(t.raw(), m()) << std::endl; } +void iz3mgr::show_symb(symb s){ + std::cout << mk_pp(s, m()) << std::endl; +} + void iz3mgr::print_expr(std::ostream &s, const ast &e){ s << mk_pp(e.raw(), m()); } @@ -431,3 +436,198 @@ void iz3mgr::print_sat_problem(std::ostream &out, const ast &t){ pp.set_simplify_implies(false); pp.display_smt2(out, to_expr(t.raw())); } + +iz3mgr::ast iz3mgr::z3_simplify(const ast &e){ + ::expr * a = to_expr(e.raw()); + params_ref p; + th_rewriter m_rw(m(), p); + expr_ref result(m()); + m_rw(a, result); + return cook(result); +} + + +#if 0 +static rational lcm(const rational &x, const rational &y){ + int a = x.numerator(); + int b = y.numerator(); + return rational(a * b / gcd(a, b)); +} +#endif + +static rational extract_lcd(std::vector &res){ + if(res.size() == 0) return rational(1); // shouldn't happen + rational lcd = denominator(res[0]); + for(unsigned i = 1; i < res.size(); i++) + lcd = lcm(lcd,denominator(res[i])); + for(unsigned i = 0; i < res.size(); i++) + res[i] *= lcd; + return lcd; +} + +void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& coeffs){ + std::vector rats; + get_farkas_coeffs(proof,rats); + coeffs.resize(rats.size()); + for(unsigned i = 0; i < rats.size(); i++){ + sort *is = m().mk_sort(m_arith_fid, INT_SORT); + ast coeff = cook(m_arith_util.mk_numeral(rats[i],is)); + coeffs[i] = coeff; + } +} + +void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ + symb s = sym(proof); + int numps = s->get_num_parameters(); + rats.resize(numps-2); + for(int i = 2; i < numps; i++){ + rational r; + bool ok = s->get_parameter(i).is_rational(r); + if(!ok) + throw "Bad Farkas coefficient"; + { + ast con = conc(prem(proof,i-2)); + ast temp = make_real(r); // for debugging + opr o = is_not(con) ? op(arg(con,0)) : op(con); + if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) + r = -r; + } + rats[i-2] = r; + } + extract_lcd(rats); +} + +void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& coeffs){ + std::vector rats; + get_assign_bounds_coeffs(proof,rats); + coeffs.resize(rats.size()); + for(unsigned i = 0; i < rats.size(); i++){ + coeffs[i] = make_int(rats[i]); + } +} + +void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& rats){ + symb s = sym(proof); + int numps = s->get_num_parameters(); + rats.resize(numps-1); + rats[0] = rational(1); + for(int i = 2; i < numps; i++){ + rational r; + bool ok = s->get_parameter(i).is_rational(r); + if(!ok) + throw "Bad Farkas coefficient"; + { + ast con = arg(conc(proof),i-1); + ast temp = make_real(r); // for debugging +#if 0 + opr o = is_not(con) ? op(arg(con,0)) : op(con); + if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) +#endif + r = -r; + } + rats[i-1] = r; + } + extract_lcd(rats); +} + + /** Set P to P + cQ, where P and Q are linear inequalities. Assumes P is 0 <= y or 0 < y. */ + +void iz3mgr::linear_comb(ast &P, const ast &c, const ast &Q){ + ast Qrhs; + bool strict = op(P) == Lt; + if(is_not(Q)){ + ast nQ = arg(Q,0); + switch(op(nQ)){ + case Gt: + Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); + break; + case Lt: + Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); + break; + case Geq: + Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); + strict = true; + break; + case Leq: + Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); + strict = true; + break; + default: + throw "not an inequality"; + } + } + else { + switch(op(Q)){ + case Leq: + Qrhs = make(Sub,arg(Q,1),arg(Q,0)); + break; + case Geq: + Qrhs = make(Sub,arg(Q,0),arg(Q,1)); + break; + case Lt: + Qrhs = make(Sub,arg(Q,1),arg(Q,0)); + strict = true; + break; + case Gt: + Qrhs = make(Sub,arg(Q,0),arg(Q,1)); + strict = true; + break; + default: + throw "not an inequality"; + } + } + Qrhs = make(Times,c,Qrhs); + if(strict) + P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); + else + P = make(Leq,arg(P,0),make(Plus,arg(P,1),Qrhs)); +} + +iz3mgr::ast iz3mgr::sum_inequalities(const std::vector &coeffs, const std::vector &ineqs){ + ast zero = make_int("0"); + ast thing = make(Leq,zero,zero); + for(unsigned i = 0; i < ineqs.size(); i++){ + linear_comb(thing,coeffs[i],ineqs[i]); + } + thing = simplify_ineq(thing); + return thing; +} + +void iz3mgr::mk_idiv(const ast& t, const rational &d, ast &whole, ast &frac){ + opr o = op(t); + if(o == Plus){ + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + mk_idiv(arg(t,i),d,whole,frac); + return; + } + else if(o == Times){ + rational coeff; + if(is_numeral(arg(t,0),coeff)){ + if(gcd(coeff,d) == d){ + whole = make(Plus,whole,make(Times,make_int(coeff/d),arg(t,1))); + return; + } + } + } + frac = make(Plus,frac,t); +} + +iz3mgr::ast iz3mgr::mk_idiv(const ast& q, const rational &d){ + ast t = z3_simplify(q); + if(d == rational(1)) + return t; + else { + ast whole = make_int("0"); + ast frac = whole; + mk_idiv(t,d,whole,frac); + return z3_simplify(make(Plus,whole,make(Idiv,z3_simplify(frac),make_int(d)))); + } +} + +iz3mgr::ast iz3mgr::mk_idiv(const ast& t, const ast &d){ + rational r; + if(is_numeral(d,r)) + return mk_idiv(t,r); + return make(Idiv,t,d); +} diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 4ae345319..83cd573f3 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -224,11 +224,11 @@ class iz3mgr { ast make(opr op, const ast &arg0, const ast &arg1, const ast &arg2); ast make(symb sym, const std::vector &args); ast make(symb sym); - ast make(symb sym, ast &arg0); - ast make(symb sym, ast &arg0, ast &arg1); - ast make(symb sym, ast &arg0, ast &arg1, ast &arg2); + ast make(symb sym, const ast &arg0); + ast make(symb sym, const ast &arg0, const ast &arg1); + ast make(symb sym, const ast &arg0, const ast &arg1, const ast &arg2); ast make_quant(opr op, const std::vector &bvs, ast &body); - ast clone(ast &t, const std::vector &args); + ast clone(const ast &t, const std::vector &args); ast_manager &m() {return m_manager;} @@ -276,6 +276,12 @@ class iz3mgr { return ast(); } + void get_args(const ast &t, std::vector &res){ + res.resize(num_args(t)); + for(unsigned i = 0; i < res.size(); i++) + res[i] = arg(t,i); + } + symb sym(ast t){ return to_app(t.raw())->get_decl(); } @@ -306,6 +312,19 @@ class iz3mgr { return "NaN"; } + bool is_numeral(const ast& t, rational &r){ + expr* e = to_expr(t.raw()); + assert(e); + return m_arith_util.is_numeral(e, r); + } + + rational get_coeff(const ast& t){ + rational res; + if(op(t) == Times && is_numeral(arg(t,0),res)) + return res; + return rational(1); + } + int get_quantifier_num_bound(const ast &t) { return to_quantifier(t.raw())->get_num_decls(); } @@ -337,6 +356,54 @@ class iz3mgr { return to_func_decl(s)->get_range(); } + int get_num_parameters(const symb &s){ + return to_func_decl(s)->get_num_parameters(); + } + + ast get_ast_parameter(const symb &s, int idx){ + return cook(to_func_decl(s)->get_parameters()[idx].get_ast()); + } + + enum lemma_theory {ArithTheory,UnknownTheory}; + + lemma_theory get_theory_lemma_theory(const ast &proof){ + symb s = sym(proof); + ::symbol p0; + bool ok = s->get_parameter(0).is_symbol(p0); + if(!ok) return UnknownTheory; + std::string foo(p0.bare_str()); + if(foo == "arith") + return ArithTheory; + return UnknownTheory; + } + + enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,UnknownKind}; + + lemma_kind get_theory_lemma_kind(const ast &proof){ + symb s = sym(proof); + ::symbol p0; + bool ok = s->get_parameter(1).is_symbol(p0); + if(!ok) return UnknownKind; + std::string foo(p0.bare_str()); + if(foo == "farkas") + return FarkasKind; + if(foo == "triangle-eq") + return is_not(arg(conc(proof),0)) ? Eq2LeqKind : Leq2EqKind; + if(foo == "gcd-test") + return GCDTestKind; + if(foo == "assign-bounds") + return AssignBoundsKind; + return UnknownKind; + } + + void get_farkas_coeffs(const ast &proof, std::vector& coeffs); + + void get_farkas_coeffs(const ast &proof, std::vector& rats); + + void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); + + void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); + bool is_true(ast t){ return op(t) == True; } @@ -357,6 +424,10 @@ class iz3mgr { return op(t) == Not; } + /** Simplify an expression using z3 simplifier */ + + ast z3_simplify(const ast& e); + // Some constructors that simplify things ast mk_not(ast x){ @@ -389,6 +460,20 @@ class iz3mgr { return make(Or,x,y); } + ast mk_or(const std::vector &x){ + ast res = mk_false(); + for(unsigned i = 0; i < x.size(); i++) + res = mk_or(res,x[i]); + return res; + } + + ast mk_and(const std::vector &x){ + ast res = mk_true(); + for(unsigned i = 0; i < x.size(); i++) + res = mk_and(res,x[i]); + return res; + } + ast mk_equal(ast x, ast y){ if(x == y) return make(True); opr ox = op(x); @@ -419,11 +504,74 @@ class iz3mgr { return cook(m_arith_util.mk_numeral(rational(s.c_str()),r)); } + ast make_int(const rational &s) { + sort *r = m().mk_sort(m_arith_fid, INT_SORT); + return cook(m_arith_util.mk_numeral(s,r)); + } + + ast make_real(const std::string &s) { + sort *r = m().mk_sort(m_arith_fid, REAL_SORT); + return cook(m_arith_util.mk_numeral(rational(s.c_str()),r)); + } + + ast make_real(const rational &s) { + sort *r = m().mk_sort(m_arith_fid, REAL_SORT); + return cook(m_arith_util.mk_numeral(s,r)); + } ast mk_false() { return make(False); } ast mk_true() { return make(True); } + ast mk_fresh_constant(char const * prefix, type s){ + return cook(m().mk_fresh_const(prefix, s)); + } + + type bool_type() { + ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); + return s; + } + + type int_type() { + ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); + return s; + } + + type real_type() { + ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); + return s; + } + + type array_type(type d, type r) { + parameter params[2] = { parameter(d), parameter(to_sort(r)) }; + ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); + return s; + } + + symb function(const std::string &str_name, unsigned arity, type *domain, type range) { + ::symbol name = ::symbol(str_name.c_str()); + std::vector< ::sort *> sv(arity); + for(unsigned i = 0; i < arity; i++) + sv[i] = domain[i]; + ::func_decl* d = m().mk_func_decl(name,arity,&sv[0],range); + return d; + } + + void linear_comb(ast &P, const ast &c, const ast &Q); + + ast sum_inequalities(const std::vector &coeffs, const std::vector &ineqs); + + ast simplify_ineq(const ast &ineq){ + ast res = make(op(ineq),arg(ineq,0),z3_simplify(arg(ineq,1))); + return res; + } + + void mk_idiv(const ast& t, const rational &d, ast &whole, ast &frac); + + ast mk_idiv(const ast& t, const rational &d); + + ast mk_idiv(const ast& t, const ast &d); + /** methods for destructing proof terms */ pfrule pr(const z3pf &t); @@ -437,6 +585,8 @@ class iz3mgr { /** For debugging */ void show(ast); + void show_symb(symb s); + /** Constructor */ void print_lit(ast lit); diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp new file mode 100644 index 000000000..5cd75bc26 --- /dev/null +++ b/src/interp/iz3proof_itp.cpp @@ -0,0 +1,789 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3proof.cpp + +Abstract: + + This class defines a simple interpolating proof system. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#include "iz3proof_itp.h" + +#ifndef WIN32 +using namespace stl_ext; +#endif + + +class iz3proof_itp_impl : public iz3proof_itp { + + prover *pv; + prover::range rng; + bool weak; + + enum LitType {LitA,LitB,LitMixed}; + + hash_map placeholders; + symb farkas; + + ast get_placeholder(ast t){ + hash_map::iterator it = placeholders.find(t); + if(it != placeholders.end()) + return it->second; + ast &res = placeholders[t]; + res = mk_fresh_constant("@p",get_type(t)); + std::cout << "placeholder "; + print_expr(std::cout,res); + std::cout << " = "; + print_expr(std::cout,t); + std::cout << std::endl; + return res; + } + + ast make_farkas(ast &coeff, ast &ineq){ + return make(farkas,coeff,ineq); + } + + LitType get_term_type(const ast &lit){ + prover::range r = pv->ast_scope(lit); + if(pv->range_is_empty(r)) + return LitMixed; + if(weak) { + if(pv->range_min(r) == SHRT_MIN) + return pv->range_contained(r,rng) ? LitA : LitB; + else + return pv->ranges_intersect(r,rng) ? LitA : LitB; + } + else + return pv->range_contained(r,rng) ? LitA : LitB; + } + + /** Make a resolution node with given pivot literal and premises. + The conclusion of premise1 should contain the negation of the + pivot literal, while the conclusion of premise2 should contain the + pivot literal. + */ + node make_resolution(ast pivot, const std::vector &conc, node premise1, node premise2) { + LitType lt = get_term_type(pivot); + if(lt == LitA) + return my_or(premise1,premise2); + if(lt == LitB) + return my_and(premise1,premise2); + + /* the mixed case is a bit complicated */ + + ast atom = get_lit_atom(pivot); + switch(op(atom)){ + case Equal: + return resolve_equality(pivot,conc,premise1,premise2); + case Leq: + case Geq: + case Lt: + case Gt: + return resolve_arith(pivot,conc,premise1,premise2); + break; + default:; + } + + /* we can resolve on mixed equality and inequality literals, + but nothing else. */ + throw proof_error(); + } + + /* Handles the case of resolution on a mixed equality atom. */ + + ast resolve_equality(const ast &pivot, const std::vector &conc, node premise1, node premise2){ + ast atom = get_lit_atom(pivot); + + /* Get the A term in the mixed equality. */ + ast A_term = arg(atom,0); + if(get_term_type(A_term) != LitA) + A_term = arg(atom,1); + + /* Resolve it out. */ + hash_map memo; + // ast neg_pivot_placeholder = get_placeholder(mk_not(atom)); + ast neg_pivot_placeholder = mk_not(atom); + ast A_term_placeholder = get_placeholder(atom); + if(op(pivot) != Not) + std::swap(premise1,premise2); + return resolve_equality_rec(memo, neg_pivot_placeholder, premise1, A_term_placeholder, premise2); + } + + ast resolve_equality_rec(hash_map &memo, const ast &neg_pivot_placeholder, const ast &premise1, + const ast &A_term, const ast &premise2){ + ast &res = memo[premise1]; + if(!res.null()) + return res; + + if(op(premise1) == And + && num_args(premise1) == 2 + && arg(premise1,1) == neg_pivot_placeholder){ + ast common_term = arg(arg(premise1,0),1); + res = subst_term_and_simp(A_term,common_term,premise2); + } + else { + switch(op(premise1)){ + case Or: + case And: { + unsigned nargs = num_args(premise1); + std::vector args; args.resize(nargs); + for(unsigned i = 0; i < nargs; i++) + args[i] = resolve_equality_rec(memo, neg_pivot_placeholder, arg(premise1,i), A_term, premise2); + ast foo = premise1; // get rid of const + res = clone(foo,args); + break; + } + default: + res = premise1; + } + } + return res; + } + + /* Handles the case of resolution on a mixed arith atom. */ + + ast resolve_arith(const ast &pivot, const std::vector &conc, node premise1, node premise2){ + ast atom = get_lit_atom(pivot); + hash_map memo; + ast neg_pivot_lit = mk_not(atom); + if(op(pivot) != Not) + std::swap(premise1,premise2); + return resolve_arith_rec1(memo, neg_pivot_lit, premise1, premise2); + } + + void collect_farkas_resolvents(const ast &pivot, const ast &coeff, const ast &conj, std::vector &res){ + int nargs = num_args(conj); + for(int i = 1; i < nargs; i++){ + ast f = arg(conj,i); + if(!(f == pivot)){ + ast newf = make(farkas,make(Times,arg(f,0),coeff),arg(f,1)); + res.push_back(newf); + } + } + } + + ast sum_ineq(const ast &coeff1, const ast &ineq1, const ast &coeff2, const ast &ineq2){ + opr sum_op = Leq; + if(op(ineq1) == Lt || op(ineq2) == Lt) + sum_op = Lt; + ast sum_sides[2]; + for(int i = 0; i < 2; i++){ + sum_sides[i] = make(Plus,make(Times,coeff1,arg(ineq1,i)),make(Times,coeff2,arg(ineq2,i))); + sum_sides[i] = z3_simplify(sum_sides[i]); + } + return make(sum_op,sum_sides[0],sum_sides[1]); + } + + + ast resolve_farkas(const ast &pivot1, const ast &conj1, const ast &pivot2, const ast &conj2){ + std::vector resolvent; + ast coeff1 = get_farkas_coeff(pivot1); + ast coeff2 = get_farkas_coeff(pivot2); + resolvent.push_back(sum_ineq(coeff2,arg(conj1,0),coeff1,arg(conj2,0))); + collect_farkas_resolvents(pivot1,coeff2,conj1,resolvent); + collect_farkas_resolvents(pivot2,coeff1,conj2,resolvent); + return make(And,resolvent); + } + + bool is_farkas_itp(const ast &pivot1, const ast &itp2, ast &pivot2){ + if(op(itp2) == And){ + int nargs = num_args(itp2); + for(int i = 1; i < nargs; i++){ + ast foo = arg(itp2,i); + if(op(foo) == Uninterpreted && sym(foo) == farkas){ + if(arg(foo,1) == pivot1){ + pivot2 = foo; + return true; + } + } + else break; + } + } + return false; + } + + ast resolve_arith_rec2(hash_map &memo, const ast &pivot1, const ast &conj1, const ast &itp2){ + ast &res = memo[itp2]; + if(!res.null()) + return res; + + ast pivot2; + if(is_farkas_itp(mk_not(arg(pivot1,1)),itp2,pivot2)) + res = resolve_farkas(pivot1,conj1,pivot2,itp2); + else { + switch(op(itp2)){ + case Or: + case And: { + unsigned nargs = num_args(itp2); + std::vector args; args.resize(nargs); + for(unsigned i = 0; i < nargs; i++) + args[i] = resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,i)); + ast foo = itp2; // get rid of const + res = clone(foo,args); + break; + } + default: + res = itp2; + } + } + return res; + } + + ast resolve_arith_rec1(hash_map &memo, const ast &neg_pivot_lit, const ast &itp1, const ast &itp2){ + ast &res = memo[itp1]; + if(!res.null()) + return res; + ast pivot1; + if(is_farkas_itp(neg_pivot_lit,itp1,pivot1)){ + hash_map memo2; + res = resolve_arith_rec2(memo2,pivot1,itp1,itp2); + res = resolve_arith_placeholders(pivot1,itp1,res); + } + else { + switch(op(itp1)){ + case Or: + case And: { + unsigned nargs = num_args(itp1); + std::vector args; args.resize(nargs); + for(unsigned i = 0; i < nargs; i++) + args[i] = resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,i), itp2); + ast foo = itp1; // get rid of const + res = clone(foo,args); + break; + } + default: + res = itp1; + } + } + return res; + } + + ast resolve_arith_placeholders(const ast &pivot1, const ast &conj1, const ast &itp2){ + ast coeff = arg(pivot1,0); + coeff = z3_simplify(coeff); + ast soln = mk_idiv(arg(arg(conj1,0),1),coeff); + int nargs = num_args(conj1); + for(int i = 1; i < nargs; i++){ + ast c = arg(conj1,i); + if(!(c == pivot1)){ + soln = make(Plus,soln,get_placeholder(mk_not(c))); + } + } + ast pl = get_placeholder(mk_not(arg(pivot1,1))); + ast res = subst_term_and_simp(pl,soln,itp2); + return res; + } + + hash_map subst_memo; // memo of subst function + + ast subst_term_and_simp(const ast &var, const ast &t, const ast &e){ + subst_memo.clear(); + return subst_term_and_simp_rec(var,t,e); + } + + ast subst_term_and_simp_rec(const ast &var, const ast &t, const ast &e){ + if(e == var) return t; + std::pair foo(e,ast()); + std::pair::iterator,bool> bar = subst_memo.insert(foo); + ast &res = bar.first->second; + if(bar.second){ + int nargs = num_args(e); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = subst_term_and_simp_rec(var,t,arg(e,i)); + opr f = op(e); + if(f == Equal && args[0] == args[1]) res = mk_true(); + else if(f == And) res = my_and(args); + else if(f == Or) res = my_or(args); + else if(f == Idiv) res = mk_idiv(args[0],args[1]); + else res = clone(e,args); + } + return res; + } + + + /** Make an assumption node. The given clause is assumed in the given frame. */ + virtual node make_assumption(int frame, const std::vector &assumption){ + if(pv->in_range(frame,rng)){ + std::vector itp_clause; + for(unsigned i = 0; i < assumption.size(); i++) + if(get_term_type(assumption[i]) != LitA) + itp_clause.push_back(assumption[i]); + ast res = my_or(itp_clause); + return res; + } + else { + return mk_true(); + } +} + + /** Make a modus-ponens node. This takes derivations of |- x + and |- x = y and produces |- y */ + + virtual node make_mp(const ast &p, const ast &q, const ast &prem1, const ast &prem2){ + + /* Interpolate the axiom p, p=q -> q */ + ast itp; + if(get_term_type(p) == LitA){ + if(get_term_type(q) == LitA) + itp = mk_false(); + else { + if(get_term_type(make(Equal,p,q)) == LitA) + itp = q; + else + itp = get_placeholder(make(Equal,p,q)); + } + } + else { + if(get_term_type(q) == LitA){ + if(get_term_type(make(Equal,p,q)) == LitA) + itp = mk_not(p); + else + itp = mk_not(get_placeholder(make(Equal,p,q))); + } + else + itp = mk_true(); + } + + /* Resolve it with the premises */ + std::vector conc; conc.push_back(q); conc.push_back(mk_not(make(Equal,p,q))); + itp = make_resolution(p,conc,itp,prem1); + conc.pop_back(); + itp = make_resolution(make(Equal,p,q),conc,itp,prem2); + return itp; + } + + /** Make an axiom node. The conclusion must be an instance of an axiom. */ + virtual node make_axiom(const std::vector &conclusion){ + throw proof_error(); +} + + /** Make a Contra node. This rule takes a derivation of the form + Gamma |- False and produces |- \/~Gamma. */ + + virtual node make_contra(node prem, const std::vector &conclusion){ + return prem; + } + + /** Make hypothesis. Creates a node of the form P |- P. */ + + virtual node make_hypothesis(const ast &P){ + if(is_not(P)) + return make_hypothesis(arg(P,0)); + switch(get_term_type(P)){ + case LitA: + return mk_false(); + case LitB: + return mk_true(); + default: // mixed hypothesis + switch(op(P)){ + case Equal: + { + ast x = arg(P,0); + ast y = arg(P,1); + ast A_term = (get_term_type(y) == LitA) ? y : x; + ast res = make(And,make(Equal,A_term,get_placeholder(P)),mk_not(P)); + return res; + } + case Geq: + case Leq: + case Gt: + case Lt: { + ast zleqz = make(Leq,make_int("0"),make_int("0")); + ast fark1 = make(farkas,make_int("1"),P); + ast fark2 = make(farkas,make_int("1"),mk_not(P)); + ast res = make(And,zleqz,fark1,fark2); + return res; + } + default: + throw proof_error(); + } + } + } + + /** Make a Reflexivity node. This rule produces |- x = x */ + + virtual node make_reflexivity(ast con){ + throw proof_error(); +} + + /** Make a Symmetry node. This takes a derivation of |- x = y and + produces | y = x */ + + virtual node make_symmetry(ast con, node prem){ + ast x = arg(con,0); + ast y = arg(con,1); + ast p = make(Equal,y,x); + LitType xt = get_term_type(x); + LitType yt = get_term_type(y); + ast A_term; + if(xt == LitA && yt == LitB) + A_term = x; + else if(yt == LitA && xt == LitB) + A_term = y; + else + return prem; // symmetry shmymmetry... + ast itp = make(And,make(Equal,A_term,get_placeholder(p)),mk_not(con)); + std::vector conc; conc.push_back(con); + itp = make_resolution(p,conc,itp,prem); + return itp; + } + + /** Make a transitivity node. This takes derivations of |- x = y + and |- y = z produces | x = z */ + + virtual node make_transitivity(const ast &x, const ast &y, const ast &z, node prem1, node prem2){ + + /* Interpolate the axiom x=y,y=z,-> x=z */ + ast p = make(Equal,x,y); + ast q = make(Equal,y,z); + ast r = make(Equal,x,z); + ast itp; + if(get_term_type(p) == LitA){ + if(get_term_type(q) == LitA){ + if(get_term_type(r) == LitA) + itp = mk_false(); + else + itp = r; + } + else { + if(get_term_type(r) == LitA) + itp = mk_not(q); + else { + if(get_term_type(r) == LitB) + itp = make(Equal,x,get_placeholder(q)); + else { + ast mid = (get_term_type(y) == LitA) ? get_placeholder(q) : y; + itp = make(And,make(Equal,x,mid),mk_not(r)); + } + } + } + } + else { + if(get_term_type(q) == LitA){ + if(get_term_type(r) == LitA) + itp = mk_not(p); + else { + if(get_term_type(r) == LitB) + itp = make(Equal,get_placeholder(p),z); + else { + ast mid = (get_term_type(y) == LitA) ? get_placeholder(p) : y; + itp = make(And,make(Equal,z,mid),mk_not(r)); + } + } + } + else { + if(get_term_type(r) == LitA){ + ast xr = (get_term_type(x) == LitA) ? get_placeholder(p) : x; + ast zr = (get_term_type(z) == LitA) ? get_placeholder(q) : z; + itp = mk_not(make(Equal,xr,zr)); + } + else { + LitType xt = get_term_type(x); + LitType zt = get_term_type(z); + if(xt == LitA && zt == LitB) + itp = make(And,make(Equal,x,get_placeholder(p)),mk_not(r)); + else if(zt == LitA && xt == LitB) + itp = make(And,make(Equal,z,get_placeholder(q)),mk_not(r)); + else + itp = mk_true(); + } + } + } + + /* Resolve it with the premises */ + std::vector conc; conc.push_back(r); conc.push_back(mk_not(q)); + itp = make_resolution(p,conc,itp,prem1); + conc.pop_back(); + itp = make_resolution(q,conc,itp,prem2); + return itp; + + } + + /** Make a congruence node. This takes derivations of |- x_i = y_i + and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ + + virtual node make_congruence(const ast &x, const ast &y, const ast &con, const std::vector &hyps, const ast &prem1){ + ast p = make(Equal,x,y); + ast itp; + if(get_term_type(p) == LitA){ + if(get_term_type(con) == LitA) + itp = mk_false(); + else + throw proof_error(); // itp = p; + } + else { + if(get_term_type(con) == LitA) + itp = mk_not(p); + else{ + if(get_term_type(con) == LitB) + itp = mk_true(); + else + itp = make_mixed_congruence(x, y, con, hyps, prem1); + } + } + std::vector conc; conc.push_back(con); + itp = make_resolution(p,conc,itp,prem1); + return itp; + } + + /* Interpolate a mixed congruence axiom. */ + + virtual ast make_mixed_congruence(const ast &x, const ast &y, const ast &con, const std::vector &hyps, const ast &prem1){ + ast A_term = x; + ast f_A_term = arg(con,0); + if(get_term_type(y) == LitA){ + A_term = y; + f_A_term = arg(con,1); + } + + // find the argument position of x and y + int pos = -1; + int nargs = num_args(arg(con,0)); + for(int i = 0; i < nargs; i++) + if(x == arg(arg(con,0),i) && y == arg(arg(con,1),i)) + pos = i; + if(pos == -1) + throw proof_error(); + + ast res = make(Equal,f_A_term,subst_in_arg_pos(pos,get_placeholder(make(Equal,x,y)),f_A_term)); + res = make(And,res,mk_not(con)); + return res; + } + + ast subst_in_arg_pos(int pos, ast term, ast app){ + std::vector args; + get_args(app,args); + args[pos] = term; + return clone(app,args); + } + + /** Make a farkas proof node. */ + + virtual node make_farkas(ast con, const std::vector &prems, const std::vector &prem_cons, + const std::vector &coeffs){ + + /* Compute the interpolant for the clause */ + + ast zero = make_int("0"); + std::vector conjs; conjs.resize(1); + ast thing = make(Leq,zero,zero); + for(unsigned i = 0; i < prem_cons.size(); i++){ + const ast &lit = prem_cons[i]; + if(get_term_type(lit) == LitA) + linear_comb(thing,coeffs[i],lit); + else if(get_term_type(lit) != LitB) + conjs.push_back(make(farkas,coeffs[i],prem_cons[i])); + } + thing = simplify_ineq(thing); + if(conjs.size() > 1){ + conjs[0] = thing; + thing = make(And,conjs); + } + + /* Resolve it with the premises */ + std::vector conc; conc.resize(prem_cons.size()); + for(unsigned i = 0; i < prem_cons.size(); i++) + conc[prem_cons.size()-i-1] = prem_cons[i]; + for(unsigned i = 0; i < prem_cons.size(); i++){ + thing = make_resolution(prem_cons[i],conc,thing,prems[i]); + conc.pop_back(); + } + return thing; + } + + /** Set P to P + cQ, where P and Q are linear inequalities. Assumes P is 0 <= y or 0 < y. */ + + void linear_comb(ast &P, const ast &c, const ast &Q){ + ast Qrhs; + bool strict = op(P) == Lt; + if(is_not(Q)){ + ast nQ = arg(Q,0); + switch(op(nQ)){ + case Gt: + Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); + break; + case Lt: + Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); + break; + case Geq: + Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); + strict = true; + break; + case Leq: + Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); + strict = true; + break; + default: + throw proof_error(); + } + } + else { + switch(op(Q)){ + case Leq: + Qrhs = make(Sub,arg(Q,1),arg(Q,0)); + break; + case Geq: + Qrhs = make(Sub,arg(Q,0),arg(Q,1)); + break; + case Lt: + Qrhs = make(Sub,arg(Q,1),arg(Q,0)); + strict = true; + break; + case Gt: + Qrhs = make(Sub,arg(Q,0),arg(Q,1)); + strict = true; + break; + default: + throw proof_error(); + } + } + Qrhs = make(Times,c,Qrhs); + if(strict) + P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); + else + P = make(Leq,arg(P,0),make(Plus,arg(P,1),Qrhs)); + } + + /* Make an axiom instance of the form |- x<=y, y<= x -> x =y */ + virtual node make_leq2eq(ast x, ast y, const ast &xleqy, const ast &yleqx){ + ast con = make(Equal,x,y); + ast itp; + switch(get_term_type(con)){ + case LitA: + itp = mk_false(); + break; + case LitB: + itp = mk_true(); + break; + default: { // mixed equality + ast mid,ante; + if(get_term_type(y) == LitA){ + std::swap(x,y); + mid = make(Plus,x,get_placeholder(yleqx)); + } + else { + mid = make(Plus,x,get_placeholder(xleqy)); + } + ante = make(Uminus,make(Plus,get_placeholder(xleqy),get_placeholder(yleqx))); + ante = mk_not(make(Leq,make_int("0"),ante)); +#if 0 + ast zleqz = make(Leq,make_int("0"),make_int("0")); + ast fark1 = make(farkas,make_int("1"),xleqy); + ast fark2 = make(farkas,make_int("1"),yleqx); + ast ante = make(And,zleqz,fark1,fark2); +#endif + ast conc = make(And,make(Equal,x,mid),mk_not(con)); + itp = my_or(ante,conc); + } + } + return itp; + } + + /* Make an axiom instance of the form |- x = y -> x <= y */ + virtual node make_eq2leq(ast x, ast y, const ast &xleqy){ + ast itp; + switch(get_term_type(xleqy)){ + case LitA: + itp = mk_false(); + break; + case LitB: + itp = mk_true(); + break; + default: { // mixed equality + ast mid = get_placeholder(make(Equal,x,y)); + if(get_term_type(y) == LitA){ + std::swap(x,y); + mid = make(Sub,x,mid); + } + else { + mid = make(Sub,mid,x); + } + ast zleqmid = make(Leq,make_int("0"),mid); + ast fark = make(farkas,make_int("1"),mk_not(xleqy)); + itp = make(And,zleqmid,fark); + } + } + return itp; + } + + + /* Make an inference of the form t <= c |- t/d <= floor(c/d) where t + is an affine term divisble by d and c is an integer constant */ + virtual node make_cut_rule(const ast &tleqc, const ast &d, const ast &con, const ast &prem){ + ast itp = mk_false(); + switch(get_term_type(con)){ + case LitA: + itp = mk_false(); + break; + case LitB: + itp = mk_true(); + break; + default: { + ast t = arg(tleqc,0); + ast c = arg(tleqc,1); + ast thing = make(farkas,make_int("1"),mk_not(con)); + itp = make(And,make(Leq,make_int("0"),make(Idiv,get_placeholder(tleqc),d)),thing); + } + } + std::vector conc; conc.push_back(con); + itp = make_resolution(tleqc,conc,itp,prem); + return itp; + } + + ast get_farkas_coeff(const ast &f){ + ast c = arg(f,0); + // if(!is_not(arg(f,1))) + // c = make(Uminus,c); + return c; + } + + ast my_or(const ast &a, const ast &b){ + return mk_or(a,b); + } + + ast my_and(const ast &a, const ast &b){ + return mk_and(a,b); + } + + ast my_or(const std::vector &a){ + return mk_or(a); + } + + ast my_and(const std::vector &a){ + return mk_and(a); + } + + ast get_lit_atom(const ast &l){ + if(op(l) == Not) + return arg(l,0); + return l; + } + +public: + iz3proof_itp_impl(prover *p, const prover::range &r, bool w) + : iz3proof_itp(*p) + { + pv = p; + rng = r; + weak = w; + type domain[2] = {int_type(),bool_type()}; + farkas = function("@farkas",2,domain,bool_type()); + } + +}; + +iz3proof_itp *iz3proof_itp::create(prover *p, const prover::range &r, bool w){ + return new iz3proof_itp_impl(p,r,w); +} + diff --git a/src/interp/iz3proof_itp.h b/src/interp/iz3proof_itp.h new file mode 100644 index 000000000..692fb7772 --- /dev/null +++ b/src/interp/iz3proof_itp.h @@ -0,0 +1,130 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3proof.h + +Abstract: + + This class defines a simple interpolating proof system. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#ifndef IZ3PROOF_ITP_H +#define IZ3PROOF_ITP_H + +#include + +#include "iz3base.h" +#include "iz3secondary.h" + +// #define CHECK_PROOFS + +/** This class defines a simple proof system. + + As opposed to iz3proof, this class directly computes interpolants, + so the proof representation is just the interpolant itself. + + */ + +class iz3proof_itp : public iz3mgr { + public: + + /** Enumeration of proof rules. */ + enum rule {Resolution,Assumption,Hypothesis,Theory,Axiom,Contra,Lemma,Reflexivity,Symmetry,Transitivity,Congruence,EqContra}; + + /** Interface to prover. */ + typedef iz3base prover; + + /** Ast type. */ + typedef prover::ast ast; + + /** The type of proof nodes (just interpolants). */ + typedef ast node; + + /** Object thrown in case of a proof error. */ + struct proof_error {}; + + + /** Make a resolution node with given pivot literal and premises. + The conclusion of premise1 should contain the negation of the + pivot literal, while the conclusion of premise2 should containe the + pivot literal. + */ + virtual node make_resolution(ast pivot, const std::vector &conc, node premise1, node premise2) = 0; + + /** Make an assumption node. The given clause is assumed in the given frame. */ + virtual node make_assumption(int frame, const std::vector &assumption) = 0; + + /** Make a hypothesis node. If phi is the hypothesis, this is + effectively phi |- phi. */ + virtual node make_hypothesis(const ast &hypothesis) = 0; + + /** Make an axiom node. The conclusion must be an instance of an axiom. */ + virtual node make_axiom(const std::vector &conclusion) = 0; + + /** Make a Contra node. This rule takes a derivation of the form + Gamma |- False and produces |- \/~Gamma. */ + + virtual node make_contra(node prem, const std::vector &conclusion) = 0; + + /** Make a Reflexivity node. This rule produces |- x = x */ + + virtual node make_reflexivity(ast con) = 0; + + /** Make a Symmetry node. This takes a derivation of |- x = y and + produces | y = x */ + + virtual node make_symmetry(ast con, node prem) = 0; + + /** Make a transitivity node. This takes derivations of |- x = y + and |- y = z produces | x = z */ + + virtual node make_transitivity(const ast &x, const ast &y, const ast &z, node prem1, node prem2) = 0; + + /** Make a congruence node. This takes a derivation of |- x_i = y_i + and produces |- f(...x_i,...) = f(...,y_i,...) */ + + virtual node make_congruence(const ast &x, const ast &y, const ast &con, const std::vector &hyps, const ast &prem1) = 0; + + /** Make a modus-ponens node. This takes derivations of |- x + and |- x = y and produces |- y */ + + virtual node make_mp(const ast &x, const ast &y, const ast &prem1, const ast &prem2) = 0; + + /** Make a farkas proof node. */ + + virtual node make_farkas(ast con, const std::vector &prems, const std::vector &prem_cons, const std::vector &coeffs) = 0; + + /* Make an axiom instance of the form |- x<=y, y<= x -> x =y */ + virtual node make_leq2eq(ast x, ast y, const ast &xleqy, const ast &yleqx) = 0; + + /* Make an axiom instance of the form |- x = y -> x <= y */ + virtual node make_eq2leq(ast x, ast y, const ast &xeqy) = 0; + + /* Make an inference of the form t <= c |- t/d <= floor(c/d) where t + is an affine term divisble by d and c is an integer constant */ + virtual node make_cut_rule(const ast &tleqc, const ast &d, const ast &con, const ast &prem) = 0; + + /** Create proof object to construct an interpolant. */ + static iz3proof_itp *create(prover *p, const prover::range &r, bool _weak); + + protected: + iz3proof_itp(iz3mgr &m) + : iz3mgr(m) + { + } + + public: + virtual ~iz3proof_itp(){ + } +}; + +#endif diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp new file mode 100755 index 000000000..8220fb214 --- /dev/null +++ b/src/interp/iz3translate.cpp @@ -0,0 +1,1164 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3translate.cpp + +Abstract: + + Translate a Z3 proof to in interpolated proof. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#include "iz3translate.h" +#include "iz3proof.h" +#include "iz3profiling.h" +#include "iz3interp.h" +#include "iz3proof_itp.h" + +#include +#include +#include +#include +#include +#include +#include + +//using std::vector; +#ifndef WIN32 +using namespace stl_ext; +#endif + + + +/* This translator goes directly from Z3 proofs to interpolated + proofs without an intermediate representation. No secondary + prover is used. +*/ + +class iz3translation_full : public iz3translation { +public: + + + typedef iz3proof_itp Iproof; + + Iproof *iproof; + + /* Here we have lots of hash tables for memoizing various methods and + other such global data structures. + */ + + typedef hash_map AstToInt; + AstToInt locality; // memoizes locality of Z3 proof terms + + typedef std::pair EquivEntry; + typedef hash_map EquivTab; + EquivTab equivs; // maps non-local terms to equivalent local terms, with proof + + typedef hash_set AstHashSet; + AstHashSet equivs_visited; // proofs already checked for equivalences + + typedef pair, hash_map > AstToIpf; + AstToIpf translation; // Z3 proof nodes to Iproof nodes + + AstToInt frame_map; // map assertions to frames + int frames; // number of frames + + typedef std::set AstSet; + typedef hash_map AstToAstSet; + AstToAstSet hyp_map; // map proof terms to hypothesis set + + struct LocVar { // localization vars + ast var; // a fresh variable + ast term; // term it represents + int frame; // frame in which it's defined + LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} + }; + + std::vector localization_vars; // localization vars in order of creation + typedef hash_map AstToAst; + AstToAst localization_map; // maps terms to their localization vars + + typedef hash_map AstToBool; + AstToBool occurs_in_memo; // memo of occurs_in function + + AstHashSet cont_eq_memo; // memo of cont_eq function + + AstToAst subst_memo; // memo of subst function + + public: + + +#define from_ast(x) (x) + + // determine locality of a proof term + // return frame of derivation if local, or -1 if not + // result INT_MAX means the proof term is a tautology + // memoized in hash_map "locality" + + int get_locality_rec(ast proof){ + std::pair foo(proof,INT_MAX); + std::pair bar = locality.insert(foo); + int &res = bar.first->second; + if(!bar.second) return res; + if(pr(proof) == PR_ASSERTED){ + ast ass = conc(proof); + AstToInt::iterator it = frame_map.find(ass); + assert(it != frame_map.end()); + res = it->second; + } + else { + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + int bar = get_locality_rec(arg); + if(res == INT_MAX || res == bar) res = bar; + else if(bar != INT_MAX) res = -1; + } + } + return res; + } + + + int get_locality(ast proof){ + // if(lia_z3_axioms_only) return -1; + int res = get_locality_rec(proof); + if(res != -1){ + ast con = conc(proof); + range rng = ast_scope(con); + + // hack: if a clause contains "true", it reduces to "true", + // which means we won't compute the range correctly. we handle + // this case by computing the ranges of the literals separately + + if(is_true(con)){ + std::vector lits; + get_Z3_lits(conc(proof),lits); + for(unsigned i = 0; i < lits.size(); i++) + rng = range_glb(rng,ast_scope(lits[i])); + } + + if(!range_is_empty(rng)){ + AstSet &hyps = get_hyps(proof); + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ + ast hyp = *it; + rng = range_glb(rng,ast_scope(hyp)); + } + } + + if(res == INT_MAX){ + if(range_is_empty(rng)) + res = -1; + else res = range_max(rng); + } + else { + if(!in_range(res,rng)) + res = -1; + } + } + return res; + } + + AstSet &get_hyps(ast proof){ + std::pair foo(proof,AstSet()); + std::pair bar = hyp_map.insert(foo); + AstSet &res = bar.first->second; + if(!bar.second) return res; + pfrule dk = pr(proof); + if(dk == PR_HYPOTHESIS){ + ast con = conc(proof); + res.insert(con); + } + else { + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + AstSet &arg_hyps = get_hyps(arg); + res.insert(arg_hyps.begin(),arg_hyps.end()); + } + if(dk == PR_LEMMA){ + ast con = conc(proof); + res.erase(mk_not(con)); + if(is_or(con)){ + int clause_size = num_args(con); + for(int i = 0; i < clause_size; i++){ + ast neglit = mk_not(arg(con,i)); + res.erase(neglit); + } + } + } + } +#if 0 + AstSet::iterator it = res.begin(), en = res.end(); + if(it != en){ + AstSet::iterator old = it; + ++it; + for(; it != en; ++it, ++old) + if(!(*old < *it)) + std::cout << "foo!"; + } +#endif + return res; + } + + // Find all the judgements of the form p <-> q, where + // p is local and q is non-local, recording them in "equivs" + // the map equivs_visited is used to record the already visited proof terms + + void find_equivs(ast proof){ + if(equivs_visited.find(proof) != equivs_visited.end()) + return; + equivs_visited.insert(proof); + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++) // do all the sub_terms + find_equivs(prem(proof,i)); + ast con = conc(proof); // get the conclusion + if(is_iff(con)){ + ast iff = con; + for(int i = 0; i < 2; i++) + if(!is_local(arg(iff,i)) && is_local(arg(iff,1-i))){ + std::pair > foo(arg(iff,i),std::pair(arg(iff,1-i),proof)); + equivs.insert(foo); + } + } + } + + // get the lits of a Z3 clause + void get_Z3_lits(ast t, std::vector &lits){ + opr dk = op(t); + if(dk == False) + return; // false = empty clause + if(dk == Or){ + unsigned nargs = num_args(t); + lits.resize(nargs); + for(unsigned i = 0; i < nargs; i++) // do all the sub_terms + lits[i] = arg(t,i); + } + else { + lits.push_back(t); + } + } + + // resolve two clauses represented as vectors of lits. replace first clause + void resolve(ast pivot, std::vector &cls1, std::vector &cls2){ + ast neg_pivot = mk_not(pivot); + for(unsigned i = 0; i < cls1.size(); i++){ + if(cls1[i] == pivot){ + cls1[i] = cls1.back(); + cls1.pop_back(); + bool found_pivot2 = false; + for(unsigned j = 0; j < cls2.size(); j++){ + if(cls2[j] == neg_pivot) + found_pivot2 = true; + else + cls1.push_back(cls2[j]); + } + assert(found_pivot2); + return; + } + } + assert(0 && "resolve failed"); + } + + // get lits resulting from unit resolution up to and including "position" + // TODO: this is quadratic -- fix it + void do_unit_resolution(ast proof, int position, std::vector &lits){ + ast orig_clause = conc(prem(proof,0)); + get_Z3_lits(orig_clause,lits); + for(int i = 1; i <= position; i++){ + std::vector unit(1); + unit[0] = conc(prem(proof,i)); + resolve(mk_not(unit[0]),lits,unit); + } + } + + + // clear the localization variables + void clear_localization(){ + localization_vars.clear(); + localization_map.clear(); + } + + // create a fresh variable for localization + ast fresh_localization_var(ast term, int frame){ + std::ostringstream s; + s << "%" << (localization_vars.size()); + ast var = make_var(s.str().c_str(),get_type(term)); + sym_range(sym(var)) = range_full(); // make this variable global + localization_vars.push_back(LocVar(var,term,frame)); + return var; + } + + + // "localize" a term to a given frame range by + // creating new symbols to represent non-local subterms + + ast localize_term(ast e, const range &rng){ + if(ranges_intersect(ast_scope(e),rng)) + return e; // this term occurs in range, so it's O.K. + AstToAst::iterator it = localization_map.find(e); + if(it != localization_map.end()) + return it->second; + + // if is is non-local, we must first localize the arguments to + // the range of its function symbol + + int nargs = num_args(e); + if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ + range frng = rng; + if(op(e) == Uninterpreted){ + symb f = sym(e); + range srng = sym_range(f); + if(ranges_intersect(srng,rng)) // localize to desired range if possible + frng = range_glb(srng,rng); + } + std::vector largs(nargs); + for(int i = 0; i < nargs; i++){ + largs[i] = localize_term(arg(e,i),frng); + frng = range_glb(frng,ast_scope(largs[i])); + } + e = clone(e,largs); + assert(is_local(e)); + } + + + if(ranges_intersect(ast_scope(e),rng)) + return e; // this term occurs in range, so it's O.K. + + // choose a frame for the constraint that is close to range + int frame = range_near(ast_scope(e),rng); + + ast new_var = fresh_localization_var(e,frame); + localization_map[e] = new_var; + ast cnst = make(Equal,new_var,e); + // antes.push_back(std::pair(cnst,frame)); + return new_var; + } + + // some patterm matching functions + + // match logical or with nargs arguments + // assumes AIG form + bool match_or(ast e, ast *args, int nargs){ + if(op(e) != Or) return false; + int n = num_args(e); + if(n != nargs) return false; + for(int i = 0; i < nargs; i++) + args[i] = arg(e,i); + return true; + } + + // match operator f with exactly nargs arguments + bool match_op(ast e, opr f, ast *args, int nargs){ + if(op(e) != f) return false; + int n = num_args(e); + if(n != nargs) return false; + for(int i = 0; i < nargs; i++) + args[i] = arg(e,i); + return true; + } + + // see if the given formula can be interpreted as + // an axiom instance (e.g., an array axiom instance). + // if so, add it to "antes" in an appropriate frame. + // this may require "localization" + + void get_axiom_instance(ast e){ + + // "store" axiom + // (or (= w q) (= (select (store a1 w y) q) (select a1 q))) + // std::cout << "ax: "; show(e); + ast lits[2],eq_ops_l[2],eq_ops_r[2],sel_ops[2], sto_ops[3], sel_ops2[2] ; + if(match_or(e,lits,2)) + if(match_op(lits[0],Equal,eq_ops_l,2)) + if(match_op(lits[1],Equal,eq_ops_r,2)) + for(int i = 0; i < 2; i++){ // try the second equality both ways + if(match_op(eq_ops_r[0],Select,sel_ops,2)) + if(match_op(sel_ops[0],Store,sto_ops,3)) + if(match_op(eq_ops_r[1],Select,sel_ops2,2)) + for(int j = 0; j < 2; j++){ // try the first equality both ways + if(eq_ops_l[0] == sto_ops[1] + && eq_ops_l[1] == sel_ops[1] + && eq_ops_l[1] == sel_ops2[1] + && sto_ops[0] == sel_ops2[0]) + if(is_local(sel_ops[0])) // store term must be local + { + ast sto = sel_ops[0]; + ast addr = localize_term(eq_ops_l[1],ast_scope(sto)); + ast res = make(Or, + make(Equal,eq_ops_l[0],addr), + make(Equal, + make(Select,sto,addr), + make(Select,sel_ops2[0],addr))); + // int frame = range_min(ast_scope(res)); TODO + // antes.push_back(std::pair(res,frame)); + return; + } + std::swap(eq_ops_l[0],eq_ops_l[1]); + } + std::swap(eq_ops_r[0],eq_ops_r[1]); + } + } + + // a quantifier instantation looks like (~ forall x. P) \/ P[z/x] + // we need to find a time frame for P, then localize P[z/x] in this frame + + void get_quantifier_instance(ast e){ + ast disjs[2]; + if(match_or(e,disjs,2)){ + if(is_local(disjs[0])){ + ast res = localize_term(disjs[1], ast_scope(disjs[0])); + // int frame = range_min(ast_scope(res)); TODO + // antes.push_back(std::pair(res,frame)); + return; + } + } + } + + ast get_judgement(ast proof){ + ast con = from_ast(conc(proof)); + AstSet &hyps = get_hyps(proof); + std::vector hyps_vec; + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) + hyps_vec.push_back(*it); + if(hyps_vec.size() == 0) return con; + con = make(Or,mk_not(make(And,hyps_vec)),con); + return con; + } + + // does variable occur in expression? + int occurs_in1(ast var, ast e){ + std::pair foo(e,false); + std::pair bar = occurs_in_memo.insert(foo); + bool &res = bar.first->second; + if(bar.second){ + if(e == var) res = true; + int nargs = num_args(e); + for(int i = 0; i < nargs; i++) + res |= occurs_in1(var,arg(e,i)); + } + return res; + } + + int occurs_in(ast var, ast e){ + occurs_in_memo.clear(); + return occurs_in1(var,e); + } + + // find a controlling equality for a given variable v in a term + // a controlling equality is of the form v = t, which, being + // false would force the formula to have the specifid truth value + // returns t, or null if no such + + ast cont_eq(bool truth, ast v, ast e){ + if(is_not(e)) return cont_eq(!truth,v,arg(e,0)); + if(cont_eq_memo.find(e) != cont_eq_memo.end()) + return ast(); + cont_eq_memo.insert(e); + if(!truth && op(e) == Equal){ + if(arg(e,0) == v) return(arg(e,1)); + if(arg(e,1) == v) return(arg(e,0)); + } + if((!truth && op(e) == And) || (truth && op(e) == Or)){ + int nargs = num_args(e); + for(int i = 0; i < nargs; i++){ + ast res = cont_eq(truth, v, arg(e,i)); + if(!res.null()) return res; + } + } + return ast(); + } + + // substitute a term t for unbound occurrences of variable v in e + + ast subst(ast var, ast t, ast e){ + if(e == var) return t; + std::pair foo(e,ast()); + std::pair bar = subst_memo.insert(foo); + ast &res = bar.first->second; + if(bar.second){ + int nargs = num_args(e); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = subst(var,t,arg(e,i)); + opr f = op(e); + if(f == Equal && args[0] == args[1]) res = mk_true(); + else res = clone(e,args); + } + return res; + } + + // apply a quantifier to a formula, with some optimizations + // 1) bound variable does not occur -> no quantifier + // 2) bound variable must be equal to some term -> substitute + + ast apply_quant(opr quantifier, ast var, ast e){ + if(!occurs_in(var,e))return e; + cont_eq_memo.clear(); + ast cterm = cont_eq(quantifier == Forall, var, e); + if(!cterm.null()){ + subst_memo.clear(); + return subst(var,cterm,e); + } + std::vector bvs; bvs.push_back(var); + return make_quant(quantifier,bvs,e); + } + + // add quantifiers over the localization vars + // to an interpolant for frames lo-hi + + ast add_quants(ast e, int lo, int hi){ + for(int i = localization_vars.size() - 1; i >= 0; i--){ + LocVar &lv = localization_vars[i]; + opr quantifier = (lv.frame >= lo && lv.frame <= hi) ? Exists : Forall; + e = apply_quant(quantifier,lv.var,e); + } + return e; + } + + int get_lits_locality(std::vector &lits){ + range rng = range_full(); + for(std::vector::iterator it = lits.begin(), en = lits.end(); it != en; ++it){ + ast lit = *it; + rng = range_glb(rng,ast_scope(lit)); + } + if(range_is_empty(rng)) return -1; + int hi = range_max(rng); + if(hi >= frames) return frames - 1; + return hi; + } + + int num_lits(ast ast){ + opr dk = op(ast); + if(dk == False) + return 0; + if(dk == Or){ + unsigned nargs = num_args(ast); + int n = 0; + for(unsigned i = 0; i < nargs; i++) // do all the sub_terms + n += num_lits(arg(ast,i)); + return n; + } + else + return 1; + } + + std::vector lit_trace; + hash_set marked_proofs; + + bool proof_has_lit(const ast &proof, const ast &lit){ + AstSet &hyps = get_hyps(proof); + if(hyps.find(mk_not(lit)) != hyps.end()) + return true; + std::vector lits; + ast con = conc(proof); + get_Z3_lits(con, lits); + for(unsigned i = 0; i < lits.size(); i++) + if(lits[i] == lit) + return true; + return false; + } + + + void trace_lit_rec(const ast &lit, const ast &proof, AstHashSet &memo){ + if(memo.find(proof) == memo.end()){ + memo.insert(proof); + AstSet &hyps = get_hyps(proof); + std::vector lits; + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) + lits.push_back(mk_not(*it)); + ast con = conc(proof); + get_Z3_lits(con, lits); + for(unsigned i = 0; i < lits.size(); i++){ + if(lits[i] == lit){ + print_expr(std::cout,proof); + std::cout << "\n"; + marked_proofs.insert(proof); + pfrule dk = pr(proof); + if(dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA){ + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + trace_lit_rec(lit,arg,memo); + } + } + else + lit_trace.push_back(proof); + } + } + } + } + + ast traced_lit; + + int trace_lit(const ast &lit, const ast &proof){ + marked_proofs.clear(); + lit_trace.clear(); + traced_lit = lit; + AstHashSet memo; + trace_lit_rec(lit,proof,memo); + return lit_trace.size(); + } + + bool is_literal_or_lit_iff(const ast &lit){ + if(my_is_literal(lit)) return true; + if(op(lit) == Iff){ + return my_is_literal(arg(lit,0)) && my_is_literal(arg(lit,1)); + } + return false; + } + + bool my_is_literal(const ast &lit){ + ast abslit = is_not(lit) ? arg(lit,0) : lit; + int f = op(abslit); + return !(f == And || f == Or || f == Iff); + } + + void print_lit(const ast &lit){ + ast abslit = is_not(lit) ? arg(lit,0) : lit; + if(!is_literal_or_lit_iff(lit)){ + if(is_not(lit)) std::cout << "~"; + std::cout << "["; + print_expr(std::cout,abslit); + std::cout << "]"; + } + else + print_expr(std::cout,lit); + } + + void show_lit(const ast &lit){ + print_lit(lit); + std::cout << "\n"; + } + + void print_z3_lit(const ast &a){ + print_lit(from_ast(a)); + } + + void show_z3_lit(const ast &a){ + print_z3_lit(a); + std::cout << "\n"; + } + + + void show_con(const ast &proof, bool brief){ + if(!traced_lit.null() && proof_has_lit(proof,traced_lit)) + std::cout << "(*) "; + ast con = conc(proof); + AstSet &hyps = get_hyps(proof); + int count = 0; + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ + if(brief && ++count > 5){ + std::cout << "... "; + break; + } + print_lit(*it); + std::cout << " "; + } + std::cout << "|- "; + std::vector lits; + get_Z3_lits(con,lits); + for(unsigned i = 0; i < lits.size(); i++){ + print_lit(lits[i]); + std::cout << " "; + } + std::cout << "\n"; + } + + void show_step(const ast &proof){ + std::cout << "\n"; + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + std::cout << "(" << i << ") "; + ast arg = prem(proof,i); + show_con(arg,true); + } + std::cout << "|------ "; + std::cout << string_of_symbol(sym(proof)) << "\n"; + show_con(proof,false); + } + + void show_marked( const ast &proof){ + std::cout << "\n"; + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + ast arg = prem(proof,i); + if(!traced_lit.null() && proof_has_lit(arg,traced_lit)){ + std::cout << "(" << i << ") "; + show_con(arg,true); + } + } + } + + std::vector pfhist; + int pfhist_pos; + + void pfgoto(const ast &proof){ + if(pfhist.size() == 0) + pfhist_pos = 0; + else pfhist_pos++; + pfhist.resize(pfhist_pos); + pfhist.push_back(proof); + show_step(proof); + } + + + void pfback(){ + if(pfhist_pos > 0){ + pfhist_pos--; + show_step(pfhist[pfhist_pos]); + } + } + + void pffwd(){ + if(pfhist_pos < ((int)pfhist.size()) - 1){ + pfhist_pos++; + show_step(pfhist[pfhist_pos]); + } + } + + void pfprem(int i){ + if(pfhist.size() > 0){ + ast proof = pfhist[pfhist_pos]; + unsigned nprems = num_prems(proof); + if(i >= 0 && i < (int)nprems) + pfgoto(prem(proof,i)); + } + } + + + + // translate a unit resolution sequence + Iproof::node translate_ur(ast proof){ + ast prem0 = prem(proof,0); + Iproof::node itp = translate_main(prem0,true); + std::vector clause; + get_Z3_lits(conc(prem0),clause); + int nprems = num_prems(proof); + for(int position = 1; position < nprems; position++){ + ast ante = prem(proof,position); + ast pnode = conc(ante); + ast pnode_abs = !is_not(pnode) ? pnode : mk_not(pnode); + Iproof::node neg = itp; + Iproof::node pos = translate_main(ante, false); + if(is_not(pnode)){ + pnode = mk_not(pnode); + std::swap(neg,pos); + } + std::vector unit(1); + unit[0] = conc(ante); + resolve(mk_not(conc(ante)),clause,unit); + itp = iproof->make_resolution(pnode,clause,neg,pos); + } + return itp; + } + + // get an inequality in the form 0 <= t where t is a linear term + ast rhs_normalize_inequality(const ast &ineq){ + ast zero = make_int("0"); + ast thing = make(Leq,zero,zero); + linear_comb(thing,make_int("1"),ineq); + thing = simplify_ineq(thing); + return thing; + } + + // get an inequality in the form t <= c or t < c, there t is affine and c constant + ast normalize_inequality(const ast &ineq){ + ast zero = make_int("0"); + ast thing = make(Leq,zero,zero); + linear_comb(thing,make_int("1"),ineq); + thing = simplify_ineq(thing); + ast lhs = arg(thing,0); + ast rhs = arg(thing,1); + opr o = op(rhs); + if(o != Numeral){ + if(op(rhs) == Plus){ + int nargs = num_args(rhs); + ast const_term = zero; + int i = 0; + if(nargs > 0 && op(arg(rhs,0)) == Numeral){ + const_term = arg(rhs,0); + i++; + } + if(i < nargs){ + std::vector non_const; + for(; i < nargs; i++) + non_const.push_back(arg(rhs,i)); + lhs = make(Sub,lhs,make(Plus,non_const)); + } + rhs = const_term; + } + else { + lhs = make(Sub,lhs,make(Plus,rhs)); + rhs = zero; + } + lhs = z3_simplify(lhs); + rhs = z3_simplify(rhs); + thing = make(op(thing),lhs,rhs); + } + return thing; + } + + void get_linear_coefficients(const ast &t, std::vector &coeffs){ + if(op(t) == Plus){ + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + coeffs.push_back(get_coeff(arg(t,i))); + } + else + coeffs.push_back(get_coeff(t)); + } + + /* given an affine term t, get the GCD of the coefficients in t. */ + ast gcd_of_coefficients(const ast &t){ + std::vector coeffs; + get_linear_coefficients(t,coeffs); + if(coeffs.size() == 0) + return make_int("1"); // arbitrary + rational d = coeffs[0]; + for(unsigned i = 1; i < coeffs.size(); i++){ + d = gcd(d,coeffs[i]); + } + return make_int(d); + } + + Iproof::node GCDtoDivRule(const ast &proof, bool pol, std::vector &coeffs, std::vector &prems, ast &cut_con){ + // gather the summands of the desired polarity + std::vector my_prems; + std::vector my_coeffs; + std::vector my_prem_cons; + for(unsigned i = 0; i < coeffs.size(); i++){ + rational &c = coeffs[i]; + if(pol ? c.is_pos() : c.is_neg()){ + my_prems.push_back(prems[i]); + my_coeffs.push_back(pol ? make_int(c) : make_int(-c)); + my_prem_cons.push_back(conc(prem(proof,i))); + } + } + ast my_con = sum_inequalities(my_coeffs,my_prem_cons); + my_con = normalize_inequality(my_con); + Iproof::node hyp = iproof->make_hypothesis(mk_not(my_con)); + my_prems.push_back(hyp); + my_coeffs.push_back(make_int("1")); + my_prem_cons.push_back(mk_not(my_con)); + Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_prem_cons,my_coeffs); + + ast t = arg(my_con,0); + ast c = arg(my_con,1); + ast d = gcd_of_coefficients(t); + t = z3_simplify(mk_idiv(t,d)); + c = z3_simplify(mk_idiv(c,d)); + cut_con = make(op(my_con),t,c); + return iproof->make_cut_rule(my_con,d,cut_con,res); + } + + + ast divide_inequalities(const ast &x, const ast&y){ + std::vector xcoeffs,ycoeffs; + get_linear_coefficients(arg(x,1),xcoeffs); + get_linear_coefficients(arg(y,1),ycoeffs); + if(xcoeffs.size() != ycoeffs.size() || xcoeffs.size() == 0) + throw "bad assign-bounds lemma"; + rational ratio = xcoeffs[0]/ycoeffs[0]; + return make_int(ratio); // better be integer! + } + + ast AssignBounds2Farkas(const ast &proof, const ast &con){ + std::vector farkas_coeffs; + get_assign_bounds_coeffs(proof,farkas_coeffs); + std::vector lits; + int nargs = num_args(con); + if(nargs != (int)(farkas_coeffs.size())) + throw "bad assign-bounds theory lemma"; +#if 0 + for(int i = 1; i < nargs; i++) + lits.push_back(mk_not(arg(con,i))); + ast sum = sum_inequalities(farkas_coeffs,lits); + ast conseq = rhs_normalize_inequality(arg(con,0)); + ast d = divide_inequalities(sum,conseq); + std::vector my_coeffs; + my_coeffs.push_back(d); + for(unsigned i = 0; i < farkas_coeffs.size(); i++) + my_coeffs.push_back(farkas_coeffs[i]); +#else + std::vector &my_coeffs = farkas_coeffs; +#endif + std::vector my_cons; + for(int i = 0; i < nargs; i++) + my_cons.push_back(mk_not(arg(con,i))); + std::vector my_hyps; + for(int i = 0; i < nargs; i++) + my_hyps.push_back(iproof->make_hypothesis(my_cons[i])); + ast res = iproof->make_farkas(mk_false(),my_hyps,my_cons,my_coeffs); + return res; + } + + // translate a Z3 proof term into interpolating proof system + + Iproof::node translate_main(ast proof, bool expect_clause = true){ + AstToIpf &tr = translation; + hash_map &trc = expect_clause ? tr.first : tr.second; + std::pair foo(proof,Iproof::node()); + std::pair::iterator, bool> bar = trc.insert(foo); + Iproof::node &res = bar.first->second; + if(!bar.second) return res; + + // Try the locality rule first + + int frame = get_locality(proof); + if(frame != -1){ + ast e = from_ast(conc(proof)); + if(frame >= frames) frame = frames - 1; + std::vector foo; + if(expect_clause) + get_Z3_lits(conc(proof),foo); + else + foo.push_back(e); + AstSet &hyps = get_hyps(proof); + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) + foo.push_back(mk_not(*it)); + res = iproof->make_assumption(frame,foo); + return res; + } + + // If the proof is not local, break it down by proof rule + + pfrule dk = pr(proof); + unsigned nprems = num_prems(proof); + if(dk == PR_UNIT_RESOLUTION){ + res = translate_ur(proof); + } + else if(dk == PR_LEMMA){ + ast contra = prem(proof,0); // this is a proof of false from some hyps + res = translate_main(contra); + if(!expect_clause){ + std::vector foo; // the negations of the hyps form a clause + foo.push_back(from_ast(conc(proof))); + AstSet &hyps = get_hyps(proof); + for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) + foo.push_back(mk_not(*it)); + res = iproof->make_contra(res,foo); + } + } + else { + std::vector lits; + ast con = conc(proof); + if(expect_clause) + get_Z3_lits(con, lits); + else + lits.push_back(from_ast(con)); + + // translate all the premises + std::vector args(nprems); + for(unsigned i = 0; i < nprems; i++) + args[i] = translate_main(prem(proof,i),false); + + switch(dk){ + case PR_TRANSITIVITY: { + // assume the premises are x = y, y = z + ast x = arg(conc(prem(proof,0)),0); + ast y = arg(conc(prem(proof,0)),1); + ast z = arg(conc(prem(proof,1)),1); + res = iproof->make_transitivity(x,y,z,args[0],args[1]); + break; + } + case PR_MONOTONICITY: { + // assume the premise is x = y + ast x = arg(conc(prem(proof,0)),0); + ast y = arg(conc(prem(proof,0)),1); + AstSet &hyps = get_hyps(proof); + std::vector hyps_vec; hyps_vec.resize(hyps.size()); + std::copy(hyps.begin(),hyps.end(),hyps_vec.begin()); + res = iproof->make_congruence(x,y,con,hyps_vec,args[0]); + break; + } + case PR_REFLEXIVITY: { + res = iproof->make_reflexivity(con); + break; + } + case PR_SYMMETRY: { + res = iproof->make_symmetry(con,args[0]); + break; + } + case PR_MODUS_PONENS: { + res = iproof->make_mp(conc(prem(proof,0)),arg(conc(prem(proof,1)),1),args[0],args[1]); + break; + } + case PR_TH_LEMMA: { + switch(get_theory_lemma_theory(proof)){ + case ArithTheory: + switch(get_theory_lemma_kind(proof)){ + case FarkasKind: { + std::vector farkas_coeffs, prem_cons; + get_farkas_coeffs(proof,farkas_coeffs); + prem_cons.resize(nprems); + for(unsigned i = 0; i < nprems; i++) + prem_cons[i] = conc(prem(proof,i)); + res = iproof->make_farkas(con,args,prem_cons,farkas_coeffs); + break; + } + case Leq2EqKind: { + // conc should be (or x = y (not (leq x y)) (not(leq y z)) ) + ast xeqy = arg(conc(proof),0); + ast x = arg(xeqy,0); + ast y = arg(xeqy,1); + res = iproof->make_leq2eq(x,y,arg(arg(conc(proof),1),0),arg(arg(conc(proof),2),0)); + break; + } + case Eq2LeqKind: { + // conc should be (or (not (= x y)) (leq x y)) + ast xeqy = arg(arg(conc(proof),0),0); + ast xleqy = arg(conc(proof),1); + ast x = arg(xeqy,0); + ast y = arg(xeqy,1); + res = iproof->make_eq2leq(x,y,xleqy); + break; + } + case GCDTestKind: { + std::vector farkas_coeffs; + get_farkas_coeffs(proof,farkas_coeffs); + std::vector my_prems; my_prems.resize(2); + std::vector my_prem_cons; my_prem_cons.resize(2); + std::vector my_farkas_coeffs; my_farkas_coeffs.resize(2); + my_prems[0] = GCDtoDivRule(proof, true, farkas_coeffs, args, my_prem_cons[0]); + my_prems[1] = GCDtoDivRule(proof, false, farkas_coeffs, args, my_prem_cons[1]); + ast con = mk_false(); + my_farkas_coeffs[0] = my_farkas_coeffs[1] = make_int("1"); + res = iproof->make_farkas(con,my_prems,my_prem_cons,my_farkas_coeffs); + break; + } + case AssignBoundsKind: { + res = AssignBounds2Farkas(proof,conc(proof)); + break; + } + default: + throw unsupported(); + } + break; + default: + throw unsupported(); + } + break; + } + case PR_HYPOTHESIS: { + res = iproof->make_hypothesis(conc(proof)); + break; + } + default: + assert(0 && "translate_main: unsupported proof rule"); + throw unsupported(); + } + } + + return res; + } + + // We actually compute the interpolant here and then produce a proof consisting of just a lemma + + iz3proof::node translate(ast proof, iz3proof &dst){ + std::vector itps; + for(int i = 0; i < frames -1; i++){ + iproof = iz3proof_itp::create(this,range_downward(i),weak_mode()); + ast itp = translate_main(proof); + itps.push_back(itp); + delete iproof; + } + // Very simple proof -- lemma of the empty clause with computed interpolation + iz3proof::node Ipf = dst.make_lemma(std::vector(),itps); // builds result in dst + return Ipf; + } + + iz3translation_full(iz3mgr &mgr, + iz3secondary *_secondary, + const std::vector &cnsts, + const std::vector &parents, + const std::vector &theory) + : iz3translation(mgr, cnsts, parents, theory) + { + for(unsigned i = 0; i < cnsts.size(); i++) + frame_map[cnsts[i]] = i; + for(unsigned i = 0; i < theory.size(); i++) + frame_map[theory[i]] = INT_MAX; + frames = cnsts.size(); + traced_lit = ast(); + } + + ~iz3translation_full(){ + } +}; + + + + +#ifdef IZ3_TRANSLATE_FULL + +iz3translation *iz3translation::create(iz3mgr &mgr, + iz3secondary *secondary, + const std::vector &cnsts, + const std::vector &parents, + const std::vector &theory){ + return new iz3translation_full(mgr,secondary,cnsts,parents,theory); +} + + +#if 1 + +// This is just to make sure certain methods are compiled, so we can call then from the debugger. + +void iz3translation_full_trace_lit(iz3translation_full *p, iz3mgr::ast lit, iz3mgr::ast proof){ + p->trace_lit(lit, proof); +} + +void iz3translation_full_show_step(iz3translation_full *p, iz3mgr::ast proof){ + p->show_step(proof); +} + +void iz3translation_full_show_marked(iz3translation_full *p, iz3mgr::ast proof){ + p->show_marked(proof); +} + +void iz3translation_full_show_lit(iz3translation_full *p, iz3mgr::ast lit){ + p->show_lit(lit); +} + +void iz3translation_full_show_z3_lit(iz3translation_full *p, iz3mgr::ast a){ + p->show_z3_lit(a); +} + +void iz3translation_full_pfgoto(iz3translation_full *p, iz3mgr::ast proof){ + p->pfgoto(proof); +} + + +void iz3translation_full_pfback(iz3translation_full *p ){ + p->pfback(); +} + +void iz3translation_full_pffwd(iz3translation_full *p ){ + p->pffwd(); +} + +void iz3translation_full_pfprem(iz3translation_full *p, int i){ + p->pfprem(i); +} + + +struct stdio_fixer { + stdio_fixer(){ + std::cout.rdbuf()->pubsetbuf(0,0); + } + +} my_stdio_fixer; + +#endif + +#endif + + diff --git a/src/interp/iz3translate.h b/src/interp/iz3translate.h index 40dc6a165..6663f406b 100755 --- a/src/interp/iz3translate.h +++ b/src/interp/iz3translate.h @@ -34,6 +34,10 @@ public: virtual ast quantify(ast e, const range &rng){return e;} virtual ~iz3translation(){} + /** This is thrown when the proof cannot be translated. */ + struct unsupported { + }; + static iz3translation *create(iz3mgr &mgr, iz3secondary *secondary, const std::vector &frames, @@ -50,6 +54,7 @@ public: //#define IZ3_TRANSLATE_DIRECT2 #define IZ3_TRANSLATE_DIRECT +// #define IZ3_TRANSLATE_FULL #endif From e43383b6a8fa70afde7a205e3cd6ec27c71ecf13 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 18 Aug 2013 21:11:14 -0700 Subject: [PATCH 116/509] filter query predicates from models Signed-off-by: Nikolaj Bjorner --- src/muz_qe/clp_context.cpp | 10 +++++++--- src/muz_qe/dl_mk_array_blast.cpp | 2 +- src/muz_qe/dl_rule.cpp | 9 ++++++++- src/muz_qe/horn_tactic.cpp | 12 +++++++++++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/muz_qe/clp_context.cpp b/src/muz_qe/clp_context.cpp index 3a3908f59..01299a2b7 100644 --- a/src/muz_qe/clp_context.cpp +++ b/src/muz_qe/clp_context.cpp @@ -68,9 +68,13 @@ namespace datalog { m_goals.reset(); rm.mk_query(query, m_ctx.get_rules()); m_ctx.apply_default_transformation(); - func_decl *head_decl = m_ctx.get_rules().get_output_predicate(); - - expr_ref head(m_ctx.get_rules().get_predicate_rules(head_decl)[0]->get_head(), m); + func_decl* head_decl = m_ctx.get_rules().get_output_predicate(); + rule_set& rules = m_ctx.get_rules(); + rule_vector const& rv = rules.get_predicate_rules(head_decl); + if (rv.empty()) { + return l_false; + } + expr_ref head(rv[0]->get_head(), m); ground(head); m_goals.push_back(to_app(head)); return search(20, 0); diff --git a/src/muz_qe/dl_mk_array_blast.cpp b/src/muz_qe/dl_mk_array_blast.cpp index 9f057a148..2bfb6807a 100644 --- a/src/muz_qe/dl_mk_array_blast.cpp +++ b/src/muz_qe/dl_mk_array_blast.cpp @@ -250,11 +250,11 @@ namespace datalog { rule_set new_rules(m_ctx); rm.mk_rule(fml2, p, new_rules, r.name()); - TRACE("dl", new_rules.last()->display(m_ctx, tout << "new rule\n");); rule_ref new_rule(rm); if (m_simplifier.transform_rule(new_rules.last(), new_rule)) { rules.add_rule(new_rule.get()); rm.mk_rule_rewrite_proof(r, *new_rule.get()); + TRACE("dl", new_rule->display(m_ctx, tout << "new rule\n");); } return true; } diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index ac683eca9..70da4ed4b 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -41,6 +41,7 @@ Revision History: #include"expr_replacer.h" #include"bool_rewriter.h" #include"expr_safe_replace.h" +#include"filter_model_converter.h" namespace datalog { @@ -335,8 +336,14 @@ namespace datalog { vars.reverse(); names.reverse(); func_decl* qpred = m_ctx.mk_fresh_head_predicate(symbol("query"), symbol(), vars.size(), vars.c_ptr(), body_pred); - m_ctx.register_predicate(qpred, false); + m_ctx.register_predicate(qpred, false); rules.set_output_predicate(qpred); + + if (m_ctx.get_model_converter()) { + filter_model_converter* mc = alloc(filter_model_converter, m); + mc->insert(qpred); + m_ctx.add_model_converter(mc); + } expr_ref_vector qhead_args(m); for (unsigned i = 0; i < vars.size(); i++) { diff --git a/src/muz_qe/horn_tactic.cpp b/src/muz_qe/horn_tactic.cpp index 9d331cbfa..1916839f4 100644 --- a/src/muz_qe/horn_tactic.cpp +++ b/src/muz_qe/horn_tactic.cpp @@ -24,6 +24,7 @@ Revision History: #include"expr_replacer.h" #include"dl_rule_transformer.h" #include"dl_mk_slice.h" +#include"filter_model_converter.h" class horn_tactic : public tactic { struct imp { @@ -226,6 +227,9 @@ class horn_tactic : public tactic { } queries.reset(); queries.push_back(q); + filter_model_converter* mc1 = alloc(filter_model_converter, m); + mc1->insert(to_app(q)->get_decl()); + mc = mc1; } SASSERT(queries.size() == 1); q = queries[0].get(); @@ -276,7 +280,13 @@ class horn_tactic : public tactic { g->reset(); if (produce_models) { model_ref md = m_ctx.get_model(); - mc = model2model_converter(&*md); + model_converter_ref mc2 = model2model_converter(&*md); + if (mc) { + mc = concat(mc.get(), mc2.get()); + } + else { + mc = mc2; + } } break; } From 3b1344f68145771d029266aad8fd49c5af074e47 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 19 Aug 2013 11:53:35 -0700 Subject: [PATCH 117/509] working on scale transformation Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_scale.cpp | 128 +++++++++++++++++++++++++++++++ src/muz_qe/dl_mk_scale.h | 47 ++++++++++++ src/muz_qe/fixedpoint_params.pyg | 1 + 3 files changed, 176 insertions(+) create mode 100644 src/muz_qe/dl_mk_scale.cpp create mode 100644 src/muz_qe/dl_mk_scale.h diff --git a/src/muz_qe/dl_mk_scale.cpp b/src/muz_qe/dl_mk_scale.cpp new file mode 100644 index 000000000..bcd19d0a4 --- /dev/null +++ b/src/muz_qe/dl_mk_scale.cpp @@ -0,0 +1,128 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_scale.cpp + +Abstract: + + +Author: + + Nikolaj Bjorner (nbjorner) 2013-08-19 + +Revision History: + +--*/ + +#include"dl_mk_scale.h" +#include"dl_context.h" + +namespace datalog { + + + mk_scale::mk_scale(context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx), + a(m), + m_trail(m) { + } + + mk_scale::~mk_scale() { } + + rule_set * mk_scale::operator()(rule_set const & source) { + if (!m_ctx.get_params().scale()) { + return 0; + } + context& ctx = source.get_context(); + rule_manager& rm = source.get_rule_manager(); + rule_set * result = alloc(rule_set, ctx); + unsigned sz = source.get_num_rules(); + rule_ref new_rule(rm); + app_ref_vector tail(m); + app_ref head(m); + svector neg; + ptr_vector vars; + for (unsigned i = 0; i < sz; ++i) { + rule & r = *source.get_rule(i); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + tail.reset(); + neg.reset(); + vars.reset(); + r.get_vars(vars); + m_cache.reset(); + m_trail.reset(); + unsigned num_vars = vars.size(); + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(mk_pred(num_vars, r.get_tail(j))); + neg.push_back(false); + } + for (unsigned j = 0; j < utsz; ++j) { + tail.push_back(mk_constraint(num_vars, r.get_tail(j))); + neg.push_back(false); + } + tail.push_back(a.mk_gt(m.mk_var(num_vars, a.mk_real()), a.mk_numeral(rational(0), false))); + neg.push_back(false); + new_rule = rm.mk(mk_pred(num_vars, r.get_head()), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + if (source.is_output_predicate(r.get_decl())) { + result->set_output_predicate(new_rule->get_decl()); + } + } + TRACE("dl", result->display(tout);); + return result; + } + + app_ref mk_scale::mk_pred(unsigned num_vars, app* q) { + func_decl* f = q->get_decl(); + ptr_vector domain(f->get_arity(), f->get_domain()); + domain.push_back(a.mk_real()); + func_decl_ref g(m); + g = m.mk_func_decl(f->get_name(), f->get_arity() + 1, domain.c_ptr(), f->get_range()); + ptr_vector args(q->get_num_args(), q->get_args()); + args.push_back(m.mk_var(num_vars, a.mk_real())); + m_ctx.register_predicate(g, false); + return app_ref(m.mk_app(g, q->get_num_args() + 1, args.c_ptr()), m); + } + + app_ref mk_scale::mk_constraint(unsigned num_vars, app* q) { + expr* r = linearize(num_vars, q); + SASSERT(is_app(r)); + return app_ref(to_app(r), m); + } + + expr* mk_scale::linearize(unsigned num_vars, expr* e) { + expr* r; + if (m_cache.find(e, r)) { + return expr_ref(r, m); + } + if (!is_app(e)) { + return expr_ref(e, m); + } + expr_ref result(m); + app* ap = to_app(e); + if (ap->get_family_id() == m.get_basic_family_id() || + a.is_add(e) || a.is_sub(e) || + a.is_le(e) || a.is_ge(e) || + a.is_lt(e) || a.is_gt(e)) { + expr_ref_vector args(m); + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + args.push_back(linearize(num_vars, ap->get_arg(i))); + } + result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); + } + else if (a.is_numeral(e)) { + result = a.mk_mul(m.mk_var(num_vars, a.mk_real()), e); + } + else { + result = e; + } + m_trail.push_back(result); + m_cache.insert(e, result); + return result; + } + +}; diff --git a/src/muz_qe/dl_mk_scale.h b/src/muz_qe/dl_mk_scale.h new file mode 100644 index 000000000..cff226321 --- /dev/null +++ b/src/muz_qe/dl_mk_scale.h @@ -0,0 +1,47 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_scale.h + +Abstract: + + Add scale factor to linear (Real) arithemetic Horn clauses. + The transformation replaces occurrences of isolated constants by + a scale multiplied to each constant. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-08-19 + +Revision History: + +--*/ +#ifndef _DL_MK_SCALE_H_ +#define _DL_MK_SCALE_H_ + +#include"dl_rule_transformer.h" +#include"arith_decl_plugin.h" + +namespace datalog { + + class mk_scale : public rule_transformer::plugin { + ast_manager& m; + context& m_ctx; + arith_util a; + expr_ref_vector m_trail; + obj_map m_cache; + expr* linearize(unsigned num_vars, expr* e); + app_ref mk_pred(unsigned num_vars, app* q); + app_ref mk_constraint(unsigned num_vars, app* q); + public: + mk_scale(context & ctx, unsigned priority = 33039); + ~mk_scale(); + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_SCALE_H_ */ + diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index 555c44df2..46f4fce99 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -11,6 +11,7 @@ def_module_params('fixedpoint', ('explanations_on_relation_level', BOOL, False, '(DATALOG) if true, explanations are generated as history of each relation, rather than per fact (generate_explanations must be set to true for this option to have any effect)'), ('magic_sets_for_queries', BOOL, False, "(DATALOG) magic set transformation will be used for queries"), ('magic', BOOL, False, "perform symbolic magic set transformation"), + ('scale', BOOL, False, "add scaling variable to linear real arithemtic clauses"), ('unbound_compressor', BOOL, True, "auxiliary relations will be introduced to avoid unbound variables in rule heads"), ('similarity_compressor', BOOL, True, "(DATALOG) rules that differ only in values of constants will be merged into a single rule"), ('similarity_compressor_threshold', UINT, 11, "(DATALOG) if similarity_compressor is on, this value determines how many similar rules there must be in order for them to be merged"), From 7c9e3c3b70296ef416e1bdb81fd8ef39e0b95f2d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 19 Aug 2013 14:44:34 -0700 Subject: [PATCH 118/509] debug scale transformer, add model converter Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_context.cpp | 2 + src/muz_qe/dl_mk_scale.cpp | 109 ++++++++++++++++++++++++++--- src/muz_qe/dl_mk_scale.h | 13 ++-- src/muz_qe/dl_rule_transformer.cpp | 2 +- 4 files changed, 113 insertions(+), 13 deletions(-) diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index d8a50668b..9f250742c 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -48,6 +48,7 @@ Revision History: #include"dl_mk_magic_symbolic.h" #include"dl_mk_quantifier_abstraction.h" #include"dl_mk_quantifier_instantiation.h" +#include"dl_mk_scale.h" #include"datatype_decl_plugin.h" @@ -869,6 +870,7 @@ namespace datalog { if (get_params().magic()) { m_transf.register_plugin(alloc(datalog::mk_magic_symbolic, *this, 36020)); } + m_transf.register_plugin(alloc(datalog::mk_scale, *this, 36030)); transform_rules(m_transf); } diff --git a/src/muz_qe/dl_mk_scale.cpp b/src/muz_qe/dl_mk_scale.cpp index bcd19d0a4..90cbedb6c 100644 --- a/src/muz_qe/dl_mk_scale.cpp +++ b/src/muz_qe/dl_mk_scale.cpp @@ -21,6 +21,85 @@ Revision History: namespace datalog { + class mk_scale::scale_model_converter : public model_converter { + ast_manager& m; + func_decl_ref_vector m_trail; + arith_util a; + obj_map m_new2old; + public: + scale_model_converter(ast_manager& m): m(m), m_trail(m), a(m) {} + + virtual ~scale_model_converter() {} + + void add_new2old(func_decl* new_f, func_decl* old_f) { + m_trail.push_back(old_f); + m_trail.push_back(new_f); + m_new2old.insert(new_f, old_f); + } + + virtual void operator()(model_ref& md) { + model_ref old_model = alloc(model, m); + obj_map::iterator it = m_new2old.begin(); + obj_map::iterator end = m_new2old.end(); + for (; it != end; ++it) { + func_decl* old_p = it->m_value; + func_decl* new_p = it->m_key; + func_interp* old_fi = alloc(func_interp, m, old_p->get_arity()); + + if (new_p->get_arity() == 0) { + old_fi->set_else(md->get_const_interp(new_p)); + } + else { + func_interp* new_fi = md->get_func_interp(new_p); + expr_ref_vector subst(m); + var_subst vs(m, false); + expr_ref tmp(m); + + if (!new_fi) { + TRACE("dl", tout << new_p->get_name() << " has no value in the current model\n";); + dealloc(old_fi); + continue; + } + for (unsigned i = 0; i < old_p->get_arity(); ++i) { + subst.push_back(m.mk_var(i, old_p->get_domain(i))); + } + subst.push_back(a.mk_numeral(rational(1), a.mk_real())); + + // Hedge that we don't have to handle the general case for models produced + // by Horn clause solvers. + SASSERT(!new_fi->is_partial() && new_fi->num_entries() == 0); + vs(new_fi->get_else(), subst.size(), subst.c_ptr(), tmp); + old_fi->set_else(tmp); + old_model->register_decl(old_p, old_fi); + } + } + + // register values that have not been scaled. + unsigned sz = md->get_num_constants(); + for (unsigned i = 0; i < sz; ++i) { + func_decl* c = md->get_constant(i); + if (!m_new2old.contains(c)) { + old_model->register_decl(c, md->get_const_interp(c)); + } + } + sz = md->get_num_functions(); + for (unsigned i = 0; i < sz; ++i) { + func_decl* f = md->get_function(i); + if (!m_new2old.contains(f)) { + func_interp* fi = md->get_func_interp(f); + old_model->register_decl(f, fi->copy()); + } + } + md = old_model; + //TRACE("dl", model_smt2_pp(tout, m, *md, 0); ); + } + + virtual model_converter * translate(ast_translation & translator) { + UNREACHABLE(); + return 0; + } + }; + mk_scale::mk_scale(context & ctx, unsigned priority): plugin(priority), @@ -30,21 +109,27 @@ namespace datalog { m_trail(m) { } - mk_scale::~mk_scale() { } + mk_scale::~mk_scale() { + } rule_set * mk_scale::operator()(rule_set const & source) { if (!m_ctx.get_params().scale()) { return 0; } - context& ctx = source.get_context(); rule_manager& rm = source.get_rule_manager(); - rule_set * result = alloc(rule_set, ctx); + rule_set * result = alloc(rule_set, m_ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; ptr_vector vars; + ref smc; + if (m_ctx.get_model_converter()) { + smc = alloc(scale_model_converter, m); + } + m_mc = smc.get(); + for (unsigned i = 0; i < sz; ++i) { rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); @@ -52,15 +137,15 @@ namespace datalog { tail.reset(); neg.reset(); vars.reset(); - r.get_vars(vars); m_cache.reset(); m_trail.reset(); + r.get_vars(vars); unsigned num_vars = vars.size(); - for (unsigned j = utsz; j < tsz; ++j) { + for (unsigned j = 0; j < utsz; ++j) { tail.push_back(mk_pred(num_vars, r.get_tail(j))); neg.push_back(false); } - for (unsigned j = 0; j < utsz; ++j) { + for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(mk_constraint(num_vars, r.get_tail(j))); neg.push_back(false); } @@ -73,6 +158,11 @@ namespace datalog { } } TRACE("dl", result->display(tout);); + if (m_mc) { + m_ctx.add_model_converter(m_mc); + } + m_trail.reset(); + m_cache.reset(); return result; } @@ -85,6 +175,9 @@ namespace datalog { ptr_vector args(q->get_num_args(), q->get_args()); args.push_back(m.mk_var(num_vars, a.mk_real())); m_ctx.register_predicate(g, false); + if (m_mc) { + m_mc->add_new2old(g, f); + } return app_ref(m.mk_app(g, q->get_num_args() + 1, args.c_ptr()), m); } @@ -97,10 +190,10 @@ namespace datalog { expr* mk_scale::linearize(unsigned num_vars, expr* e) { expr* r; if (m_cache.find(e, r)) { - return expr_ref(r, m); + return r; } if (!is_app(e)) { - return expr_ref(e, m); + return e; } expr_ref result(m); app* ap = to_app(e); diff --git a/src/muz_qe/dl_mk_scale.h b/src/muz_qe/dl_mk_scale.h index cff226321..387a8868f 100644 --- a/src/muz_qe/dl_mk_scale.h +++ b/src/muz_qe/dl_mk_scale.h @@ -27,17 +27,22 @@ Revision History: namespace datalog { class mk_scale : public rule_transformer::plugin { + + class scale_model_converter; + ast_manager& m; context& m_ctx; arith_util a; expr_ref_vector m_trail; obj_map m_cache; - expr* linearize(unsigned num_vars, expr* e); - app_ref mk_pred(unsigned num_vars, app* q); - app_ref mk_constraint(unsigned num_vars, app* q); + scale_model_converter* m_mc; + + expr* linearize(unsigned num_vars, expr* e); + app_ref mk_pred(unsigned num_vars, app* q); + app_ref mk_constraint(unsigned num_vars, app* q); public: mk_scale(context & ctx, unsigned priority = 33039); - ~mk_scale(); + virtual ~mk_scale(); rule_set * operator()(rule_set const & source); }; diff --git a/src/muz_qe/dl_rule_transformer.cpp b/src/muz_qe/dl_rule_transformer.cpp index 0cad08cb4..9055007b9 100644 --- a/src/muz_qe/dl_rule_transformer.cpp +++ b/src/muz_qe/dl_rule_transformer.cpp @@ -37,7 +37,7 @@ namespace datalog { void rule_transformer::reset() { plugin_vector::iterator it = m_plugins.begin(); plugin_vector::iterator end = m_plugins.end(); - for(; it!=end; ++it) { + for(; it!=end; ++it) { dealloc(*it); } m_plugins.reset(); From e9ad4ab5840dccbb7506e3bbf20b8b4013ecb26e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 19 Aug 2013 18:57:10 -0700 Subject: [PATCH 119/509] fix scaling Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_scale.cpp | 48 +++++++++++++++++++++++++++----------- src/muz_qe/dl_mk_scale.h | 1 + 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/muz_qe/dl_mk_scale.cpp b/src/muz_qe/dl_mk_scale.cpp index 90cbedb6c..d666d0ca8 100644 --- a/src/muz_qe/dl_mk_scale.cpp +++ b/src/muz_qe/dl_mk_scale.cpp @@ -106,7 +106,8 @@ namespace datalog { m(ctx.get_manager()), m_ctx(ctx), a(m), - m_trail(m) { + m_trail(m), + m_eqs(m) { } mk_scale::~mk_scale() { @@ -135,23 +136,23 @@ namespace datalog { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); tail.reset(); - neg.reset(); vars.reset(); m_cache.reset(); m_trail.reset(); + m_eqs.reset(); r.get_vars(vars); unsigned num_vars = vars.size(); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(mk_pred(num_vars, r.get_tail(j))); - neg.push_back(false); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(mk_constraint(num_vars, r.get_tail(j))); - neg.push_back(false); } + app_ref new_pred = mk_pred(num_vars, r.get_head()); + tail.append(m_eqs); tail.push_back(a.mk_gt(m.mk_var(num_vars, a.mk_real()), a.mk_numeral(rational(0), false))); - neg.push_back(false); - new_rule = rm.mk(mk_pred(num_vars, r.get_head()), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + neg.resize(tail.size(), false); + new_rule = rm.mk(new_pred, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); if (source.is_output_predicate(r.get_decl())) { result->set_output_predicate(new_rule->get_decl()); @@ -166,14 +167,33 @@ namespace datalog { return result; } - app_ref mk_scale::mk_pred(unsigned num_vars, app* q) { + app_ref mk_scale::mk_pred(unsigned sigma_idx, app* q) { func_decl* f = q->get_decl(); ptr_vector domain(f->get_arity(), f->get_domain()); domain.push_back(a.mk_real()); func_decl_ref g(m); g = m.mk_func_decl(f->get_name(), f->get_arity() + 1, domain.c_ptr(), f->get_range()); - ptr_vector args(q->get_num_args(), q->get_args()); - args.push_back(m.mk_var(num_vars, a.mk_real())); + expr_ref_vector args(m); + for (unsigned i = 0; i < q->get_num_args(); ++i) { + expr* arg = q->get_arg(i); + rational val; + if (a.is_numeral(arg, val)) { + if (val.is_zero()) { + // arg is unchanged. + } + else if (val.is_one()) { + arg = m.mk_var(sigma_idx, a.mk_real()); + } + else { + // create a fresh variable 'v', add 'v == sigma*arg' + expr* v = m.mk_var(sigma_idx + 1 + m_eqs.size(), a.mk_real()); + m_eqs.push_back(m.mk_eq(v, a.mk_mul(arg, m.mk_var(sigma_idx, a.mk_real())))); + arg = v; + } + } + args.push_back(arg); + } + args.push_back(m.mk_var(sigma_idx, a.mk_real())); m_ctx.register_predicate(g, false); if (m_mc) { m_mc->add_new2old(g, f); @@ -181,13 +201,13 @@ namespace datalog { return app_ref(m.mk_app(g, q->get_num_args() + 1, args.c_ptr()), m); } - app_ref mk_scale::mk_constraint(unsigned num_vars, app* q) { - expr* r = linearize(num_vars, q); + app_ref mk_scale::mk_constraint(unsigned sigma_idx, app* q) { + expr* r = linearize(sigma_idx, q); SASSERT(is_app(r)); return app_ref(to_app(r), m); } - expr* mk_scale::linearize(unsigned num_vars, expr* e) { + expr* mk_scale::linearize(unsigned sigma_idx, expr* e) { expr* r; if (m_cache.find(e, r)) { return r; @@ -203,12 +223,12 @@ namespace datalog { a.is_lt(e) || a.is_gt(e)) { expr_ref_vector args(m); for (unsigned i = 0; i < ap->get_num_args(); ++i) { - args.push_back(linearize(num_vars, ap->get_arg(i))); + args.push_back(linearize(sigma_idx, ap->get_arg(i))); } result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); } else if (a.is_numeral(e)) { - result = a.mk_mul(m.mk_var(num_vars, a.mk_real()), e); + result = a.mk_mul(m.mk_var(sigma_idx, a.mk_real()), e); } else { result = e; diff --git a/src/muz_qe/dl_mk_scale.h b/src/muz_qe/dl_mk_scale.h index 387a8868f..8498c891f 100644 --- a/src/muz_qe/dl_mk_scale.h +++ b/src/muz_qe/dl_mk_scale.h @@ -34,6 +34,7 @@ namespace datalog { context& m_ctx; arith_util a; expr_ref_vector m_trail; + app_ref_vector m_eqs; obj_map m_cache; scale_model_converter* m_mc; From 0b56440cba4ce3a9a066362fd36142c7487989b7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 19 Aug 2013 19:48:09 -0700 Subject: [PATCH 120/509] fix convex converter for multi-arity addition Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_generalizers.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index 28226dffa..55d8a05f8 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -434,9 +434,19 @@ namespace pdr { if (translate(app->get_decl(), index, result)) { return true; } - if (a.is_add(term, e1, e2) && mk_convex(e1, index, is_mul, r1) && mk_convex(e2, index, is_mul, r2)) { - result = a.mk_add(r1, r2); - return true; + if (a.is_add(term)) { + bool ok = true; + expr_ref_vector args(m); + for (unsigned i = 0; ok && i < app->get_num_args(); ++i) { + ok = mk_convex(app->get_arg(i), index, is_mul, r1); + if (ok) { + args.push_back(r1); + } + } + if (ok) { + result = a.mk_add(args.size(), args.c_ptr()); + } + return ok; } if (a.is_sub(term, e1, e2) && mk_convex(e1, index, is_mul, r1) && mk_convex(e2, index, is_mul, r2)) { result = a.mk_sub(r1, r2); From 5c145dcd4b4f8a03036c1123c18626be52d37c52 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 22 Aug 2013 15:00:52 -0700 Subject: [PATCH 121/509] fix parameter checking on quantifiers (thanks to Esteban Pavese), fix query tracking in rel_context (thanks to Nuno Lopes), fix counter for free variables under quantfiers (thanks to Tomer Weiss) Signed-off-by: Nikolaj Bjorner --- src/api/api_quant.cpp | 5 ++++- src/ast/rewriter/ast_counter.cpp | 24 ++++++++++++++++-------- src/muz_qe/dl_context.cpp | 1 + src/muz_qe/dl_rule.cpp | 13 ++++++++++++- src/muz_qe/hnf.cpp | 7 ++++++- src/muz_qe/rel_context.cpp | 20 ++++++++++---------- src/muz_qe/rel_context.h | 2 +- 7 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/api/api_quant.cpp b/src/api/api_quant.cpp index 883e9e752..4170852a9 100644 --- a/src/api/api_quant.cpp +++ b/src/api/api_quant.cpp @@ -165,7 +165,10 @@ extern "C" { } for (unsigned i = 0; i < num_bound; ++i) { app* a = to_app(bound[i]); - SASSERT(a->get_kind() == AST_APP); + if (a->get_kind() != AST_APP) { + SET_ERROR_CODE(Z3_INVALID_ARG); + RETURN_Z3(0); + } symbol s(to_app(a)->get_decl()->get_name()); names.push_back(of_symbol(s)); types.push_back(of_sort(mk_c(c)->m().get_sort(a))); diff --git a/src/ast/rewriter/ast_counter.cpp b/src/ast/rewriter/ast_counter.cpp index a807237c5..6f49a232f 100644 --- a/src/ast/rewriter/ast_counter.cpp +++ b/src/ast/rewriter/ast_counter.cpp @@ -108,6 +108,7 @@ void var_counter::count_vars(ast_manager & m, const app * pred, int coef) { unsigned var_counter::get_max_var(bool& has_var) { has_var = false; unsigned max_var = 0; + ptr_vector qs; while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); @@ -117,14 +118,7 @@ unsigned var_counter::get_max_var(bool& has_var) { m_visited.mark(e, true); switch(e->get_kind()) { case AST_QUANTIFIER: { - var_counter aux_counter; - quantifier* q = to_quantifier(e); - bool has_var1 = false; - unsigned max_v = aux_counter.get_max_var(has_var1); - if (max_v > max_var + q->get_num_decls()) { - max_var = max_v - q->get_num_decls(); - has_var = true; - } + qs.push_back(to_quantifier(e)); break; } case AST_VAR: { @@ -147,6 +141,20 @@ unsigned var_counter::get_max_var(bool& has_var) { } } m_visited.reset(); + + while (!qs.empty()) { + var_counter aux_counter; + quantifier* q = qs.back(); + qs.pop_back(); + aux_counter.m_todo.push_back(q->get_expr()); + bool has_var1 = false; + unsigned max_v = aux_counter.get_max_var(has_var1); + if (max_v >= max_var + q->get_num_decls()) { + max_var = max_v - q->get_num_decls(); + has_var = has_var || has_var1; + } + } + return max_var; } diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 9f250742c..7da9808e7 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -652,6 +652,7 @@ namespace datalog { if (check_pred(e)) { std::ostringstream out; out << "recursive predicate " << mk_ismt2_pp(e, get_manager()) << " occurs nested in body"; + r->display(*this, out << "\n"); throw default_exception(out.str()); } diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index 70da4ed4b..455f2c244 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -186,15 +186,20 @@ namespace datalog { } void rule_manager::mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name) { + DEBUG_CODE(ptr_vector sorts; + ::get_free_vars(fml, sorts); ); expr_ref_vector fmls(m); proof_ref_vector prs(m); m_hnf.reset(); m_hnf.set_name(name); + m_hnf(fml, p, fmls, prs); for (unsigned i = 0; i < m_hnf.get_fresh_predicates().size(); ++i) { m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false); } for (unsigned i = 0; i < fmls.size(); ++i) { + DEBUG_CODE(ptr_vector sorts; + ::get_free_vars(fmls[i].get(), sorts); ); mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name); } } @@ -265,7 +270,7 @@ namespace datalog { } else { head = ensure_app(fml); - } + } return index; } @@ -491,6 +496,12 @@ namespace datalog { app * * uninterp_tail = r->m_tail; //grows upwards app * * interp_tail = r->m_tail+n; //grows downwards + DEBUG_CODE(ptr_vector sorts; + ::get_free_vars(head, sorts); + for (unsigned i = 0; i < n; ++i) { + ::get_free_vars(tail[i], sorts); + }); + bool has_neg = false; for (unsigned i = 0; i < n; i++) { diff --git a/src/muz_qe/hnf.cpp b/src/muz_qe/hnf.cpp index 75a85061c..36316cfa6 100644 --- a/src/muz_qe/hnf.cpp +++ b/src/muz_qe/hnf.cpp @@ -128,7 +128,12 @@ public: } void set_name(symbol const& n) { - m_name = n; + if (n == symbol::null) { + m_name = symbol("P"); + } + else { + m_name = n; + } } func_decl_ref_vector const& get_fresh_predicates() { diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index ce1b30b88..d597500b2 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -105,14 +105,12 @@ namespace datalog { } } - lbool rel_context::saturate() { + lbool rel_context::saturate(scoped_query& sq) { m_context.ensure_closed(); bool time_limit = m_context.soft_timeout()!=0; unsigned remaining_time_limit = m_context.soft_timeout(); unsigned restart_time = m_context.initial_restart_timeout(); - - scoped_query scoped_query(m_context); - + instruction_block termination_code; lbool result; @@ -191,7 +189,7 @@ namespace datalog { else { restart_time = static_cast(new_restart_time); } - scoped_query.reset(); + sq.reset(); } m_context.record_transformed_rules(); TRACE("dl", display_profile(tout);); @@ -206,7 +204,7 @@ namespace datalog { } m_context.close(); reset_negated_tables(); - lbool res = saturate(); + lbool res = saturate(_scoped_query); switch(res) { case l_true: { @@ -215,7 +213,8 @@ namespace datalog { bool some_non_empty = num_rels == 0; bool is_approx = false; for (unsigned i = 0; i < num_rels; ++i) { - relation_base& rel = get_relation(rels[i]); + func_decl* q = m_context.get_rules().get_pred(rels[i]); + relation_base& rel = get_relation(q); if (!rel.empty()) { some_non_empty = true; } @@ -272,13 +271,14 @@ namespace datalog { if (m_context.magic_sets_for_queries()) { m_context.transform_rules(alloc(mk_magic_sets, m_context, query_pred)); + query_pred = m_context.get_rules().get_pred(query_pred); } + lbool res = saturate(_scoped_query); + query_pred = m_context.get_rules().get_pred(query_pred); - lbool res = saturate(); - - if (res != l_undef) { + if (res != l_undef) { m_last_result_relation = get_relation(query_pred).clone(); if (m_last_result_relation->empty()) { res = l_false; diff --git a/src/muz_qe/rel_context.h b/src/muz_qe/rel_context.h index 057d1c15f..0b73caaa6 100644 --- a/src/muz_qe/rel_context.h +++ b/src/muz_qe/rel_context.h @@ -109,7 +109,7 @@ namespace datalog { void display_profile(std::ostream& out); - lbool saturate(); + lbool saturate(scoped_query& sq); }; }; From 4db8db7484a9b7ad6feb46812382508a3f82317b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 25 Aug 2013 12:40:16 -0700 Subject: [PATCH 122/509] extend tracing for rule transformations Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_rule_transformer.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/muz_qe/dl_rule_transformer.cpp b/src/muz_qe/dl_rule_transformer.cpp index 9055007b9..2df8b9453 100644 --- a/src/muz_qe/dl_rule_transformer.cpp +++ b/src/muz_qe/dl_rule_transformer.cpp @@ -22,6 +22,7 @@ Revision History: #include"dl_context.h" #include"dl_rule_transformer.h" +#include"stopwatch.h" namespace datalog { @@ -86,8 +87,16 @@ namespace datalog { for(; it!=end && !m_context.canceled(); ++it) { plugin & p = **it; + + IF_VERBOSE(1, verbose_stream() << "(transform " << typeid(p).name() << "...";); + stopwatch sw; + sw.start(); rule_set * new_rules1 = p(*new_rules); + sw.stop(); + double sec = sw.get_seconds(); + if (sec < 0.001) sec = 0.0; if (!new_rules1) { + IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";); continue; } if (p.can_destratify_negation() && @@ -96,6 +105,7 @@ namespace datalog { warning_msg("a rule transformation skipped " "because it destratified negation"); dealloc(new_rules1); + IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";); continue; } modified = true; @@ -103,6 +113,7 @@ namespace datalog { new_rules = new_rules1; new_rules->ensure_closed(); + IF_VERBOSE(1, verbose_stream() << new_rules->get_num_rules() << " rules " << sec << "s)\n";); TRACE("dl_rule_transf", tout << typeid(p).name()<<":\n"; new_rules->display(tout); From 2d6b3fa28434a8c16fdf8cfbdfcfd08dbaf4b197 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 27 Aug 2013 19:45:21 -0700 Subject: [PATCH 123/509] wworking on generalizing post-image Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_farkas_learner.cpp | 24 +++++++-- src/muz_qe/pdr_farkas_learner.h | 18 +++++++ src/muz_qe/pdr_generalizers.cpp | 89 +++++++++++++++++++++++-------- src/muz_qe/pdr_generalizers.h | 6 +-- src/muz_qe/pdr_prop_solver.cpp | 9 +++- src/muz_qe/pdr_prop_solver.h | 3 ++ 6 files changed, 119 insertions(+), 30 deletions(-) diff --git a/src/muz_qe/pdr_farkas_learner.cpp b/src/muz_qe/pdr_farkas_learner.cpp index 71404ab12..8bc77ecc6 100644 --- a/src/muz_qe/pdr_farkas_learner.cpp +++ b/src/muz_qe/pdr_farkas_learner.cpp @@ -249,6 +249,7 @@ namespace pdr { farkas_learner::farkas_learner(smt_params& params, ast_manager& outer_mgr) : m_proof_params(get_proof_params(params)), m_pr(PROOF_MODE), + m_combine_farkas_coefficients(true), p2o(m_pr, outer_mgr), o2p(outer_mgr, m_pr) { @@ -412,11 +413,17 @@ namespace pdr { void farkas_learner::combine_constraints(unsigned n, app * const * lits, rational const * coeffs, expr_ref& res) { ast_manager& m = res.get_manager(); - constr res_c(m); - for(unsigned i = 0; i < n; ++i) { - res_c.add(coeffs[i], lits[i]); + if (m_combine_farkas_coefficients) { + constr res_c(m); + for(unsigned i = 0; i < n; ++i) { + res_c.add(coeffs[i], lits[i]); + } + res_c.get(res); + } + else { + bool_rewriter rw(m); + rw.mk_or(n, (expr*const*)(lits), res); } - res_c.get(res); } class farkas_learner::constant_replacer_cfg : public default_rewriter_cfg @@ -694,7 +701,7 @@ namespace pdr { tout << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; } tout << mk_pp(m.get_fact(p), m) << "\n"; - ); + ); // NB. Taking 'abs' of coefficients is a workaround. // The Farkas coefficient extraction in arith_core must be wrong. @@ -753,6 +760,13 @@ namespace pdr { simplify_lemmas(lemmas); } + void farkas_learner::get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences) { + TRACE("farkas_learner", tout << "get consequences\n";); + m_combine_farkas_coefficients = false; + get_lemmas(root, bs, consequences); + m_combine_farkas_coefficients = true; + } + void farkas_learner::get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas) { ast_manager& m = lemmas.get_manager(); ast_mark visited; diff --git a/src/muz_qe/pdr_farkas_learner.h b/src/muz_qe/pdr_farkas_learner.h index eb38455ab..b623c2035 100644 --- a/src/muz_qe/pdr_farkas_learner.h +++ b/src/muz_qe/pdr_farkas_learner.h @@ -43,6 +43,12 @@ class farkas_learner { ast_manager m_pr; scoped_ptr m_ctx; + // + // true: produce a combined constraint by applying Farkas coefficients. + // false: produce a conjunction of the negated literals from the theory lemmas. + // + bool m_combine_farkas_coefficients; + static smt_params get_proof_params(smt_params& orig_params); @@ -92,6 +98,18 @@ public: */ void get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas); + /** + Traverse a proof and retrieve consequences of A that are used to establish ~B. + The assumption is that: + + A => \/ ~consequences[i] and \/ ~consequences[i] => ~B + + e.g., the second implication can be rewritten as: + + B => /\ consequences[i] + */ + void get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences); + /** \brief Simplify lemmas using subsumption. */ diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index 55d8a05f8..f51df79a6 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -159,6 +159,7 @@ namespace pdr { } void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { + // method3(n, core, uses_level, new_cores); method1(n, core, uses_level, new_cores); } @@ -187,7 +188,7 @@ namespace pdr { new_cores.push_back(std::make_pair(core, uses_level)); return; } - add_variables(n, eqs); + add_variables(n, 2, eqs); if (!mk_convex(core, 0, conv1)) { new_cores.push_back(std::make_pair(core, uses_level)); IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core), m) << "\n";); @@ -257,7 +258,7 @@ namespace pdr { IF_VERBOSE(0, verbose_stream() << "unexpected result from satisfiability check\n";); return; } - add_variables(n, conv1); + add_variables(n, 2, conv1); model_ref mdl; ctx.get_model(mdl); @@ -267,7 +268,7 @@ namespace pdr { expr* left, *right; func_decl* fn0 = n.pt().sig(i); func_decl* fn1 = pm.o2n(fn0, 0); - if (m_left.find(fn1, left) && m_right.find(fn1, right)) { + if (m_vars[0].find(fn1, left) && m_vars[1].find(fn1, right)) { expr_ref val(m); mdl->eval(fn1, val); if (val) { @@ -306,34 +307,84 @@ namespace pdr { } } - void core_convex_hull_generalizer::add_variables(model_node& n, expr_ref_vector& eqs) { + /* + Extract the lemmas from the transition relation that were used to establish unsatisfiability. + Take convex closures of conbinations of these lemmas. + */ + void core_convex_hull_generalizer::method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { + TRACE("dl", tout << "method: generalize consequences of F(R)\n"; + for (unsigned i = 0; i < core.size(); ++i) { + tout << "B:" << mk_pp(core[i], m) << "\n"; + }); manager& pm = n.pt().get_pdr_manager(); - if (!m_left.contains(n.pt().head())) { - expr_ref left(m), right(m); - m_left.insert(n.pt().head(), 0); + bool uses_level1; + expr_ref_vector core1(m); + core1.append(core); + obj_hashtable bs; + for (unsigned i = 0; i < core.size(); ++i) { + bs.insert(core[i]); + } + expr_ref_vector consequences(m); + { + n.pt().get_solver().set_consequences(&consequences); + pred_transformer::scoped_farkas sf (n.pt(), true); + VERIFY(l_false == n.pt().is_reachable(n, &core1, uses_level1)); + n.pt().get_solver().set_consequences(0); + } + IF_VERBOSE(0, + verbose_stream() << "Consequences: " << consequences.size() << "\n"; + for (unsigned i = 0; i < consequences.size(); ++i) { + verbose_stream() << mk_pp(consequences[i].get(), m) << "\n"; + } + verbose_stream() << "core: " << core1.size() << "\n"; + for (unsigned i = 0; i < core1.size(); ++i) { + verbose_stream() << mk_pp(core1[i].get(), m) << "\n"; + }); + + // now create the convex closure of the consequences: + expr_ref_vector conv(m); + for (unsigned i = 0; i < consequences.size(); ++i) { + if (m_sigma.size() == i) { + m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); + } + conv.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); + ;; // mk_convex + } + } + + void core_convex_hull_generalizer::add_variables(model_node& n, unsigned num_vars, expr_ref_vector& eqs) { + manager& pm = n.pt().get_pdr_manager(); + if (m_vars.size() < num_vars) { + m_vars.resize(num_vars); + } + if (!m_vars[0].contains(n.pt().head())) { + expr_ref var(m); + m_vars[0].insert(n.pt().head(), 0); unsigned sz = n.pt().sig_size(); for (unsigned i = 0; i < sz; ++i) { func_decl* fn0 = n.pt().sig(i); sort* srt = fn0->get_range(); if (a.is_int_real(srt)) { func_decl* fn1 = pm.o2n(fn0, 0); - left = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); - right = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); - m_left.insert(fn1, left); - m_right.insert(fn1, right); - m_trail.push_back(left); - m_trail.push_back(right); + for (unsigned j = 0; j < num_vars; ++j) { + var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); + m_vars[j].insert(fn1, var); + m_trail.push_back(var); + } } } } unsigned sz = n.pt().sig_size(); for (unsigned i = 0; i < sz; ++i) { - expr* left, *right; + expr* var; + ptr_vector vars; func_decl* fn0 = n.pt().sig(i); func_decl* fn1 = pm.o2n(fn0, 0); - if (m_left.find(fn1, left) && m_right.find(fn1, right)) { - eqs.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(left, right))); + for (unsigned j = 0; j < num_vars; ++j) { + VERIFY (m_vars[j].find(fn1, var)); + vars.push_back(var); } + eqs.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr()))); } } @@ -412,11 +463,7 @@ namespace pdr { bool core_convex_hull_generalizer::translate(func_decl* f, unsigned index, expr_ref& result) { expr* tmp; - if (index == 0 && m_left.find(f, tmp)) { - result = tmp; - return true; - } - if (index == 1 && m_right.find(f, tmp)) { + if (m_vars[index].find(f, tmp)) { result = tmp; return true; } diff --git a/src/muz_qe/pdr_generalizers.h b/src/muz_qe/pdr_generalizers.h index ece1f51f1..9dcdf44c0 100644 --- a/src/muz_qe/pdr_generalizers.h +++ b/src/muz_qe/pdr_generalizers.h @@ -78,8 +78,7 @@ namespace pdr { arith_util a; expr_ref_vector m_sigma; expr_ref_vector m_trail; - obj_map m_left; - obj_map m_right; + vector > m_vars; obj_map m_models; bool m_is_closure; expr_ref mk_closure(expr* e); @@ -90,7 +89,8 @@ namespace pdr { bool translate(func_decl* fn, unsigned index, expr_ref& result); void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); void method2(model_node& n, expr_ref_vector& core, bool& uses_level); - void add_variables(model_node& n, expr_ref_vector& eqs); + void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); + void add_variables(model_node& n, unsigned num_vars, expr_ref_vector& eqs); public: core_convex_hull_generalizer(context& ctx, bool is_closure); virtual ~core_convex_hull_generalizer() {} diff --git a/src/muz_qe/pdr_prop_solver.cpp b/src/muz_qe/pdr_prop_solver.cpp index c7f0bfbc3..eb3c0ee96 100644 --- a/src/muz_qe/pdr_prop_solver.cpp +++ b/src/muz_qe/pdr_prop_solver.cpp @@ -236,9 +236,12 @@ namespace pdr { m_neg_level_atoms(m), m_proxies(m), m_core(0), + m_model(0), + m_consequences(0), m_subset_based_core(false), m_use_farkas(false), - m_in_level(false) + m_in_level(false), + m_current_level(0) { m_ctx->assert_expr(m_pm.get_background()); } @@ -413,6 +416,10 @@ namespace pdr { m_core->reset(); m_core->append(lemmas); + + if (m_consequences) { + fl.get_consequences(pr, bs, *m_consequences); + } } } diff --git a/src/muz_qe/pdr_prop_solver.h b/src/muz_qe/pdr_prop_solver.h index 5a6b09360..0c60a7124 100644 --- a/src/muz_qe/pdr_prop_solver.h +++ b/src/muz_qe/pdr_prop_solver.h @@ -48,6 +48,7 @@ namespace pdr { app_ref_vector m_proxies; // predicates for assumptions expr_ref_vector* m_core; model_ref* m_model; + expr_ref_vector* m_consequences; bool m_subset_based_core; bool m_assumes_level; bool m_use_farkas; @@ -84,6 +85,8 @@ namespace pdr { void set_core(expr_ref_vector* core) { m_core = core; } void set_model(model_ref* mdl) { m_model = mdl; } void set_subset_based_core(bool f) { m_subset_based_core = f; } + void set_consequences(expr_ref_vector* consequences) { m_consequences = consequences; } + bool assumes_level() const { return m_assumes_level; } void add_level(); From e5541bad17639c9318d0b8d5aeb8c29b6b45cdfc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 27 Aug 2013 21:11:45 -0700 Subject: [PATCH 124/509] working on convex lemma gneralization Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_generalizers.cpp | 108 +++++++++++++++++++++++--------- src/muz_qe/pdr_generalizers.h | 2 +- 2 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index f51df79a6..ed9e4d999 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -154,8 +154,6 @@ namespace pdr { m_sigma(m), m_trail(m), m_is_closure(is_closure) { - m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); - m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); } void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { @@ -182,29 +180,18 @@ namespace pdr { // void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { manager& pm = n.pt().get_pdr_manager(); - expr_ref_vector conv1(m), conv2(m), core1(m), core2(m), eqs(m); + expr_ref_vector conv1(m), conv2(m), core1(m), core2(m); unsigned orig_size = new_cores.size(); if (core.empty()) { new_cores.push_back(std::make_pair(core, uses_level)); return; } - add_variables(n, 2, eqs); + add_variables(n, 2, conv1); if (!mk_convex(core, 0, conv1)) { new_cores.push_back(std::make_pair(core, uses_level)); IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core), m) << "\n";); return; } - conv1.append(eqs); - if (m_is_closure) { - conv1.push_back(a.mk_ge(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); - conv1.push_back(a.mk_ge(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); - } - else { - // is interior: - conv1.push_back(a.mk_gt(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); - conv1.push_back(a.mk_gt(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); - } - conv1.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(m_sigma[0].get(), m_sigma[1].get()))); expr_ref fml = n.pt().get_formulas(n.level(), false); expr_ref_vector fmls(m); datalog::flatten_and(fml, fmls); @@ -281,10 +268,6 @@ namespace pdr { m_trail.push_back(goal); m_models.insert(goal, new_model); } - conv1.push_back(a.mk_gt(m_sigma[0].get(), a.mk_numeral(rational(0), a.mk_real()))); - conv1.push_back(a.mk_gt(m_sigma[1].get(), a.mk_numeral(rational(0), a.mk_real()))); - conv1.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(m_sigma[0].get(), m_sigma[1].get()))); - obj_map::iterator it = m_models.begin(), end = m_models.end(); for (; it != end; ++it) { if (it->m_key == goal) { @@ -342,21 +325,76 @@ namespace pdr { }); // now create the convex closure of the consequences: - expr_ref_vector conv(m); + expr_ref tmp(m), zero(m); + expr_ref_vector conv(m), es(m), enabled(m); + zero = a.mk_numeral(rational(0), a.mk_real()); + add_variables(n, consequences.size(), conv); for (unsigned i = 0; i < consequences.size(); ++i) { - if (m_sigma.size() == i) { - m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); + es.reset(); + tmp = m.mk_not(consequences[i].get()); + datalog::flatten_and(tmp, es); + if (!mk_convex(es, i, conv)) { + IF_VERBOSE(0, verbose_stream() << "Failed to create convex closure\n";); + return; } - conv.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); - ;; // mk_convex + es.reset(); + // + // enabled[i] = not (sigma_i = 0 and z_i1 = 0 and .. and z_im = 0) + // + es.push_back(m.mk_eq(m_sigma[i].get(), zero)); + for (unsigned j = 0; j < n.pt().sig_size(); ++j) { + func_decl* fn0 = n.pt().sig(j); + func_decl* fn1 = pm.o2n(fn0, 0); + expr* var; + VERIFY (m_vars[i].find(fn1, var)); + es.push_back(m.mk_eq(var, zero)); + } + + enabled.push_back(m.mk_not(m.mk_and(es.size(), es.c_ptr()))); } + + // the convex closure was created of all consequences. + // now determine a subset of enabled constraints. + smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); + for (unsigned i = 0; i < conv.size(); ++i) { + ctx.assert_expr(conv[i].get()); + } + for (unsigned i = 0; i < core.size(); ++i) { + ctx.assert_expr(core[i]); + } + vector transversal; + while (l_true == ctx.check()) { + model_ref md; + ctx.get_model(md); + expr_ref_vector lits(m); + unsigned_vector pos; + for (unsigned i = 0; i < consequences.size(); ++i) { + if (md->eval(enabled[i].get(), tmp, false)) { + if (m.is_true(tmp)) { + lits.push_back(tmp); + pos.push_back(i); + } + } + } + transversal.push_back(pos); + SASSERT(!lits.empty()); + tmp = m.mk_not(m.mk_and(lits.size(), lits.c_ptr())); + TRACE("pdr", tout << "add block: " << mk_pp(tmp, m) << "\n";); + ctx.assert_expr(tmp); + } + // + // we could no longer satisfy core using a partition. + // + } - void core_convex_hull_generalizer::add_variables(model_node& n, unsigned num_vars, expr_ref_vector& eqs) { + void core_convex_hull_generalizer::add_variables(model_node& n, unsigned num_vars, expr_ref_vector& fmls) { manager& pm = n.pt().get_pdr_manager(); - if (m_vars.size() < num_vars) { - m_vars.resize(num_vars); - } + while (m_vars.size() < num_vars) { + m_vars.resize(m_vars.size()+1); + m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); + } + if (!m_vars[0].contains(n.pt().head())) { expr_ref var(m); m_vars[0].insert(n.pt().head(), 0); @@ -384,8 +422,20 @@ namespace pdr { VERIFY (m_vars[j].find(fn1, var)); vars.push_back(var); } - eqs.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr()))); + fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr()))); } + if (m_is_closure) { + for (unsigned i = 0; i < num_vars; ++i) { + fmls.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); + } + } + else { + // is interior: + for (unsigned i = 0; i < num_vars; ++i) { + fmls.push_back(a.mk_gt(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); + } + } + fmls.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(num_vars, m_sigma.c_ptr()))); } expr_ref core_convex_hull_generalizer::mk_closure(expr* e) { diff --git a/src/muz_qe/pdr_generalizers.h b/src/muz_qe/pdr_generalizers.h index 9dcdf44c0..10aa5b978 100644 --- a/src/muz_qe/pdr_generalizers.h +++ b/src/muz_qe/pdr_generalizers.h @@ -90,7 +90,7 @@ namespace pdr { void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); void method2(model_node& n, expr_ref_vector& core, bool& uses_level); void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); - void add_variables(model_node& n, unsigned num_vars, expr_ref_vector& eqs); + void add_variables(model_node& n, unsigned num_vars, expr_ref_vector& fmls); public: core_convex_hull_generalizer(context& ctx, bool is_closure); virtual ~core_convex_hull_generalizer() {} From d795792304295ed08f37f57a2fa8529e9e3a782c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2013 08:48:18 -0700 Subject: [PATCH 125/509] add API for fixing tests Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_generalizers.cpp | 1 + src/muz_qe/rel_context.cpp | 5 +++++ src/muz_qe/rel_context.h | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index ed9e4d999..8e15fa983 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -364,6 +364,7 @@ namespace pdr { } vector transversal; while (l_true == ctx.check()) { + IF_VERBOSE(0, ctx.display(verbose_stream());); model_ref md; ctx.get_model(md); expr_ref_vector lits(m); diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index d597500b2..75f68c7f1 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -105,6 +105,11 @@ namespace datalog { } } + lbool rel_context::saturate() { + scoped_query sq(m_context); + return saturate(sq); + } + lbool rel_context::saturate(scoped_query& sq) { m_context.ensure_closed(); bool time_limit = m_context.soft_timeout()!=0; diff --git a/src/muz_qe/rel_context.h b/src/muz_qe/rel_context.h index 0b73caaa6..20da827ce 100644 --- a/src/muz_qe/rel_context.h +++ b/src/muz_qe/rel_context.h @@ -48,6 +48,8 @@ namespace datalog { void reset_tables(); + lbool saturate(scoped_query& sq); + public: rel_context(context& ctx); @@ -109,7 +111,7 @@ namespace datalog { void display_profile(std::ostream& out); - lbool saturate(scoped_query& sq); + lbool saturate(); }; }; From 137339a2e17697e715d0ec3909176c5c13f3ab02 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2013 12:08:47 -0700 Subject: [PATCH 126/509] split muz_qe into two directories Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 8 +- src/{muz_qe => muz}/README | 0 src/{muz_qe => muz}/aig_exporter.cpp | 0 src/{muz_qe => muz}/aig_exporter.h | 0 src/{muz_qe => muz}/arith_bounds_tactic.cpp | 0 src/{muz_qe => muz}/arith_bounds_tactic.h | 0 src/{muz_qe => muz}/clp_context.cpp | 0 src/{muz_qe => muz}/clp_context.h | 0 src/{muz_qe => muz}/datalog_parser.cpp | 0 src/{muz_qe => muz}/datalog_parser.h | 0 src/{muz_qe => muz}/dl_base.cpp | 0 src/{muz_qe => muz}/dl_base.h | 0 src/{muz_qe => muz}/dl_bmc_engine.cpp | 0 src/{muz_qe => muz}/dl_bmc_engine.h | 0 src/{muz_qe => muz}/dl_boogie_proof.cpp | 4 +- src/{muz_qe => muz}/dl_boogie_proof.h | 0 src/{muz_qe => muz}/dl_bound_relation.cpp | 0 src/{muz_qe => muz}/dl_bound_relation.h | 0 src/{muz_qe => muz}/dl_check_table.cpp | 0 src/{muz_qe => muz}/dl_check_table.h | 0 src/{muz_qe => muz}/dl_cmds.cpp | 0 src/{muz_qe => muz}/dl_cmds.h | 0 src/{muz_qe => muz}/dl_compiler.cpp | 0 src/{muz_qe => muz}/dl_compiler.h | 0 src/{muz_qe => muz}/dl_context.cpp | 12 +- src/{muz_qe => muz}/dl_context.h | 0 src/{muz_qe => muz}/dl_costs.cpp | 0 src/{muz_qe => muz}/dl_costs.h | 0 src/{muz_qe => muz}/dl_external_relation.cpp | 0 src/{muz_qe => muz}/dl_external_relation.h | 0 .../dl_finite_product_relation.cpp | 0 .../dl_finite_product_relation.h | 0 src/{muz_qe => muz}/dl_instruction.cpp | 0 src/{muz_qe => muz}/dl_instruction.h | 0 src/{muz_qe => muz}/dl_interval_relation.cpp | 0 src/{muz_qe => muz}/dl_interval_relation.h | 0 src/{muz_qe => muz}/dl_mk_array_blast.cpp | 6 +- src/{muz_qe => muz}/dl_mk_array_blast.h | 0 src/{muz_qe => muz}/dl_mk_backwards.cpp | 0 src/{muz_qe => muz}/dl_mk_backwards.h | 0 src/{muz_qe => muz}/dl_mk_bit_blast.cpp | 0 src/{muz_qe => muz}/dl_mk_bit_blast.h | 0 src/{muz_qe => muz}/dl_mk_coalesce.cpp | 0 src/{muz_qe => muz}/dl_mk_coalesce.h | 0 src/{muz_qe => muz}/dl_mk_coi_filter.cpp | 0 src/{muz_qe => muz}/dl_mk_coi_filter.h | 0 src/muz/dl_mk_different.h | 38 ++++++ src/{muz_qe => muz}/dl_mk_explanations.cpp | 0 src/{muz_qe => muz}/dl_mk_explanations.h | 0 src/{muz_qe => muz}/dl_mk_filter_rules.cpp | 0 src/{muz_qe => muz}/dl_mk_filter_rules.h | 0 .../dl_mk_interp_tail_simplifier.cpp | 2 +- .../dl_mk_interp_tail_simplifier.h | 0 src/{muz_qe => muz}/dl_mk_karr_invariants.cpp | 2 +- src/{muz_qe => muz}/dl_mk_karr_invariants.h | 0 src/{muz_qe => muz}/dl_mk_loop_counter.cpp | 0 src/{muz_qe => muz}/dl_mk_loop_counter.h | 0 src/{muz_qe => muz}/dl_mk_magic_sets.cpp | 0 src/{muz_qe => muz}/dl_mk_magic_sets.h | 0 src/{muz_qe => muz}/dl_mk_magic_symbolic.cpp | 0 src/{muz_qe => muz}/dl_mk_magic_symbolic.h | 0 src/{muz_qe => muz}/dl_mk_partial_equiv.cpp | 0 src/{muz_qe => muz}/dl_mk_partial_equiv.h | 0 .../dl_mk_quantifier_abstraction.cpp | 0 .../dl_mk_quantifier_abstraction.h | 0 .../dl_mk_quantifier_instantiation.cpp | 2 +- .../dl_mk_quantifier_instantiation.h | 0 src/{muz_qe => muz}/dl_mk_rule_inliner.cpp | 0 src/{muz_qe => muz}/dl_mk_rule_inliner.h | 0 src/{muz_qe => muz}/dl_mk_scale.cpp | 0 src/{muz_qe => muz}/dl_mk_scale.h | 0 .../dl_mk_similarity_compressor.cpp | 0 .../dl_mk_similarity_compressor.h | 0 src/{muz_qe => muz}/dl_mk_simple_joins.cpp | 0 src/{muz_qe => muz}/dl_mk_simple_joins.h | 0 src/{muz_qe => muz}/dl_mk_slice.cpp | 2 +- src/{muz_qe => muz}/dl_mk_slice.h | 0 .../dl_mk_subsumption_checker.cpp | 0 .../dl_mk_subsumption_checker.h | 0 .../dl_mk_unbound_compressor.cpp | 0 .../dl_mk_unbound_compressor.h | 0 src/{muz_qe => muz}/dl_mk_unfold.cpp | 0 src/{muz_qe => muz}/dl_mk_unfold.h | 0 src/{muz_qe => muz}/dl_product_relation.cpp | 0 src/{muz_qe => muz}/dl_product_relation.h | 0 src/{muz_qe => muz}/dl_relation_manager.cpp | 0 src/{muz_qe => muz}/dl_relation_manager.h | 0 src/{muz_qe => muz}/dl_rule.cpp | 4 +- src/{muz_qe => muz}/dl_rule.h | 0 src/{muz_qe => muz}/dl_rule_set.cpp | 0 src/{muz_qe => muz}/dl_rule_set.h | 0 .../dl_rule_subsumption_index.cpp | 0 .../dl_rule_subsumption_index.h | 0 src/{muz_qe => muz}/dl_rule_transformer.cpp | 0 src/{muz_qe => muz}/dl_rule_transformer.h | 0 src/{muz_qe => muz}/dl_sieve_relation.cpp | 0 src/{muz_qe => muz}/dl_sieve_relation.h | 0 src/{muz_qe => muz}/dl_sparse_table.cpp | 0 src/{muz_qe => muz}/dl_sparse_table.h | 0 src/{muz_qe => muz}/dl_table.cpp | 0 src/{muz_qe => muz}/dl_table.h | 0 src/{muz_qe => muz}/dl_table_plugin.h | 0 src/{muz_qe => muz}/dl_table_relation.cpp | 0 src/{muz_qe => muz}/dl_table_relation.h | 0 src/{muz_qe => muz}/dl_util.cpp | 112 ----------------- src/{muz_qe => muz}/dl_util.h | 13 +- src/{muz_qe => muz}/dl_vector_relation.h | 0 src/{muz_qe => muz}/equiv_proof_converter.cpp | 0 src/{muz_qe => muz}/equiv_proof_converter.h | 0 src/{muz_qe => muz}/fixedpoint_params.pyg | 0 src/{muz_qe => muz}/heap_trie.h | 0 src/{muz_qe => muz}/hilbert_basis.cpp | 0 src/{muz_qe => muz}/hilbert_basis.h | 0 src/{muz_qe => muz}/hnf.cpp | 2 +- src/{muz_qe => muz}/hnf.h | 0 .../horn_subsume_model_converter.cpp | 0 .../horn_subsume_model_converter.h | 0 src/{muz_qe => muz}/horn_tactic.cpp | 2 +- src/{muz_qe => muz}/horn_tactic.h | 0 src/{muz_qe => muz}/model2expr.cpp | 0 src/{muz_qe => muz}/model2expr.h | 0 src/{muz_qe => muz}/pdr_context.cpp | 11 +- src/{muz_qe => muz}/pdr_context.h | 0 src/{muz_qe => muz}/pdr_dl_interface.cpp | 0 src/{muz_qe => muz}/pdr_dl_interface.h | 0 src/{muz_qe => muz}/pdr_farkas_learner.cpp | 2 +- src/{muz_qe => muz}/pdr_farkas_learner.h | 0 src/{muz_qe => muz}/pdr_generalizers.cpp | 10 +- src/{muz_qe => muz}/pdr_generalizers.h | 0 src/{muz_qe => muz}/pdr_manager.cpp | 6 +- src/{muz_qe => muz}/pdr_manager.h | 0 src/{muz_qe => muz}/pdr_prop_solver.cpp | 2 +- src/{muz_qe => muz}/pdr_prop_solver.h | 0 src/{muz_qe => muz}/pdr_reachable_cache.cpp | 0 src/{muz_qe => muz}/pdr_reachable_cache.h | 0 .../pdr_smt_context_manager.cpp | 0 src/{muz_qe => muz}/pdr_smt_context_manager.h | 0 src/{muz_qe => muz}/pdr_sym_mux.cpp | 0 src/{muz_qe => muz}/pdr_sym_mux.h | 0 src/{muz_qe => muz}/pdr_util.cpp | 2 +- src/{muz_qe => muz}/pdr_util.h | 0 src/{muz_qe => muz}/proof_utils.cpp | 0 src/{muz_qe => muz}/proof_utils.h | 0 src/{muz_qe => muz}/rel_context.cpp | 0 src/{muz_qe => muz}/rel_context.h | 0 .../replace_proof_converter.cpp | 0 src/{muz_qe => muz}/replace_proof_converter.h | 0 src/{muz_qe => muz}/skip_list_base.h | 0 src/{muz_qe => muz}/tab_context.cpp | 6 +- src/{muz_qe => muz}/tab_context.h | 0 .../unit_subsumption_tactic.cpp | 0 src/{muz_qe => muz}/unit_subsumption_tactic.h | 0 src/{muz_qe => muz}/vsubst_tactic.cpp | 0 src/{muz_qe => muz}/vsubst_tactic.h | 0 src/{muz_qe => qe}/nlarith_util.cpp | 0 src/{muz_qe => qe}/nlarith_util.h | 0 src/{muz_qe => qe}/qe.cpp | 6 +- src/{muz_qe => qe}/qe.h | 0 src/{muz_qe => qe}/qe_arith_plugin.cpp | 0 src/{muz_qe => qe}/qe_array_plugin.cpp | 0 src/{muz_qe => qe}/qe_bool_plugin.cpp | 0 src/{muz_qe => qe}/qe_bv_plugin.cpp | 0 src/{muz_qe => qe}/qe_cmd.cpp | 0 src/{muz_qe => qe}/qe_cmd.h | 0 src/{muz_qe => qe}/qe_datatype_plugin.cpp | 0 src/{muz_qe => qe}/qe_dl_plugin.cpp | 0 src/{muz_qe => qe}/qe_lite.cpp | 9 +- src/{muz_qe => qe}/qe_lite.h | 0 src/{muz_qe => qe}/qe_sat_tactic.cpp | 0 src/{muz_qe => qe}/qe_sat_tactic.h | 0 src/{muz_qe => qe}/qe_tactic.cpp | 0 src/{muz_qe => qe}/qe_tactic.h | 0 src/qe/qe_util.cpp | 116 ++++++++++++++++++ src/qe/qe_util.h | 37 ++++++ 174 files changed, 242 insertions(+), 174 deletions(-) rename src/{muz_qe => muz}/README (100%) rename src/{muz_qe => muz}/aig_exporter.cpp (100%) mode change 100755 => 100644 rename src/{muz_qe => muz}/aig_exporter.h (100%) mode change 100755 => 100644 rename src/{muz_qe => muz}/arith_bounds_tactic.cpp (100%) rename src/{muz_qe => muz}/arith_bounds_tactic.h (100%) rename src/{muz_qe => muz}/clp_context.cpp (100%) rename src/{muz_qe => muz}/clp_context.h (100%) rename src/{muz_qe => muz}/datalog_parser.cpp (100%) rename src/{muz_qe => muz}/datalog_parser.h (100%) rename src/{muz_qe => muz}/dl_base.cpp (100%) rename src/{muz_qe => muz}/dl_base.h (100%) rename src/{muz_qe => muz}/dl_bmc_engine.cpp (100%) rename src/{muz_qe => muz}/dl_bmc_engine.h (100%) rename src/{muz_qe => muz}/dl_boogie_proof.cpp (99%) rename src/{muz_qe => muz}/dl_boogie_proof.h (100%) rename src/{muz_qe => muz}/dl_bound_relation.cpp (100%) rename src/{muz_qe => muz}/dl_bound_relation.h (100%) rename src/{muz_qe => muz}/dl_check_table.cpp (100%) rename src/{muz_qe => muz}/dl_check_table.h (100%) rename src/{muz_qe => muz}/dl_cmds.cpp (100%) rename src/{muz_qe => muz}/dl_cmds.h (100%) rename src/{muz_qe => muz}/dl_compiler.cpp (100%) rename src/{muz_qe => muz}/dl_compiler.h (100%) rename src/{muz_qe => muz}/dl_context.cpp (100%) rename src/{muz_qe => muz}/dl_context.h (100%) rename src/{muz_qe => muz}/dl_costs.cpp (100%) rename src/{muz_qe => muz}/dl_costs.h (100%) rename src/{muz_qe => muz}/dl_external_relation.cpp (100%) rename src/{muz_qe => muz}/dl_external_relation.h (100%) rename src/{muz_qe => muz}/dl_finite_product_relation.cpp (100%) rename src/{muz_qe => muz}/dl_finite_product_relation.h (100%) rename src/{muz_qe => muz}/dl_instruction.cpp (100%) rename src/{muz_qe => muz}/dl_instruction.h (100%) rename src/{muz_qe => muz}/dl_interval_relation.cpp (100%) rename src/{muz_qe => muz}/dl_interval_relation.h (100%) rename src/{muz_qe => muz}/dl_mk_array_blast.cpp (98%) rename src/{muz_qe => muz}/dl_mk_array_blast.h (100%) rename src/{muz_qe => muz}/dl_mk_backwards.cpp (100%) rename src/{muz_qe => muz}/dl_mk_backwards.h (100%) rename src/{muz_qe => muz}/dl_mk_bit_blast.cpp (100%) rename src/{muz_qe => muz}/dl_mk_bit_blast.h (100%) rename src/{muz_qe => muz}/dl_mk_coalesce.cpp (100%) rename src/{muz_qe => muz}/dl_mk_coalesce.h (100%) rename src/{muz_qe => muz}/dl_mk_coi_filter.cpp (100%) rename src/{muz_qe => muz}/dl_mk_coi_filter.h (100%) create mode 100644 src/muz/dl_mk_different.h rename src/{muz_qe => muz}/dl_mk_explanations.cpp (100%) rename src/{muz_qe => muz}/dl_mk_explanations.h (100%) rename src/{muz_qe => muz}/dl_mk_filter_rules.cpp (100%) rename src/{muz_qe => muz}/dl_mk_filter_rules.h (100%) rename src/{muz_qe => muz}/dl_mk_interp_tail_simplifier.cpp (99%) rename src/{muz_qe => muz}/dl_mk_interp_tail_simplifier.h (100%) rename src/{muz_qe => muz}/dl_mk_karr_invariants.cpp (99%) rename src/{muz_qe => muz}/dl_mk_karr_invariants.h (100%) rename src/{muz_qe => muz}/dl_mk_loop_counter.cpp (100%) rename src/{muz_qe => muz}/dl_mk_loop_counter.h (100%) rename src/{muz_qe => muz}/dl_mk_magic_sets.cpp (100%) rename src/{muz_qe => muz}/dl_mk_magic_sets.h (100%) rename src/{muz_qe => muz}/dl_mk_magic_symbolic.cpp (100%) rename src/{muz_qe => muz}/dl_mk_magic_symbolic.h (100%) rename src/{muz_qe => muz}/dl_mk_partial_equiv.cpp (100%) rename src/{muz_qe => muz}/dl_mk_partial_equiv.h (100%) rename src/{muz_qe => muz}/dl_mk_quantifier_abstraction.cpp (100%) rename src/{muz_qe => muz}/dl_mk_quantifier_abstraction.h (100%) rename src/{muz_qe => muz}/dl_mk_quantifier_instantiation.cpp (99%) rename src/{muz_qe => muz}/dl_mk_quantifier_instantiation.h (100%) rename src/{muz_qe => muz}/dl_mk_rule_inliner.cpp (100%) rename src/{muz_qe => muz}/dl_mk_rule_inliner.h (100%) rename src/{muz_qe => muz}/dl_mk_scale.cpp (100%) rename src/{muz_qe => muz}/dl_mk_scale.h (100%) rename src/{muz_qe => muz}/dl_mk_similarity_compressor.cpp (100%) rename src/{muz_qe => muz}/dl_mk_similarity_compressor.h (100%) rename src/{muz_qe => muz}/dl_mk_simple_joins.cpp (100%) rename src/{muz_qe => muz}/dl_mk_simple_joins.h (100%) rename src/{muz_qe => muz}/dl_mk_slice.cpp (99%) rename src/{muz_qe => muz}/dl_mk_slice.h (100%) rename src/{muz_qe => muz}/dl_mk_subsumption_checker.cpp (100%) rename src/{muz_qe => muz}/dl_mk_subsumption_checker.h (100%) rename src/{muz_qe => muz}/dl_mk_unbound_compressor.cpp (100%) rename src/{muz_qe => muz}/dl_mk_unbound_compressor.h (100%) rename src/{muz_qe => muz}/dl_mk_unfold.cpp (100%) rename src/{muz_qe => muz}/dl_mk_unfold.h (100%) rename src/{muz_qe => muz}/dl_product_relation.cpp (100%) rename src/{muz_qe => muz}/dl_product_relation.h (100%) rename src/{muz_qe => muz}/dl_relation_manager.cpp (100%) rename src/{muz_qe => muz}/dl_relation_manager.h (100%) rename src/{muz_qe => muz}/dl_rule.cpp (99%) rename src/{muz_qe => muz}/dl_rule.h (100%) rename src/{muz_qe => muz}/dl_rule_set.cpp (100%) rename src/{muz_qe => muz}/dl_rule_set.h (100%) rename src/{muz_qe => muz}/dl_rule_subsumption_index.cpp (100%) rename src/{muz_qe => muz}/dl_rule_subsumption_index.h (100%) rename src/{muz_qe => muz}/dl_rule_transformer.cpp (100%) rename src/{muz_qe => muz}/dl_rule_transformer.h (100%) rename src/{muz_qe => muz}/dl_sieve_relation.cpp (100%) rename src/{muz_qe => muz}/dl_sieve_relation.h (100%) rename src/{muz_qe => muz}/dl_sparse_table.cpp (100%) rename src/{muz_qe => muz}/dl_sparse_table.h (100%) rename src/{muz_qe => muz}/dl_table.cpp (100%) rename src/{muz_qe => muz}/dl_table.h (100%) rename src/{muz_qe => muz}/dl_table_plugin.h (100%) rename src/{muz_qe => muz}/dl_table_relation.cpp (100%) rename src/{muz_qe => muz}/dl_table_relation.h (100%) rename src/{muz_qe => muz}/dl_util.cpp (83%) rename src/{muz_qe => muz}/dl_util.h (98%) rename src/{muz_qe => muz}/dl_vector_relation.h (100%) rename src/{muz_qe => muz}/equiv_proof_converter.cpp (100%) rename src/{muz_qe => muz}/equiv_proof_converter.h (100%) rename src/{muz_qe => muz}/fixedpoint_params.pyg (100%) rename src/{muz_qe => muz}/heap_trie.h (100%) rename src/{muz_qe => muz}/hilbert_basis.cpp (100%) rename src/{muz_qe => muz}/hilbert_basis.h (100%) rename src/{muz_qe => muz}/hnf.cpp (99%) rename src/{muz_qe => muz}/hnf.h (100%) rename src/{muz_qe => muz}/horn_subsume_model_converter.cpp (100%) rename src/{muz_qe => muz}/horn_subsume_model_converter.h (100%) rename src/{muz_qe => muz}/horn_tactic.cpp (99%) rename src/{muz_qe => muz}/horn_tactic.h (100%) rename src/{muz_qe => muz}/model2expr.cpp (100%) rename src/{muz_qe => muz}/model2expr.h (100%) rename src/{muz_qe => muz}/pdr_context.cpp (99%) rename src/{muz_qe => muz}/pdr_context.h (100%) rename src/{muz_qe => muz}/pdr_dl_interface.cpp (100%) rename src/{muz_qe => muz}/pdr_dl_interface.h (100%) rename src/{muz_qe => muz}/pdr_farkas_learner.cpp (99%) rename src/{muz_qe => muz}/pdr_farkas_learner.h (100%) rename src/{muz_qe => muz}/pdr_generalizers.cpp (99%) rename src/{muz_qe => muz}/pdr_generalizers.h (100%) rename src/{muz_qe => muz}/pdr_manager.cpp (98%) rename src/{muz_qe => muz}/pdr_manager.h (100%) rename src/{muz_qe => muz}/pdr_prop_solver.cpp (99%) rename src/{muz_qe => muz}/pdr_prop_solver.h (100%) rename src/{muz_qe => muz}/pdr_reachable_cache.cpp (100%) rename src/{muz_qe => muz}/pdr_reachable_cache.h (100%) rename src/{muz_qe => muz}/pdr_smt_context_manager.cpp (100%) rename src/{muz_qe => muz}/pdr_smt_context_manager.h (100%) rename src/{muz_qe => muz}/pdr_sym_mux.cpp (100%) rename src/{muz_qe => muz}/pdr_sym_mux.h (100%) rename src/{muz_qe => muz}/pdr_util.cpp (99%) rename src/{muz_qe => muz}/pdr_util.h (100%) rename src/{muz_qe => muz}/proof_utils.cpp (100%) rename src/{muz_qe => muz}/proof_utils.h (100%) rename src/{muz_qe => muz}/rel_context.cpp (100%) rename src/{muz_qe => muz}/rel_context.h (100%) rename src/{muz_qe => muz}/replace_proof_converter.cpp (100%) rename src/{muz_qe => muz}/replace_proof_converter.h (100%) rename src/{muz_qe => muz}/skip_list_base.h (100%) rename src/{muz_qe => muz}/tab_context.cpp (99%) rename src/{muz_qe => muz}/tab_context.h (100%) rename src/{muz_qe => muz}/unit_subsumption_tactic.cpp (100%) rename src/{muz_qe => muz}/unit_subsumption_tactic.h (100%) rename src/{muz_qe => muz}/vsubst_tactic.cpp (100%) rename src/{muz_qe => muz}/vsubst_tactic.h (100%) rename src/{muz_qe => qe}/nlarith_util.cpp (100%) rename src/{muz_qe => qe}/nlarith_util.h (100%) rename src/{muz_qe => qe}/qe.cpp (99%) rename src/{muz_qe => qe}/qe.h (100%) rename src/{muz_qe => qe}/qe_arith_plugin.cpp (100%) rename src/{muz_qe => qe}/qe_array_plugin.cpp (100%) rename src/{muz_qe => qe}/qe_bool_plugin.cpp (100%) rename src/{muz_qe => qe}/qe_bv_plugin.cpp (100%) rename src/{muz_qe => qe}/qe_cmd.cpp (100%) rename src/{muz_qe => qe}/qe_cmd.h (100%) rename src/{muz_qe => qe}/qe_datatype_plugin.cpp (100%) rename src/{muz_qe => qe}/qe_dl_plugin.cpp (100%) rename src/{muz_qe => qe}/qe_lite.cpp (99%) rename src/{muz_qe => qe}/qe_lite.h (100%) rename src/{muz_qe => qe}/qe_sat_tactic.cpp (100%) rename src/{muz_qe => qe}/qe_sat_tactic.h (100%) rename src/{muz_qe => qe}/qe_tactic.cpp (100%) rename src/{muz_qe => qe}/qe_tactic.h (100%) create mode 100644 src/qe/qe_util.cpp create mode 100644 src/qe/qe_util.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index e6e7d5dc8..44ad9ccbc 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -53,11 +53,11 @@ def init_project_def(): add_lib('fpa', ['core_tactics', 'bv_tactics', 'sat_tactic'], 'tactic/fpa') add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') - # TODO: split muz_qe into muz, qe. Perhaps, we should also consider breaking muz into muz and pdr. - add_lib('muz_qe', ['smt', 'sat', 'smt2parser', 'aig_tactic']) - add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'muz_qe'], 'tactic/smtlogics') + add_lib('qe', ['smt','sat'], 'qe') + add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic','qe']) + add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'muz','qe'], 'tactic/smtlogics') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') - add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') + add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h'] add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure'], diff --git a/src/muz_qe/README b/src/muz/README similarity index 100% rename from src/muz_qe/README rename to src/muz/README diff --git a/src/muz_qe/aig_exporter.cpp b/src/muz/aig_exporter.cpp old mode 100755 new mode 100644 similarity index 100% rename from src/muz_qe/aig_exporter.cpp rename to src/muz/aig_exporter.cpp diff --git a/src/muz_qe/aig_exporter.h b/src/muz/aig_exporter.h old mode 100755 new mode 100644 similarity index 100% rename from src/muz_qe/aig_exporter.h rename to src/muz/aig_exporter.h diff --git a/src/muz_qe/arith_bounds_tactic.cpp b/src/muz/arith_bounds_tactic.cpp similarity index 100% rename from src/muz_qe/arith_bounds_tactic.cpp rename to src/muz/arith_bounds_tactic.cpp diff --git a/src/muz_qe/arith_bounds_tactic.h b/src/muz/arith_bounds_tactic.h similarity index 100% rename from src/muz_qe/arith_bounds_tactic.h rename to src/muz/arith_bounds_tactic.h diff --git a/src/muz_qe/clp_context.cpp b/src/muz/clp_context.cpp similarity index 100% rename from src/muz_qe/clp_context.cpp rename to src/muz/clp_context.cpp diff --git a/src/muz_qe/clp_context.h b/src/muz/clp_context.h similarity index 100% rename from src/muz_qe/clp_context.h rename to src/muz/clp_context.h diff --git a/src/muz_qe/datalog_parser.cpp b/src/muz/datalog_parser.cpp similarity index 100% rename from src/muz_qe/datalog_parser.cpp rename to src/muz/datalog_parser.cpp diff --git a/src/muz_qe/datalog_parser.h b/src/muz/datalog_parser.h similarity index 100% rename from src/muz_qe/datalog_parser.h rename to src/muz/datalog_parser.h diff --git a/src/muz_qe/dl_base.cpp b/src/muz/dl_base.cpp similarity index 100% rename from src/muz_qe/dl_base.cpp rename to src/muz/dl_base.cpp diff --git a/src/muz_qe/dl_base.h b/src/muz/dl_base.h similarity index 100% rename from src/muz_qe/dl_base.h rename to src/muz/dl_base.h diff --git a/src/muz_qe/dl_bmc_engine.cpp b/src/muz/dl_bmc_engine.cpp similarity index 100% rename from src/muz_qe/dl_bmc_engine.cpp rename to src/muz/dl_bmc_engine.cpp diff --git a/src/muz_qe/dl_bmc_engine.h b/src/muz/dl_bmc_engine.h similarity index 100% rename from src/muz_qe/dl_bmc_engine.h rename to src/muz/dl_bmc_engine.h diff --git a/src/muz_qe/dl_boogie_proof.cpp b/src/muz/dl_boogie_proof.cpp similarity index 99% rename from src/muz_qe/dl_boogie_proof.cpp rename to src/muz/dl_boogie_proof.cpp index e14512973..d11e4a932 100644 --- a/src/muz_qe/dl_boogie_proof.cpp +++ b/src/muz/dl_boogie_proof.cpp @@ -49,7 +49,7 @@ Example from Boogie: #include "model_pp.h" #include "proof_utils.h" #include "ast_pp.h" -#include "dl_util.h" +#include "qe_util.h" namespace datalog { @@ -91,7 +91,7 @@ namespace datalog { if (!m.is_implies(premise, l1, l2)) { continue; } - datalog::flatten_and(l1, literals); + qe::flatten_and(l1, literals); positions2.reset(); premises2.reset(); premises2.push_back(premise); diff --git a/src/muz_qe/dl_boogie_proof.h b/src/muz/dl_boogie_proof.h similarity index 100% rename from src/muz_qe/dl_boogie_proof.h rename to src/muz/dl_boogie_proof.h diff --git a/src/muz_qe/dl_bound_relation.cpp b/src/muz/dl_bound_relation.cpp similarity index 100% rename from src/muz_qe/dl_bound_relation.cpp rename to src/muz/dl_bound_relation.cpp diff --git a/src/muz_qe/dl_bound_relation.h b/src/muz/dl_bound_relation.h similarity index 100% rename from src/muz_qe/dl_bound_relation.h rename to src/muz/dl_bound_relation.h diff --git a/src/muz_qe/dl_check_table.cpp b/src/muz/dl_check_table.cpp similarity index 100% rename from src/muz_qe/dl_check_table.cpp rename to src/muz/dl_check_table.cpp diff --git a/src/muz_qe/dl_check_table.h b/src/muz/dl_check_table.h similarity index 100% rename from src/muz_qe/dl_check_table.h rename to src/muz/dl_check_table.h diff --git a/src/muz_qe/dl_cmds.cpp b/src/muz/dl_cmds.cpp similarity index 100% rename from src/muz_qe/dl_cmds.cpp rename to src/muz/dl_cmds.cpp diff --git a/src/muz_qe/dl_cmds.h b/src/muz/dl_cmds.h similarity index 100% rename from src/muz_qe/dl_cmds.h rename to src/muz/dl_cmds.h diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz/dl_compiler.cpp similarity index 100% rename from src/muz_qe/dl_compiler.cpp rename to src/muz/dl_compiler.cpp diff --git a/src/muz_qe/dl_compiler.h b/src/muz/dl_compiler.h similarity index 100% rename from src/muz_qe/dl_compiler.h rename to src/muz/dl_compiler.h diff --git a/src/muz_qe/dl_context.cpp b/src/muz/dl_context.cpp similarity index 100% rename from src/muz_qe/dl_context.cpp rename to src/muz/dl_context.cpp index 7da9808e7..f5bfb6b5e 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz/dl_context.cpp @@ -35,12 +35,6 @@ Revision History: #include"dl_mk_similarity_compressor.h" #include"dl_mk_unbound_compressor.h" #include"dl_mk_subsumption_checker.h" -#include"dl_compiler.h" -#include"dl_instruction.h" -#include"dl_context.h" -#include"for_each_expr.h" -#include"ast_smt_pp.h" -#include"ast_smt2_pp.h" #include"dl_mk_partial_equiv.h" #include"dl_mk_bit_blast.h" #include"dl_mk_array_blast.h" @@ -49,6 +43,12 @@ Revision History: #include"dl_mk_quantifier_abstraction.h" #include"dl_mk_quantifier_instantiation.h" #include"dl_mk_scale.h" +#include"dl_compiler.h" +#include"dl_instruction.h" +#include"dl_context.h" +#include"for_each_expr.h" +#include"ast_smt_pp.h" +#include"ast_smt2_pp.h" #include"datatype_decl_plugin.h" diff --git a/src/muz_qe/dl_context.h b/src/muz/dl_context.h similarity index 100% rename from src/muz_qe/dl_context.h rename to src/muz/dl_context.h diff --git a/src/muz_qe/dl_costs.cpp b/src/muz/dl_costs.cpp similarity index 100% rename from src/muz_qe/dl_costs.cpp rename to src/muz/dl_costs.cpp diff --git a/src/muz_qe/dl_costs.h b/src/muz/dl_costs.h similarity index 100% rename from src/muz_qe/dl_costs.h rename to src/muz/dl_costs.h diff --git a/src/muz_qe/dl_external_relation.cpp b/src/muz/dl_external_relation.cpp similarity index 100% rename from src/muz_qe/dl_external_relation.cpp rename to src/muz/dl_external_relation.cpp diff --git a/src/muz_qe/dl_external_relation.h b/src/muz/dl_external_relation.h similarity index 100% rename from src/muz_qe/dl_external_relation.h rename to src/muz/dl_external_relation.h diff --git a/src/muz_qe/dl_finite_product_relation.cpp b/src/muz/dl_finite_product_relation.cpp similarity index 100% rename from src/muz_qe/dl_finite_product_relation.cpp rename to src/muz/dl_finite_product_relation.cpp diff --git a/src/muz_qe/dl_finite_product_relation.h b/src/muz/dl_finite_product_relation.h similarity index 100% rename from src/muz_qe/dl_finite_product_relation.h rename to src/muz/dl_finite_product_relation.h diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz/dl_instruction.cpp similarity index 100% rename from src/muz_qe/dl_instruction.cpp rename to src/muz/dl_instruction.cpp diff --git a/src/muz_qe/dl_instruction.h b/src/muz/dl_instruction.h similarity index 100% rename from src/muz_qe/dl_instruction.h rename to src/muz/dl_instruction.h diff --git a/src/muz_qe/dl_interval_relation.cpp b/src/muz/dl_interval_relation.cpp similarity index 100% rename from src/muz_qe/dl_interval_relation.cpp rename to src/muz/dl_interval_relation.cpp diff --git a/src/muz_qe/dl_interval_relation.h b/src/muz/dl_interval_relation.h similarity index 100% rename from src/muz_qe/dl_interval_relation.h rename to src/muz/dl_interval_relation.h diff --git a/src/muz_qe/dl_mk_array_blast.cpp b/src/muz/dl_mk_array_blast.cpp similarity index 98% rename from src/muz_qe/dl_mk_array_blast.cpp rename to src/muz/dl_mk_array_blast.cpp index 2bfb6807a..776a2da5b 100644 --- a/src/muz_qe/dl_mk_array_blast.cpp +++ b/src/muz/dl_mk_array_blast.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "dl_mk_array_blast.h" - +#include "qe_util.h" namespace datalog { @@ -101,7 +101,7 @@ namespace datalog { bool mk_array_blast::ackermanize(rule const& r, expr_ref& body, expr_ref& head) { expr_ref_vector conjs(m); - flatten_and(body, conjs); + qe::flatten_and(body, conjs); m_defs.reset(); m_sub.reset(); m_next_var = 0; @@ -207,7 +207,7 @@ namespace datalog { for (unsigned i = utsz; i < tsz; ++i) { conjs.push_back(r.get_tail(i)); } - flatten_and(conjs); + qe::flatten_and(conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr* x, *y, *e = conjs[i].get(); diff --git a/src/muz_qe/dl_mk_array_blast.h b/src/muz/dl_mk_array_blast.h similarity index 100% rename from src/muz_qe/dl_mk_array_blast.h rename to src/muz/dl_mk_array_blast.h diff --git a/src/muz_qe/dl_mk_backwards.cpp b/src/muz/dl_mk_backwards.cpp similarity index 100% rename from src/muz_qe/dl_mk_backwards.cpp rename to src/muz/dl_mk_backwards.cpp diff --git a/src/muz_qe/dl_mk_backwards.h b/src/muz/dl_mk_backwards.h similarity index 100% rename from src/muz_qe/dl_mk_backwards.h rename to src/muz/dl_mk_backwards.h diff --git a/src/muz_qe/dl_mk_bit_blast.cpp b/src/muz/dl_mk_bit_blast.cpp similarity index 100% rename from src/muz_qe/dl_mk_bit_blast.cpp rename to src/muz/dl_mk_bit_blast.cpp diff --git a/src/muz_qe/dl_mk_bit_blast.h b/src/muz/dl_mk_bit_blast.h similarity index 100% rename from src/muz_qe/dl_mk_bit_blast.h rename to src/muz/dl_mk_bit_blast.h diff --git a/src/muz_qe/dl_mk_coalesce.cpp b/src/muz/dl_mk_coalesce.cpp similarity index 100% rename from src/muz_qe/dl_mk_coalesce.cpp rename to src/muz/dl_mk_coalesce.cpp diff --git a/src/muz_qe/dl_mk_coalesce.h b/src/muz/dl_mk_coalesce.h similarity index 100% rename from src/muz_qe/dl_mk_coalesce.h rename to src/muz/dl_mk_coalesce.h diff --git a/src/muz_qe/dl_mk_coi_filter.cpp b/src/muz/dl_mk_coi_filter.cpp similarity index 100% rename from src/muz_qe/dl_mk_coi_filter.cpp rename to src/muz/dl_mk_coi_filter.cpp diff --git a/src/muz_qe/dl_mk_coi_filter.h b/src/muz/dl_mk_coi_filter.h similarity index 100% rename from src/muz_qe/dl_mk_coi_filter.h rename to src/muz/dl_mk_coi_filter.h diff --git a/src/muz/dl_mk_different.h b/src/muz/dl_mk_different.h new file mode 100644 index 000000000..2def011f9 --- /dev/null +++ b/src/muz/dl_mk_different.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_different_symbolic.h + +Abstract: + + Create Horn clauses for different symbolic transformation. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-06-19 + +Revision History: + +--*/ +#ifndef _DL_MK_DIFFERENT_SYMBOLIC_H_ +#define _DL_MK_DIFFERENT_SYMBOLIC_H_ + +#include"dl_rule_transformer.h" + +namespace datalog { + + class mk_different_symbolic : public rule_transformer::plugin { + ast_manager& m; + context& m_ctx; + public: + mk_different_symbolic(context & ctx, unsigned priority = 33037); + ~mk_different_symbolic(); + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_DIFFERENT_SYMBOLIC_H_ */ + diff --git a/src/muz_qe/dl_mk_explanations.cpp b/src/muz/dl_mk_explanations.cpp similarity index 100% rename from src/muz_qe/dl_mk_explanations.cpp rename to src/muz/dl_mk_explanations.cpp diff --git a/src/muz_qe/dl_mk_explanations.h b/src/muz/dl_mk_explanations.h similarity index 100% rename from src/muz_qe/dl_mk_explanations.h rename to src/muz/dl_mk_explanations.h diff --git a/src/muz_qe/dl_mk_filter_rules.cpp b/src/muz/dl_mk_filter_rules.cpp similarity index 100% rename from src/muz_qe/dl_mk_filter_rules.cpp rename to src/muz/dl_mk_filter_rules.cpp diff --git a/src/muz_qe/dl_mk_filter_rules.h b/src/muz/dl_mk_filter_rules.h similarity index 100% rename from src/muz_qe/dl_mk_filter_rules.h rename to src/muz/dl_mk_filter_rules.h diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp b/src/muz/dl_mk_interp_tail_simplifier.cpp similarity index 99% rename from src/muz_qe/dl_mk_interp_tail_simplifier.cpp rename to src/muz/dl_mk_interp_tail_simplifier.cpp index afd586627..b01b4326c 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz/dl_mk_interp_tail_simplifier.cpp @@ -546,7 +546,7 @@ namespace datalog { if (modified) { m_conj.reset(); - flatten_and(simp_res, m_conj); + qe::flatten_and(simp_res, m_conj); for (unsigned i = 0; i < m_conj.size(); ++i) { expr* e = m_conj[i].get(); if (is_app(e)) { diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.h b/src/muz/dl_mk_interp_tail_simplifier.h similarity index 100% rename from src/muz_qe/dl_mk_interp_tail_simplifier.h rename to src/muz/dl_mk_interp_tail_simplifier.h diff --git a/src/muz_qe/dl_mk_karr_invariants.cpp b/src/muz/dl_mk_karr_invariants.cpp similarity index 99% rename from src/muz_qe/dl_mk_karr_invariants.cpp rename to src/muz/dl_mk_karr_invariants.cpp index e4e5a1ff8..11bc850bb 100644 --- a/src/muz_qe/dl_mk_karr_invariants.cpp +++ b/src/muz/dl_mk_karr_invariants.cpp @@ -430,7 +430,7 @@ namespace datalog { var* v, *w; rational n1, n2; expr_ref_vector conjs(m); - datalog::flatten_and(cond, conjs); + qe::flatten_and(cond, conjs); matrix& M = get_ineqs(); unsigned num_columns = get_signature().size(); diff --git a/src/muz_qe/dl_mk_karr_invariants.h b/src/muz/dl_mk_karr_invariants.h similarity index 100% rename from src/muz_qe/dl_mk_karr_invariants.h rename to src/muz/dl_mk_karr_invariants.h diff --git a/src/muz_qe/dl_mk_loop_counter.cpp b/src/muz/dl_mk_loop_counter.cpp similarity index 100% rename from src/muz_qe/dl_mk_loop_counter.cpp rename to src/muz/dl_mk_loop_counter.cpp diff --git a/src/muz_qe/dl_mk_loop_counter.h b/src/muz/dl_mk_loop_counter.h similarity index 100% rename from src/muz_qe/dl_mk_loop_counter.h rename to src/muz/dl_mk_loop_counter.h diff --git a/src/muz_qe/dl_mk_magic_sets.cpp b/src/muz/dl_mk_magic_sets.cpp similarity index 100% rename from src/muz_qe/dl_mk_magic_sets.cpp rename to src/muz/dl_mk_magic_sets.cpp diff --git a/src/muz_qe/dl_mk_magic_sets.h b/src/muz/dl_mk_magic_sets.h similarity index 100% rename from src/muz_qe/dl_mk_magic_sets.h rename to src/muz/dl_mk_magic_sets.h diff --git a/src/muz_qe/dl_mk_magic_symbolic.cpp b/src/muz/dl_mk_magic_symbolic.cpp similarity index 100% rename from src/muz_qe/dl_mk_magic_symbolic.cpp rename to src/muz/dl_mk_magic_symbolic.cpp diff --git a/src/muz_qe/dl_mk_magic_symbolic.h b/src/muz/dl_mk_magic_symbolic.h similarity index 100% rename from src/muz_qe/dl_mk_magic_symbolic.h rename to src/muz/dl_mk_magic_symbolic.h diff --git a/src/muz_qe/dl_mk_partial_equiv.cpp b/src/muz/dl_mk_partial_equiv.cpp similarity index 100% rename from src/muz_qe/dl_mk_partial_equiv.cpp rename to src/muz/dl_mk_partial_equiv.cpp diff --git a/src/muz_qe/dl_mk_partial_equiv.h b/src/muz/dl_mk_partial_equiv.h similarity index 100% rename from src/muz_qe/dl_mk_partial_equiv.h rename to src/muz/dl_mk_partial_equiv.h diff --git a/src/muz_qe/dl_mk_quantifier_abstraction.cpp b/src/muz/dl_mk_quantifier_abstraction.cpp similarity index 100% rename from src/muz_qe/dl_mk_quantifier_abstraction.cpp rename to src/muz/dl_mk_quantifier_abstraction.cpp diff --git a/src/muz_qe/dl_mk_quantifier_abstraction.h b/src/muz/dl_mk_quantifier_abstraction.h similarity index 100% rename from src/muz_qe/dl_mk_quantifier_abstraction.h rename to src/muz/dl_mk_quantifier_abstraction.h diff --git a/src/muz_qe/dl_mk_quantifier_instantiation.cpp b/src/muz/dl_mk_quantifier_instantiation.cpp similarity index 99% rename from src/muz_qe/dl_mk_quantifier_instantiation.cpp rename to src/muz/dl_mk_quantifier_instantiation.cpp index ddbddca01..b24e3e81c 100644 --- a/src/muz_qe/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/dl_mk_quantifier_instantiation.cpp @@ -48,7 +48,7 @@ namespace datalog { for (unsigned j = 0; j < tsz; ++j) { conjs.push_back(r.get_tail(j)); } - datalog::flatten_and(conjs); + qe::flatten_and(conjs); for (unsigned j = 0; j < conjs.size(); ++j) { expr* e = conjs[j].get(); quantifier* q; diff --git a/src/muz_qe/dl_mk_quantifier_instantiation.h b/src/muz/dl_mk_quantifier_instantiation.h similarity index 100% rename from src/muz_qe/dl_mk_quantifier_instantiation.h rename to src/muz/dl_mk_quantifier_instantiation.h diff --git a/src/muz_qe/dl_mk_rule_inliner.cpp b/src/muz/dl_mk_rule_inliner.cpp similarity index 100% rename from src/muz_qe/dl_mk_rule_inliner.cpp rename to src/muz/dl_mk_rule_inliner.cpp diff --git a/src/muz_qe/dl_mk_rule_inliner.h b/src/muz/dl_mk_rule_inliner.h similarity index 100% rename from src/muz_qe/dl_mk_rule_inliner.h rename to src/muz/dl_mk_rule_inliner.h diff --git a/src/muz_qe/dl_mk_scale.cpp b/src/muz/dl_mk_scale.cpp similarity index 100% rename from src/muz_qe/dl_mk_scale.cpp rename to src/muz/dl_mk_scale.cpp diff --git a/src/muz_qe/dl_mk_scale.h b/src/muz/dl_mk_scale.h similarity index 100% rename from src/muz_qe/dl_mk_scale.h rename to src/muz/dl_mk_scale.h diff --git a/src/muz_qe/dl_mk_similarity_compressor.cpp b/src/muz/dl_mk_similarity_compressor.cpp similarity index 100% rename from src/muz_qe/dl_mk_similarity_compressor.cpp rename to src/muz/dl_mk_similarity_compressor.cpp diff --git a/src/muz_qe/dl_mk_similarity_compressor.h b/src/muz/dl_mk_similarity_compressor.h similarity index 100% rename from src/muz_qe/dl_mk_similarity_compressor.h rename to src/muz/dl_mk_similarity_compressor.h diff --git a/src/muz_qe/dl_mk_simple_joins.cpp b/src/muz/dl_mk_simple_joins.cpp similarity index 100% rename from src/muz_qe/dl_mk_simple_joins.cpp rename to src/muz/dl_mk_simple_joins.cpp diff --git a/src/muz_qe/dl_mk_simple_joins.h b/src/muz/dl_mk_simple_joins.h similarity index 100% rename from src/muz_qe/dl_mk_simple_joins.h rename to src/muz/dl_mk_simple_joins.h diff --git a/src/muz_qe/dl_mk_slice.cpp b/src/muz/dl_mk_slice.cpp similarity index 99% rename from src/muz_qe/dl_mk_slice.cpp rename to src/muz/dl_mk_slice.cpp index 5b9d43acc..0df75324d 100644 --- a/src/muz_qe/dl_mk_slice.cpp +++ b/src/muz/dl_mk_slice.cpp @@ -619,7 +619,7 @@ namespace datalog { for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { conjs.push_back(r.get_tail(j)); } - datalog::flatten_and(conjs); + qe::flatten_and(conjs); return conjs; } diff --git a/src/muz_qe/dl_mk_slice.h b/src/muz/dl_mk_slice.h similarity index 100% rename from src/muz_qe/dl_mk_slice.h rename to src/muz/dl_mk_slice.h diff --git a/src/muz_qe/dl_mk_subsumption_checker.cpp b/src/muz/dl_mk_subsumption_checker.cpp similarity index 100% rename from src/muz_qe/dl_mk_subsumption_checker.cpp rename to src/muz/dl_mk_subsumption_checker.cpp diff --git a/src/muz_qe/dl_mk_subsumption_checker.h b/src/muz/dl_mk_subsumption_checker.h similarity index 100% rename from src/muz_qe/dl_mk_subsumption_checker.h rename to src/muz/dl_mk_subsumption_checker.h diff --git a/src/muz_qe/dl_mk_unbound_compressor.cpp b/src/muz/dl_mk_unbound_compressor.cpp similarity index 100% rename from src/muz_qe/dl_mk_unbound_compressor.cpp rename to src/muz/dl_mk_unbound_compressor.cpp diff --git a/src/muz_qe/dl_mk_unbound_compressor.h b/src/muz/dl_mk_unbound_compressor.h similarity index 100% rename from src/muz_qe/dl_mk_unbound_compressor.h rename to src/muz/dl_mk_unbound_compressor.h diff --git a/src/muz_qe/dl_mk_unfold.cpp b/src/muz/dl_mk_unfold.cpp similarity index 100% rename from src/muz_qe/dl_mk_unfold.cpp rename to src/muz/dl_mk_unfold.cpp diff --git a/src/muz_qe/dl_mk_unfold.h b/src/muz/dl_mk_unfold.h similarity index 100% rename from src/muz_qe/dl_mk_unfold.h rename to src/muz/dl_mk_unfold.h diff --git a/src/muz_qe/dl_product_relation.cpp b/src/muz/dl_product_relation.cpp similarity index 100% rename from src/muz_qe/dl_product_relation.cpp rename to src/muz/dl_product_relation.cpp diff --git a/src/muz_qe/dl_product_relation.h b/src/muz/dl_product_relation.h similarity index 100% rename from src/muz_qe/dl_product_relation.h rename to src/muz/dl_product_relation.h diff --git a/src/muz_qe/dl_relation_manager.cpp b/src/muz/dl_relation_manager.cpp similarity index 100% rename from src/muz_qe/dl_relation_manager.cpp rename to src/muz/dl_relation_manager.cpp diff --git a/src/muz_qe/dl_relation_manager.h b/src/muz/dl_relation_manager.h similarity index 100% rename from src/muz_qe/dl_relation_manager.h rename to src/muz/dl_relation_manager.h diff --git a/src/muz_qe/dl_rule.cpp b/src/muz/dl_rule.cpp similarity index 99% rename from src/muz_qe/dl_rule.cpp rename to src/muz/dl_rule.cpp index 455f2c244..5ac580cb8 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz/dl_rule.cpp @@ -263,7 +263,7 @@ namespace datalog { if (m.is_implies(fml, e1, e2)) { expr_ref_vector es(m); head = ensure_app(e2); - datalog::flatten_and(e1, es); + qe::flatten_and(e1, es); for (unsigned i = 0; i < es.size(); ++i) { body.push_back(ensure_app(es[i].get())); } @@ -380,7 +380,7 @@ namespace datalog { for (unsigned i = 0; i < body.size(); ++i) { r.push_back(body[i].get()); } - flatten_and(r); + qe::flatten_and(r); body.reset(); for (unsigned i = 0; i < r.size(); ++i) { body.push_back(ensure_app(r[i].get())); diff --git a/src/muz_qe/dl_rule.h b/src/muz/dl_rule.h similarity index 100% rename from src/muz_qe/dl_rule.h rename to src/muz/dl_rule.h diff --git a/src/muz_qe/dl_rule_set.cpp b/src/muz/dl_rule_set.cpp similarity index 100% rename from src/muz_qe/dl_rule_set.cpp rename to src/muz/dl_rule_set.cpp diff --git a/src/muz_qe/dl_rule_set.h b/src/muz/dl_rule_set.h similarity index 100% rename from src/muz_qe/dl_rule_set.h rename to src/muz/dl_rule_set.h diff --git a/src/muz_qe/dl_rule_subsumption_index.cpp b/src/muz/dl_rule_subsumption_index.cpp similarity index 100% rename from src/muz_qe/dl_rule_subsumption_index.cpp rename to src/muz/dl_rule_subsumption_index.cpp diff --git a/src/muz_qe/dl_rule_subsumption_index.h b/src/muz/dl_rule_subsumption_index.h similarity index 100% rename from src/muz_qe/dl_rule_subsumption_index.h rename to src/muz/dl_rule_subsumption_index.h diff --git a/src/muz_qe/dl_rule_transformer.cpp b/src/muz/dl_rule_transformer.cpp similarity index 100% rename from src/muz_qe/dl_rule_transformer.cpp rename to src/muz/dl_rule_transformer.cpp diff --git a/src/muz_qe/dl_rule_transformer.h b/src/muz/dl_rule_transformer.h similarity index 100% rename from src/muz_qe/dl_rule_transformer.h rename to src/muz/dl_rule_transformer.h diff --git a/src/muz_qe/dl_sieve_relation.cpp b/src/muz/dl_sieve_relation.cpp similarity index 100% rename from src/muz_qe/dl_sieve_relation.cpp rename to src/muz/dl_sieve_relation.cpp diff --git a/src/muz_qe/dl_sieve_relation.h b/src/muz/dl_sieve_relation.h similarity index 100% rename from src/muz_qe/dl_sieve_relation.h rename to src/muz/dl_sieve_relation.h diff --git a/src/muz_qe/dl_sparse_table.cpp b/src/muz/dl_sparse_table.cpp similarity index 100% rename from src/muz_qe/dl_sparse_table.cpp rename to src/muz/dl_sparse_table.cpp diff --git a/src/muz_qe/dl_sparse_table.h b/src/muz/dl_sparse_table.h similarity index 100% rename from src/muz_qe/dl_sparse_table.h rename to src/muz/dl_sparse_table.h diff --git a/src/muz_qe/dl_table.cpp b/src/muz/dl_table.cpp similarity index 100% rename from src/muz_qe/dl_table.cpp rename to src/muz/dl_table.cpp diff --git a/src/muz_qe/dl_table.h b/src/muz/dl_table.h similarity index 100% rename from src/muz_qe/dl_table.h rename to src/muz/dl_table.h diff --git a/src/muz_qe/dl_table_plugin.h b/src/muz/dl_table_plugin.h similarity index 100% rename from src/muz_qe/dl_table_plugin.h rename to src/muz/dl_table_plugin.h diff --git a/src/muz_qe/dl_table_relation.cpp b/src/muz/dl_table_relation.cpp similarity index 100% rename from src/muz_qe/dl_table_relation.cpp rename to src/muz/dl_table_relation.cpp diff --git a/src/muz_qe/dl_table_relation.h b/src/muz/dl_table_relation.h similarity index 100% rename from src/muz_qe/dl_table_relation.h rename to src/muz/dl_table_relation.h diff --git a/src/muz_qe/dl_util.cpp b/src/muz/dl_util.cpp similarity index 83% rename from src/muz_qe/dl_util.cpp rename to src/muz/dl_util.cpp index 1b1042345..0b88136e2 100644 --- a/src/muz_qe/dl_util.cpp +++ b/src/muz/dl_util.cpp @@ -40,118 +40,6 @@ namespace datalog { ptr->deallocate(); } - void flatten_and(expr_ref_vector& result) { - ast_manager& m = result.get_manager(); - expr* e1, *e2, *e3; - for (unsigned i = 0; i < result.size(); ++i) { - if (m.is_and(result[i].get())) { - app* a = to_app(result[i].get()); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(a->get_arg(j)); - } - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { - result[i] = e2; - --i; - } - else if (m.is_not(result[i].get(), e1) && m.is_or(e1)) { - app* a = to_app(e1); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(m.mk_not(a->get_arg(j))); - } - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_not(result[i].get(), e1) && m.is_implies(e1,e2,e3)) { - result.push_back(e2); - result[i] = m.mk_not(e3); - --i; - } - else if (m.is_true(result[i].get()) || - (m.is_not(result[i].get(), e1) && - m.is_false(e1))) { - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_false(result[i].get()) || - (m.is_not(result[i].get(), e1) && - m.is_true(e1))) { - result.reset(); - result.push_back(m.mk_false()); - return; - } - } - } - - void flatten_and(expr* fml, expr_ref_vector& result) { - SASSERT(result.get_manager().is_bool(fml)); - result.push_back(fml); - flatten_and(result); - } - - void flatten_or(expr_ref_vector& result) { - ast_manager& m = result.get_manager(); - expr* e1, *e2, *e3; - for (unsigned i = 0; i < result.size(); ++i) { - if (m.is_or(result[i].get())) { - app* a = to_app(result[i].get()); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(a->get_arg(j)); - } - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { - result[i] = e2; - --i; - } - else if (m.is_not(result[i].get(), e1) && m.is_and(e1)) { - app* a = to_app(e1); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(m.mk_not(a->get_arg(j))); - } - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_implies(result[i].get(),e2,e3)) { - result.push_back(e3); - result[i] = m.mk_not(e2); - --i; - } - else if (m.is_false(result[i].get()) || - (m.is_not(result[i].get(), e1) && - m.is_true(e1))) { - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_true(result[i].get()) || - (m.is_not(result[i].get(), e1) && - m.is_false(e1))) { - result.reset(); - result.push_back(m.mk_true()); - return; - } - } - } - - - void flatten_or(expr* fml, expr_ref_vector& result) { - SASSERT(result.get_manager().is_bool(fml)); - result.push_back(fml); - flatten_or(result); - } bool contains_var(expr * trm, unsigned var_idx) { ptr_vector vars; diff --git a/src/muz_qe/dl_util.h b/src/muz/dl_util.h similarity index 98% rename from src/muz_qe/dl_util.h rename to src/muz/dl_util.h index b92a33d1a..debb4d3e2 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz/dl_util.h @@ -30,6 +30,7 @@ Revision History: #include"ast_counter.h" #include"statistics.h" #include"lbool.h" +#include"qe_util.h" namespace datalog { @@ -107,18 +108,6 @@ namespace datalog { typedef u_map varidx2var_map; typedef obj_hashtable func_decl_set; //!< Rule dependencies. typedef vector string_vector; - - - /** - \brief Collect top-level conjunctions and disjunctions. - */ - void flatten_and(expr_ref_vector& result); - - void flatten_and(expr* fml, expr_ref_vector& result); - - void flatten_or(expr_ref_vector& result); - - void flatten_or(expr* fml, expr_ref_vector& result); bool contains_var(expr * trm, unsigned var_idx); diff --git a/src/muz_qe/dl_vector_relation.h b/src/muz/dl_vector_relation.h similarity index 100% rename from src/muz_qe/dl_vector_relation.h rename to src/muz/dl_vector_relation.h diff --git a/src/muz_qe/equiv_proof_converter.cpp b/src/muz/equiv_proof_converter.cpp similarity index 100% rename from src/muz_qe/equiv_proof_converter.cpp rename to src/muz/equiv_proof_converter.cpp diff --git a/src/muz_qe/equiv_proof_converter.h b/src/muz/equiv_proof_converter.h similarity index 100% rename from src/muz_qe/equiv_proof_converter.h rename to src/muz/equiv_proof_converter.h diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz/fixedpoint_params.pyg similarity index 100% rename from src/muz_qe/fixedpoint_params.pyg rename to src/muz/fixedpoint_params.pyg diff --git a/src/muz_qe/heap_trie.h b/src/muz/heap_trie.h similarity index 100% rename from src/muz_qe/heap_trie.h rename to src/muz/heap_trie.h diff --git a/src/muz_qe/hilbert_basis.cpp b/src/muz/hilbert_basis.cpp similarity index 100% rename from src/muz_qe/hilbert_basis.cpp rename to src/muz/hilbert_basis.cpp diff --git a/src/muz_qe/hilbert_basis.h b/src/muz/hilbert_basis.h similarity index 100% rename from src/muz_qe/hilbert_basis.h rename to src/muz/hilbert_basis.h diff --git a/src/muz_qe/hnf.cpp b/src/muz/hnf.cpp similarity index 99% rename from src/muz_qe/hnf.cpp rename to src/muz/hnf.cpp index 36316cfa6..9d6f3c1ab 100644 --- a/src/muz_qe/hnf.cpp +++ b/src/muz/hnf.cpp @@ -214,7 +214,7 @@ private: m_body.push_back(e1); head = e2; } - datalog::flatten_and(m_body); + qe::flatten_and(m_body); if (premise) { p = m.mk_rewrite(fml0, mk_implies(m_body, head)); } diff --git a/src/muz_qe/hnf.h b/src/muz/hnf.h similarity index 100% rename from src/muz_qe/hnf.h rename to src/muz/hnf.h diff --git a/src/muz_qe/horn_subsume_model_converter.cpp b/src/muz/horn_subsume_model_converter.cpp similarity index 100% rename from src/muz_qe/horn_subsume_model_converter.cpp rename to src/muz/horn_subsume_model_converter.cpp diff --git a/src/muz_qe/horn_subsume_model_converter.h b/src/muz/horn_subsume_model_converter.h similarity index 100% rename from src/muz_qe/horn_subsume_model_converter.h rename to src/muz/horn_subsume_model_converter.h diff --git a/src/muz_qe/horn_tactic.cpp b/src/muz/horn_tactic.cpp similarity index 99% rename from src/muz_qe/horn_tactic.cpp rename to src/muz/horn_tactic.cpp index 1916839f4..07a0e2568 100644 --- a/src/muz_qe/horn_tactic.cpp +++ b/src/muz/horn_tactic.cpp @@ -141,7 +141,7 @@ class horn_tactic : public tactic { expr_ref_vector args(m), body(m); expr_ref head(m); expr* a = 0, *a1 = 0; - datalog::flatten_or(tmp, args); + qe::flatten_or(tmp, args); for (unsigned i = 0; i < args.size(); ++i) { a = args[i].get(); check_predicate(mark, a); diff --git a/src/muz_qe/horn_tactic.h b/src/muz/horn_tactic.h similarity index 100% rename from src/muz_qe/horn_tactic.h rename to src/muz/horn_tactic.h diff --git a/src/muz_qe/model2expr.cpp b/src/muz/model2expr.cpp similarity index 100% rename from src/muz_qe/model2expr.cpp rename to src/muz/model2expr.cpp diff --git a/src/muz_qe/model2expr.h b/src/muz/model2expr.h similarity index 100% rename from src/muz_qe/model2expr.h rename to src/muz/model2expr.h diff --git a/src/muz_qe/pdr_context.cpp b/src/muz/pdr_context.cpp similarity index 99% rename from src/muz_qe/pdr_context.cpp rename to src/muz/pdr_context.cpp index ff40be4f3..db85bc5a9 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz/pdr_context.cpp @@ -45,6 +45,7 @@ Notes: #include "smt_value_sort.h" #include "proof_utils.h" #include "dl_boogie_proof.h" +#include "qe_util.h" namespace pdr { @@ -342,7 +343,7 @@ namespace pdr { void pred_transformer::add_property(expr* lemma, unsigned lvl) { expr_ref_vector lemmas(m); - datalog::flatten_and(lemma, lemmas); + qe::flatten_and(lemma, lemmas); for (unsigned i = 0; i < lemmas.size(); ++i) { expr* lemma_i = lemmas[i].get(); if (add_property1(lemma_i, lvl)) { @@ -587,7 +588,7 @@ namespace pdr { for (unsigned i = ut_size; i < t_size; ++i) { tail.push_back(rule.get_tail(i)); } - datalog::flatten_and(tail); + qe::flatten_and(tail); for (unsigned i = 0; i < tail.size(); ++i) { expr_ref tmp(m); var_subst(m, false)(tail[i].get(), var_reprs.size(), (expr*const*)var_reprs.c_ptr(), tmp); @@ -778,7 +779,7 @@ namespace pdr { ast_manager& m = pt().get_manager(); expr_ref_vector conjs(m); obj_map model; - datalog::flatten_and(state(), conjs); + qe::flatten_and(state(), conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(), *e1, *e2; if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) { @@ -1979,7 +1980,7 @@ namespace pdr { expr_ref_vector mdl(m), forms(m), Phi(m); forms.push_back(T); forms.push_back(phi); - datalog::flatten_and(forms); + qe::flatten_and(forms); ptr_vector forms1(forms.size(), forms.c_ptr()); if (use_model_generalizer) { Phi.append(mev.minimize_model(forms1, M)); @@ -2035,7 +2036,7 @@ namespace pdr { TRACE("pdr", tout << "Projected:\n" << mk_pp(phi1, m) << "\n";); } Phi.reset(); - datalog::flatten_and(phi1, Phi); + qe::flatten_and(phi1, Phi); unsigned_vector indices; vector child_states; child_states.resize(preds.size(), expr_ref_vector(m)); diff --git a/src/muz_qe/pdr_context.h b/src/muz/pdr_context.h similarity index 100% rename from src/muz_qe/pdr_context.h rename to src/muz/pdr_context.h diff --git a/src/muz_qe/pdr_dl_interface.cpp b/src/muz/pdr_dl_interface.cpp similarity index 100% rename from src/muz_qe/pdr_dl_interface.cpp rename to src/muz/pdr_dl_interface.cpp diff --git a/src/muz_qe/pdr_dl_interface.h b/src/muz/pdr_dl_interface.h similarity index 100% rename from src/muz_qe/pdr_dl_interface.h rename to src/muz/pdr_dl_interface.h diff --git a/src/muz_qe/pdr_farkas_learner.cpp b/src/muz/pdr_farkas_learner.cpp similarity index 99% rename from src/muz_qe/pdr_farkas_learner.cpp rename to src/muz/pdr_farkas_learner.cpp index 8bc77ecc6..6202c913d 100644 --- a/src/muz_qe/pdr_farkas_learner.cpp +++ b/src/muz/pdr_farkas_learner.cpp @@ -320,7 +320,7 @@ namespace pdr { expr_set bs; expr_ref_vector blist(m_pr); - datalog::flatten_and(B, blist); + qe::flatten_and(B, blist); for (unsigned i = 0; i < blist.size(); ++i) { bs.insert(blist[i].get()); } diff --git a/src/muz_qe/pdr_farkas_learner.h b/src/muz/pdr_farkas_learner.h similarity index 100% rename from src/muz_qe/pdr_farkas_learner.h rename to src/muz/pdr_farkas_learner.h diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz/pdr_generalizers.cpp similarity index 99% rename from src/muz_qe/pdr_generalizers.cpp rename to src/muz/pdr_generalizers.cpp index 8e15fa983..93a1c1844 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz/pdr_generalizers.cpp @@ -137,7 +137,7 @@ namespace pdr { C = pm.mk_or(Bs); TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";); core.reset(); - datalog::flatten_and(C, core); + qe::flatten_and(C, core); uses_level = true; } } @@ -157,7 +157,7 @@ namespace pdr { } void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - // method3(n, core, uses_level, new_cores); + method3(n, core, uses_level, new_cores); method1(n, core, uses_level, new_cores); } @@ -194,11 +194,11 @@ namespace pdr { } expr_ref fml = n.pt().get_formulas(n.level(), false); expr_ref_vector fmls(m); - datalog::flatten_and(fml, fmls); + qe::flatten_and(fml, fmls); for (unsigned i = 0; i < fmls.size(); ++i) { fml = m.mk_not(fmls[i].get()); core2.reset(); - datalog::flatten_and(fml, core2); + qe::flatten_and(fml, core2); if (!mk_convex(core2, 1, conv2)) { IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core2), m) << "\n";); continue; @@ -332,7 +332,7 @@ namespace pdr { for (unsigned i = 0; i < consequences.size(); ++i) { es.reset(); tmp = m.mk_not(consequences[i].get()); - datalog::flatten_and(tmp, es); + qe::flatten_and(tmp, es); if (!mk_convex(es, i, conv)) { IF_VERBOSE(0, verbose_stream() << "Failed to create convex closure\n";); return; diff --git a/src/muz_qe/pdr_generalizers.h b/src/muz/pdr_generalizers.h similarity index 100% rename from src/muz_qe/pdr_generalizers.h rename to src/muz/pdr_generalizers.h diff --git a/src/muz_qe/pdr_manager.cpp b/src/muz/pdr_manager.cpp similarity index 98% rename from src/muz_qe/pdr_manager.cpp rename to src/muz/pdr_manager.cpp index 3e79e4f00..9eb10aba9 100644 --- a/src/muz_qe/pdr_manager.cpp +++ b/src/muz/pdr_manager.cpp @@ -56,7 +56,7 @@ namespace pdr { expr_ref inductive_property::fixup_clause(expr* fml) const { expr_ref_vector disjs(m); - datalog::flatten_or(fml, disjs); + qe::flatten_or(fml, disjs); expr_ref result(m); bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), result); return result; @@ -65,7 +65,7 @@ namespace pdr { expr_ref inductive_property::fixup_clauses(expr* fml) const { expr_ref_vector conjs(m); expr_ref result(m); - datalog::flatten_and(fml, conjs); + qe::flatten_and(fml, conjs); for (unsigned i = 0; i < conjs.size(); ++i) { conjs[i] = fixup_clause(conjs[i].get()); } @@ -238,7 +238,7 @@ namespace pdr { expr_ref manager::mk_not_and(expr_ref_vector const& conjs) { expr_ref result(m), e(m); expr_ref_vector es(conjs); - datalog::flatten_and(es); + qe::flatten_and(es); for (unsigned i = 0; i < es.size(); ++i) { m_brwr.mk_not(es[i].get(), e); es[i] = e; diff --git a/src/muz_qe/pdr_manager.h b/src/muz/pdr_manager.h similarity index 100% rename from src/muz_qe/pdr_manager.h rename to src/muz/pdr_manager.h diff --git a/src/muz_qe/pdr_prop_solver.cpp b/src/muz/pdr_prop_solver.cpp similarity index 99% rename from src/muz_qe/pdr_prop_solver.cpp rename to src/muz/pdr_prop_solver.cpp index eb3c0ee96..d9b22e04e 100644 --- a/src/muz_qe/pdr_prop_solver.cpp +++ b/src/muz/pdr_prop_solver.cpp @@ -76,7 +76,7 @@ namespace pdr { } void mk_safe(expr_ref_vector& conjs) { - datalog::flatten_and(conjs); + qe::flatten_and(conjs); expand_literals(conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr * lit = conjs[i].get(); diff --git a/src/muz_qe/pdr_prop_solver.h b/src/muz/pdr_prop_solver.h similarity index 100% rename from src/muz_qe/pdr_prop_solver.h rename to src/muz/pdr_prop_solver.h diff --git a/src/muz_qe/pdr_reachable_cache.cpp b/src/muz/pdr_reachable_cache.cpp similarity index 100% rename from src/muz_qe/pdr_reachable_cache.cpp rename to src/muz/pdr_reachable_cache.cpp diff --git a/src/muz_qe/pdr_reachable_cache.h b/src/muz/pdr_reachable_cache.h similarity index 100% rename from src/muz_qe/pdr_reachable_cache.h rename to src/muz/pdr_reachable_cache.h diff --git a/src/muz_qe/pdr_smt_context_manager.cpp b/src/muz/pdr_smt_context_manager.cpp similarity index 100% rename from src/muz_qe/pdr_smt_context_manager.cpp rename to src/muz/pdr_smt_context_manager.cpp diff --git a/src/muz_qe/pdr_smt_context_manager.h b/src/muz/pdr_smt_context_manager.h similarity index 100% rename from src/muz_qe/pdr_smt_context_manager.h rename to src/muz/pdr_smt_context_manager.h diff --git a/src/muz_qe/pdr_sym_mux.cpp b/src/muz/pdr_sym_mux.cpp similarity index 100% rename from src/muz_qe/pdr_sym_mux.cpp rename to src/muz/pdr_sym_mux.cpp diff --git a/src/muz_qe/pdr_sym_mux.h b/src/muz/pdr_sym_mux.h similarity index 100% rename from src/muz_qe/pdr_sym_mux.h rename to src/muz/pdr_sym_mux.h diff --git a/src/muz_qe/pdr_util.cpp b/src/muz/pdr_util.cpp similarity index 99% rename from src/muz_qe/pdr_util.cpp rename to src/muz/pdr_util.cpp index 3d93f8104..d23dc186e 100644 --- a/src/muz_qe/pdr_util.cpp +++ b/src/muz/pdr_util.cpp @@ -954,7 +954,7 @@ namespace pdr { void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); - datalog::flatten_and(fml, conjs); + qe::flatten_and(fml, conjs); obj_map diseqs; expr* n, *lhs, *rhs; for (unsigned i = 0; i < conjs.size(); ++i) { diff --git a/src/muz_qe/pdr_util.h b/src/muz/pdr_util.h similarity index 100% rename from src/muz_qe/pdr_util.h rename to src/muz/pdr_util.h diff --git a/src/muz_qe/proof_utils.cpp b/src/muz/proof_utils.cpp similarity index 100% rename from src/muz_qe/proof_utils.cpp rename to src/muz/proof_utils.cpp diff --git a/src/muz_qe/proof_utils.h b/src/muz/proof_utils.h similarity index 100% rename from src/muz_qe/proof_utils.h rename to src/muz/proof_utils.h diff --git a/src/muz_qe/rel_context.cpp b/src/muz/rel_context.cpp similarity index 100% rename from src/muz_qe/rel_context.cpp rename to src/muz/rel_context.cpp diff --git a/src/muz_qe/rel_context.h b/src/muz/rel_context.h similarity index 100% rename from src/muz_qe/rel_context.h rename to src/muz/rel_context.h diff --git a/src/muz_qe/replace_proof_converter.cpp b/src/muz/replace_proof_converter.cpp similarity index 100% rename from src/muz_qe/replace_proof_converter.cpp rename to src/muz/replace_proof_converter.cpp diff --git a/src/muz_qe/replace_proof_converter.h b/src/muz/replace_proof_converter.h similarity index 100% rename from src/muz_qe/replace_proof_converter.h rename to src/muz/replace_proof_converter.h diff --git a/src/muz_qe/skip_list_base.h b/src/muz/skip_list_base.h similarity index 100% rename from src/muz_qe/skip_list_base.h rename to src/muz/skip_list_base.h diff --git a/src/muz_qe/tab_context.cpp b/src/muz/tab_context.cpp similarity index 99% rename from src/muz_qe/tab_context.cpp rename to src/muz/tab_context.cpp index 4f4c14cc7..a56d16a0e 100644 --- a/src/muz_qe/tab_context.cpp +++ b/src/muz/tab_context.cpp @@ -208,7 +208,7 @@ namespace tb { fmls.push_back(m_predicates[i]); } fmls.push_back(m_constraint); - datalog::flatten_and(fmls); + qe::flatten_and(fmls); bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), fml); return fml; } @@ -337,7 +337,7 @@ namespace tb { expr_ref tmp(m); substitution subst(m); subst.reserve(1, get_num_vars()); - datalog::flatten_and(m_constraint, fmls); + qe::flatten_and(m_constraint, fmls); unsigned num_fmls = fmls.size(); for (unsigned i = 0; i < num_fmls; ++i) { if (get_subst(rw, subst, i, fmls)) { @@ -664,7 +664,7 @@ namespace tb { m_qe(m_empty_set, false, fmls); - datalog::flatten_and(fmls); + qe::flatten_and(fmls); for (unsigned i = 0; i < fmls.size(); ++i) { expr_ref n = normalize(fmls[i].get()); if (m_sat_lits.contains(n)) { diff --git a/src/muz_qe/tab_context.h b/src/muz/tab_context.h similarity index 100% rename from src/muz_qe/tab_context.h rename to src/muz/tab_context.h diff --git a/src/muz_qe/unit_subsumption_tactic.cpp b/src/muz/unit_subsumption_tactic.cpp similarity index 100% rename from src/muz_qe/unit_subsumption_tactic.cpp rename to src/muz/unit_subsumption_tactic.cpp diff --git a/src/muz_qe/unit_subsumption_tactic.h b/src/muz/unit_subsumption_tactic.h similarity index 100% rename from src/muz_qe/unit_subsumption_tactic.h rename to src/muz/unit_subsumption_tactic.h diff --git a/src/muz_qe/vsubst_tactic.cpp b/src/muz/vsubst_tactic.cpp similarity index 100% rename from src/muz_qe/vsubst_tactic.cpp rename to src/muz/vsubst_tactic.cpp diff --git a/src/muz_qe/vsubst_tactic.h b/src/muz/vsubst_tactic.h similarity index 100% rename from src/muz_qe/vsubst_tactic.h rename to src/muz/vsubst_tactic.h diff --git a/src/muz_qe/nlarith_util.cpp b/src/qe/nlarith_util.cpp similarity index 100% rename from src/muz_qe/nlarith_util.cpp rename to src/qe/nlarith_util.cpp diff --git a/src/muz_qe/nlarith_util.h b/src/qe/nlarith_util.h similarity index 100% rename from src/muz_qe/nlarith_util.h rename to src/qe/nlarith_util.h diff --git a/src/muz_qe/qe.cpp b/src/qe/qe.cpp similarity index 99% rename from src/muz_qe/qe.cpp rename to src/qe/qe.cpp index 894a935b5..d0aafb896 100644 --- a/src/muz_qe/qe.cpp +++ b/src/qe/qe.cpp @@ -36,7 +36,7 @@ Revision History: #include "expr_functors.h" #include "quant_hoist.h" #include "bool_rewriter.h" -#include "dl_util.h" +#include "qe_util.h" #include "th_rewriter.h" #include "smt_kernel.h" #include "model_evaluator.h" @@ -81,7 +81,7 @@ namespace qe { ptr_vector todo; ptr_vector conjs_closed, conjs_mixed, conjs_open; - datalog::flatten_and(fml, conjs); + qe::flatten_and(fml, conjs); for (unsigned i = 0; i < conjs.size(); ++i) { todo.push_back(conjs[i].get()); @@ -306,7 +306,7 @@ namespace qe { // conj_enum conj_enum::conj_enum(ast_manager& m, expr* e): m(m), m_conjs(m) { - datalog::flatten_and(e, m_conjs); + qe::flatten_and(e, m_conjs); } void conj_enum::extract_equalities(expr_ref_vector& eqs) { diff --git a/src/muz_qe/qe.h b/src/qe/qe.h similarity index 100% rename from src/muz_qe/qe.h rename to src/qe/qe.h diff --git a/src/muz_qe/qe_arith_plugin.cpp b/src/qe/qe_arith_plugin.cpp similarity index 100% rename from src/muz_qe/qe_arith_plugin.cpp rename to src/qe/qe_arith_plugin.cpp diff --git a/src/muz_qe/qe_array_plugin.cpp b/src/qe/qe_array_plugin.cpp similarity index 100% rename from src/muz_qe/qe_array_plugin.cpp rename to src/qe/qe_array_plugin.cpp diff --git a/src/muz_qe/qe_bool_plugin.cpp b/src/qe/qe_bool_plugin.cpp similarity index 100% rename from src/muz_qe/qe_bool_plugin.cpp rename to src/qe/qe_bool_plugin.cpp diff --git a/src/muz_qe/qe_bv_plugin.cpp b/src/qe/qe_bv_plugin.cpp similarity index 100% rename from src/muz_qe/qe_bv_plugin.cpp rename to src/qe/qe_bv_plugin.cpp diff --git a/src/muz_qe/qe_cmd.cpp b/src/qe/qe_cmd.cpp similarity index 100% rename from src/muz_qe/qe_cmd.cpp rename to src/qe/qe_cmd.cpp diff --git a/src/muz_qe/qe_cmd.h b/src/qe/qe_cmd.h similarity index 100% rename from src/muz_qe/qe_cmd.h rename to src/qe/qe_cmd.h diff --git a/src/muz_qe/qe_datatype_plugin.cpp b/src/qe/qe_datatype_plugin.cpp similarity index 100% rename from src/muz_qe/qe_datatype_plugin.cpp rename to src/qe/qe_datatype_plugin.cpp diff --git a/src/muz_qe/qe_dl_plugin.cpp b/src/qe/qe_dl_plugin.cpp similarity index 100% rename from src/muz_qe/qe_dl_plugin.cpp rename to src/qe/qe_dl_plugin.cpp diff --git a/src/muz_qe/qe_lite.cpp b/src/qe/qe_lite.cpp similarity index 99% rename from src/muz_qe/qe_lite.cpp rename to src/qe/qe_lite.cpp index 01537b574..130673527 100644 --- a/src/muz_qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -30,9 +30,8 @@ Revision History: #include "bool_rewriter.h" #include "var_subst.h" #include "uint_set.h" -#include "dl_util.h" +#include "qe_util.h" #include "th_rewriter.h" -#include "dl_util.h" #include "for_each_expr.h" #include "expr_safe_replace.h" #include "cooperate.h" @@ -696,7 +695,7 @@ namespace eq { m_subst(r, m_subst_map.size(), m_subst_map.c_ptr(), new_r); m_rewriter(new_r); conjs.reset(); - datalog::flatten_and(new_r, conjs); + qe::flatten_and(new_r, conjs); reduced = true; } } @@ -2387,7 +2386,7 @@ public: void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml) { expr_ref_vector disjs(m); - datalog::flatten_or(fml, disjs); + qe::flatten_or(fml, disjs); for (unsigned i = 0; i < disjs.size(); ++i) { expr_ref_vector conjs(m); conjs.push_back(disjs[i].get()); @@ -2400,7 +2399,7 @@ public: void operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& fmls) { - datalog::flatten_and(fmls); + qe::flatten_and(fmls); unsigned index; if (has_unique_non_ground(fmls, index)) { expr_ref fml(m); diff --git a/src/muz_qe/qe_lite.h b/src/qe/qe_lite.h similarity index 100% rename from src/muz_qe/qe_lite.h rename to src/qe/qe_lite.h diff --git a/src/muz_qe/qe_sat_tactic.cpp b/src/qe/qe_sat_tactic.cpp similarity index 100% rename from src/muz_qe/qe_sat_tactic.cpp rename to src/qe/qe_sat_tactic.cpp diff --git a/src/muz_qe/qe_sat_tactic.h b/src/qe/qe_sat_tactic.h similarity index 100% rename from src/muz_qe/qe_sat_tactic.h rename to src/qe/qe_sat_tactic.h diff --git a/src/muz_qe/qe_tactic.cpp b/src/qe/qe_tactic.cpp similarity index 100% rename from src/muz_qe/qe_tactic.cpp rename to src/qe/qe_tactic.cpp diff --git a/src/muz_qe/qe_tactic.h b/src/qe/qe_tactic.h similarity index 100% rename from src/muz_qe/qe_tactic.h rename to src/qe/qe_tactic.h diff --git a/src/qe/qe_util.cpp b/src/qe/qe_util.cpp new file mode 100644 index 000000000..629fe4b56 --- /dev/null +++ b/src/qe/qe_util.cpp @@ -0,0 +1,116 @@ +#include "qe_util.h" + +namespace qe { + void flatten_and(expr_ref_vector& result) { + ast_manager& m = result.get_manager(); + expr* e1, *e2, *e3; + for (unsigned i = 0; i < result.size(); ++i) { + if (m.is_and(result[i].get())) { + app* a = to_app(result[i].get()); + unsigned num_args = a->get_num_args(); + for (unsigned j = 0; j < num_args; ++j) { + result.push_back(a->get_arg(j)); + } + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { + result[i] = e2; + --i; + } + else if (m.is_not(result[i].get(), e1) && m.is_or(e1)) { + app* a = to_app(e1); + unsigned num_args = a->get_num_args(); + for (unsigned j = 0; j < num_args; ++j) { + result.push_back(m.mk_not(a->get_arg(j))); + } + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_not(result[i].get(), e1) && m.is_implies(e1,e2,e3)) { + result.push_back(e2); + result[i] = m.mk_not(e3); + --i; + } + else if (m.is_true(result[i].get()) || + (m.is_not(result[i].get(), e1) && + m.is_false(e1))) { + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_false(result[i].get()) || + (m.is_not(result[i].get(), e1) && + m.is_true(e1))) { + result.reset(); + result.push_back(m.mk_false()); + return; + } + } + } + + void flatten_and(expr* fml, expr_ref_vector& result) { + SASSERT(result.get_manager().is_bool(fml)); + result.push_back(fml); + flatten_and(result); + } + + void flatten_or(expr_ref_vector& result) { + ast_manager& m = result.get_manager(); + expr* e1, *e2, *e3; + for (unsigned i = 0; i < result.size(); ++i) { + if (m.is_or(result[i].get())) { + app* a = to_app(result[i].get()); + unsigned num_args = a->get_num_args(); + for (unsigned j = 0; j < num_args; ++j) { + result.push_back(a->get_arg(j)); + } + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { + result[i] = e2; + --i; + } + else if (m.is_not(result[i].get(), e1) && m.is_and(e1)) { + app* a = to_app(e1); + unsigned num_args = a->get_num_args(); + for (unsigned j = 0; j < num_args; ++j) { + result.push_back(m.mk_not(a->get_arg(j))); + } + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_implies(result[i].get(),e2,e3)) { + result.push_back(e3); + result[i] = m.mk_not(e2); + --i; + } + else if (m.is_false(result[i].get()) || + (m.is_not(result[i].get(), e1) && + m.is_true(e1))) { + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_true(result[i].get()) || + (m.is_not(result[i].get(), e1) && + m.is_false(e1))) { + result.reset(); + result.push_back(m.mk_true()); + return; + } + } + } + + + void flatten_or(expr* fml, expr_ref_vector& result) { + SASSERT(result.get_manager().is_bool(fml)); + result.push_back(fml); + flatten_or(result); + } +} diff --git a/src/qe/qe_util.h b/src/qe/qe_util.h new file mode 100644 index 000000000..7e1fe7f79 --- /dev/null +++ b/src/qe/qe_util.h @@ -0,0 +1,37 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + qe_util.h + +Abstract: + + Utilities for quantifiers. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-08-28 + +Revision History: + +--*/ +#ifndef _QE_UTIL_H_ +#define _QE_UTIL_H_ + +#include "ast.h" + +namespace qe { + /** + \brief Collect top-level conjunctions and disjunctions. + */ + void flatten_and(expr_ref_vector& result); + + void flatten_and(expr* fml, expr_ref_vector& result); + + void flatten_or(expr_ref_vector& result); + + void flatten_or(expr* fml, expr_ref_vector& result); + +} +#endif From 4597872be803dfd54062f2268ab15a311f4ba96a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2013 12:18:02 -0700 Subject: [PATCH 127/509] fix reset regression with mk_convex: Signed-off-by: Nikolaj Bjorner --- src/muz/pdr_generalizers.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/muz/pdr_generalizers.cpp b/src/muz/pdr_generalizers.cpp index 93a1c1844..883429315 100644 --- a/src/muz/pdr_generalizers.cpp +++ b/src/muz/pdr_generalizers.cpp @@ -157,7 +157,7 @@ namespace pdr { } void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - method3(n, core, uses_level, new_cores); + // method3(n, core, uses_level, new_cores); method1(n, core, uses_level, new_cores); } @@ -180,18 +180,19 @@ namespace pdr { // void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { manager& pm = n.pt().get_pdr_manager(); - expr_ref_vector conv1(m), conv2(m), core1(m), core2(m); + expr_ref_vector conv1(m), conv2(m), core1(m), core2(m), fmls(m); unsigned orig_size = new_cores.size(); if (core.empty()) { new_cores.push_back(std::make_pair(core, uses_level)); return; } - add_variables(n, 2, conv1); + add_variables(n, 2, fmls); if (!mk_convex(core, 0, conv1)) { new_cores.push_back(std::make_pair(core, uses_level)); IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core), m) << "\n";); return; } + conv1.append(fmls); expr_ref fml = n.pt().get_formulas(n.level(), false); expr_ref_vector fmls(m); qe::flatten_and(fml, fmls); From 0d56499e2d96db8963f513e3a871c202b29ed70d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2013 21:20:24 -0700 Subject: [PATCH 128/509] re-organize muz_qe into separate units Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 13 +- src/api/api_datalog.cpp | 8 +- src/muz/{ => bmc}/dl_bmc_engine.cpp | 5 +- src/muz/{ => bmc}/dl_bmc_engine.h | 0 src/muz/{ => clp}/clp_context.cpp | 4 +- src/muz/{ => clp}/clp_context.h | 2 +- src/muz/dl_base.cpp | 459 ---- src/muz/dl_base.h | 1261 --------- src/muz/dl_bound_relation.cpp | 707 ----- src/muz/dl_bound_relation.h | 176 -- src/muz/dl_check_table.cpp | 439 --- src/muz/dl_check_table.h | 135 - src/muz/dl_cmds.h | 37 - src/muz/dl_context.cpp | 128 +- src/muz/dl_context.h | 100 +- src/muz/dl_engine_base.h | 82 + src/muz/dl_external_relation.cpp | 456 ---- src/muz/dl_external_relation.h | 154 -- src/muz/dl_finite_product_relation.cpp | 2377 ----------------- src/muz/dl_finite_product_relation.h | 366 --- src/muz/dl_interval_relation.cpp | 652 ----- src/muz/dl_interval_relation.h | 140 - src/muz/dl_mk_explanations.cpp | 880 ------ src/muz/dl_mk_explanations.h | 86 - src/muz/dl_mk_karr_invariants.cpp | 1114 -------- src/muz/dl_mk_karr_invariants.h | 138 - src/muz/dl_mk_partial_equiv.cpp | 154 -- src/muz/dl_mk_partial_equiv.h | 50 - src/muz/dl_mk_similarity_compressor.cpp | 545 ---- src/muz/dl_mk_similarity_compressor.h | 78 - src/muz/dl_mk_simple_joins.cpp | 741 ----- src/muz/dl_mk_simple_joins.h | 63 - src/muz/dl_product_relation.cpp | 1117 -------- src/muz/dl_product_relation.h | 190 -- src/muz/dl_relation_manager.cpp | 1702 ------------ src/muz/dl_relation_manager.h | 688 ----- src/muz/dl_sieve_relation.cpp | 666 ----- src/muz/dl_sieve_relation.h | 197 -- src/muz/dl_sparse_table.cpp | 1246 --------- src/muz/dl_sparse_table.h | 480 ---- src/muz/dl_table.cpp | 772 ------ src/muz/dl_table.h | 265 -- src/muz/dl_table_plugin.h | 193 -- src/muz/dl_table_relation.cpp | 490 ---- src/muz/dl_table_relation.h | 133 - src/muz/dl_util.cpp | 27 - src/muz/dl_util.h | 59 - src/muz/dl_vector_relation.h | 407 --- src/muz/{ => fp}/dl_cmds.cpp | 4 +- src/muz/fp/dl_register_engine.cpp | 51 + src/muz/fp/dl_register_engine.h | 36 + src/muz/{ => fp}/horn_tactic.cpp | 7 +- src/muz/{ => fp}/horn_tactic.h | 0 src/muz/{ => pdr}/pdr_context.cpp | 0 src/muz/{ => pdr}/pdr_context.h | 1 - src/muz/{ => pdr}/pdr_dl_interface.cpp | 4 +- src/muz/{ => pdr}/pdr_dl_interface.h | 1 + src/muz/{ => pdr}/pdr_farkas_learner.cpp | 0 src/muz/{ => pdr}/pdr_farkas_learner.h | 0 src/muz/{ => pdr}/pdr_generalizers.cpp | 2 +- src/muz/{ => pdr}/pdr_generalizers.h | 0 src/muz/{ => pdr}/pdr_manager.cpp | 0 src/muz/{ => pdr}/pdr_manager.h | 0 src/muz/{ => pdr}/pdr_prop_solver.cpp | 0 src/muz/{ => pdr}/pdr_prop_solver.h | 0 src/muz/{ => pdr}/pdr_reachable_cache.cpp | 0 src/muz/{ => pdr}/pdr_reachable_cache.h | 0 src/muz/{ => pdr}/pdr_smt_context_manager.cpp | 0 src/muz/{ => pdr}/pdr_smt_context_manager.h | 0 src/muz/{ => pdr}/pdr_sym_mux.cpp | 0 src/muz/{ => pdr}/pdr_sym_mux.h | 0 src/muz/{ => pdr}/pdr_util.cpp | 0 src/muz/{ => pdr}/pdr_util.h | 0 src/muz/{ => rel}/aig_exporter.cpp | 0 src/muz/{ => rel}/aig_exporter.h | 2 +- src/muz/{ => rel}/dl_compiler.cpp | 1 + src/muz/{ => rel}/dl_compiler.h | 0 src/muz/{ => rel}/dl_instruction.cpp | 11 +- src/muz/{ => rel}/dl_instruction.h | 9 +- src/muz/{ => rel}/rel_context.cpp | 64 +- src/muz/{ => rel}/rel_context.h | 53 +- src/muz/{ => tab}/tab_context.cpp | 0 src/muz/{ => tab}/tab_context.h | 2 +- .../{ => transforms}/dl_mk_array_blast.cpp | 0 src/muz/{ => transforms}/dl_mk_array_blast.h | 0 src/muz/{ => transforms}/dl_mk_backwards.cpp | 0 src/muz/{ => transforms}/dl_mk_backwards.h | 0 src/muz/{ => transforms}/dl_mk_bit_blast.cpp | 0 src/muz/{ => transforms}/dl_mk_bit_blast.h | 0 src/muz/{ => transforms}/dl_mk_coalesce.cpp | 0 src/muz/{ => transforms}/dl_mk_coalesce.h | 0 src/muz/{ => transforms}/dl_mk_coi_filter.cpp | 18 +- src/muz/{ => transforms}/dl_mk_coi_filter.h | 0 src/muz/{ => transforms}/dl_mk_different.h | 0 .../{ => transforms}/dl_mk_filter_rules.cpp | 0 src/muz/{ => transforms}/dl_mk_filter_rules.h | 0 .../dl_mk_interp_tail_simplifier.cpp | 0 .../dl_mk_interp_tail_simplifier.h | 0 src/muz/transforms/dl_mk_karr_invariants.cpp | 325 +++ src/muz/transforms/dl_mk_karr_invariants.h | 81 + .../{ => transforms}/dl_mk_loop_counter.cpp | 0 src/muz/{ => transforms}/dl_mk_loop_counter.h | 0 src/muz/{ => transforms}/dl_mk_magic_sets.cpp | 2 +- src/muz/{ => transforms}/dl_mk_magic_sets.h | 0 .../{ => transforms}/dl_mk_magic_symbolic.cpp | 0 .../{ => transforms}/dl_mk_magic_symbolic.h | 0 .../dl_mk_quantifier_abstraction.cpp | 0 .../dl_mk_quantifier_abstraction.h | 0 .../dl_mk_quantifier_instantiation.cpp | 0 .../dl_mk_quantifier_instantiation.h | 0 .../{ => transforms}/dl_mk_rule_inliner.cpp | 7 +- src/muz/{ => transforms}/dl_mk_rule_inliner.h | 6 +- src/muz/{ => transforms}/dl_mk_scale.cpp | 0 src/muz/{ => transforms}/dl_mk_scale.h | 0 src/muz/{ => transforms}/dl_mk_slice.cpp | 0 src/muz/{ => transforms}/dl_mk_slice.h | 0 .../dl_mk_subsumption_checker.cpp | 18 +- .../dl_mk_subsumption_checker.h | 0 .../dl_mk_unbound_compressor.cpp | 4 +- .../dl_mk_unbound_compressor.h | 2 +- src/muz/{ => transforms}/dl_mk_unfold.cpp | 0 src/muz/{ => transforms}/dl_mk_unfold.h | 0 src/muz/transforms/dl_transforms.cpp | 81 + src/muz/transforms/dl_transforms.h | 30 + src/shell/datalog_frontend.cpp | 9 +- src/test/datalog_parser.cpp | 8 +- src/test/dl_context.cpp | 8 +- src/test/dl_product_relation.cpp | 10 +- src/test/dl_query.cpp | 11 +- src/test/dl_relation.cpp | 8 +- src/test/dl_table.cpp | 5 +- 131 files changed, 994 insertions(+), 20069 deletions(-) rename src/muz/{ => bmc}/dl_bmc_engine.cpp (99%) rename src/muz/{ => bmc}/dl_bmc_engine.h (100%) rename src/muz/{ => clp}/clp_context.cpp (98%) rename src/muz/{ => clp}/clp_context.h (96%) delete mode 100644 src/muz/dl_base.cpp delete mode 100644 src/muz/dl_base.h delete mode 100644 src/muz/dl_bound_relation.cpp delete mode 100644 src/muz/dl_bound_relation.h delete mode 100644 src/muz/dl_check_table.cpp delete mode 100644 src/muz/dl_check_table.h delete mode 100644 src/muz/dl_cmds.h create mode 100644 src/muz/dl_engine_base.h delete mode 100644 src/muz/dl_external_relation.cpp delete mode 100644 src/muz/dl_external_relation.h delete mode 100644 src/muz/dl_finite_product_relation.cpp delete mode 100644 src/muz/dl_finite_product_relation.h delete mode 100644 src/muz/dl_interval_relation.cpp delete mode 100644 src/muz/dl_interval_relation.h delete mode 100644 src/muz/dl_mk_explanations.cpp delete mode 100644 src/muz/dl_mk_explanations.h delete mode 100644 src/muz/dl_mk_karr_invariants.cpp delete mode 100644 src/muz/dl_mk_karr_invariants.h delete mode 100644 src/muz/dl_mk_partial_equiv.cpp delete mode 100644 src/muz/dl_mk_partial_equiv.h delete mode 100644 src/muz/dl_mk_similarity_compressor.cpp delete mode 100644 src/muz/dl_mk_similarity_compressor.h delete mode 100644 src/muz/dl_mk_simple_joins.cpp delete mode 100644 src/muz/dl_mk_simple_joins.h delete mode 100644 src/muz/dl_product_relation.cpp delete mode 100644 src/muz/dl_product_relation.h delete mode 100644 src/muz/dl_relation_manager.cpp delete mode 100644 src/muz/dl_relation_manager.h delete mode 100644 src/muz/dl_sieve_relation.cpp delete mode 100644 src/muz/dl_sieve_relation.h delete mode 100644 src/muz/dl_sparse_table.cpp delete mode 100644 src/muz/dl_sparse_table.h delete mode 100644 src/muz/dl_table.cpp delete mode 100644 src/muz/dl_table.h delete mode 100644 src/muz/dl_table_plugin.h delete mode 100644 src/muz/dl_table_relation.cpp delete mode 100644 src/muz/dl_table_relation.h delete mode 100644 src/muz/dl_vector_relation.h rename src/muz/{ => fp}/dl_cmds.cpp (98%) create mode 100644 src/muz/fp/dl_register_engine.cpp create mode 100644 src/muz/fp/dl_register_engine.h rename src/muz/{ => fp}/horn_tactic.cpp (98%) rename src/muz/{ => fp}/horn_tactic.h (100%) rename src/muz/{ => pdr}/pdr_context.cpp (100%) rename src/muz/{ => pdr}/pdr_context.h (99%) rename src/muz/{ => pdr}/pdr_dl_interface.cpp (98%) rename src/muz/{ => pdr}/pdr_dl_interface.h (98%) rename src/muz/{ => pdr}/pdr_farkas_learner.cpp (100%) rename src/muz/{ => pdr}/pdr_farkas_learner.h (100%) rename src/muz/{ => pdr}/pdr_generalizers.cpp (99%) rename src/muz/{ => pdr}/pdr_generalizers.h (100%) rename src/muz/{ => pdr}/pdr_manager.cpp (100%) rename src/muz/{ => pdr}/pdr_manager.h (100%) rename src/muz/{ => pdr}/pdr_prop_solver.cpp (100%) rename src/muz/{ => pdr}/pdr_prop_solver.h (100%) rename src/muz/{ => pdr}/pdr_reachable_cache.cpp (100%) rename src/muz/{ => pdr}/pdr_reachable_cache.h (100%) rename src/muz/{ => pdr}/pdr_smt_context_manager.cpp (100%) rename src/muz/{ => pdr}/pdr_smt_context_manager.h (100%) rename src/muz/{ => pdr}/pdr_sym_mux.cpp (100%) rename src/muz/{ => pdr}/pdr_sym_mux.h (100%) rename src/muz/{ => pdr}/pdr_util.cpp (100%) rename src/muz/{ => pdr}/pdr_util.h (100%) rename src/muz/{ => rel}/aig_exporter.cpp (100%) rename src/muz/{ => rel}/aig_exporter.h (100%) rename src/muz/{ => rel}/dl_compiler.cpp (99%) rename src/muz/{ => rel}/dl_compiler.h (100%) rename src/muz/{ => rel}/dl_instruction.cpp (98%) rename src/muz/{ => rel}/dl_instruction.h (96%) rename src/muz/{ => rel}/rel_context.cpp (89%) rename src/muz/{ => rel}/rel_context.h (55%) rename src/muz/{ => tab}/tab_context.cpp (100%) rename src/muz/{ => tab}/tab_context.h (96%) rename src/muz/{ => transforms}/dl_mk_array_blast.cpp (100%) rename src/muz/{ => transforms}/dl_mk_array_blast.h (100%) rename src/muz/{ => transforms}/dl_mk_backwards.cpp (100%) rename src/muz/{ => transforms}/dl_mk_backwards.h (100%) rename src/muz/{ => transforms}/dl_mk_bit_blast.cpp (100%) rename src/muz/{ => transforms}/dl_mk_bit_blast.h (100%) rename src/muz/{ => transforms}/dl_mk_coalesce.cpp (100%) rename src/muz/{ => transforms}/dl_mk_coalesce.h (100%) rename src/muz/{ => transforms}/dl_mk_coi_filter.cpp (92%) rename src/muz/{ => transforms}/dl_mk_coi_filter.h (100%) rename src/muz/{ => transforms}/dl_mk_different.h (100%) rename src/muz/{ => transforms}/dl_mk_filter_rules.cpp (100%) rename src/muz/{ => transforms}/dl_mk_filter_rules.h (100%) rename src/muz/{ => transforms}/dl_mk_interp_tail_simplifier.cpp (100%) rename src/muz/{ => transforms}/dl_mk_interp_tail_simplifier.h (100%) create mode 100644 src/muz/transforms/dl_mk_karr_invariants.cpp create mode 100644 src/muz/transforms/dl_mk_karr_invariants.h rename src/muz/{ => transforms}/dl_mk_loop_counter.cpp (100%) rename src/muz/{ => transforms}/dl_mk_loop_counter.h (100%) rename src/muz/{ => transforms}/dl_mk_magic_sets.cpp (99%) rename src/muz/{ => transforms}/dl_mk_magic_sets.h (100%) rename src/muz/{ => transforms}/dl_mk_magic_symbolic.cpp (100%) rename src/muz/{ => transforms}/dl_mk_magic_symbolic.h (100%) rename src/muz/{ => transforms}/dl_mk_quantifier_abstraction.cpp (100%) rename src/muz/{ => transforms}/dl_mk_quantifier_abstraction.h (100%) rename src/muz/{ => transforms}/dl_mk_quantifier_instantiation.cpp (100%) rename src/muz/{ => transforms}/dl_mk_quantifier_instantiation.h (100%) rename src/muz/{ => transforms}/dl_mk_rule_inliner.cpp (99%) rename src/muz/{ => transforms}/dl_mk_rule_inliner.h (98%) rename src/muz/{ => transforms}/dl_mk_scale.cpp (100%) rename src/muz/{ => transforms}/dl_mk_scale.h (100%) rename src/muz/{ => transforms}/dl_mk_slice.cpp (100%) rename src/muz/{ => transforms}/dl_mk_slice.h (100%) rename src/muz/{ => transforms}/dl_mk_subsumption_checker.cpp (96%) rename src/muz/{ => transforms}/dl_mk_subsumption_checker.h (100%) rename src/muz/{ => transforms}/dl_mk_unbound_compressor.cpp (99%) rename src/muz/{ => transforms}/dl_mk_unbound_compressor.h (98%) rename src/muz/{ => transforms}/dl_mk_unfold.cpp (100%) rename src/muz/{ => transforms}/dl_mk_unfold.h (100%) create mode 100644 src/muz/transforms/dl_transforms.cpp create mode 100644 src/muz/transforms/dl_transforms.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 44ad9ccbc..a2b4ef9b2 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -54,10 +54,17 @@ def init_project_def(): add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') add_lib('qe', ['smt','sat'], 'qe') - add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic','qe']) - add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'muz','qe'], 'tactic/smtlogics') + add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe']) + add_lib('transforms', ['muz'], 'muz/transforms') + add_lib('rel', ['muz', 'transforms'], 'muz/rel') + add_lib('pdr', ['muz', 'transforms'], 'muz/pdr') + add_lib('clp', ['muz', 'transforms'], 'muz/clp') + add_lib('tab', ['muz', 'transforms'], 'muz/tab') + add_lib('bmc', ['muz', 'transforms'], 'muz/bmc') + add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc'], 'muz/fp') + add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') - add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') + add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'fp', 'muz', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h'] add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure'], diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index 64b8b064e..ad6b5ad69 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -18,7 +18,6 @@ Revision History: #include"api_datalog.h" #include"api_context.h" #include"api_util.h" -#include"dl_context.h" #include"ast_pp.h" #include"api_ast_vector.h" #include"api_log_macros.h" @@ -30,8 +29,10 @@ Revision History: #include"cmd_context.h" #include"smt2parser.h" #include"dl_context.h" +#include"dl_register_engine.h" #include"dl_external_relation.h" #include"dl_decl_plugin.h" +#include"rel_context.h" namespace api { @@ -39,6 +40,7 @@ namespace api { void * m_state; reduce_app_callback_fptr m_reduce_app; reduce_assign_callback_fptr m_reduce_assign; + datalog::register_engine m_register_engine; datalog::context m_context; ast_ref_vector m_trail; public: @@ -46,7 +48,7 @@ namespace api { m_state(0), m_reduce_app(0), m_reduce_assign(0), - m_context(m, p), + m_context(m, m_register_engine, p), m_trail(m) {} virtual ~fixedpoint_context() {} @@ -59,7 +61,7 @@ namespace api { if (!m.has_plugin(name)) { m.register_plugin(name, alloc(datalog::dl_decl_plugin)); } - datalog::rel_context* rel = m_context.get_rel_context(); + datalog::rel_context_base* rel = m_context.get_rel_context(); if (rel) { datalog::relation_manager& r = rel->get_rmanager(); r.register_plugin(alloc(datalog::external_relation_plugin, *this, r)); diff --git a/src/muz/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp similarity index 99% rename from src/muz/dl_bmc_engine.cpp rename to src/muz/bmc/dl_bmc_engine.cpp index 65a60cdeb..72e40590e 100644 --- a/src/muz/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -23,13 +23,14 @@ Revision History: #include "dl_mk_slice.h" #include "smt_kernel.h" #include "datatype_decl_plugin.h" -#include "dl_mk_rule_inliner.h" #include "dl_decl_plugin.h" #include "bool_rewriter.h" #include "model_smt2_pp.h" #include "ast_smt_pp.h" #include "well_sorted.h" #include "rewriter_def.h" +#include "dl_transforms.h" +#include "dl_mk_rule_inliner.h" namespace datalog { @@ -1437,7 +1438,7 @@ namespace datalog { datalog::rule_set old_rules(m_ctx.get_rules()); rule_manager.mk_query(query, m_ctx.get_rules()); expr_ref bg_assertion = m_ctx.get_background_assertion(); - m_ctx.apply_default_transformation(); + apply_default_transformation(m_ctx); if (m_ctx.get_params().slice()) { datalog::rule_transformer transformer(m_ctx); diff --git a/src/muz/dl_bmc_engine.h b/src/muz/bmc/dl_bmc_engine.h similarity index 100% rename from src/muz/dl_bmc_engine.h rename to src/muz/bmc/dl_bmc_engine.h diff --git a/src/muz/clp_context.cpp b/src/muz/clp/clp_context.cpp similarity index 98% rename from src/muz/clp_context.cpp rename to src/muz/clp/clp_context.cpp index 01299a2b7..5f3a09e2d 100644 --- a/src/muz/clp_context.cpp +++ b/src/muz/clp/clp_context.cpp @@ -22,6 +22,8 @@ Revision History: #include "unifier.h" #include "var_subst.h" #include "substitution.h" +#include "smt_kernel.h" +#include "dl_transforms.h" namespace datalog { @@ -67,7 +69,7 @@ namespace datalog { m_solver.reset(); m_goals.reset(); rm.mk_query(query, m_ctx.get_rules()); - m_ctx.apply_default_transformation(); + apply_default_transformation(m_ctx); func_decl* head_decl = m_ctx.get_rules().get_output_predicate(); rule_set& rules = m_ctx.get_rules(); rule_vector const& rv = rules.get_predicate_rules(head_decl); diff --git a/src/muz/clp_context.h b/src/muz/clp/clp_context.h similarity index 96% rename from src/muz/clp_context.h rename to src/muz/clp/clp_context.h index 5184b474d..6464ae634 100644 --- a/src/muz/clp_context.h +++ b/src/muz/clp/clp_context.h @@ -22,7 +22,7 @@ Revision History: #include "ast.h" #include "lbool.h" #include "statistics.h" -#include "dl_util.h" +#include "dl_engine_base.h" namespace datalog { class context; diff --git a/src/muz/dl_base.cpp b/src/muz/dl_base.cpp deleted file mode 100644 index 0f250d76c..000000000 --- a/src/muz/dl_base.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_base.cpp - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-14. - -Revision History: - ---*/ - - -#include"ast_pp.h" -#include"union_find.h" -#include"vector.h" -#include"dl_context.h" -#include"dl_base.h" -#include"bool_rewriter.h" -#include - - -namespace datalog { - - context & get_context_from_rel_manager(const relation_manager & rm) { - return rm.get_context(); - } - - ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm) { - return rm.get_context().get_manager(); - } - -#if DL_LEAK_HUNTING - void leak_guard_check(const symbol & s) { - } -#endif - - void relation_signature::output(ast_manager & m, std::ostream & out) const { - unsigned sz=size(); - out<<"("; - for(unsigned i=0; i reset_fn = - get_manager().mk_filter_interpreted_fn(static_cast(*this), bottom_ref); - if(!reset_fn) { - NOT_IMPLEMENTED_YET(); - } - (*reset_fn)(*this); - } - - - - void table_signature::from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2, table_signature & result) { - result.reset(); - - unsigned s1sz=s1.size(); - unsigned s2sz=s2.size(); - unsigned s1first_func=s1sz-s1.functional_columns(); - unsigned s2first_func=s2sz-s2.functional_columns(); - for(unsigned i=0; i=col_cnt); - result.set_functional_columns(func_cnt-col_cnt); - } - } - - void table_signature::from_project_with_reduce(const table_signature & src, unsigned col_cnt, - const unsigned * removed_cols, table_signature & result) { - signature_base::from_project(src, col_cnt, removed_cols, result); - - unsigned remaining_fun = src.functional_columns(); - unsigned first_src_fun = src.first_functional(); - for(int i=col_cnt-1; i>=0; i--) { - if(removed_cols[i] remaining_in_equivalence_class; - remaining_in_equivalence_class.resize(join_sig_sz, 0); - bool merging_rows_can_happen = false; - - union_find_default_ctx uf_ctx; - union_find<> uf(uf_ctx); //the numbers in uf correspond to column indexes after the join - for(unsigned i=0; icols1[i]) ? cols1[i] : (first_func_ofs+cols1[i]-s1_first_func); - unsigned idx2 = (s2_first_func>cols2[i]) ? (second_ofs+cols2[i]) : (second_func_ofs+cols2[i]-s2_first_func); - uf.merge(idx1, idx2); - } - for(unsigned i=0; i=first_func_ofs) { - //removing functional columns won't make us merge rows - continue; - } - unsigned eq_class_idx = uf.find(rc); - if(remaining_in_equivalence_class[eq_class_idx]>1) { - remaining_in_equivalence_class[eq_class_idx]--; - } - else { - merging_rows_can_happen = true; - break; - } - } - - if(merging_rows_can_happen) { - //this one marks all columns as non-functional - from_project(aux, removed_col_cnt, removed_cols, result); - SASSERT(result.functional_columns()==0); - } - else { - //this one preserves columns to be functional - from_project_with_reduce(aux, removed_col_cnt, removed_cols, result); - } - } - - - - - // ----------------------------------- - // - // table_base - // - // ----------------------------------- - - //here we give generic implementation of table operations using iterators - - bool table_base::empty() const { - return begin()==end(); - } - - void table_base::remove_facts(unsigned fact_cnt, const table_fact * facts) { - for(unsigned i=0; i to_remove; - table_base::iterator it = begin(); - table_base::iterator iend = end(); - table_fact row; - for(; it!=iend; ++it) { - it->get_fact(row); - to_remove.push_back(row); - } - remove_facts(to_remove.size(), to_remove.c_ptr()); - } - - bool table_base::contains_fact(const table_fact & f) const { - iterator it = begin(); - iterator iend = end(); - - table_fact row; - - for(; it!=iend; ++it) { - it->get_fact(row); - if(vectors_equal(row, f)) { - return true; - } - } - return false; - } - - bool table_base::fetch_fact(table_fact & f) const { - if(get_signature().functional_columns()==0) { - return contains_fact(f); - } - else { - unsigned sig_sz = get_signature().size(); - unsigned non_func_cnt = sig_sz-get_signature().functional_columns(); - table_base::iterator it = begin(); - table_base::iterator iend = end(); - table_fact row; - for(; it!=iend; ++it) { - it->get_fact(row); - bool differs = false; - for(unsigned i=0; iget_fact(row); - res->add_new_fact(row); - } - return res; - } - - /** - \brief Default method for complementation. - - It assumes that the compiler creates only tables with - at most one column (0 or 1 columns). - Complementation of tables with more than one columns - is transformed into a cross product of complements and/or - difference. - - */ - table_base * table_base::complement(func_decl* p, const table_element * func_columns) const { - const table_signature & sig = get_signature(); - SASSERT(sig.functional_columns()==0 || func_columns!=0); - SASSERT(sig.first_functional() <= 1); - - table_base * res = get_plugin().mk_empty(sig); - - table_fact fact; - fact.resize(sig.first_functional()); - fact.append(sig.functional_columns(), func_columns); - - if (sig.first_functional() == 0) { - if (empty()) { - res->add_fact(fact); - } - return res; - } - - VERIFY(sig.first_functional() == 1); - - uint64 upper_bound = get_signature()[0]; - bool empty_table = empty(); - - if (upper_bound > (1 << 18)) { - std::ostringstream buffer; - buffer << "creating large table of size " << upper_bound; - if (p) buffer << " for relation " << p->get_name(); - warning_msg(buffer.str().c_str()); - } - - for(table_element i = 0; i < upper_bound; i++) { - fact[0] = i; - if(empty_table || !contains_fact(fact)) { - res->add_fact(fact); - } - } - return res; - } - - void table_base::display(std::ostream & out) const { - out << "table with signature "; - print_container(get_signature(), out); - out << ":\n"; - - iterator it = begin(); - iterator iend = end(); - for(; it!=iend; ++it) { - const row_interface & r = *it; - r.display(out); - } - - out << "\n"; - } - - - class table_base::row_interface::fact_row_iterator : public table_base::row_iterator_core { - const row_interface & m_parent; - unsigned m_index; - protected: - virtual bool is_finished() const { return m_index==m_parent.size(); } - public: - fact_row_iterator(const row_interface & row, bool finished) - : m_parent(row), m_index(finished ? row.size() : 0) {} - - virtual table_element operator*() { - SASSERT(!is_finished()); - return m_parent[m_index]; - } - - virtual void operator++() { - m_index++; - SASSERT(m_index<=m_parent.size()); - } - }; - - table_base::row_iterator table_base::row_interface::begin() const { - return row_iterator(alloc(fact_row_iterator, *this, false)); - } - table_base::row_iterator table_base::row_interface::end() const { - return row_iterator(alloc(fact_row_iterator, *this, true)); - } - - void table_base::row_interface::get_fact(table_fact & result) const { - result.reset(); - unsigned n=size(); - for(unsigned i=0; i - -Author: - - Krystof Hoder (t-khoder) 2010-09-23. - -Revision History: - ---*/ -#ifndef _DL_BASE_H_ -#define _DL_BASE_H_ - -#define DL_LEAK_HUNTING 0 - -#include - -#include"ast.h" -#include"map.h" -#include"vector.h" -#include"ref.h" -#include"dl_util.h" - -namespace datalog { - - class context; - class relation_manager; - - ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm); - context & get_context_from_rel_manager(const relation_manager & rm); - - typedef func_decl_set decl_set; - -#if DL_LEAK_HUNTING - void leak_guard_check(const symbol & s); -#endif - - - /** - Termplate class containing common infrastructure for relations and tables - */ - template - struct tr_infrastructure { - - typedef typename Traits::plugin plugin; - typedef typename Traits::base_object base_object; - typedef typename Traits::element element; - typedef typename Traits::fact fact; - typedef typename Traits::sort sort; - typedef typename Traits::signature_base_base signature_base_base; //this must be a vector-like type - typedef typename Traits::signature signature; //this must be a vector-like type - - /** - The client submits an initial class to be used as a base for signature. Then we excend it by - the common signature methods into a signature_base class which then the client inherits from - to obtain the actual signature class. - */ - class signature_base : public signature_base_base { - public: - bool operator==(const signature & o) const { - unsigned n=signature_base_base::size(); - if(n!=o.size()) { - return false; - } - return memcmp(this->c_ptr(), o.c_ptr(), n*sizeof(sort))==0; - /*for(unsigned i=0; i=2); - result=src; - - permutate_by_cycle(result, cycle_len, permutation_cycle); - } - - /** - \brief Into \c result assign signature \c src with reordered columns. - */ - static void from_permutation_rename(const signature & src, - const unsigned * permutation, signature & result) { - result.reset(); - unsigned n = src.size(); - for(unsigned i=0; i(0)); - } - }; - - /** - \brief Mutator for relations that propagate constraints as a consequence of - combination. - - - supports_attachment - is used to query the mutator if it allows communicating - constraints to relations of the kind of the relation. - - - attach - is used to associate downstream clients. - It assumes that the relation kind is supported (supports_kind returns true) - */ - class mutator_fn : public base_fn { - public: - virtual void operator()(base_object & t) = 0; - - virtual bool supports_attachment(base_object& other) { return false; } - - virtual void attach(base_object& other) { UNREACHABLE(); } - }; - - class intersection_filter_fn : public base_fn { - public: - virtual void operator()(base_object & t, const base_object & intersected_obj) = 0; - }; - - class default_join_project_fn; - - /** - \brief Plugin class providing factory functions for a table and operations on it. - - The functor factory functions (mk_*_fn) may return 0. It means that the plugin - is unable to perform the operation on relations/tables of the particular kind. - */ - class plugin_object { - friend class relation_manager; - friend class check_table_plugin; - - family_id m_kind; - symbol m_name; - relation_manager & m_manager; - protected: - plugin_object(symbol const& name, relation_manager & manager) - : m_kind(null_family_id), m_name(name), m_manager(manager) {} - - /** - \brief Check \c r is of the same kind as the plugin. - */ - bool check_kind(base_object const& r) const { return &r.get_plugin()==this; } - public: - virtual ~plugin_object() {} - - virtual void initialize(family_id fid) { m_kind = fid; } - - family_id get_kind() const { return m_kind; } - - symbol const& get_name() const { return m_name; } - - virtual void set_cancel(bool f) {} - - relation_manager & get_manager() const { return m_manager; } - ast_manager& get_ast_manager() const { return datalog::get_ast_manager_from_rel_manager(m_manager); } - context& get_context() const { return datalog::get_context_from_rel_manager(m_manager); } - - virtual bool can_handle_signature(const signature & s) = 0; - - virtual bool can_handle_signature(const signature & s, family_id kind) { - return can_handle_signature(s); - } - - /** - \brief Create empty table/relation with given signature and return pointer to it. - - Precondition: can_handle_signature(s)==true - */ - virtual base_object * mk_empty(const signature & s) = 0; - - /** - \brief Create empty table/relation with signature \c s and kind \c kind. - - Precondition: &orig.get_plugin()==this - */ - virtual base_object * mk_empty(const signature & s, family_id kind) { - SASSERT(kind==get_kind()); //if plugin uses multiple kinds, this function needs to be overriden - return mk_empty(s); - } - - /** - \brief Create empty table/relation of the same specification as \c orig and return pointer to it. - - Precondition: &orig.get_plugin()==this - */ - virtual base_object * mk_empty(const base_object & orig) { - return mk_empty(orig.get_signature(), orig.get_kind()); - } - - /** - \brief Create full table/relation with given signature and return pointer to it. - - Precondition: can_handle_signature(s)==true - */ - virtual base_object * mk_full(func_decl* p, const signature & s) { - base_object * aux = mk_empty(s); - base_object * res = aux->complement(p); - aux->deallocate(); - return res; - } - - virtual base_object * mk_full(func_decl* p, const signature & s, family_id kind) { - if (kind == get_kind() || kind == null_family_id) { - return mk_full(p, s); - } - base_object * aux = mk_empty(s, kind); - base_object * res = aux->complement(p); - aux->deallocate(); - return res; - } - - protected: - //see \c relation_manager for documentation of the operations - - virtual join_fn * mk_join_fn(const base_object & t1, const base_object & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { return 0; } - - virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, - const unsigned * removed_cols) { return 0; } - - virtual join_fn * mk_join_project_fn(const base_object & t1, const base_object & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } - - virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle) { return 0; } - - virtual transformer_fn * mk_permutation_rename_fn(const base_object & t, - const unsigned * permutation) { return 0; } - - public: - virtual union_fn * mk_union_fn(const base_object & tgt, const base_object & src, - const base_object * delta) { return 0; } - protected: - - virtual union_fn * mk_widen_fn(const base_object & tgt, const base_object & src, - const base_object * delta) { return 0; } - - virtual mutator_fn * mk_filter_identical_fn(const base_object & t, unsigned col_cnt, - const unsigned * identical_cols) { return 0; } - - virtual mutator_fn * mk_filter_equal_fn(const base_object & t, const element & value, - unsigned col) { return 0; } - - virtual mutator_fn * mk_filter_interpreted_fn(const base_object & t, app * condition) - { return 0; } - - virtual transformer_fn * mk_filter_interpreted_and_project_fn(const base_object & t, - app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) - { return 0; } - - virtual transformer_fn * mk_select_equal_and_project_fn(const base_object & t, - const element & value, unsigned col) { return 0; } - - virtual intersection_filter_fn * mk_filter_by_intersection_fn(const base_object & t, - const base_object & src, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * src_cols) - { return 0; } - - virtual intersection_filter_fn * mk_filter_by_negation_fn(const base_object & t, - const base_object & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols) - { return 0; } - }; - - class base_ancestor { - plugin & m_plugin; - signature m_signature; - family_id m_kind; -#if DL_LEAK_HUNTING - sort_ref m_leak_guard; -#endif - protected: - base_ancestor(plugin & p, const signature & s) - : m_plugin(p), m_signature(s), m_kind(p.get_kind()) -#if DL_LEAK_HUNTING - , m_leak_guard(p.get_ast_manager().mk_fresh_sort(p.get_name().bare_str()), p.get_ast_manager()) -#endif - { -#if DL_LEAK_HUNTING - leak_guard_check(m_leak_guard->get_name()); -#endif - } - -#if DL_LEAK_HUNTING - base_ancestor(const base_ancestor & o) - : m_plugin(o.m_plugin), - m_signature(o.m_signature), - m_kind(o.m_kind), - m_leak_guard(m_plugin.get_ast_manager().mk_fresh_sort(m_plugin.get_name().bare_str()), - m_plugin.get_ast_manager()) { - leak_guard_check(m_leak_guard->get_name()); - } -#endif - - virtual ~base_ancestor() {} - - void set_kind(family_id kind) { SASSERT(kind>=0); m_kind = kind; } - - /** - Since the destructor is protected, we cannot use the \c dealloc macro. - */ - void destroy() { - SASSERT(this); - this->~base_ancestor(); -#if _DEBUG - memory::deallocate(__FILE__, __LINE__, this); -#else - memory::deallocate(this); -#endif - } - public: - /** - Deallocate the current object. - - Pointers and references to the current object become invalid after a call to this function. - */ - virtual void deallocate() { - destroy(); - } - /** - It must hold that operations created for particular table/relation are able to operate on - tables/relations of the same signature and kind. In most cases it is sufficient to use the kind - of the plugin object. - - However, it there is some parameter that is not reflected in the signature, the plugin may need to - allocate different kind numbers to the tables is creates. - */ - family_id get_kind() const { return m_kind; } - const signature & get_signature() const { return m_signature; } - plugin & get_plugin() const { return m_plugin; } - relation_manager & get_manager() const { return get_plugin().get_manager(); } - - virtual bool empty() const = 0; - virtual void add_fact(const fact & f) = 0; - /** - \brief Like \c add_fact, only here the caller guarantees that the fact is not present in - the table yet. - */ - virtual void add_new_fact(const fact & f) { - add_fact(f); - } - virtual bool contains_fact(const fact & f) const = 0; - - virtual void reset() = 0; - - /** - \brief Return table/relation that contains the same data as the current one. - */ - virtual base_object * clone() const = 0; - - virtual bool can_swap(const base_object & o) const { return false; } - - virtual void swap(base_object & o) { - std::swap(m_kind, o.m_kind); -#if DL_LEAK_HUNTING - m_leak_guard = get_plugin().get_ast_manager().mk_fresh_sort(get_plugin().get_name().bare_str()); - o.m_leak_guard = get_plugin().get_ast_manager().mk_fresh_sort(get_plugin().get_name().bare_str()); - leak_guard_check(m_leak_guard->get_name()); - leak_guard_check(o.m_leak_guard->get_name()); -#endif - } - - virtual unsigned get_size_estimate_rows() const { return UINT_MAX; } - virtual unsigned get_size_estimate_bytes() const { return UINT_MAX; } - virtual bool knows_exact_size() const { return false; } - - virtual void display(std::ostream & out) const = 0; - }; - - - class convenient_join_fn : public join_fn { - signature m_result_sig; - protected: - unsigned_vector m_cols1; - unsigned_vector m_cols2; - - convenient_join_fn(const signature & o1_sig, const signature & o2_sig, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2) - : m_cols1(col_cnt, cols1), - m_cols2(col_cnt, cols2) { - signature::from_join(o1_sig, o2_sig, col_cnt, cols1, cols2, m_result_sig); - } - - const signature & get_result_signature() const { return m_result_sig; } - }; - - class convenient_join_project_fn : public join_fn { - signature m_result_sig; - protected: - unsigned_vector m_cols1; - unsigned_vector m_cols2; - //it is non-const because it needs to be modified in sparse_table version of the join_project operator - unsigned_vector m_removed_cols; - - convenient_join_project_fn(const signature & o1_sig, const signature & o2_sig, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols) - : m_cols1(joined_col_cnt, cols1), - m_cols2(joined_col_cnt, cols2), - m_removed_cols(removed_col_cnt, removed_cols) { - - signature::from_join_project(o1_sig, o2_sig, joined_col_cnt, cols1, cols2, - removed_col_cnt, removed_cols, m_result_sig); - } - - const signature & get_result_signature() const { return m_result_sig; } - }; - - class convenient_transformer_fn : public transformer_fn { - signature m_result_sig; - protected: - signature & get_result_signature() { return m_result_sig; } - const signature & get_result_signature() const { return m_result_sig; } - }; - - class convenient_project_fn : public convenient_transformer_fn { - protected: - unsigned_vector m_removed_cols; - - convenient_project_fn(const signature & orig_sig, unsigned col_cnt, const unsigned * removed_cols) - : m_removed_cols(col_cnt, removed_cols) { - signature::from_project(orig_sig, col_cnt, removed_cols, - convenient_transformer_fn::get_result_signature()); - } - }; - - class convenient_rename_fn : public convenient_transformer_fn { - protected: - const unsigned_vector m_cycle; - - convenient_rename_fn(const signature & orig_sig, unsigned cycle_len, - const unsigned * permutation_cycle) - : m_cycle(cycle_len, permutation_cycle) { - signature::from_rename(orig_sig, cycle_len, permutation_cycle, - convenient_transformer_fn::get_result_signature()); - } - }; - - class convenient_negation_filter_fn : public intersection_filter_fn { - protected: - unsigned m_joined_col_cnt; - const unsigned_vector m_cols1; - const unsigned_vector m_cols2; - bool m_all_neg_bound; //all columns are bound at least once - bool m_overlap; //one column in negated table is bound multiple times - svector m_bound; - - convenient_negation_filter_fn(const base_object & tgt, const base_object & neg_t, - unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) - : m_joined_col_cnt(joined_col_cnt), m_cols1(joined_col_cnt, t_cols), - m_cols2(joined_col_cnt, negated_cols) { - unsigned neg_sig_size = neg_t.get_signature().size(); - m_overlap = false; - m_bound.resize(neg_sig_size, false); - for(unsigned i=0; i - void make_neg_bindings(T & tgt_neg, const U & src) const { - SASSERT(m_all_neg_bound); - SASSERT(!m_overlap); - for(unsigned i=0; i - bool bindings_match(const T & tgt_neg, const U & src) const { - for(unsigned i=0; i renamer_vector; - - unsigned_vector m_permutation; //this is valid only before m_renamers_initialized becomes true - bool m_renamers_initialized; - renamer_vector m_renamers; - public: - default_permutation_rename_fn(const base_object & o, const unsigned * permutation) - : m_permutation(o.get_signature().size(), permutation), - m_renamers_initialized(false) {} - - ~default_permutation_rename_fn() { - dealloc_ptr_vector_content(m_renamers); - } - - base_object * operator()(const base_object & o) { - const base_object * res = &o; - scoped_rel res_scoped; - if(m_renamers_initialized) { - typename renamer_vector::iterator rit = m_renamers.begin(); - typename renamer_vector::iterator rend = m_renamers.end(); - for(; rit!=rend; ++rit) { - res_scoped = (**rit)(*res); - res = res_scoped.get(); - } - } - else { - SASSERT(m_renamers.empty()); - unsigned_vector cycle; - while(try_remove_cycle_from_permutation(m_permutation, cycle)) { - transformer_fn * renamer = o.get_manager().mk_rename_fn(*res, cycle); - SASSERT(renamer); - m_renamers.push_back(renamer); - cycle.reset(); - - res_scoped = (*renamer)(*res); - res = res_scoped.get(); - } - m_renamers_initialized = true; - } - if(res_scoped) { - SASSERT(res==res_scoped.get()); - //we don't want to delete the last one since we'll be returning it - return res_scoped.release(); - } - else { - SASSERT(res==&o); - return res->clone(); - } - } - - }; - - - }; - - - // ----------------------------------- - // - // relation_base - // - // ----------------------------------- - - class relation_signature; - class relation_plugin; - class relation_base; - - typedef sort * relation_sort; - typedef ptr_vector relation_signature_base0; - typedef ptr_hash relation_sort_hash; - - typedef app * relation_element; - typedef app_ref relation_element_ref; - - class relation_fact : public app_ref_vector { - public: - class el_proxy { - friend class relation_fact; - - relation_fact & m_parent; - unsigned m_idx; - - el_proxy(relation_fact & parent, unsigned idx) : m_parent(parent), m_idx(idx) {} - public: - operator relation_element() const { - return m_parent.get(m_idx); - } - relation_element operator->() const { - return m_parent.get(m_idx); - } - relation_element operator=(const relation_element & val) const { - m_parent.set(m_idx, val); - return m_parent.get(m_idx); - } - relation_element operator=(const el_proxy & val) { - m_parent.set(m_idx, val); - return m_parent.get(m_idx); - } - }; - - typedef const relation_element * iterator; - - relation_fact(ast_manager & m) : app_ref_vector(m) {} - relation_fact(ast_manager & m, unsigned sz) : app_ref_vector(m) { resize(sz); } - relation_fact(context & ctx); - - iterator begin() const { return c_ptr(); } - iterator end() const { return c_ptr()+size(); } - - relation_element operator[](unsigned i) const { return get(i); } - el_proxy operator[](unsigned i) { return el_proxy(*this, i); } - }; - - struct relation_traits { - typedef relation_plugin plugin; - typedef relation_base base_object; - typedef relation_element element; - typedef relation_fact fact; - typedef relation_sort sort; - typedef relation_signature_base0 signature_base_base; - typedef relation_signature signature; - }; - - typedef tr_infrastructure relation_infrastructure; - - typedef relation_infrastructure::base_fn base_relation_fn; - typedef relation_infrastructure::join_fn relation_join_fn; - typedef relation_infrastructure::transformer_fn relation_transformer_fn; - typedef relation_infrastructure::union_fn relation_union_fn; - typedef relation_infrastructure::mutator_fn relation_mutator_fn; - typedef relation_infrastructure::intersection_filter_fn relation_intersection_filter_fn; - - typedef relation_infrastructure::convenient_join_fn convenient_relation_join_fn; - typedef relation_infrastructure::convenient_join_project_fn convenient_relation_join_project_fn; - typedef relation_infrastructure::convenient_transformer_fn convenient_relation_transformer_fn; - typedef relation_infrastructure::convenient_project_fn convenient_relation_project_fn; - typedef relation_infrastructure::convenient_rename_fn convenient_relation_rename_fn; - typedef relation_infrastructure::convenient_negation_filter_fn convenient_relation_negation_filter_fn; - typedef relation_infrastructure::identity_transformer_fn identity_relation_transformer_fn; - typedef relation_infrastructure::identity_mutator_fn identity_relation_mutator_fn; - typedef relation_infrastructure::identity_intersection_filter_fn identity_relation_intersection_filter_fn; - typedef relation_infrastructure::default_permutation_rename_fn default_relation_permutation_rename_fn; - - class relation_signature : public relation_infrastructure::signature_base { - public: - bool operator!=(const relation_signature & o) const { - return !(*this==o); - } - - void output(ast_manager & m, std::ostream & out) const; - - struct hash { - unsigned operator()(relation_signature const& s) const { - return obj_vector_hash(s); - } - }; - - struct eq { - bool operator()(relation_signature const& s1, relation_signature const& s2) const { - return s1 == s2; - } - }; - }; - - class relation_plugin : public relation_infrastructure::plugin_object { - protected: - enum special_relation_type { - ST_ORDINARY, - ST_TABLE_RELATION, - ST_FINITE_PRODUCT_RELATION, - ST_PRODUCT_RELATION, - ST_SIEVE_RELATION - }; - private: - special_relation_type m_special_type; - protected: - relation_plugin(symbol const& name, relation_manager & manager, - special_relation_type special_type = ST_ORDINARY) - : plugin_object(name, manager), - m_special_type(special_type) {} - public: - bool from_table() const { return m_special_type==ST_TABLE_RELATION; } - bool is_finite_product_relation() const { return m_special_type==ST_FINITE_PRODUCT_RELATION; } - bool is_product_relation() const { return m_special_type==ST_PRODUCT_RELATION; } - bool is_sieve_relation() const { return m_special_type==ST_SIEVE_RELATION; } - - /** - \brief If true, the relation can contain only one or zero elements. - - Having this zero allows the finite_product_relation to perform some operations in a simpler way. - (KH: I started implementing finite_product_relation::inner_singleton_union_fn that takes advantage of - it, but it's not finished.) - */ - virtual bool is_singleton_relation() const { return false; } - }; - - class relation_base : public relation_infrastructure::base_ancestor { - protected: - relation_base(relation_plugin & plugin, const relation_signature & s) - : base_ancestor(plugin, s) {} - virtual ~relation_base() {} - public: - virtual relation_base * complement(func_decl* p) const = 0; - - virtual void reset(); - - virtual void display_tuples(func_decl & pred, std::ostream & out) const { - out << "Tuples in " << pred.get_name() << ": \n"; - display(out); - } - - virtual void to_formula(expr_ref& fml) const = 0; - - bool from_table() const { return get_plugin().from_table(); } - virtual bool is_precise() const { return true; } - }; - - typedef ptr_vector relation_vector; - - // ----------------------------------- - // - // table_base - // - // ----------------------------------- - - class table_signature; - class table_plugin; - class table_base; - - typedef uint64 table_sort; - typedef svector table_signature_base0; - typedef uint64_hash table_sort_hash; - - typedef uint64 table_element; - typedef svector table_fact; - typedef uint64_hash table_element_hash; - - struct table_traits { - typedef table_plugin plugin; - typedef table_base base_object; - typedef table_element element; - typedef table_fact fact; - typedef table_sort sort; - typedef table_signature_base0 signature_base_base; - typedef table_signature signature; - }; - - typedef tr_infrastructure table_infrastructure; - - typedef table_infrastructure::base_fn base_table_fn; - typedef table_infrastructure::join_fn table_join_fn; - typedef table_infrastructure::transformer_fn table_transformer_fn; - typedef table_infrastructure::union_fn table_union_fn; - typedef table_infrastructure::mutator_fn table_mutator_fn; - typedef table_infrastructure::intersection_filter_fn table_intersection_filter_fn; - - typedef table_infrastructure::convenient_join_fn convenient_table_join_fn; - typedef table_infrastructure::convenient_join_project_fn convenient_table_join_project_fn; - typedef table_infrastructure::convenient_transformer_fn convenient_table_transformer_fn; - typedef table_infrastructure::convenient_project_fn convenient_table_project_fn; - typedef table_infrastructure::convenient_rename_fn convenient_table_rename_fn; - typedef table_infrastructure::convenient_negation_filter_fn convenient_table_negation_filter_fn; - typedef table_infrastructure::identity_transformer_fn identity_table_transformer_fn; - typedef table_infrastructure::identity_mutator_fn identity_table_mutator_fn; - typedef table_infrastructure::identity_intersection_filter_fn identity_table_intersection_filter_fn; - typedef table_infrastructure::default_permutation_rename_fn default_table_permutation_rename_fn; - - class table_row_mutator_fn { - public: - /** - \brief The function is called for a particular table row. The \c func_columns contains - a pointer to an array of functional column values that can be modified. If the function - returns true, the modification will appear in the table; otherwise the row will be deleted. - - It is possible that one call to the function stands for multiple table rows that share - the same functional column values. - */ - virtual bool operator()(table_element * func_columns) = 0; - }; - - class table_row_pair_reduce_fn { - public: - /** - \brief The function is called for pair of table rows that became duplicit due to projection. - The values that are in the first array after return from the function will be used for the - resulting row. - - It is assumed that the function is idempotent: when the two functional sub-tuples are equal, - the result is assumed to be equal to them as well, so the function may not be evaluated for them. - */ - virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) = 0; - }; - - - class table_signature : public table_infrastructure::signature_base { - public: - struct hash { - unsigned operator()(table_signature const& s) const { - return svector_hash()(s); - } - }; - - struct eq { - bool operator()(table_signature const& s1, table_signature const& s2) const { - return s1 == s2; - } - }; - private: - unsigned m_functional_columns; - public: - table_signature() : m_functional_columns(0) {} - - void swap(table_signature & s) { - signature_base::swap(s); - std::swap(m_functional_columns, s.m_functional_columns); - } - - /** - \brief The returned value is the number of last columns that are functional. - - The uniqueness is enforced on non-functional columns. When projection causes two - facts to have equal non-functional parts, it is not defined which one of them is retained. - */ - unsigned functional_columns() const { return m_functional_columns; } - void set_functional_columns(unsigned val) { SASSERT(size()>=val); m_functional_columns = val; } - - /** - \brief Return index of the first functional column, or the size of the signature if there - are no functional columns. - */ - unsigned first_functional() const { return size()-m_functional_columns; } - - bool operator==(const table_signature & o) const { - return signature_base::operator==(o) && m_functional_columns==o.m_functional_columns; - } - bool operator!=(const table_signature & o) const { - return !(*this==o); - } - - /** - \brief return true iof the two signatures are equal when we ignore which columns are functional. - */ - bool equal_up_to_fn_mark(const table_signature & o) const { - return signature_base::operator==(o); - } - - - /** - \brief Into \c result assign signature of result of join of relations with signatures \c s1 - and \c s2. The result is - - (non-functional of s1)(non-functional of s2)(functional of s1)(functional of s2) - */ - static void from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2, table_signature & result); - - static void from_join_project(const table_signature & s1, const table_signature & s2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols, table_signature & result); - - - /** - \brief Into \c result assign signature projected from \c src. - - The array of removed columns must be sorted in ascending order. - - If we remove at least one non-functional column, all the columns in the result are non-functional. - */ - static void from_project(const table_signature & src, unsigned col_cnt, - const unsigned * removed_cols, table_signature & result); - - static void from_project_with_reduce(const table_signature & src, unsigned col_cnt, - const unsigned * removed_cols, table_signature & result); - - /** - \brief Into \c result assign signature \c src with reordered columns. - - Permutations between functional and nonfunctional columns are not allowed. - */ - static void from_rename(const table_signature & src, unsigned cycle_len, - const unsigned * permutation_cycle, table_signature & result) { - signature_base::from_rename(src, cycle_len, permutation_cycle, result); - result.set_functional_columns(src.functional_columns()); -#if Z3DEBUG - unsigned first_src_fun = src.size()-src.functional_columns(); - bool in_func = permutation_cycle[0]>=first_src_fun; - for(unsigned i=1;i=first_src_fun)); - } -#endif - } - - /** - \brief Into \c result assign signature \c src with reordered columns. - - Permutations mixing functional and nonfunctional columns are not allowed. - */ - static void from_permutation_rename(const table_signature & src, - const unsigned * permutation, table_signature & result) { - signature_base::from_permutation_rename(src, permutation, result); - result.set_functional_columns(src.functional_columns()); -#if Z3DEBUG - unsigned sz = src.size(); - unsigned first_src_fun = sz-src.functional_columns(); - for(unsigned i=first_src_fun;i=first_src_fun); - } -#endif - } - - }; - - class table_plugin : public table_infrastructure::plugin_object { - friend class relation_manager; - protected: - table_plugin(symbol const& n, relation_manager & manager) : plugin_object(n, manager) {} - public: - - virtual bool can_handle_signature(const table_signature & s) { return s.functional_columns()==0; } - - protected: - /** - If the returned value is non-zero, the returned object must take ownership of \c mapper. - Otherwise \c mapper must remain unmodified. - */ - virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { return 0; } - - /** - If the returned value is non-zero, the returned object must take ownership of \c reducer. - Otherwise \c reducer must remain unmodified. - */ - virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { return 0; } - - }; - - class table_base : public table_infrastructure::base_ancestor { - protected: - table_base(table_plugin & plugin, const table_signature & s) - : base_ancestor(plugin, s) {} - virtual ~table_base() {} - public: - virtual table_base * clone() const; - virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; - virtual bool empty() const; - - /** - \brief Return true if table contains fact that corresponds to \c f in all non-functional - columns. - */ - virtual bool contains_fact(const table_fact & f) const; - - /** - \brief If \c f (i.e. its non-functional part) is not present in the table, - add it and return true. Otherwise update \c f, so that the values of functional - columns correspond to the ones present in the table. - */ - virtual bool suggest_fact(table_fact & f); - - /** - \brief If \c f (i.e. its non-functional part) is not present in the table, - return false. Otherwise update \c f, so that the values of functional - columns correspond to the ones present in the table and return true. - */ - virtual bool fetch_fact(table_fact & f) const; - - /** - \brief Ensure fact \c f is present in the table (including the values of its functional columns). - */ - virtual void ensure_fact(const table_fact & f); - - virtual void remove_fact(const table_fact & fact) { - SASSERT(fact.size() == get_signature().size()); - remove_fact(fact.c_ptr()); } - - virtual void remove_fact(table_element const* fact) = 0; - virtual void remove_facts(unsigned fact_cnt, const table_fact * facts); - virtual void remove_facts(unsigned fact_cnt, const table_element * facts); - virtual void reset(); - - class row_interface; - - virtual void display(std::ostream & out) const; - - /** - \brief Convert table to a formula that encodes the table. - The columns correspond to bound variables indexed as - 0, .., sig.size()-1 - */ - virtual void to_formula(relation_signature const& sig, expr_ref& fml) const; - - protected: - - - class iterator_core { - unsigned m_ref_cnt; - public: - iterator_core() : m_ref_cnt(0) {} - virtual ~iterator_core() {} - - void inc_ref() { m_ref_cnt++; } - void dec_ref() { - SASSERT(m_ref_cnt>0); - m_ref_cnt--; - if(m_ref_cnt==0) { - dealloc(this); - } - } - - virtual bool is_finished() const = 0; - - virtual row_interface & operator*() = 0; - virtual void operator++() = 0; - virtual bool operator==(const iterator_core & it) { - //we worry about the equality operator only because of checking - //the equality with the end() iterator - if(is_finished() && it.is_finished()) { - return true; - } - return false; - } - private: - //private and undefined copy constructor and assignment operator - iterator_core(const iterator_core &); - iterator_core & operator=(const iterator_core &); - }; - - struct row_iterator_core { - unsigned m_ref_cnt; - public: - row_iterator_core() : m_ref_cnt(0) {} - virtual ~row_iterator_core() {} - - void inc_ref() { m_ref_cnt++; } - void dec_ref() { - SASSERT(m_ref_cnt>0); - m_ref_cnt--; - if(m_ref_cnt==0) { - dealloc(this); - } - } - - virtual bool is_finished() const = 0; - - virtual table_element operator*() = 0; - virtual void operator++() = 0; - virtual bool operator==(const row_iterator_core & it) { - //we worry about the equality operator only because of checking - //the equality with the end() iterator - if(is_finished() && it.is_finished()) { - return true; - } - return false; - } - private: - //private and undefined copy constructor and assignment operator - row_iterator_core(const row_iterator_core &); - row_iterator_core & operator=(const row_iterator_core &); - }; - - public: - class iterator { - friend class table_base; - - ref m_core; - - iterator(iterator_core * core) : m_core(core) {} - public: - /** - \brief Return reference to a row_interface object for the current row. - - The reference is valid only until the \c operator++() is called or - until the iterator is invalidated. - */ - row_interface & operator*() - { return *(*m_core); } - row_interface * operator->() - { return &(*(*m_core)); } - iterator & operator++() - { ++(*m_core); return *this; } - bool operator==(const iterator & it) - { return (*m_core)==(*it.m_core); } - bool operator!=(const iterator & it) - { return !operator==(it); } - }; - - class row_iterator { - friend class table_base; - friend class row_interface; - - ref m_core; - - row_iterator(row_iterator_core * core) : m_core(core) {} - public: - table_element operator*() - { return *(*m_core); } - row_iterator & operator++() - { ++(*m_core); return *this; } - bool operator==(const row_iterator & it) - { return (*m_core)==(*it.m_core); } - bool operator!=(const row_iterator & it) - { return !operator==(it); } - }; - - virtual iterator begin() const = 0; - virtual iterator end() const = 0; - - class row_interface { - class fact_row_iterator; - - const table_base & m_parent_table; - public: - typedef row_iterator iterator; - typedef row_iterator const_iterator; - - row_interface(const table_base & parent_table) : m_parent_table(parent_table) {} - virtual ~row_interface() {} - - virtual table_element operator[](unsigned col) const = 0; - - unsigned size() const { return m_parent_table.get_signature().size(); } - virtual void get_fact(table_fact & result) const; - virtual row_iterator begin() const; - virtual row_iterator end() const; - virtual void display(std::ostream & out) const; - }; - - protected: - - class caching_row_interface : public row_interface { - mutable table_fact m_current; - - bool populated() const { return !m_current.empty(); } - void ensure_populated() const { - if(!populated()) { - get_fact(m_current); - } - } - public: - caching_row_interface(const table_base & parent) : row_interface(parent) {} - - virtual void get_fact(table_fact & result) const = 0; - - virtual table_element operator[](unsigned col) const { - ensure_populated(); - return m_current[col]; - } - /** - \brief Resets the cache of the row object. - - Must be called when the row object begins to represent a different row in the table. - */ - void reset() { m_current.reset(); } - }; - - //This function is here to create iterator instances in classes that derive from table_base. - //We do not want to make the constructor of the iterator class public, and being private, the - //inheritor classes cannot see it directly. - static iterator mk_iterator(iterator_core * core) { - return iterator(core); - } - }; - - -}; - -#endif /* _DL_BASE_H_ */ - diff --git a/src/muz/dl_bound_relation.cpp b/src/muz/dl_bound_relation.cpp deleted file mode 100644 index 182046c1e..000000000 --- a/src/muz/dl_bound_relation.cpp +++ /dev/null @@ -1,707 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_bound_relation.cpp - -Abstract: - - Basic (strict upper) bound relation. - -Author: - - Nikolaj Bjorner (nbjorner) 2010-2-11 - -Revision History: - ---*/ - -#include "dl_bound_relation.h" -#include "debug.h" -#include "ast_pp.h" - -namespace datalog { - - bound_relation_plugin::bound_relation_plugin(relation_manager& m): - relation_plugin(bound_relation_plugin::get_name(), m), - m_arith(get_ast_manager()), - m_bsimp(get_ast_manager()) { - } - - bool bound_relation_plugin::can_handle_signature(const relation_signature & sig) { - for (unsigned i = 0; i < sig.size(); ++i) { - if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) { - return false; - } - } - return true; - } - - bound_relation& bound_relation_plugin::get(relation_base& r) { - return dynamic_cast(r); - } - - bound_relation const & bound_relation_plugin::get(relation_base const& r) { - return dynamic_cast(r); - } - - bound_relation* bound_relation_plugin::get(relation_base* r) { - return dynamic_cast(r); - } - - bool bound_relation_plugin::is_interval_relation(relation_base const& r) { - return symbol("interval_relation") == r.get_plugin().get_name(); - } - - interval_relation& bound_relation_plugin::get_interval_relation(relation_base& r) { - SASSERT(is_interval_relation(r)); - return dynamic_cast(r); - } - - interval_relation const& bound_relation_plugin::get_interval_relation(relation_base const& r) { - SASSERT(is_interval_relation(r)); - return dynamic_cast(r); - } - - relation_base * bound_relation_plugin::mk_empty(const relation_signature & s) { - return alloc(bound_relation, *this, s, true); - } - - relation_base * bound_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { - return alloc(bound_relation, *this, s, false); - } - - class bound_relation_plugin::join_fn : public convenient_relation_join_fn { - public: - join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2) - : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2) { - } - - virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { - bound_relation const& r1 = get(_r1); - bound_relation const& r2 = get(_r2); - bound_relation_plugin& p = r1.get_plugin(); - bound_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); - result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); - return result; - } - }; - - relation_join_fn * bound_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (!check_kind(r1) || !check_kind(r2)) { - return 0; - } - return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); - } - - - class bound_relation_plugin::project_fn : public convenient_relation_project_fn { - public: - project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) - : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { - } - - virtual relation_base * operator()(const relation_base & _r) { - bound_relation const& r = get(_r); - bound_relation_plugin& p = r.get_plugin(); - bound_relation* result = get(p.mk_full(0, get_result_signature())); - result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); - return result; - } - }; - - relation_transformer_fn * bound_relation_plugin::mk_project_fn(const relation_base & r, - unsigned col_cnt, const unsigned * removed_cols) { - return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); - } - - class bound_relation_plugin::rename_fn : public convenient_relation_rename_fn { - public: - rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) - : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { - } - - virtual relation_base * operator()(const relation_base & _r) { - bound_relation const& r = get(_r); - bound_relation_plugin& p = r.get_plugin(); - bound_relation* result = get(p.mk_full(0, get_result_signature())); - result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); - return result; - } - }; - - relation_transformer_fn * bound_relation_plugin::mk_rename_fn(const relation_base & r, - unsigned cycle_len, const unsigned * permutation_cycle) { - if(check_kind(r)) { - return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); - } - return 0; - } - - - class bound_relation_plugin::union_fn : public relation_union_fn { - bool m_is_widen; - public: - union_fn(bool is_widen) : - m_is_widen(is_widen) { - } - virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { - TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); - get(_r).mk_union(get(_src), get(_delta), m_is_widen); - } - }; - - class bound_relation_plugin::union_fn_i : public relation_union_fn { - bool m_is_widen; - public: - union_fn_i(bool is_widen) : - m_is_widen(is_widen) { - } - virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { - TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); - get(_r).mk_union_i(get_interval_relation(_src), get(_delta), m_is_widen); - TRACE("bound_relation", _r.display(tout << "dst':\n");); - } - }; - - - relation_union_fn * bound_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) { - return alloc(union_fn_i, false); - } - if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { - return alloc(union_fn, false); - } - return 0; - } - - relation_union_fn * bound_relation_plugin::mk_widen_fn( - const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) { - return alloc(union_fn_i, true); - } - if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { - return alloc(union_fn, true); - } - return 0; - } - - class bound_relation_plugin::filter_identical_fn : public relation_mutator_fn { - unsigned_vector m_cols; - public: - filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) - : m_cols(col_cnt, identical_cols) {} - - virtual void operator()(relation_base & r) { - for (unsigned i = 1; i < m_cols.size(); ++i) { - get(r).equate(m_cols[0], m_cols[i]); - } - } - }; - - relation_mutator_fn * bound_relation_plugin::mk_filter_identical_fn( - const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { - if(check_kind(t)) { - return alloc(filter_identical_fn, col_cnt, identical_cols); - } - return 0; - } - - class bound_relation_plugin::filter_equal_fn : public relation_mutator_fn { - public: - filter_equal_fn(relation_element const& value, unsigned col) {} - - virtual void operator()(relation_base & r) { } - }; - - relation_mutator_fn * bound_relation_plugin::mk_filter_equal_fn(const relation_base & r, - const relation_element & value, unsigned col) { - if (check_kind(r)) { - return alloc(filter_equal_fn, value, col); - } - return 0; - } - - class bound_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { - enum kind_t { NOT_APPLICABLE, EQ_VAR, EQ_SUB, LT_VAR, LE_VAR, K_FALSE }; - app_ref m_cond; - app_ref m_lt; - arith_util m_arith; - interval_relation* m_interval; - unsigned_vector m_vars; - kind_t m_kind; - - unsigned get_var(expr* a) { - SASSERT(is_var(a)); - return to_var(a)->get_idx(); - } - - // x = z - y - void mk_sub_eq(expr* x, expr* z, expr* y) { - SASSERT(is_var(x)); - SASSERT(is_var(z)); - SASSERT(is_var(y)); - m_vars.push_back(get_var(x)); - m_vars.push_back(get_var(z)); - m_vars.push_back(get_var(y)); - m_kind = EQ_SUB; - } - - void mk_lt(expr* l, expr* r) { - SASSERT(is_var(l)); - SASSERT(is_var(r)); - m_vars.push_back(get_var(l)); - m_vars.push_back(get_var(r)); - m_lt = m_arith.mk_lt(l, r); - m_kind = LT_VAR; - } - - - void mk_le(expr* l, expr* r) { - SASSERT(is_var(l)); - SASSERT(is_var(r)); - m_vars.push_back(get_var(l)); - m_vars.push_back(get_var(r)); - m_kind = LE_VAR; - } - - void mk_eq(expr* l, expr* r) { - m_vars.push_back(get_var(l)); - m_vars.push_back(get_var(r)); - m_kind = EQ_VAR; - } - - public: - - filter_interpreted_fn(ast_manager& m, app* cond) : - m_cond(cond, m), - m_lt(m), m_arith(m), m_interval(0), m_kind(NOT_APPLICABLE) { - expr* l, *r, *r1, *r2, *c2; - rational n1; - if ((m_arith.is_lt(cond, l, r) || m_arith.is_gt(cond, r, l)) && - is_var(l) && is_var(r)) { - mk_lt(l, r); - } - else if (m.is_not(cond, c2) && - (m_arith.is_ge(c2, l, r) || m_arith.is_le(c2, r, l)) && - is_var(l) && is_var(r)) { - mk_lt(l, r); - } - else if ((m_arith.is_le(cond, l, r) || m_arith.is_ge(cond, r, l)) && - is_var(l) && is_var(r)) { - mk_le(l, r); - } - else if (m.is_not(cond, c2) && - (m_arith.is_gt(c2, l, r) || m_arith.is_lt(c2, r, l)) && - is_var(l) && is_var(r)) { - mk_le(l, r); - } - else if (m.is_false(cond)) { - m_kind = K_FALSE; - } - else if (m.is_eq(cond, l, r) && is_var(l) && is_var(r)) { - mk_eq(l, r); - } - else if (m.is_eq(cond, l, r) && - m_arith.is_sub(r, r1, r2) && - is_var(l) && is_var(r1) && is_var(r2)) { - mk_sub_eq(l, r1, r2); - } - else if (m.is_eq(cond, l, r) && - m_arith.is_sub(l, r1, r2) && - is_var(r) && is_var(r1) && is_var(r2)) { - mk_sub_eq(r, r1, r2); - } - else if (m.is_eq(cond, l, r) && - m_arith.is_add(r, r1, r2) && - m_arith.is_numeral(r1, n1) && - n1.is_pos() && is_var(l) && is_var(r2)) { - mk_lt(r2, l); - } - else if (m.is_eq(cond, l, r) && - m_arith.is_add(r, r1, r2) && - m_arith.is_numeral(r2, n1) && - n1.is_pos() && is_var(l) && is_var(r1)) { - mk_lt(r1, l); - } - else { - - } - } - - // - // x = z - y - // x = y - // x < y - // x <= y - // x < y + z - // - - void operator()(relation_base& t) { - TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); - bound_relation& r = get(t); - switch(m_kind) { - case K_FALSE: - r.set_empty(); - break; - case NOT_APPLICABLE: - break; - case EQ_VAR: - r.equate(m_vars[0], m_vars[1]); - break; - case EQ_SUB: - // TBD - break; - case LT_VAR: - r.mk_lt(m_vars[0], m_vars[1]); - break; - case LE_VAR: - r.mk_le(m_vars[0], m_vars[1]); - break; - default: - UNREACHABLE(); - break; - } - TRACE("dl", t.display(tout << "result\n");); - } - - bool supports_attachment(relation_base& t) { - return is_interval_relation(t); - } - - void attach(relation_base& t) { - SASSERT(is_interval_relation(t)); - interval_relation& r = get_interval_relation(t); - m_interval = &r; - } - }; - - relation_mutator_fn * bound_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { - return alloc(filter_interpreted_fn, t.get_plugin().get_ast_manager(), condition); - } - - // ----------------------------- - // bound_relation - - void bound_relation_helper::mk_project_t(uint_set2& t, unsigned_vector const& renaming) { - if (t.lt.empty() && t.le.empty()) { - return; - } - uint_set::iterator it = t.lt.begin(), end = t.lt.end(); - unsigned_vector ltv, lev; - for (; it != end; ++it) { - ltv.push_back(renaming[*it]); - } - it = t.le.begin(), end = t.le.end(); - for (; it != end; ++it) { - lev.push_back(renaming[*it]); - } - TRACE("dl", - tout << "project: "; - for (unsigned i = 0; i < renaming.size(); ++i) - if (renaming[i] == UINT_MAX) tout << i << " "; - tout << ": "; - it = t.lt.begin(); end = t.lt.end(); - for (; it != end; ++it) tout << *it << " "; - tout << " le "; - it = t.le.begin(); end = t.le.end(); - for (; it != end; ++it) tout << *it << " "; - tout << " => "; - for (unsigned i = 0; i < ltv.size(); ++i) tout << ltv[i] << " "; - tout << " le "; - for (unsigned i = 0; i < lev.size(); ++i) tout << lev[i] << " "; - tout << "\n";); - t.lt.reset(); - for (unsigned i = 0; i < ltv.size(); ++i) { - t.lt.insert(ltv[i]); - } - t.le.reset(); - for (unsigned i = 0; i < lev.size(); ++i) { - t.le.insert(lev[i]); - } - } - - bound_relation::bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty): - vector_relation(p, s, is_empty, uint_set2()) - { - } - - - uint_set2 bound_relation::mk_intersect(uint_set2 const& t1, uint_set2 const& t2, bool& is_empty) const { - is_empty = false; - uint_set2 r(t1); - r.lt |= t2.lt; - r.le |= t2.le; - return r; - } - - uint_set2 bound_relation::mk_widen(uint_set2 const& t1, uint_set2 const& t2) const { - return mk_unite(t1, t2); - } - - uint_set2 bound_relation::mk_unite(uint_set2 const& t1, uint_set2 const& t2) const { - uint_set2 s1(t1); - s1.lt &= t2.lt; - s1.le &= t2.le; - return s1; - } - - uint_set2 bound_relation::mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, uint_set2 const& t) const { - unsigned sz = old_eqs.get_num_vars(); - SASSERT(sz == new_eqs.get_num_vars()); - uint_set2 result; - for (unsigned i = 0; i < sz; ++i) { - if (t.lt.contains(i)) { - unsigned j = i; - do { - result.lt.insert(new_eqs.find(j)); - j = old_eqs.next(j); - } - while (j != i); - } - if (t.le.contains(i)) { - unsigned j = i; - do { - result.le.insert(new_eqs.find(j)); - j = old_eqs.next(j); - } - while (j != i); - } - } - return result; - } - - bool bound_relation::is_subset_of(uint_set2 const& t1, uint_set2 const& t2) const { - uint_set2 s1, s2; - normalize(t1, s1); - normalize(t2, s2); - return s1.lt.subset_of(s2.lt) && s1.le.subset_of(s2.le); - } - - void bound_relation::mk_rename_elem(uint_set2& t, unsigned col_cnt, unsigned const* cycle) { - // [ 0 -> 2 -> 3 -> 0] - if (col_cnt == 0) return; - unsigned col1, col2; - col1 = find(cycle[0]); - col2 = find(cycle[col_cnt-1]); - bool has_col2_lt = t.lt.contains(col2); - t.lt.remove(col2); - bool has_col2_le = t.le.contains(col2); - t.le.remove(col2); - for (unsigned i = 0; i + 1 < col_cnt; ++i) { - col1 = find(cycle[i]); - col2 = find(cycle[i+1]); - if (t.lt.contains(col1)) { - t.lt.remove(col1); - t.lt.insert(col2); - } - if (t.le.contains(col1)) { - t.le.remove(col1); - t.le.insert(col2); - } - } - if (has_col2_lt) { - col1 = find(cycle[0]); - t.lt.insert(col1); - } - if (has_col2_le) { - col1 = find(cycle[0]); - t.le.insert(col1); - } - } - - - bool bound_relation::is_full(uint_set2 const& t) const { - return t.lt.empty() && t.le.empty(); - } - - bool bound_relation::is_empty(unsigned index, uint_set2 const& t) const { - return t.lt.contains(find(index)) || t.le.contains(find(index)); - } - - void bound_relation::normalize(uint_set const& src, uint_set& dst) const { - uint_set::iterator it = src.begin(), end = src.end(); - for (; it != end; ++it) { - dst.insert(find(*it)); - } - } - void bound_relation::normalize(uint_set2 const& src, uint_set2& dst) const { - normalize(src.lt, dst.lt); - normalize(src.le, dst.le); - } - - - void bound_relation::mk_lt(unsigned i) { - uint_set2& dst = (*this)[i]; - while (!m_todo.empty()) { - unsigned j = m_todo.back().first; - bool strict = m_todo.back().second; - if (i == j && strict) { - m_todo.reset(); - m_empty = true; - return; - } - m_todo.pop_back(); - if (i == j) { - continue; - } - uint_set2& src = (*m_elems)[j]; - uint_set::iterator it = src.lt.begin(), end = src.lt.end(); - for(; it != end; ++it) { - m_todo.push_back(std::make_pair(*it, true)); - } - it = src.le.begin(), end = src.le.end(); - for(; it != end; ++it) { - m_todo.push_back(std::make_pair(*it, strict)); - } - if (strict) { - dst.lt.insert(j); - } - else { - dst.le.insert(j); - } - } - } - - void bound_relation::mk_lt(unsigned i, unsigned j) { - m_todo.reset(); - i = find(i); - m_todo.push_back(std::make_pair(find(j), true)); - mk_lt(i); - } - - void bound_relation::mk_le(unsigned i, unsigned j) { - m_todo.reset(); - i = find(i); - m_todo.push_back(std::make_pair(find(j), false)); - mk_lt(i); - } - - bool bound_relation::is_lt(unsigned i, unsigned j) const { - return (*this)[i].lt.contains(find(j)); - } - - void bound_relation::add_fact(const relation_fact & f) { - bound_relation r(get_plugin(), get_signature(), false); - for (unsigned i = 0; i < f.size(); ++i) { - scoped_ptr fe = get_plugin().mk_filter_equal_fn(r, f[i], i); - (*fe)(r); - } - mk_union(r, 0, false); - } - - bool bound_relation::contains_fact(const relation_fact & f) const { - if (empty()) { - return false; - } - // this is a very rough approximation. - return true; - } - - bound_relation * bound_relation::clone() const { - bound_relation* result = 0; - if (empty()) { - result = bound_relation_plugin::get(get_plugin().mk_empty(get_signature())); - } - else { - result = bound_relation_plugin::get(get_plugin().mk_full(0, get_signature())); - result->copy(*this); - } - return result; - } - - void bound_relation::mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen) { - unsigned size = get_signature().size(); - for (unsigned i = 0; i < size; ++i) { - if (find(i) != i) { - continue; - } - uint_set2& s = (*this)[i]; - ext_numeral const& lo = src[i].sup(); - if (lo.is_infinite()) { - s.lt.reset(); - s.le.reset(); - continue; - } - uint_set::iterator it = s.lt.begin(), end = s.lt.end(); - for(; it != end; ++it) { - ext_numeral const& hi = src[*it].inf(); - if (hi.is_infinite() || lo.to_rational() >= hi.to_rational()) { - s.lt.remove(*it); - } - } - it = s.le.begin(), end = s.le.end(); - for(; it != end; ++it) { - ext_numeral const& hi = src[*it].inf(); - if (hi.is_infinite() || lo.to_rational() > hi.to_rational()) { - s.le.remove(*it); - } - } - } - } - - bound_relation * bound_relation::complement(func_decl* p) const { - UNREACHABLE(); - return 0; - } - - void bound_relation::to_formula(expr_ref& fml) const { - ast_manager& m = get_plugin().get_ast_manager(); - arith_util& arith = get_plugin().m_arith; - basic_simplifier_plugin& bsimp = get_plugin().m_bsimp; - expr_ref_vector conjs(m); - relation_signature const& sig = get_signature(); - for (unsigned i = 0; i < sig.size(); ++i) { - if (i != find(i)) { - conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), m.mk_var(find(i), sig[find(i)]))); - continue; - } - uint_set2 const& upper = (*this)[i]; - uint_set::iterator it = upper.lt.begin(), end = upper.lt.end(); - for (; it != end; ++it) { - conjs.push_back(arith.mk_lt(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it]))); - } - it = upper.le.begin(), end = upper.le.end(); - for (; it != end; ++it) { - conjs.push_back(arith.mk_le(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it]))); - } - } - bsimp.mk_and(conjs.size(), conjs.c_ptr(), fml); - } - - - void bound_relation::display_index(unsigned i, uint_set2 const& src, std::ostream & out) const { - uint_set::iterator it = src.lt.begin(), end = src.lt.end(); - out << "#" << i; - if (!src.lt.empty()) { - out << " < "; - for(; it != end; ++it) { - out << *it << " "; - } - } - if (!src.le.empty()) { - it = src.le.begin(), end = src.le.end(); - out << " <= "; - for(; it != end; ++it) { - out << *it << " "; - } - } - if (src.lt.empty() && src.le.empty()) { - out << " < oo"; - } - out << "\n"; - } - - bound_relation_plugin& bound_relation::get_plugin() const { - return dynamic_cast(relation_base::get_plugin()); - } - - -}; - - diff --git a/src/muz/dl_bound_relation.h b/src/muz/dl_bound_relation.h deleted file mode 100644 index 04479b3b6..000000000 --- a/src/muz/dl_bound_relation.h +++ /dev/null @@ -1,176 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_bound_relation.h - -Abstract: - - Basic (strict upper) bound relation. - -Author: - - Nikolaj Bjorner (nbjorner) 2010-2-11 - -Revision History: - ---*/ -#ifndef _DL_BOUND_RELATION_H_ -#define _DL_BOUND_RELATION_H_ - -#include "dl_context.h" -#include "uint_set.h" -#include "dl_vector_relation.h" -#include "dl_interval_relation.h" -#include "arith_decl_plugin.h" -#include "basic_simplifier_plugin.h" - -namespace datalog { - - class bound_relation; - - class bound_relation_plugin : public relation_plugin { - friend class bound_relation; - class join_fn; - class project_fn; - class rename_fn; - class union_fn; - class union_fn_i; - class filter_equal_fn; - class filter_identical_fn; - class filter_interpreted_fn; - class filter_intersection_fn; - arith_util m_arith; - basic_simplifier_plugin m_bsimp; - public: - bound_relation_plugin(relation_manager& m); - virtual bool can_handle_signature(const relation_signature & s); - static symbol get_name() { return symbol("bound_relation"); } - virtual relation_base * mk_empty(const relation_signature & s); - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - - virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } - - -#if 0 - virtual intersection_filter_fn * mk_filter_by_intersection_fn( - const relation_base & t, - const relation_base & src, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * src_cols) { - return 0; - } -#endif - - static bound_relation* get(relation_base* r); - private: - static bound_relation& get(relation_base& r); - static bound_relation const & get(relation_base const& r); - - - static bool is_interval_relation(relation_base const& r); - static interval_relation& get_interval_relation(relation_base& r); - static interval_relation const& get_interval_relation(relation_base const& r); - }; - - struct uint_set2 { - uint_set lt; - uint_set le; - uint_set2(uint_set2 const& other):lt(other.lt), le(other.le) {} - uint_set2() {} - bool operator==(const uint_set2& other) const { - return other.lt == lt && other.le == le; - } - bool operator!=(const uint_set2& other) const { - return other.lt != lt || other.le != le; - } - }; - - inline std::ostream & operator<<(std::ostream & target, const uint_set2 & s) { - return target << s.lt << " " << s.le; - } - - - class bound_relation_helper { - public: - static void mk_project_t(uint_set2& t, unsigned_vector const& renaming); - }; - - class bound_relation : public vector_relation { - friend class bound_relation_plugin; - svector > m_todo; - - public: - bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty); - bound_relation& operator=(bound_relation const& other); - - virtual bool empty() const { return m_empty; } - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual bound_relation * clone() const; - virtual bound_relation * complement(func_decl* p) const; - virtual void to_formula(expr_ref& fml) const; - bound_relation_plugin& get_plugin() const; - - void mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen); - - void mk_lt(unsigned i, unsigned j); - - void mk_lt(unsigned i); - - void mk_le(unsigned i, unsigned j); - - bool is_lt(unsigned i, unsigned j) const; - - virtual bool is_precise() const { return false; } - - private: - typedef uint_set2 T; - virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const; - - virtual T mk_widen(T const& t1, T const& t2) const; - - virtual T mk_unite(T const& t1, T const& t2) const; - - virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, T const& t) const; - - virtual void mk_rename_elem(T& i, unsigned col_cnt, unsigned const* cycle); - - - virtual bool is_subset_of(T const& t1, T const& t2) const; - - virtual bool is_full(T const& t) const; - - virtual bool is_empty(unsigned idx, T const& t) const; - - virtual void display_index(unsigned idx, T const& t, std::ostream& out) const; - - void normalize(T const& src, T& dst) const; - - void normalize(uint_set const& src, uint_set& dst) const; - - - - }; - -}; - -#endif - diff --git a/src/muz/dl_check_table.cpp b/src/muz/dl_check_table.cpp deleted file mode 100644 index ea4003e5f..000000000 --- a/src/muz/dl_check_table.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_check_table.cpp - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) 2010-11-15 - - -Revision History: - ---*/ - - -#include "dl_check_table.h" -#include "dl_table.h" - - -namespace datalog { - - bool check_table_plugin::can_handle_signature(table_signature const& s) { - return m_tocheck.can_handle_signature(s) && m_checker.can_handle_signature(s); - } - - - check_table & check_table_plugin::get(table_base& r) { - return static_cast(r); - } - - check_table const & check_table_plugin::get(table_base const& r) { - return static_cast(r); - } - - table_base& check_table_plugin::checker(table_base& r) { return *get(r).m_checker; } - table_base const& check_table_plugin::checker(table_base const& r) { return *get(r).m_checker; } - table_base* check_table_plugin::checker(table_base* r) { return r?(get(*r).m_checker):0; } - table_base const* check_table_plugin::checker(table_base const* r) { return r?(get(*r).m_checker):0; } - table_base& check_table_plugin::tocheck(table_base& r) { return *get(r).m_tocheck; } - table_base const& check_table_plugin::tocheck(table_base const& r) { return *get(r).m_tocheck; } - table_base* check_table_plugin::tocheck(table_base* r) { return r?(get(*r).m_tocheck):0; } - table_base const* check_table_plugin::tocheck(table_base const* r) { return r?(get(*r).m_tocheck):0; } - - table_base * check_table_plugin::mk_empty(const table_signature & s) { - IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); - table_base* checker = m_checker.mk_empty(s); - table_base* tocheck = m_tocheck.mk_empty(s); - return alloc(check_table, *this, s, tocheck, checker); - } - - class check_table_plugin::join_fn : public table_join_fn { - scoped_ptr m_tocheck; - scoped_ptr m_checker; - public: - join_fn(check_table_plugin& p, - const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - m_tocheck = p.get_manager().mk_join_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2); - m_checker = p.get_manager().mk_join_fn(checker(t1), checker(t2), col_cnt, cols1, cols2); - } - - virtual table_base* operator()(const table_base & t1, const table_base & t2) { - IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); - table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); - table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); - check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); - return result; - } - }; - - table_join_fn * check_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (!check_kind(t1) || !check_kind(t2)) { - return 0; - } - return alloc(join_fn, *this, t1, t2, col_cnt, cols1, cols2); - } - - class check_table_plugin::join_project_fn : public table_join_fn { - scoped_ptr m_tocheck; - scoped_ptr m_checker; - public: - join_project_fn(check_table_plugin& p, const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols) { - m_tocheck = p.get_manager().mk_join_project_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); - m_checker = p.get_manager().mk_join_project_fn(checker(t1), checker(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); - } - - virtual table_base* operator()(const table_base & t1, const table_base & t2) { - table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); - table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); - check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); - return result; - } - }; - - table_join_fn * check_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols) { - if (!check_kind(t1) || !check_kind(t2)) { - return 0; - } - return alloc(join_project_fn, *this, t1, t2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols); - } - - class check_table_plugin::union_fn : public table_union_fn { - scoped_ptr m_tocheck; - scoped_ptr m_checker; - public: - union_fn(check_table_plugin& p, table_base const& tgt, const table_base& src, table_base const* delta) { - m_tocheck = p.get_manager().mk_union_fn(tocheck(tgt), tocheck(src), tocheck(delta)); - m_checker = p.get_manager().mk_union_fn(checker(tgt), checker(src), checker(delta)); - } - - virtual void operator()(table_base& tgt, const table_base& src, table_base* delta) { - IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); - (*m_tocheck)(tocheck(tgt), tocheck(src), tocheck(delta)); - (*m_checker)(checker(tgt), checker(src), checker(delta)); - get(tgt).well_formed(); - if (delta) { - get(*delta).well_formed(); - } - } - }; - - table_union_fn * check_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { - if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; - } - return alloc(union_fn, *this, tgt, src, delta); - - } - - class check_table_plugin::project_fn : public table_transformer_fn { - scoped_ptr m_checker; - scoped_ptr m_tocheck; - public: - project_fn(check_table_plugin& p, const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { - m_checker = p.get_manager().mk_project_fn(checker(t), col_cnt, removed_cols); - m_tocheck = p.get_manager().mk_project_fn(tocheck(t), col_cnt, removed_cols); - } - - table_base* operator()(table_base const& src) { - table_base* tchecker = (*m_checker)(checker(src)); - table_base* ttocheck = (*m_tocheck)(tocheck(src)); - check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); - return result; - } - }; - - table_transformer_fn * check_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { - if (!check_kind(t)) { - return 0; - } - return alloc(project_fn, *this, t, col_cnt, removed_cols); - } - - class check_table_plugin::select_equal_and_project_fn : public table_transformer_fn { - scoped_ptr m_checker; - scoped_ptr m_tocheck; - public: - select_equal_and_project_fn(check_table_plugin& p, const table_base & t, const table_element & value, unsigned col) { - m_checker = p.get_manager().mk_select_equal_and_project_fn(checker(t), value, col); - m_tocheck = p.get_manager().mk_select_equal_and_project_fn(tocheck(t), value, col); - } - - table_base* operator()(table_base const& src) { - table_base* tchecker = (*m_checker)(checker(src)); - table_base* ttocheck = (*m_tocheck)(tocheck(src)); - check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); - return result; - } - }; - - table_transformer_fn * check_table_plugin::mk_select_equal_and_project_fn(const table_base & t, - const table_element & value, unsigned col) { - if (!check_kind(t)) { - return 0; - } - return alloc(select_equal_and_project_fn, *this, t, value, col); - } - - class check_table_plugin::rename_fn : public table_transformer_fn { - scoped_ptr m_checker; - scoped_ptr m_tocheck; - public: - rename_fn(check_table_plugin& p, const table_base & t, unsigned cycle_len, unsigned const* cycle) { - m_checker = p.get_manager().mk_rename_fn(checker(t), cycle_len, cycle); - m_tocheck = p.get_manager().mk_rename_fn(tocheck(t), cycle_len, cycle); - } - - table_base* operator()(table_base const& src) { - IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); - table_base* tchecker = (*m_checker)(checker(src)); - table_base* ttocheck = (*m_tocheck)(tocheck(src)); - check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); - return result; - } - }; - - table_transformer_fn * check_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) { - if (!check_kind(t)) { - return 0; - } - return alloc(rename_fn, *this, t, len, cycle); - } - - class check_table_plugin::filter_identical_fn : public table_mutator_fn { - scoped_ptr m_checker; - scoped_ptr m_tocheck; - public: - filter_identical_fn(check_table_plugin& p, const table_base & t,unsigned cnt, unsigned const* cols) - { - m_checker = p.get_manager().mk_filter_identical_fn(checker(t), cnt, cols); - m_tocheck = p.get_manager().mk_filter_identical_fn(tocheck(t), cnt, cols); - } - - void operator()(table_base & t) { - (*m_checker)(checker(t)); - (*m_tocheck)(tocheck(t)); - get(t).well_formed(); - } - }; - - table_mutator_fn * check_table_plugin::mk_filter_identical_fn(const table_base & t, unsigned col_cnt, - const unsigned * identical_cols) { - if (check_kind(t)) { - return alloc(filter_identical_fn, *this, t, col_cnt, identical_cols); - } - return 0; - } - - class check_table_plugin::filter_equal_fn : public table_mutator_fn { - scoped_ptr m_checker; - scoped_ptr m_tocheck; - public: - filter_equal_fn(check_table_plugin& p, const table_base & t, const table_element & v, unsigned col) - { - m_checker = p.get_manager().mk_filter_equal_fn(checker(t), v, col); - m_tocheck = p.get_manager().mk_filter_equal_fn(tocheck(t), v, col); - } - - virtual void operator()(table_base& src) { - (*m_checker)(checker(src)); - (*m_tocheck)(tocheck(src)); - get(src).well_formed(); - } - }; - - table_mutator_fn * check_table_plugin::mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col) { - if (check_kind(t)) { - return alloc(filter_equal_fn, *this, t, value, col); - } - return 0; - } - - class check_table_plugin::filter_interpreted_fn : public table_mutator_fn { - scoped_ptr m_checker; - scoped_ptr m_tocheck; - public: - filter_interpreted_fn(check_table_plugin& p, const table_base & t, app * condition) - { - m_checker = p.get_manager().mk_filter_interpreted_fn(checker(t), condition); - m_tocheck = p.get_manager().mk_filter_interpreted_fn(tocheck(t), condition); - } - - virtual void operator()(table_base& src) { - (*m_checker)(checker(src)); - (*m_tocheck)(tocheck(src)); - get(src).well_formed(); - } - }; - - table_mutator_fn * check_table_plugin::mk_filter_interpreted_fn(const table_base & t, app * condition) { - if (check_kind(t)) { - return alloc(filter_interpreted_fn, *this, t, condition); - } - return 0; - } - - class check_table_plugin::filter_interpreted_and_project_fn : public table_transformer_fn { - scoped_ptr m_checker; - scoped_ptr m_tocheck; - public: - filter_interpreted_and_project_fn(check_table_plugin& p, const table_base & t, app * condition, - unsigned removed_col_cnt, const unsigned * removed_cols) - { - m_checker = p.get_manager().mk_filter_interpreted_and_project_fn(checker(t), condition, removed_col_cnt, removed_cols); - m_tocheck = p.get_manager().mk_filter_interpreted_and_project_fn(tocheck(t), condition, removed_col_cnt, removed_cols); - } - - table_base* operator()(table_base const& src) { - table_base* tchecker = (*m_checker)(checker(src)); - table_base* ttocheck = (*m_tocheck)(tocheck(src)); - check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); - return result; - } - }; - - table_transformer_fn * check_table_plugin::mk_filter_interpreted_and_project_fn(const table_base & t, - app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { - if (check_kind(t)) { - return alloc(filter_interpreted_and_project_fn, *this, t, condition, removed_col_cnt, removed_cols); - } - return 0; - } - - class check_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { - scoped_ptr m_checker; - scoped_ptr m_tocheck; - public: - filter_by_negation_fn( - check_table_plugin& p, - const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols) { - m_checker = p.get_manager().mk_filter_by_negation_fn(checker(t), checker(negated_obj), joined_col_cnt, t_cols, negated_cols); - m_tocheck = p.get_manager().mk_filter_by_negation_fn(tocheck(t), tocheck(negated_obj), joined_col_cnt, t_cols, negated_cols); - } - - virtual void operator()(table_base& src, table_base const& negated_obj) { - IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); - (*m_checker)(checker(src), checker(negated_obj)); - (*m_tocheck)(tocheck(src), tocheck(negated_obj)); - get(src).well_formed(); - } - - }; - - table_intersection_filter_fn * check_table_plugin::mk_filter_by_negation_fn(const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols) { - if (check_kind(t) && check_kind(negated_obj)) { - return alloc(filter_by_negation_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); - } - return 0; - } - - // ------------------ - // check_table - - - check_table::check_table(check_table_plugin & p, const table_signature & sig): - table_base(p, sig) { - (well_formed()); - } - - check_table::check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker): - table_base(p, sig), - m_checker(checker), - m_tocheck(tocheck) { - well_formed(); - } - - check_table::~check_table() { - m_tocheck->deallocate(); - m_checker->deallocate(); - } - - bool check_table::well_formed() const { - get_plugin().m_count++; - iterator it = m_tocheck->begin(), end = m_tocheck->end(); - for (; it != end; ++it) { - table_fact fact; - it->get_fact(fact); - if (!m_checker->contains_fact(fact)) { - m_tocheck->display(verbose_stream()); - m_checker->display(verbose_stream()); - verbose_stream() << get_plugin().m_count << "\n"; - UNREACHABLE(); - fatal_error(0); - return false; - } - } - iterator it2 = m_checker->begin(), end2 = m_checker->end(); - for (; it2 != end2; ++it2) { - table_fact fact; - it2->get_fact(fact); - if (!m_tocheck->contains_fact(fact)) { - m_tocheck->display(verbose_stream()); - m_checker->display(verbose_stream()); - verbose_stream() << get_plugin().m_count << "\n"; - UNREACHABLE(); - fatal_error(0); - return false; - } - } - return true; - } - - bool check_table::empty() const { - if (m_tocheck->empty() != m_checker->empty()) { - m_tocheck->display(verbose_stream()); - m_checker->display(verbose_stream()); - verbose_stream() << get_plugin().m_count << "\n"; - fatal_error(0); - } - return m_tocheck->empty(); - } - - - void check_table::add_fact(const table_fact & f) { - IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); - m_tocheck->add_fact(f); - m_checker->add_fact(f); - well_formed(); - } - - void check_table::remove_fact(const table_element* f) { - IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); - m_tocheck->remove_fact(f); - m_checker->remove_fact(f); - well_formed(); - } - - bool check_table::contains_fact(const table_fact & f) const { - return m_checker->contains_fact(f); - } - - table_base * check_table::clone() const { - IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); - check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->clone(), m_checker->clone()); - return result; - } - - table_base * check_table::complement(func_decl* p, const table_element * func_columns) const { - check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->complement(p, func_columns), m_checker->complement(p, func_columns)); - return result; - } - -}; - diff --git a/src/muz/dl_check_table.h b/src/muz/dl_check_table.h deleted file mode 100644 index e4f439590..000000000 --- a/src/muz/dl_check_table.h +++ /dev/null @@ -1,135 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_check_table.h - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) 2010-11-15 - - -Revision History: - ---*/ - -#ifndef _DL_CHECK_TABLE_H_ -#define _DL_CHECK_TABLE_H_ - -#include "dl_base.h" -#include "dl_decl_plugin.h" -#include "dl_relation_manager.h" - -namespace datalog { - class check_table; - - class check_table_plugin : public table_plugin { - friend class check_table; - table_plugin& m_checker; - table_plugin& m_tocheck; - unsigned m_count; - protected: - class join_fn; - class join_project_fn; - class union_fn; - class transformer_fn; - class rename_fn; - class project_fn; - class select_equal_and_project_fn; - class filter_equal_fn; - class filter_identical_fn; - class filter_interpreted_fn; - class filter_interpreted_and_project_fn; - class filter_by_negation_fn; - - public: - check_table_plugin(relation_manager & manager, symbol const& checker, symbol const& tocheck) - : table_plugin(symbol("check"), manager), - m_checker(*manager.get_table_plugin(checker)), - m_tocheck(*manager.get_table_plugin(tocheck)), m_count(0) {} - - virtual table_base * mk_empty(const table_signature & s); - - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols); - virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta); - virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, - const table_element & value, unsigned col); - virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, - unsigned col); - virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); - virtual table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, - app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); - virtual table_intersection_filter_fn * mk_filter_by_negation_fn( - const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - - virtual bool can_handle_signature(table_signature const& s); - - private: - static check_table& get(table_base& r); - - static check_table const & get(table_base const& r); - - static table_base& checker(table_base& r); - static table_base const& checker(table_base const& r); - static table_base* checker(table_base* r); - static table_base const* checker(table_base const* r); - static table_base& tocheck(table_base& r); - static table_base const& tocheck(table_base const& r); - static table_base* tocheck(table_base* r); - static table_base const* tocheck(table_base const* r); - }; - - class check_table : public table_base { - friend class check_table_plugin; - - table_base* m_checker; - table_base* m_tocheck; - - check_table(check_table_plugin & p, const table_signature & sig); - check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker); - - virtual ~check_table(); - - bool well_formed() const; - - public: - - check_table_plugin & get_plugin() const { - return static_cast(table_base::get_plugin()); - } - - virtual bool empty() const; - virtual void add_fact(const table_fact & f); - virtual void remove_fact(const table_element* fact); - virtual bool contains_fact(const table_fact & f) const; - virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; - virtual table_base * clone() const; - - virtual iterator begin() const { SASSERT(well_formed()); return m_tocheck->begin(); } - virtual iterator end() const { return m_tocheck->end(); } - - virtual unsigned get_size_estimate_rows() const { return m_tocheck->get_size_estimate_rows(); } - virtual unsigned get_size_estimate_bytes() const { return m_tocheck->get_size_estimate_bytes(); } - }; - - }; - - #endif /* _DL_CHECK_TABLE_H_ */ diff --git a/src/muz/dl_cmds.h b/src/muz/dl_cmds.h deleted file mode 100644 index d71b319c4..000000000 --- a/src/muz/dl_cmds.h +++ /dev/null @@ -1,37 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - dl_cmds.h - -Abstract: - Datalog commands for SMT2 front-end. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-11-17 - -Notes: - ---*/ -#ifndef _DL_CMDS_H_ -#define _DL_CMDS_H_ - -#include "ast.h" - -class cmd_context; - -struct dl_collected_cmds { - expr_ref_vector m_rules; - svector m_names; - expr_ref_vector m_queries; - func_decl_ref_vector m_rels; - dl_collected_cmds(ast_manager& m) : m_rules(m), m_queries(m), m_rels(m) {} -}; - -void install_dl_cmds(cmd_context & ctx); -void install_dl_collect_cmds(dl_collected_cmds& collected_cmds, cmd_context& ctx); - - -#endif diff --git a/src/muz/dl_context.cpp b/src/muz/dl_context.cpp index f5bfb6b5e..5e707e315 100644 --- a/src/muz/dl_context.cpp +++ b/src/muz/dl_context.cpp @@ -23,28 +23,6 @@ Revision History: #include"basic_simplifier_plugin.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" -#include"dl_table.h" -#include"dl_table_relation.h" -#include"dl_rule_transformer.h" -#include"dl_mk_coi_filter.h" -#include"dl_mk_explanations.h" -#include"dl_mk_filter_rules.h" -#include"dl_mk_interp_tail_simplifier.h" -#include"dl_mk_rule_inliner.h" -#include"dl_mk_simple_joins.h" -#include"dl_mk_similarity_compressor.h" -#include"dl_mk_unbound_compressor.h" -#include"dl_mk_subsumption_checker.h" -#include"dl_mk_partial_equiv.h" -#include"dl_mk_bit_blast.h" -#include"dl_mk_array_blast.h" -#include"dl_mk_karr_invariants.h" -#include"dl_mk_magic_symbolic.h" -#include"dl_mk_quantifier_abstraction.h" -#include"dl_mk_quantifier_instantiation.h" -#include"dl_mk_scale.h" -#include"dl_compiler.h" -#include"dl_instruction.h" #include"dl_context.h" #include"for_each_expr.h" #include"ast_smt_pp.h" @@ -217,8 +195,9 @@ namespace datalog { // // ----------------------------------- - context::context(ast_manager & m, smt_params& fp, params_ref const& pa): + context::context(ast_manager & m, register_engine_base& re, smt_params& fp, params_ref const& pa): m(m), + m_register_engine(re), m_fparams(fp), m_params_ref(pa), m_params(m_params_ref), @@ -248,6 +227,7 @@ namespace datalog { m_last_answer(m), m_engine_type(LAST_ENGINE), m_cancel(false) { + re.set_context(this); } context::~context() { @@ -780,29 +760,6 @@ namespace datalog { m_closed = false; } - void context::transform_rules() { - m_transf.reset(); - m_transf.register_plugin(alloc(mk_coi_filter, *this)); - m_transf.register_plugin(alloc(mk_filter_rules, *this)); - m_transf.register_plugin(alloc(mk_simple_joins, *this)); - if (unbound_compressor()) { - m_transf.register_plugin(alloc(mk_unbound_compressor, *this)); - } - if (similarity_compressor()) { - m_transf.register_plugin(alloc(mk_similarity_compressor, *this)); - } - m_transf.register_plugin(alloc(mk_partial_equivalence_transformer, *this)); - m_transf.register_plugin(alloc(mk_rule_inliner, *this)); - m_transf.register_plugin(alloc(mk_interp_tail_simplifier, *this)); - - if (get_params().bit_blast()) { - m_transf.register_plugin(alloc(mk_bit_blast, *this, 22000)); - m_transf.register_plugin(alloc(mk_interp_tail_simplifier, *this, 21000)); - } - - transform_rules(m_transf); - } - void context::transform_rules(rule_transformer::plugin* plugin) { rule_transformer transformer(*this); transformer.register_plugin(plugin); @@ -834,45 +791,6 @@ namespace datalog { } void context::apply_default_transformation() { - ensure_closed(); - m_transf.reset(); - m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this)); - m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this)); - - m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 35005)); - m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 35000)); - m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34990)); - m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34980)); - - //and another round of inlining - m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34975)); - m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34970)); - m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34960)); - m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34950)); - - m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34940)); - m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34930)); - m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34920)); - m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34910)); - m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34900)); - m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34890)); - m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34880)); - - - if (get_params().quantify_arrays()) { - m_transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, *this, 33000)); - m_transf.register_plugin(alloc(datalog::mk_array_blast, *this, 32500)); - } - m_transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, *this, 32000)); - - m_transf.register_plugin(alloc(datalog::mk_bit_blast, *this, 35000)); - m_transf.register_plugin(alloc(datalog::mk_array_blast, *this, 36000)); - m_transf.register_plugin(alloc(datalog::mk_karr_invariants, *this, 36010)); - if (get_params().magic()) { - m_transf.register_plugin(alloc(datalog::mk_magic_symbolic, *this, 36020)); - } - m_transf.register_plugin(alloc(datalog::mk_scale, *this, 36030)); - transform_rules(m_transf); } void context::collect_params(param_descrs& p) { @@ -1024,39 +942,18 @@ namespace datalog { void context::ensure_engine() { if (!m_engine.get()) { - switch (get_engine()) { - case PDR_ENGINE: - case QPDR_ENGINE: - m_engine = alloc(pdr::dl_interface, *this); - break; - case DATALOG_ENGINE: - m_rel = alloc(rel_context, *this); - m_engine = m_rel; - break; - case BMC_ENGINE: - case QBMC_ENGINE: - m_engine = alloc(bmc, *this); - break; - case TAB_ENGINE: - m_engine = alloc(tab, *this); - break; - case CLP_ENGINE: - m_engine = alloc(clp, *this); - break; - case LAST_ENGINE: - UNREACHABLE(); + m_engine = m_register_engine.mk_engine(get_engine()); + + // break abstraction. + if (get_engine() == DATALOG_ENGINE) { + m_rel = dynamic_cast(m_engine.get()); } - } + } } lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { ensure_engine(); - if (m_rel) { - return m_rel->query(num_rels, rels); - } - else { - return l_undef; - } + return m_engine->query(num_rels, rels); } expr* context::get_answer_as_formula() { @@ -1073,6 +970,11 @@ namespace datalog { m_engine->display_certificate(out); } + void context::display(std::ostream & out) const { + display_rules(out); + if (m_rel) m_rel->display_facts(out); + } + void context::display_profile(std::ostream& out) const { out << "\n---------------\n"; out << "Original rules\n"; diff --git a/src/muz/dl_context.h b/src/muz/dl_context.h index a3c7e583c..97a371f5a 100644 --- a/src/muz/dl_context.h +++ b/src/muz/dl_context.h @@ -28,15 +28,9 @@ Revision History: #include"th_rewriter.h" #include"str_hashtable.h" #include"var_subst.h" -#include"dl_base.h" #include"dl_costs.h" #include"dl_decl_plugin.h" -#include"dl_relation_manager.h" #include"dl_rule_set.h" -#include"pdr_dl_interface.h" -#include"dl_bmc_engine.h" -#include"tab_context.h" -#include"rel_context.h" #include"lbool.h" #include"statistics.h" #include"params.h" @@ -47,7 +41,7 @@ Revision History: #include"dl_rule_transformer.h" #include"expr_abstract.h" #include"expr_functors.h" -#include"clp_context.h" +#include"dl_engine_base.h" namespace datalog { @@ -60,6 +54,84 @@ namespace datalog { CANCELED }; + class relation_manager; + + typedef sort * relation_sort; + typedef uint64 table_element; + typedef svector table_fact; + + typedef app * relation_element; + typedef app_ref relation_element_ref; + + class relation_fact : public app_ref_vector { + public: + class el_proxy { + friend class relation_fact; + + relation_fact & m_parent; + unsigned m_idx; + + el_proxy(relation_fact & parent, unsigned idx) : m_parent(parent), m_idx(idx) {} + public: + operator relation_element() const { + return m_parent.get(m_idx); + } + relation_element operator->() const { + return m_parent.get(m_idx); + } + relation_element operator=(const relation_element & val) const { + m_parent.set(m_idx, val); + return m_parent.get(m_idx); + } + relation_element operator=(const el_proxy & val) { + m_parent.set(m_idx, val); + return m_parent.get(m_idx); + } + }; + + typedef const relation_element * iterator; + + relation_fact(ast_manager & m) : app_ref_vector(m) {} + relation_fact(ast_manager & m, unsigned sz) : app_ref_vector(m) { resize(sz); } + relation_fact(context & ctx); + + iterator begin() const { return c_ptr(); } + iterator end() const { return c_ptr()+size(); } + + relation_element operator[](unsigned i) const { return get(i); } + el_proxy operator[](unsigned i) { return el_proxy(*this, i); } + }; + + // attempt to modularize context code. + class rel_context_base : public engine_base { + public: + rel_context_base(ast_manager& m, char const* name): engine_base(m, name) {} + virtual ~rel_context_base() {} + virtual relation_manager & get_rmanager() = 0; + virtual const relation_manager & get_rmanager() const = 0; + virtual relation_base & get_relation(func_decl * pred) = 0; + virtual relation_base * try_get_relation(func_decl * pred) const = 0; + virtual bool is_empty_relation(func_decl* pred) const = 0; + virtual expr_ref try_get_formula(func_decl * pred) const = 0; + virtual void display_output_facts(rule_set const& rules, std::ostream & out) const = 0; + virtual void display_facts(std::ostream & out) const = 0; + virtual void display_profile(std::ostream& out) = 0; + virtual void restrict_predicates(func_decl_set const& predicates) = 0; + virtual bool result_contains_fact(relation_fact const& f) = 0; + virtual void add_fact(func_decl* pred, relation_fact const& fact) = 0; + virtual void add_fact(func_decl* pred, table_fact const& fact) = 0; + virtual bool has_facts(func_decl * pred) const = 0; + virtual void store_relation(func_decl * pred, relation_base * rel) = 0; + virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) = 0; + virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, + symbol const * relation_names) = 0; + virtual bool output_profile() const = 0; + virtual void collect_non_empty_predicates(func_decl_set& preds) = 0; + virtual void transform_rules() = 0; + virtual bool try_get_size(func_decl* pred, unsigned& rel_sz) const = 0; + virtual lbool saturate() = 0; + }; + class context { public: typedef unsigned finite_element; @@ -93,6 +165,7 @@ namespace datalog { ast_manager & m; + register_engine_base& m_register_engine; smt_params & m_fparams; params_ref m_params_ref; fixedpoint_params m_params; @@ -122,7 +195,7 @@ namespace datalog { model_converter_ref m_mc; proof_converter_ref m_pc; - rel_context* m_rel; + rel_context_base* m_rel; scoped_ptr m_engine; bool m_closed; @@ -143,7 +216,7 @@ namespace datalog { public: - context(ast_manager & m, smt_params& fp, params_ref const& p = params_ref()); + context(ast_manager & m, register_engine_base& re, smt_params& fp, params_ref const& p = params_ref()); ~context(); void reset(); @@ -160,6 +233,7 @@ namespace datalog { smt_params & get_fparams() const { return m_fparams; } fixedpoint_params const& get_params() const { return m_params; } DL_ENGINE get_engine() { configure_engine(); return m_engine_type; } + register_engine_base& get_register_engine() { return m_register_engine; } th_rewriter& get_rewriter() { return m_rewriter; } var_subst & get_var_subst() { return m_var_subst; } dl_decl_util & get_decl_util() { return m_decl_util; } @@ -355,7 +429,6 @@ namespace datalog { proof_converter_ref& get_proof_converter() { return m_pc; } void add_proof_converter(proof_converter* pc) { m_pc = concat(m_pc.get(), pc); } - void transform_rules(); void transform_rules(rule_transformer& transf); void transform_rules(rule_transformer::plugin* plugin); void replace_rules(rule_set const& rs); @@ -371,10 +444,7 @@ namespace datalog { m_rule_set.display(out); } - void display(std::ostream & out) const { - display_rules(out); - if (m_rel) m_rel->display_facts(out); - } + void display(std::ostream & out) const; void display_smt2(unsigned num_queries, expr* const* queries, std::ostream& out); @@ -459,7 +529,7 @@ namespace datalog { */ bool result_contains_fact(relation_fact const& f); - rel_context* get_rel_context() { ensure_engine(); return m_rel; } + rel_context_base* get_rel_context() { ensure_engine(); return m_rel; } private: diff --git a/src/muz/dl_engine_base.h b/src/muz/dl_engine_base.h new file mode 100644 index 000000000..52dc9acd4 --- /dev/null +++ b/src/muz/dl_engine_base.h @@ -0,0 +1,82 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_engine_base.h + +Abstract: + + Base class for Datalog engines. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-08-28 + +Revision History: + +--*/ +#ifndef _DL_ENGINE_BASE_H_ +#define _DL_ENGINE_BASE_H_ + +#include "model.h" + +namespace datalog { + enum DL_ENGINE { + DATALOG_ENGINE, + PDR_ENGINE, + QPDR_ENGINE, + BMC_ENGINE, + QBMC_ENGINE, + TAB_ENGINE, + CLP_ENGINE, + LAST_ENGINE + }; + + class engine_base { + ast_manager& m; + std::string m_name; + public: + engine_base(ast_manager& m, char const* name): m(m), m_name(name) {} + virtual ~engine_base() {} + + virtual expr_ref get_answer() = 0; + virtual lbool query(expr* q) = 0; + virtual lbool query(unsigned num_rels, func_decl*const* rels) { return l_undef; } + + virtual void reset_statistics() {} + virtual void display_profile(std::ostream& out) const {} + virtual void collect_statistics(statistics& st) const {} + virtual unsigned get_num_levels(func_decl* pred) { + throw default_exception(std::string("get_num_levels is not supported for ") + m_name); + } + virtual expr_ref get_cover_delta(int level, func_decl* pred) { + throw default_exception(std::string("operation is not supported for ") + m_name); + } + virtual void add_cover(int level, func_decl* pred, expr* property) { + throw default_exception(std::string("operation is not supported for ") + m_name); + } + virtual void display_certificate(std::ostream& out) const { + throw default_exception(std::string("certificates are not supported for ") + m_name); + } + virtual model_ref get_model() { + return model_ref(alloc(model, m)); + } + virtual proof_ref get_proof() { + return proof_ref(m.mk_asserted(m.mk_true()), m); + } + virtual void updt_params() {} + virtual void cancel() {} + virtual void cleanup() {} + }; + + class context; + + class register_engine_base { + public: + virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0; + virtual void set_context(context* ctx) = 0; + }; +} + +#endif diff --git a/src/muz/dl_external_relation.cpp b/src/muz/dl_external_relation.cpp deleted file mode 100644 index f32509473..000000000 --- a/src/muz/dl_external_relation.cpp +++ /dev/null @@ -1,456 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_external_relation.cpp - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) 2010-05-10 - -Revision History: - ---*/ - -#include "debug.h" -#include "ast_pp.h" -#include "dl_context.h" -#include "dl_external_relation.h" -#include "dl_decl_plugin.h" - -namespace datalog { - - external_relation::external_relation(external_relation_plugin & p, const relation_signature & s, expr* r) - : relation_base(p, s), - m_rel(r, p.get_ast_manager()), - m_select_fn(p.get_ast_manager()), - m_store_fn(p.get_ast_manager()), - m_is_empty_fn(p.get_ast_manager()) - { - } - - external_relation::~external_relation() { - } - - void external_relation::mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const { - ast_manager& m = m_rel.get_manager(); - family_id fid = get_plugin().get_family_id(); - ptr_vector args; - args.push_back(m_rel); - for (unsigned i = 0; i < f.size(); ++i) { - args.push_back(f[i]); - } - if (!fn.get()) { - fn = m.mk_func_decl(fid, k, 0, 0, args.size(), args.c_ptr()); - } - if (destructive) { - get_plugin().reduce_assign(fn, args.size(), args.c_ptr(), 1, args.c_ptr()); - res = m_rel; - } - else { - get_plugin().reduce(fn, args.size(), args.c_ptr(), res); - } - } - - bool external_relation::empty() const { - ast_manager& m = m_rel.get_manager(); - expr* r = m_rel.get(); - expr_ref res(m); - if (!m_is_empty_fn.get()) { - family_id fid = get_plugin().get_family_id(); - const_cast(m_is_empty_fn) = m.mk_func_decl(fid, OP_RA_IS_EMPTY, 0, 0, 1, &r); - } - get_plugin().reduce(m_is_empty_fn, 1, &r, res); - return m.is_true(res); - } - - void external_relation::add_fact(const relation_fact & f) { - mk_accessor(OP_RA_STORE, m_store_fn, f, true, m_rel); - } - - bool external_relation::contains_fact(const relation_fact & f) const { - ast_manager& m = get_plugin().get_ast_manager(); - expr_ref res(m); - mk_accessor(OP_RA_SELECT, const_cast(m_select_fn), f, false, res); - return !m.is_false(res); - } - - external_relation * external_relation::clone() const { - ast_manager& m = m_rel.get_manager(); - family_id fid = get_plugin().get_family_id(); - expr* rel = m_rel.get(); - expr_ref res(m.mk_fresh_const("T", m.get_sort(rel)), m); - expr* rel_out = res.get(); - func_decl_ref fn(m.mk_func_decl(fid, OP_RA_CLONE,0,0, 1, &rel), m); - get_plugin().reduce_assign(fn, 1, &rel, 1, &rel_out); - return alloc(external_relation, get_plugin(), get_signature(), res); - } - - external_relation * external_relation::complement(func_decl* p) const { - ast_manager& m = m_rel.get_manager(); - family_id fid = get_plugin().get_family_id(); - expr_ref res(m); - expr* rel = m_rel; - func_decl_ref fn(m.mk_func_decl(fid, OP_RA_COMPLEMENT,0,0, 1, &rel), m); - get_plugin().reduce(fn, 1, &rel, res); - return alloc(external_relation, get_plugin(), get_signature(), res); - } - - void external_relation::display(std::ostream & out) const { - out << mk_pp(m_rel, m_rel.get_manager()) << "\n"; - } - - void external_relation::display_tuples(func_decl & pred, std::ostream & out) const { - display(out); - } - - - external_relation_plugin & external_relation::get_plugin() const { - return static_cast(relation_base::get_plugin()); - } - - - // ----------------------------------- - // - // external_relation_plugin - // - // ----------------------------------- - - - external_relation_plugin::external_relation_plugin(external_relation_context& ctx, relation_manager & m) - : relation_plugin(external_relation_plugin::get_name(), m), m_ext(ctx) {} - - external_relation const & external_relation_plugin::get(relation_base const& r) { - return dynamic_cast(r); - } - - external_relation & external_relation_plugin::get(relation_base & r) { - return dynamic_cast(r); - } - - relation_base * external_relation_plugin::mk_empty(const relation_signature & s) { - ast_manager& m = get_ast_manager(); - sort* r_sort = get_relation_sort(s); - parameter param(r_sort); - family_id fid = get_family_id(); - expr_ref e(m.mk_fresh_const("T", r_sort), m); - expr* args[1] = { e.get() }; - func_decl_ref empty_decl(m.mk_func_decl(fid, OP_RA_EMPTY, 1, ¶m, 0, (sort*const*)0), m); - reduce_assign(empty_decl, 0, 0, 1, args); - return alloc(external_relation, *this, s, e); - } - - sort* external_relation_plugin::get_relation_sort(relation_signature const& sig) { - vector sorts; - ast_manager& m = get_ast_manager(); - family_id fid = get_family_id(); - for (unsigned i = 0; i < sig.size(); ++i) { - sorts.push_back(parameter(sig[i])); - } - return m.mk_sort(fid, DL_RELATION_SORT, sorts.size(), sorts.c_ptr()); - } - - sort* external_relation_plugin::get_column_sort(unsigned col, sort* s) { - SASSERT(s->get_num_parameters() > col); - SASSERT(s->get_parameter(col).is_ast()); - SASSERT(is_sort(s->get_parameter(col).get_ast())); - return to_sort(s->get_parameter(col).get_ast()); - } - - family_id external_relation_plugin::get_family_id() { - return m_ext.get_family_id(); - } - - - void external_relation_plugin::mk_filter_fn(sort* s, app* condition, func_decl_ref& f) { - ast_manager& m = get_ast_manager(); - family_id fid = get_family_id(); - parameter param(condition); - f = m.mk_func_decl(fid, OP_RA_FILTER, 1, ¶m, 1, &s); - } - - class external_relation_plugin::join_fn : public convenient_relation_join_fn { - external_relation_plugin& m_plugin; - func_decl_ref m_join_fn; - expr* m_args[2]; - public: - join_fn(external_relation_plugin& p, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2) - : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2), - m_plugin(p), - m_join_fn(p.get_ast_manager()) { - ast_manager& m = p.get_ast_manager(); - family_id fid = p.get_family_id(); - vector params; - for (unsigned i = 0; i < col_cnt; ++i) { - params.push_back(parameter(cols1[i])); - params.push_back(parameter(cols2[i])); - } - sort* domain[2] = { p.get_relation_sort(o1_sig), p.get_relation_sort(o2_sig) }; - m_join_fn = m.mk_func_decl(fid, OP_RA_JOIN, params.size(), params.c_ptr(), 2, domain); - } - - virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { - expr_ref res(m_plugin.get_ast_manager()); - m_args[0] = get(r1).get_relation(); - m_args[1] = get(r2).get_relation(); - m_plugin.reduce(m_join_fn, 2, m_args, res); - return alloc(external_relation, m_plugin, get_result_signature(), res); - } - }; - - relation_join_fn * external_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (!check_kind(r1) || !check_kind(r2)) { - return 0; - } - return alloc(join_fn, *this, r1.get_signature(), r2.get_signature() , col_cnt, cols1, cols2); - } - - - class external_relation_plugin::project_fn : public convenient_relation_project_fn { - external_relation_plugin& m_plugin; - func_decl_ref m_project_fn; - public: - project_fn(external_relation_plugin& p, sort* relation_sort, - const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) - : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols), - m_plugin(p), - m_project_fn(p.get_ast_manager()) { - vector params; - ast_manager& m = p.get_ast_manager(); - family_id fid = p.get_family_id(); - for (unsigned i = 0; i < removed_col_cnt; ++i) { - params.push_back(parameter(removed_cols[i])); - } - m_project_fn = m.mk_func_decl(fid, OP_RA_PROJECT, params.size(), params.c_ptr(), 1, &relation_sort); - } - - virtual relation_base * operator()(const relation_base & r) { - expr_ref res(m_plugin.get_ast_manager()); - expr* rel = get(r).get_relation(); - m_plugin.reduce(m_project_fn, 1, &rel, res); - return alloc(external_relation, m_plugin, get_result_signature(), to_app(res)); - } - }; - - relation_transformer_fn * external_relation_plugin::mk_project_fn(const relation_base & r, - unsigned col_cnt, const unsigned * removed_cols) { - return alloc(project_fn, *this, get(r).get_sort(), r.get_signature(), col_cnt, removed_cols); - } - - - class external_relation_plugin::rename_fn : public convenient_relation_rename_fn { - external_relation_plugin& m_plugin; - func_decl_ref m_rename_fn; - expr* m_args[2]; - public: - rename_fn(external_relation_plugin& p, sort* relation_sort, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) - : convenient_relation_rename_fn(orig_sig, cycle_len, cycle), - m_plugin(p), - m_rename_fn(p.get_ast_manager()) { - - ast_manager& m = p.get_ast_manager(); - family_id fid = p.get_family_id(); - vector params; - for (unsigned i = 0; i < cycle_len; ++i) { - SASSERT(cycle[i] < orig_sig.size()); - params.push_back(parameter(cycle[i])); - } - m_rename_fn = m.mk_func_decl(fid, OP_RA_RENAME, params.size(), params.c_ptr(), 1, &relation_sort); - } - - virtual relation_base * operator()(const relation_base & r) { - expr* rel = get(r).get_relation(); - expr_ref res(m_plugin.get_ast_manager()); - m_args[0] = rel; - m_plugin.reduce(m_rename_fn, 1, &rel, res); - return alloc(external_relation, m_plugin, get_result_signature(), res); - } - }; - - relation_transformer_fn * external_relation_plugin::mk_rename_fn(const relation_base & r, - unsigned cycle_len, const unsigned * permutation_cycle) { - if(!check_kind(r)) { - return 0; - } - return alloc(rename_fn, *this, get(r).get_sort(), r.get_signature(), cycle_len, permutation_cycle); - } - - - class external_relation_plugin::union_fn : public relation_union_fn { - external_relation_plugin& m_plugin; - func_decl_ref m_union_fn; - expr* m_args[2]; - expr* m_outs[2]; - - public: - union_fn(external_relation_plugin& p, decl_kind k, sort* relation_sort): - m_plugin(p), - m_union_fn(p.get_ast_manager()) { - ast_manager& m = p.get_ast_manager(); - sort* domain[2] = { relation_sort, relation_sort }; - m_union_fn = m.mk_func_decl(p.get_family_id(), k, 0, 0, 2, domain); - } - - virtual void operator()(relation_base & r, const relation_base & src, relation_base * delta) { - ast_manager& m = m_plugin.get_ast_manager(); - expr_ref_vector res(m); - m_args[0] = get(r).get_relation(); - m_args[1] = get(src).get_relation(); - m_outs[0] = m_args[0]; - unsigned num_out = 1; - if (delta) { - m_outs[1] = get(*delta).get_relation(); - ++num_out; - } - m_plugin.reduce_assign(m_union_fn, 2, m_args, num_out, m_outs); - } - }; - - relation_union_fn * external_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; - } - return alloc(union_fn, *this, OP_RA_UNION, get(src).get_sort()); - } - - relation_union_fn * external_relation_plugin::mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; - } - return alloc(union_fn, *this, OP_RA_WIDEN, get(src).get_sort()); - } - - class external_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { - external_relation_plugin& m_plugin; - app_ref m_condition; - func_decl_ref m_filter_fn; - public: - filter_interpreted_fn(external_relation_plugin& p, sort* relation_sort, app * condition) - : m_plugin(p), - m_condition(condition, p.get_ast_manager()), - m_filter_fn(p.get_ast_manager()) { - ast_manager& m = p.get_ast_manager(); - p.mk_filter_fn(relation_sort, condition, m_filter_fn); - SASSERT(m.is_bool(condition)); - } - - virtual void operator()(relation_base & r) { - SASSERT(m_plugin.check_kind(r)); - expr* arg = get(r).get_relation(); - m_plugin.reduce_assign(m_filter_fn, 1, &arg, 1, &arg); - } - }; - - relation_mutator_fn * external_relation_plugin::mk_filter_interpreted_fn(const relation_base & r, app * condition) { - if(!check_kind(r)) { - return 0; - } - return alloc(filter_interpreted_fn, *this, get(r).get_sort(), condition); - } - - relation_mutator_fn * external_relation_plugin::mk_filter_equal_fn(const relation_base & r, - const relation_element & value, unsigned col) { - if(!check_kind(r)) { - return 0; - } - ast_manager& m = get_ast_manager(); - app_ref condition(m); - expr_ref var(m); - sort* relation_sort = get(r).get_sort(); - sort* column_sort = get_column_sort(col, relation_sort); - var = m.mk_var(col, column_sort); - condition = m.mk_eq(var, value); - return mk_filter_interpreted_fn(r, condition); - } - - class external_relation_plugin::filter_identical_fn : public relation_mutator_fn { - external_relation_plugin& m_plugin; - func_decl_ref_vector m_filter_fn; - public: - filter_identical_fn(external_relation_plugin& p, sort* relation_sort, - unsigned col_cnt, const unsigned * identical_cols) - : m_plugin(p), m_filter_fn(p.get_ast_manager()) { - ast_manager& m = p.get_ast_manager(); - func_decl_ref fn(m); - app_ref eq(m); - if (col_cnt <= 1) { - return; - } - unsigned col = identical_cols[0]; - sort* s = p.get_column_sort(col, relation_sort); - var* v0 = m.mk_var(col, s); - for (unsigned i = 1; i < col_cnt; ++i) { - col = identical_cols[i]; - s = p.get_column_sort(col, relation_sort); - eq = m.mk_eq(v0, m.mk_var(col, s)); - p.mk_filter_fn(relation_sort, eq.get(), fn); - m_filter_fn.push_back(fn); - } - } - - virtual void operator()(relation_base & r) { - expr* r0 = get(r).get_relation(); - for (unsigned i = 0; i < m_filter_fn.size(); ++i) { - m_plugin.reduce_assign(m_filter_fn[i].get(), 1, &r0, 1, &r0); - } - } - }; - - relation_mutator_fn * external_relation_plugin::mk_filter_identical_fn(const relation_base & r, - unsigned col_cnt, const unsigned * identical_cols) { - if (!check_kind(r)) { - return 0; - } - return alloc(filter_identical_fn, *this, get(r).get_sort(), col_cnt, identical_cols); - } - - - class external_relation_plugin::negation_filter_fn : public convenient_relation_negation_filter_fn { - external_relation_plugin& m_plugin; - func_decl_ref m_negated_filter_fn; - expr* m_args[2]; - public: - negation_filter_fn(external_relation_plugin& p, - const relation_base & tgt, const relation_base & neg_t, - unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : - convenient_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), - m_plugin(p), - m_negated_filter_fn(p.get_ast_manager()) - { - ast_manager& m = p.get_ast_manager(); - family_id fid = p.get_family_id(); - vector params; - for (unsigned i = 0; i < joined_col_cnt; ++i) { - params.push_back(parameter(t_cols[i])); - params.push_back(parameter(negated_cols[i])); - } - sort* domain[2] = { get(tgt).get_sort(), get(neg_t).get_sort() }; - m_negated_filter_fn = m.mk_func_decl(fid, OP_RA_NEGATION_FILTER, params.size(), params.c_ptr(), 2, domain); - } - - void operator()(relation_base & t, const relation_base & negated_obj) { - m_args[0] = get(t).get_relation(); - m_args[1] = get(negated_obj).get_relation(); - m_plugin.reduce_assign(m_negated_filter_fn.get(), 2, m_args, 1, m_args); - } - }; - - relation_intersection_filter_fn * external_relation_plugin::mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols) { - if (!check_kind(t) || !check_kind(negated_obj)) { - return 0; - } - return alloc(negation_filter_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); - } - -}; diff --git a/src/muz/dl_external_relation.h b/src/muz/dl_external_relation.h deleted file mode 100644 index 03838ecc4..000000000 --- a/src/muz/dl_external_relation.h +++ /dev/null @@ -1,154 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_external_relation.h - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) 2010-05-10 - -Revision History: - ---*/ -#ifndef _DL_EXTERNAL_RELATION_H_ -#define _DL_EXTERNAL_RELATION_H_ - -#include "dl_base.h" - -namespace datalog { - - class external_relation; - - class external_relation_context { - public: - virtual ~external_relation_context() {} - - virtual family_id get_family_id() const = 0; - - // reduce arguments. - virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) = 0; - - // overwrite terms passed in outs vector with values computed by function. - virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) = 0; - }; - - class external_relation_plugin : public relation_plugin { - - friend class external_relation; - class join_fn; - class project_fn; - class rename_fn; - class union_fn; - class filter_identical_fn; - class filter_interpreted_fn; - class negation_filter_fn; - - external_relation_context& m_ext; - - public: - external_relation_plugin(external_relation_context& ctx, relation_manager & m); - - virtual bool can_handle_signature(const relation_signature & s) { return true; } - - static symbol get_name() { return symbol("external_relation"); } - - virtual relation_base * mk_empty(const relation_signature & s); - - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - - private: - - static external_relation& get(relation_base& r); - static external_relation const & get(relation_base const& r); - - void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { - m_ext.reduce(f, num_args, args, result); - } - - void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { - m_ext.reduce_assign(f, num_args, args, num_out, outs); - } - - sort* get_relation_sort(relation_signature const& sig); - - sort* get_column_sort(unsigned col, sort* relation_sort); - - void mk_filter_fn(sort* s, app* condition, func_decl_ref& f); - - family_id get_family_id(); - }; - - class external_relation : public relation_base { - friend class external_relation_plugin; - friend class external_relation_plugin::join_fn; - friend class external_relation_plugin::project_fn; - friend class external_relation_plugin::rename_fn; - friend class external_relation_plugin::union_fn; - friend class external_relation_plugin::filter_identical_fn; - friend class external_relation_plugin::filter_interpreted_fn; - friend class external_relation_plugin::negation_filter_fn; - - expr_ref m_rel; - func_decl_ref m_select_fn; - func_decl_ref m_store_fn; - func_decl_ref m_is_empty_fn; - - unsigned size() const { return get_signature().size(); } - - sort* get_sort() const { return m_rel.get_manager().get_sort(m_rel); } - - void mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const; - - external_relation(external_relation_plugin & p, const relation_signature & s, expr* r); - virtual ~external_relation(); - - public: - external_relation_plugin & get_plugin() const; - - virtual bool empty() const; - - virtual void add_fact(const relation_fact & f); - - virtual bool contains_fact(const relation_fact & f) const; - - virtual external_relation * clone() const; - - virtual external_relation * complement(func_decl*) const; - - virtual void display(std::ostream & out) const; - - virtual void display_tuples(func_decl & pred, std::ostream & out) const; - - expr* get_relation() const { return m_rel.get(); } - - virtual void to_formula(expr_ref& fml) const { fml = get_relation(); } - - }; - - -}; - -#endif diff --git a/src/muz/dl_finite_product_relation.cpp b/src/muz/dl_finite_product_relation.cpp deleted file mode 100644 index 86fef433b..000000000 --- a/src/muz/dl_finite_product_relation.cpp +++ /dev/null @@ -1,2377 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_finite_product_relation.cpp - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-14. - -Revision History: - ---*/ - - -#include -#include"dl_context.h" -#include"dl_relation_manager.h" -#include"dl_table_relation.h" -#include"dl_finite_product_relation.h" -#include"bool_rewriter.h" - -namespace datalog { - - //static variables - - const table_sort finite_product_relation::s_rel_idx_sort = INT_MAX; - - void universal_delete(finite_product_relation* ptr) { - ptr->deallocate(); - } - - - // ----------------------------------- - // - // finite_product_relation_plugin - // - // ----------------------------------- - - finite_product_relation & finite_product_relation_plugin::get(relation_base & r) { - SASSERT(r.get_plugin().is_finite_product_relation()); - return static_cast(r); - } - - const finite_product_relation & finite_product_relation_plugin::get(const relation_base & r) { - SASSERT(r.get_plugin().is_finite_product_relation()); - return static_cast(r); - } - - finite_product_relation * finite_product_relation_plugin::get(relation_base * r) { - SASSERT(!r || r->get_plugin().is_finite_product_relation()); - return static_cast(r); - } - - const finite_product_relation * finite_product_relation_plugin::get(const relation_base * r) { - SASSERT(!r || r->get_plugin().is_finite_product_relation()); - return static_cast(r); - } - - symbol finite_product_relation_plugin::get_name(relation_plugin & inner_plugin) { - std::string str = std::string("fpr_")+inner_plugin.get_name().bare_str(); - return symbol(str.c_str()); - } - - finite_product_relation_plugin & finite_product_relation_plugin::get_plugin(relation_manager & rmgr, - relation_plugin & inner) { - finite_product_relation_plugin * res; - if(!rmgr.try_get_finite_product_relation_plugin(inner, res)) { - res = alloc(finite_product_relation_plugin, inner, rmgr); - rmgr.register_plugin(res); - } - return *res; - } - - finite_product_relation_plugin::finite_product_relation_plugin(relation_plugin & inner_plugin, - relation_manager & manager) - : relation_plugin(get_name(inner_plugin), manager, ST_FINITE_PRODUCT_RELATION), - m_inner_plugin(inner_plugin), m_spec_store(*this) { - } - - void finite_product_relation_plugin::initialize(family_id fid) { - relation_plugin::initialize(fid); - m_spec_store.add_available_kind(get_kind()); - } - - family_id finite_product_relation_plugin::get_relation_kind(finite_product_relation & r, - const bool * table_columns) { - const relation_signature & sig = r.get_signature(); - svector table_cols_vect(sig.size(), table_columns); - return m_spec_store.get_relation_kind(sig, rel_spec(table_cols_vect)); - } - - void finite_product_relation_plugin::get_all_possible_table_columns(relation_manager & rmgr, - const relation_signature & s, svector & table_columns) { - SASSERT(table_columns.empty()); - unsigned s_sz = s.size(); - for(unsigned i=0; i table_columns; - get_all_possible_table_columns(s, table_columns); -#ifndef _EXTERNAL_RELEASE - unsigned s_sz = s.size(); - unsigned rel_col_cnt = 0; - for(unsigned i=0; icomplement_self(p); - return res; - } - - bool finite_product_relation_plugin::can_convert_to_table_relation(const finite_product_relation & r) { - return r.m_other_sig.empty(); - } - - table_relation * finite_product_relation_plugin::to_table_relation(const finite_product_relation & r) { - SASSERT(can_convert_to_table_relation(r)); - r.garbage_collect(true); - //now all rows in the table will correspond to rows in the resulting table_relation - - const table_base & t = r.get_table(); - - unsigned removed_col = t.get_signature().size()-1; - scoped_ptr project_fun = - get_manager().mk_project_fn(r.get_table(), 1, &removed_col); - - table_base * res_table = (*project_fun)(t); - SASSERT(res_table->get_signature().functional_columns()==0); - return static_cast(get_manager().mk_table_relation(r.get_signature(), res_table)); - } - - - bool finite_product_relation_plugin::can_be_converted(const relation_base & r) { - if(&r.get_plugin()==&get_inner_plugin()) { - //can be converted by mk_from_inner_relation - return true; - } - if(r.from_table()) { - //We can convert directly from table plugin only if the inner plugin can handle empty signatures. - - //TODO: If the inner plugin cannot handle empty signatures, we may try to move some of the - //table columns into the inner relation signature. - return get_inner_plugin().can_handle_signature(relation_signature()); - } - return false; - } - - finite_product_relation * finite_product_relation_plugin::mk_from_table_relation(const table_relation & r) { - func_decl* pred = 0; - const relation_signature & sig = r.get_signature(); - const table_base & t = r.get_table(); - table_plugin & tplugin = r.get_table().get_plugin(); - - relation_signature inner_sig; //empty signature for the inner relation - if(!get_inner_plugin().can_handle_signature(inner_sig)) { - return 0; - } - - table_signature idx_singleton_sig; - idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort); - idx_singleton_sig.set_functional_columns(1); - - scoped_rel idx_singleton; - if(tplugin.can_handle_signature(idx_singleton_sig)) { - idx_singleton = tplugin.mk_empty(idx_singleton_sig); - } - else { - idx_singleton = get_manager().mk_empty_table(idx_singleton_sig); - } - table_fact idx_singleton_fact; - idx_singleton_fact.push_back(0); - idx_singleton->add_fact(idx_singleton_fact); - - scoped_ptr join_fun = get_manager().mk_join_fn(t, *idx_singleton, 0, 0, 0); - SASSERT(join_fun); - scoped_rel res_table = (*join_fun)(t, *idx_singleton); - - svector table_cols(sig.size(), true); - finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); - - //this one does not need to be deleted -- it will be taken over by \c res in the \c init function - relation_base * inner_rel = get_inner_plugin().mk_full(pred, inner_sig, get_inner_plugin().get_kind()); - - relation_vector rels; - rels.push_back(inner_rel); - - res->init(*res_table, rels, true); - return res; - } - - finite_product_relation * finite_product_relation_plugin::mk_from_inner_relation(const relation_base & r) { - SASSERT(&r.get_plugin()==&get_inner_plugin()); - const relation_signature & sig = r.get_signature(); - - table_signature idx_singleton_sig; - idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort); - idx_singleton_sig.set_functional_columns(1); - - scoped_rel idx_singleton = get_manager().mk_empty_table(idx_singleton_sig); - table_fact idx_singleton_fact; - idx_singleton_fact.push_back(0); - idx_singleton->add_fact(idx_singleton_fact); - - svector table_cols(sig.size(), false); - finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); - - relation_vector rels; - rels.push_back(r.clone()); - - res->init(*idx_singleton, rels, true); - return res; - } - - class finite_product_relation_plugin::converting_join_fn : public convenient_relation_join_fn { - finite_product_relation_plugin & m_plugin; - scoped_ptr m_native_join; - - finite_product_relation * convert(const relation_base & r) { - SASSERT(&r.get_plugin()!=&m_plugin); - if(&r.get_plugin()==&m_plugin.get_inner_plugin()) { - return m_plugin.mk_from_inner_relation(r); - } - SASSERT(r.from_table()); - const table_relation & tr = static_cast(r); - finite_product_relation * res = m_plugin.mk_from_table_relation(tr); - SASSERT(res); - return res; - } - - public: - converting_join_fn(finite_product_relation_plugin & plugin, const relation_signature & sig1, - const relation_signature & sig2, unsigned col_cnt, const unsigned * cols1, - const unsigned * cols2) - : convenient_relation_join_fn(sig1, sig2, col_cnt, cols1, cols2), - m_plugin(plugin) {} - - virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { - scoped_rel r1_conv; - if(&r1.get_plugin()!=&m_plugin) { - r1_conv = convert(r1); - } - scoped_rel r2_conv; - if(&r2.get_plugin()!=&m_plugin) { - r2_conv = convert(r2); - } - - const finite_product_relation & fpr1 = r1_conv ? *r1_conv : get(r1); - const finite_product_relation & fpr2 = r2_conv ? *r2_conv : get(r2); - - SASSERT(&fpr1.get_plugin()==&m_plugin); - SASSERT(&fpr2.get_plugin()==&m_plugin); - - if(!m_native_join) { - m_native_join = m_plugin.get_manager().mk_join_fn(fpr1, fpr2, m_cols1, m_cols2, false); - } - return (*m_native_join)(fpr1, fpr2); - } - }; - - - class finite_product_relation_plugin::join_fn : public convenient_relation_join_fn { - scoped_ptr m_tjoin_fn; - scoped_ptr m_rjoin_fn; - - unsigned_vector m_t_joined_cols1; - unsigned_vector m_t_joined_cols2; - unsigned_vector m_r_joined_cols1; - unsigned_vector m_r_joined_cols2; - - //Column equalities betweet table and inner relations. - //The columns numbers correspont to the columns of the table/inner relation - //in the result of the join operation - unsigned_vector m_tr_table_joined_cols; - unsigned_vector m_tr_rel_joined_cols; - - scoped_ptr m_filter_tr_identities; - - scoped_ptr m_tjoined_second_rel_remover; - - //determines which columns of the result are table columns and which are in the inner relation - svector m_res_table_columns; - - public: - class join_maker : public table_row_mutator_fn { - join_fn & m_parent; - const finite_product_relation & m_r1; - const finite_product_relation & m_r2; - relation_vector & m_rjoins; - public: - join_maker(join_fn & parent, const finite_product_relation & r1, const finite_product_relation & r2, - relation_vector & rjoins) - : m_parent(parent), m_r1(r1), m_r2(r2), m_rjoins(rjoins) {} - - virtual bool operator()(table_element * func_columns) { - const relation_base & or1 = m_r1.get_inner_rel(func_columns[0]); - const relation_base & or2 = m_r2.get_inner_rel(func_columns[1]); - SASSERT(&or1); - SASSERT(&or2); - unsigned new_rel_num = m_rjoins.size(); - m_rjoins.push_back(m_parent.do_rjoin(or1, or2)); - func_columns[0]=new_rel_num; - return true; - } - }; - - join_fn(const finite_product_relation & r1, const finite_product_relation & r2, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2) - : convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2) { - unsigned second_table_after_join_ofs = r1.m_table2sig.size(); - unsigned second_inner_rel_after_join_ofs = r1.m_other2sig.size(); - for(unsigned i=0;i tjoined = (*m_tjoin_fn)(r1.get_table(), r2.get_table()); - - relation_vector joined_orelations; - - { - join_maker * mutator = alloc(join_maker, *this, r1, r2, joined_orelations); //dealocated in inner_join_mapper - scoped_ptr inner_join_mapper = rmgr.mk_map_fn(*tjoined, mutator); - (*inner_join_mapper)(*tjoined); - } - - - if(!m_tjoined_second_rel_remover) { - unsigned removed_col = tjoined->get_signature().size()-1; - m_tjoined_second_rel_remover = rmgr.mk_project_fn(*tjoined, 1, &removed_col); - } - //remove the second functional column from tjoined to get a table that corresponds - //to the table signature of the resulting relation - scoped_rel res_table = (*m_tjoined_second_rel_remover)(*tjoined); - - relation_plugin & res_oplugin = - joined_orelations.empty() ? r1.m_other_plugin : joined_orelations.back()->get_plugin(); - - //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. - //It would however need to be somehow inferred for the new signature. - - finite_product_relation * res = alloc(finite_product_relation, r1.get_plugin(), get_result_signature(), - m_res_table_columns.c_ptr(), res_table->get_plugin(), res_oplugin, null_family_id); - - res->init(*res_table, joined_orelations, true); - - if(m_tr_table_joined_cols.size()) { - //There were some shared variables between the table and the relation part. - //We enforce those equalities here. - if(!m_filter_tr_identities) { - m_filter_tr_identities = plugin.mk_filter_identical_pairs(*res, m_tr_table_joined_cols.size(), - m_tr_table_joined_cols.c_ptr(), m_tr_rel_joined_cols.c_ptr()); - SASSERT(m_filter_tr_identities); - } - (*m_filter_tr_identities)(*res); - } - return res; - } - }; - - - - - relation_join_fn * finite_product_relation_plugin::mk_join_fn(const relation_base & rb1, const relation_base & rb2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if(!check_kind(rb1) || !check_kind(rb2)) { - bool r1foreign = &rb1.get_plugin()!=this; - bool r2foreign = &rb2.get_plugin()!=this; - if( (!r1foreign || can_be_converted(rb1)) && (!r2foreign || can_be_converted(rb2))) { - return alloc(converting_join_fn, *this, rb1.get_signature(), rb2.get_signature(), col_cnt, cols1, - cols2); - } - return 0; - } - const finite_product_relation & r1 = get(rb1); - const finite_product_relation & r2 = get(rb2); - - return alloc(join_fn, r1, r2, col_cnt, cols1, cols2); - } - - - class finite_product_relation_plugin::project_fn : public convenient_relation_project_fn { - unsigned_vector m_removed_table_cols; - unsigned_vector m_removed_rel_cols; - - scoped_ptr m_rel_projector; - scoped_ptr m_inner_rel_union; - - //determines which columns of the result are table columns and which are in the inner relation - svector m_res_table_columns; - public: - project_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * removed_cols) - : convenient_relation_project_fn(r.get_signature(), col_cnt, removed_cols) { - SASSERT(col_cnt>0); - for(unsigned i=0; ii); - m_res_table_columns.push_back(r.is_table_column(i)); - } - } - - class project_reducer : public table_row_pair_reduce_fn { - project_fn & m_parent; - relation_vector & m_relations; - public: - - project_reducer(project_fn & parent, relation_vector & relations) - : m_parent(parent), m_relations(relations) {} - - virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { - relation_base * tgt = m_relations[static_cast(func_columns[0])]->clone(); - relation_base & src = *m_relations[static_cast(merged_func_columns[0])]; - if(!m_parent.m_inner_rel_union) { - m_parent.m_inner_rel_union = tgt->get_manager().mk_union_fn(*tgt, src); - } - (*m_parent.m_inner_rel_union)(*tgt, src); - - unsigned new_idx = m_relations.size(); - m_relations.push_back(tgt); - func_columns[0] = new_idx; - } - }; - - virtual relation_base * operator()(const relation_base & rb) { - const finite_product_relation & r = get(rb); - finite_product_relation_plugin & plugin = r.get_plugin(); - const table_base & rtable = r.get_table(); - relation_manager & rmgr = plugin.get_manager(); - - r.garbage_collect(false); - relation_vector res_relations; - unsigned orig_rel_cnt = r.m_others.size(); - for(unsigned i=0; iclone() : 0); - } - SASSERT(res_relations.size()==orig_rel_cnt); - - bool shared_res_table = false; - const table_base * res_table; - - if(m_removed_table_cols.empty()) { - shared_res_table = true; - res_table = &rtable; - } - else { - project_reducer * preducer = alloc(project_reducer, *this, res_relations); - scoped_ptr tproject = - rmgr.mk_project_with_reduce_fn(rtable, m_removed_table_cols.size(), m_removed_table_cols.c_ptr(), preducer); - res_table = (*tproject)(rtable); - } - - relation_plugin * res_oplugin = 0; - - if(!m_removed_rel_cols.empty()) { - unsigned res_rel_cnt = res_relations.size(); - for(unsigned i=0; ideallocate(); - if(!res_oplugin) { - res_oplugin = &res_relations[i]->get_plugin(); - } - } - } - - if(!res_oplugin) { - res_oplugin = &r.m_other_plugin; - } - - //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. - //It would however need to be somehow inferred for the new signature. - - finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), - m_res_table_columns.c_ptr(), res_table->get_plugin(), *res_oplugin, null_family_id); - - res->init(*res_table, res_relations, false); - - if(!shared_res_table) { - const_cast(res_table)->deallocate(); - } - - return res; - } - }; - - relation_transformer_fn * finite_product_relation_plugin::mk_project_fn(const relation_base & rb, unsigned col_cnt, - const unsigned * removed_cols) { - if(&rb.get_plugin()!=this) { - return 0; - } - return alloc(project_fn, get(rb), col_cnt, removed_cols); - } - - - - class finite_product_relation_plugin::rename_fn : public convenient_relation_rename_fn { - scoped_ptr m_table_renamer; - scoped_ptr m_rel_renamer; - bool m_rel_identity; - - unsigned_vector m_rel_permutation; - - //determines which columns of the result are table columns and which are in the inner relation - svector m_res_table_columns; - public: - rename_fn(const finite_product_relation & r, unsigned cycle_len, const unsigned * permutation_cycle) - : convenient_relation_rename_fn(r.get_signature(), cycle_len, permutation_cycle) { - SASSERT(cycle_len>1); - - unsigned sig_sz = r.get_signature().size(); - unsigned_vector permutation; - add_sequence(0, sig_sz, permutation); - permutate_by_cycle(permutation, cycle_len, permutation_cycle); - - unsigned_vector table_permutation; - - bool table_identity = true; - m_rel_identity = true; - for(unsigned new_i=0; new_iclone() : 0); - } - - if(!m_rel_identity) { - unsigned res_rel_cnt = res_relations.size(); - for(unsigned i=0; i inner_rel = res_relations[i]; - if(!m_rel_renamer) { - m_rel_renamer = r.get_manager().mk_permutation_rename_fn(*inner_rel, m_rel_permutation); - } - - res_relations[i] = (*m_rel_renamer)(*inner_rel); - } - } - scoped_rel res_table_scoped; - const table_base * res_table = &rtable; - - if(m_table_renamer) { - res_table_scoped = (*m_table_renamer)(*res_table); - res_table = res_table_scoped.get(); - } - - //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. - //It would however need to be somehow inferred for the new signature. - - finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), - m_res_table_columns.c_ptr(), res_table->get_plugin(), r.m_other_plugin, null_family_id); - - res->init(*res_table, res_relations, false); - - return res; - } - }; - - relation_transformer_fn * finite_product_relation_plugin::mk_rename_fn(const relation_base & rb, - unsigned permutation_cycle_len, const unsigned * permutation_cycle) { - - if(&rb.get_plugin()!=this) { - return 0; - } - const finite_product_relation & r = get(rb); - return alloc(rename_fn, r, permutation_cycle_len, permutation_cycle); - } - - - class finite_product_relation_plugin::union_fn : public relation_union_fn { - bool m_use_delta; - unsigned_vector m_data_cols;//non-functional columns in the product-relation table (useful for creating operations) - scoped_ptr m_common_join; //result of the join contains (data columns), tgt_rel_idx, src_rel_idx - scoped_ptr m_rel_union; - scoped_ptr m_table_union; - scoped_ptr m_remove_overlaps; - scoped_ptr m_remove_src_column_from_overlap; - - //this one is populated only if we're doing union with delta - scoped_ptr m_delta_merging_union; - - scoped_ptr m_overlap_delta_table_builder; - public: - union_fn(const finite_product_relation & tgt, bool use_delta) : m_use_delta(use_delta) {} - - relation_union_fn & get_inner_rel_union_op(relation_base & r) { - if(!m_rel_union) { - m_rel_union = r.get_manager().mk_union_fn(r, r, m_use_delta ? &r : 0); - } - return *m_rel_union; - } - - class union_mapper : public table_row_mutator_fn { - union_fn & m_parent; - finite_product_relation & m_tgt; - const finite_product_relation & m_src; - table_base * m_delta_indexes; //table with signature (updated tgt rel index, delta_index in m_delta_rels) - relation_vector * m_delta_rels; - table_fact m_di_fact; //auxiliary fact for inserting into \c m_delta_indexes - public: - /** - If \c delta_indexes is 0, it means we are not collecting indexes. - */ - union_mapper(union_fn & parent, finite_product_relation & tgt, const finite_product_relation & src, - table_base * delta_indexes, relation_vector * delta_rels) - : m_parent(parent), - m_tgt(tgt), - m_src(src), - m_delta_indexes(delta_indexes), - m_delta_rels(delta_rels) {} - - virtual ~union_mapper() {} - - virtual bool operator()(table_element * func_columns) { - relation_base & otgt_orig = m_tgt.get_inner_rel(func_columns[0]); - const relation_base & osrc = m_src.get_inner_rel(func_columns[1]); - - relation_base * otgt = otgt_orig.clone(); - unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); - m_tgt.set_inner_rel(new_tgt_idx, otgt); - if(m_delta_indexes) { - relation_base * odelta = otgt->get_plugin().mk_empty(otgt->get_signature()); - m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc, odelta); - - unsigned delta_idx = m_delta_rels->size(); - m_delta_rels->push_back(odelta); - m_di_fact.reset(); - m_di_fact.push_back(new_tgt_idx); - m_di_fact.push_back(delta_idx); - m_delta_indexes->add_fact(m_di_fact); - } - else { - m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc); - } - - func_columns[0]=new_tgt_idx; - return true; - } - }; - - /** - Makes a table whose last column has indexes to relations in \c src into a table - with indexes to relation \c tgt. - */ - class src_copying_mapper : public table_row_mutator_fn { - finite_product_relation & m_tgt; - const finite_product_relation & m_src; - public: - /** - If \c delta_indexes is 0, it means we are not collecting indexes. - */ - src_copying_mapper(finite_product_relation & tgt, const finite_product_relation & src) - : m_tgt(tgt), m_src(src) {} - - virtual bool operator()(table_element * func_columns) { - const relation_base & osrc = m_src.get_inner_rel(func_columns[0]); - unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); - m_tgt.set_inner_rel(new_tgt_idx, osrc.clone()); - func_columns[0]=new_tgt_idx; - return true; - } - }; - - virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { - finite_product_relation & tgt = get(tgtb); - const finite_product_relation & src0 = get(srcb); - finite_product_relation * delta = get(deltab); - - relation_manager & rmgr = tgt.get_manager(); - - scoped_rel src_aux_copy; //copy of src in case its specification needs to be modified - - if(!vectors_equal(tgt.m_table2sig, src0.m_table2sig) - || (delta && !vectors_equal(tgt.m_table2sig, delta->m_table2sig)) ) { - src_aux_copy = src0.clone(); - ptr_vector orig_rels; - orig_rels.push_back(src_aux_copy.get()); - orig_rels.push_back(&tgt); - if(delta) { - orig_rels.push_back(delta); - } - if(!finite_product_relation::try_unify_specifications(orig_rels)) { - throw default_exception("finite_product_relation union: cannot convert relations to common specification"); - } - } - - const finite_product_relation & src = src_aux_copy ? *src_aux_copy : src0; - - table_plugin & tplugin = tgt.get_table_plugin(); - - if(!m_common_join) { - unsigned data_cols_cnt = tgt.m_table_sig.size()-1; - for(unsigned i=0; i table_overlap = (*m_common_join)(tgt.get_table(), src.get_table()); - - scoped_rel delta_indexes; - relation_vector delta_rels; - if(m_use_delta) { - table_signature di_sig; - di_sig.push_back(finite_product_relation::s_rel_idx_sort); - di_sig.push_back(finite_product_relation::s_rel_idx_sort); - di_sig.set_functional_columns(1); - delta_indexes = tplugin.mk_empty(di_sig); - } - - { - union_mapper * umapper = alloc(union_mapper, *this, tgt, src, delta_indexes.get(), &delta_rels); - scoped_ptr mapping_fn = rmgr.mk_map_fn(*table_overlap, umapper); - (*mapping_fn)(*table_overlap); - } - - if(!m_remove_src_column_from_overlap) { - unsigned removed_cols[] = { table_overlap->get_signature().size()-1 }; - m_remove_src_column_from_overlap = rmgr.mk_project_fn(*table_overlap, 1, removed_cols); - } - //transform table_overlap into the signature of tgt.get_table(), so that the functional - //column contains indexes of the united relations - scoped_rel regular_overlap = (*m_remove_src_column_from_overlap)(*table_overlap); - - - if(!m_remove_overlaps) { - m_remove_overlaps = rmgr.mk_filter_by_negation_fn(tgt.get_table(), *regular_overlap, m_data_cols, - m_data_cols); - } - - //in tgt keep only the rows that are in tgt only - (*m_remove_overlaps)(tgt.get_table(), *regular_overlap); - - //add rows in which tgt and src overlapped - if(!m_table_union) { - m_table_union = rmgr.mk_union_fn(tgt.get_table(), tgt.get_table()); - } - (*m_table_union)(tgt.get_table(), *regular_overlap); - - scoped_rel src_only = src.get_table().clone(); - (*m_remove_overlaps)(*src_only, *regular_overlap); - - scoped_rel src_only2; //a copy of src_only for use in building the delta - if(m_use_delta) { - src_only2 = src_only->clone(); - } - - { - src_copying_mapper * cpmapper = alloc(src_copying_mapper, tgt, src); - scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only, cpmapper); - (*mapping_fn)(*src_only); - } - - //add rows that were only in src - (*m_table_union)(tgt.get_table(), *src_only); - - if(m_use_delta) { - bool extending_delta = !delta->empty(); - //current delta, we will add it to the deltab argument later if it was not given to us empty - finite_product_relation * cdelta; - if(extending_delta) { - cdelta = delta->get_plugin().mk_empty(*delta); - } - else { - cdelta = delta; - } - - if(!m_overlap_delta_table_builder) { - unsigned table_fn_col = regular_overlap->get_signature().size()-1; - unsigned first_col = 0; - unsigned removed_cols[] = { table_fn_col, table_fn_col+1 }; - m_overlap_delta_table_builder = rmgr.mk_join_project_fn(*regular_overlap, *delta_indexes, 1, - &table_fn_col, &first_col, 2, removed_cols); - } - - scoped_rel overlap_delta_table = - (*m_overlap_delta_table_builder)(*regular_overlap, *delta_indexes); - - cdelta->init(*overlap_delta_table, delta_rels, true); - - { - src_copying_mapper * cpmapper = alloc(src_copying_mapper, *cdelta, src); - scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only2, cpmapper); - (*mapping_fn)(*src_only2); - } - - //add rows that were only in src - (*m_table_union)(cdelta->get_table(), *src_only2); - - if(extending_delta) { - if(!m_delta_merging_union) { - m_delta_merging_union = rmgr.mk_union_fn(*delta, *cdelta); - } - (*m_delta_merging_union)(*delta, *cdelta); - cdelta->deallocate(); - } - } - } - }; - -#if 0 - /** - Union operation taking advantage of the fact that the inner relation of all the arguments - is a singleton relation. - */ - class finite_product_relation_plugin::inner_singleton_union_fn : public relation_union_fn { - - class offset_row_mapper : public table_row_mutator_fn { - public: - unsigned m_ofs; - virtual bool operator()(table_element * func_columns) { - func_columns[0] += m_ofs; - return true; - } - }; - - // [Leo]: gcc complained about the following class. - // It does not have a constructor and uses a reference - - class inner_relation_copier : public table_row_mutator_fn { - finite_product_relation & m_tgt; - const finite_product_relation & m_src; - finite_product_relation * m_delta; - unsigned m_tgt_ofs; - unsigned m_delta_ofs; - public: - virtual bool operator()(table_element * func_columns) { - unsigned src_idx = static_cast(func_columns[0]); - unsigned tgt_idx = src_idx + m_tgt_ofs; - unsigned delta_idx = m_delta ? (src_idx + m_delta_ofs) : 0; - SASSERT(!m_delta || m_tgt.m_others[tgt_idx]==m_delta->m_others[delta_idx]); - SASSERT(m_tgt.m_others[tgt_idx]==0 || m_tgt.m_others[tgt_idx]==m_src.m_others[src_idx]); - if(m_tgt.m_others[tgt_idx]==0) { - m_tgt.m_others[tgt_idx] = m_src.m_others[src_idx]->clone(); - if(m_delta) { - m_delta->m_others[delta_idx] = m_src.m_others[src_idx]->clone(); - } - } - return true; - } - }; - - scoped_ptr m_t_union_fun; - offset_row_mapper * m_offset_mapper_obj; //initialized together with m_offset_mapper_fun, and deallocated by it - scoped_ptr m_offset_mapper_fun; - - - - public: - virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { - finite_product_relation & tgt = get(tgtb); - const finite_product_relation & src = get(srcb); - finite_product_relation * delta = get(deltab); - - finite_product_relation_plugin & plugin = tgt.get_plugin(); - relation_manager & rmgr = plugin.get_manager(); - - //we want only non-empty inner relations to remain - tgt.garbage_collect(true); - src.garbage_collect(true); - - table_base & tgt_table = tgt.get_table(); - const table_base & src_table = src.get_table(); - - scoped_rel offsetted_src = src_table.clone(); - - if(!m_offset_mapper_fun) { - m_offset_mapper_obj = alloc(offset_row_mapper); - m_offset_mapper_fun = rmgr.mk_map_fn(*offsetted_src, m_offset_mapper_obj); - } - unsigned src_rel_offset = tgt.m_others.size(); - m_offset_mapper_obj->m_ofs = src_rel_offset; - - (*m_offset_mapper_fun)(*offsetted_src); - - //if we need to generate a delta, we get collect it into an empty relation and then union - //it with the delta passed in as argument. - scoped_rel loc_delta = delta ? get(plugin.mk_empty(*delta)) : 0; - //even if we don't need to generate the delta for the caller, we still want to have a delta - //table to know which relations to copy. - scoped_rel loc_delta_table_scoped; - if(!loc_delta) { - loc_delta_table_scoped = tgt_table.get_plugin().mk_empty(tgt_table); - } - table_base * loc_delta_table = loc_delta ? &loc_delta->get_table() : loc_delta_table_scoped.get(); - - if(!m_t_union_fun) { - m_t_union_fun = rmgr.mk_union_fn(tgt_table, *offsetted_src, loc_delta_table); - } - (*m_t_union_fun)(tgt_table, *offsetted_src, loc_delta_table); - - - //TODO: copy the relations into tgt and (possibly) delta using inner_relation_copier - //TODO: unite the local delta with the delta passed in as an argument - NOT_IMPLEMENTED_YET(); - } - }; -#endif - - class finite_product_relation_plugin::converting_union_fn : public relation_union_fn { - scoped_ptr m_tr_union_fun; - public: - virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { - SASSERT(srcb.get_plugin().is_finite_product_relation()); - const finite_product_relation & src = get(srcb); - finite_product_relation_plugin & plugin = src.get_plugin(); - scoped_rel tr_src = plugin.to_table_relation(src); - if(!m_tr_union_fun) { - m_tr_union_fun = plugin.get_manager().mk_union_fn(tgtb, *tr_src, deltab); - SASSERT(m_tr_union_fun); - } - (*m_tr_union_fun)(tgtb, *tr_src, deltab); - } - }; - - relation_union_fn * finite_product_relation_plugin::mk_union_fn(const relation_base & tgtb, const relation_base & srcb, - const relation_base * deltab) { - if(&srcb.get_plugin()!=this) { - return 0; - } - const finite_product_relation & src = get(srcb); - if(&tgtb.get_plugin()!=this || (deltab && &deltab->get_plugin()!=this) ) { - if(can_convert_to_table_relation(src)) { - return alloc(converting_union_fn); - } - return 0; - } - - const finite_product_relation * delta = get(deltab); - - return alloc(union_fn, get(tgtb), delta!=0); - } - - - class finite_product_relation_plugin::filter_identical_fn : public relation_mutator_fn { - //the table and relation columns that should be identical - //the column numbering is local to the table or inner relation - unsigned_vector m_table_cols; - unsigned_vector m_rel_cols; - - scoped_ptr m_table_filter; - scoped_ptr m_rel_filter; - scoped_ptr m_tr_filter; - public: - filter_identical_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * identical_cols) - : m_table_filter(0), m_rel_filter(0), m_tr_filter(0) { - finite_product_relation_plugin & plugin = r.get_plugin(); - for(unsigned i=0; i1) { - m_table_filter = r.get_manager().mk_filter_identical_fn(r.get_table(), m_table_cols.size(), - m_table_cols.c_ptr()); - SASSERT(m_table_filter); - } - if(!m_table_cols.empty() && !m_rel_cols.empty()) { - unsigned tr_filter_table_cols[] = { m_table_cols[0] }; - unsigned tr_filter_rel_cols[] = { m_rel_cols[0] }; - m_tr_filter = plugin.mk_filter_identical_pairs(r, 1, tr_filter_table_cols, tr_filter_rel_cols); - SASSERT(m_tr_filter); - } - } - - void ensure_rel_filter(const relation_base & orel) { - SASSERT(m_rel_cols.size()>1); - if(m_rel_filter) { - return; - } - m_rel_filter = orel.get_manager().mk_filter_identical_fn(orel, m_rel_cols.size(), m_rel_cols.c_ptr()); - SASSERT(m_rel_filter); - } - - virtual void operator()(relation_base & rb) { - finite_product_relation & r = get(rb); - - if(m_table_cols.size()>1) { - (*m_table_filter)(r.get_table()); - } - - if(m_rel_cols.size()>1) { - r.garbage_collect(true); - unsigned rel_cnt = r.m_others.size(); - for(unsigned rel_idx=0; rel_idx m_table_filter; - scoped_ptr m_rel_filter; - unsigned m_col; - app_ref m_value; - public: - filter_equal_fn(const finite_product_relation & r, const relation_element & value, unsigned col) - : m_col(col), m_value(value, r.get_context().get_manager()) { - if(r.is_table_column(col)) { - table_element tval; - r.get_manager().relation_to_table(r.get_signature()[col], value, tval); - m_table_filter = r.get_manager().mk_filter_equal_fn(r.get_table(), tval, r.m_sig2table[col]); - } - } - - virtual void operator()(relation_base & rb) { - finite_product_relation & r = get(rb); - - if(m_table_filter) { - (*m_table_filter)(r.get_table()); - return; - } - r.garbage_collect(false); - relation_vector & inner_rels = r.m_others; - unsigned rel_cnt = inner_rels.size(); - for(unsigned i=0; i m_table_filter; - scoped_ptr m_rel_filter; - app_ref m_cond; - - idx_set m_table_cond_columns; - idx_set m_rel_cond_columns; - - //like m_table_cond_columns and m_rel_cond_columns, only the indexes are local to the - //table/relation, not to the signature of the whole relation - idx_set m_table_local_cond_columns; - idx_set m_rel_local_cond_columns; - - /** - If equal to 0, it means the interpreted condition uses all table columns. Then the original - table is used instead of the result of the projection. - */ - scoped_ptr m_table_cond_columns_project; - /** - \brief Column indexes of the global relations to which correspond the data columns in the table - that is result of applying the \c m_table_cond_columns_project functor. - */ - unsigned_vector m_global_origins_of_projected_columns; - - scoped_ptr m_assembling_join_project; - - - /** - \brief Renaming that transforms the variable numbers pointing to the global relation into - variables that point to the inner relation variables. - - The elements that do not correspond to columns of the inner relation (but rather to the table - columns) is modified in \c operator() when evaluating the condition for all the relevant - combinations of table values. - */ - expr_ref_vector m_renaming_for_inner_rel; - public: - filter_interpreted_fn(const finite_product_relation & r, app * condition) - : m_manager(r.get_context().get_manager()), - m_subst(r.get_context().get_var_subst()), - m_cond(condition, m_manager), - m_renaming_for_inner_rel(m_manager) { - relation_manager & rmgr = r.get_manager(); - - rule_manager& rm = r.get_context().get_rule_manager(); - idx_set& cond_columns = rm.collect_vars(m_cond); - - unsigned sig_sz = r.get_signature().size(); - for(unsigned i=0; i tproj_scope; - const table_base * tproj; - if(m_table_cond_columns_project) { - tproj_scope = (*m_table_cond_columns_project)(rtable); - tproj = tproj_scope.get(); - } - else { - tproj = &rtable; - } - unsigned projected_data_cols = tproj->get_signature().size()-1; - SASSERT(m_table_cond_columns.num_elems()==projected_data_cols); - - table_signature filtered_sig = tproj->get_signature(); - filtered_sig.push_back(finite_product_relation::s_rel_idx_sort); - filtered_sig.set_functional_columns(1); - - relation_vector new_rels; - - scoped_rel filtered_table = tplugin.mk_empty(filtered_sig); - table_fact f; - unsigned renaming_ofs = m_renaming_for_inner_rel.size()-1; - table_base::iterator pit = tproj->begin(); - table_base::iterator pend = tproj->end(); - for(; pit!=pend; ++pit) { - pit->get_fact(f); - unsigned old_rel_idx = static_cast(f.back()); - const relation_base & old_rel = r.get_inner_rel(old_rel_idx); - - //put the table values into the substitution - for(unsigned i=0; i filter = rmgr.mk_filter_interpreted_fn(*new_rel, to_app(inner_cond)); - (*filter)(*new_rel); - - if(new_rel->empty()) { - new_rel->deallocate(); - continue; - } - - unsigned new_rel_idx = new_rels.size(); - new_rels.push_back(new_rel); - f.push_back(new_rel_idx); - filtered_table->add_fact(f); - } - - if(!m_assembling_join_project) { - unsigned_vector table_cond_columns_vect; - for(unsigned i=0; i new_table = (*m_assembling_join_project)(rtable, *filtered_table); - r.reset(); - r.init(*new_table, new_rels, true); - } - }; - - relation_mutator_fn * finite_product_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, - app * condition) { - if(&rb.get_plugin()!=this) { - return 0; - } - return alloc(filter_interpreted_fn, get(rb), condition); - } - - class finite_product_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { - - unsigned_vector m_r_cols; - unsigned_vector m_neg_cols; - - scoped_ptr m_table_neg_filter; - scoped_ptr m_table_neg_complement_selector; - scoped_ptr m_neg_intersection_join; - scoped_ptr m_table_intersection_join; - scoped_ptr m_table_overlap_union; - scoped_ptr m_table_subtract; - scoped_ptr m_inner_subtract; - scoped_ptr m_overlap_table_last_column_remover; - scoped_ptr m_r_table_union; - - bool m_table_overlaps_only; - - unsigned_vector m_r_shared_table_cols; - unsigned_vector m_neg_shared_table_cols; - - unsigned_vector m_r_shared_rel_cols; - unsigned_vector m_neg_shared_rel_cols; - public: - negation_filter_fn(const finite_product_relation & r, const finite_product_relation & neg, - unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * neg_cols) - : m_r_cols(joined_col_cnt, r_cols), - m_neg_cols(joined_col_cnt, neg_cols), - m_table_overlaps_only(true) { - const table_signature & tsig = r.m_table_sig; - const table_base & rtable = r.get_table(); - relation_manager & rmgr = r.get_manager(); - - for(unsigned i=0; iget_signature().size() , all_rel_cols); - m_parent.m_inner_subtract = m_r.get_manager().mk_filter_by_negation_fn(*r_inner, - inters_inner, all_rel_cols, all_rel_cols); - } - (*m_parent.m_inner_subtract)(*r_inner, inters_inner); - - unsigned new_rel_num = m_r.get_next_rel_idx(); - m_r.set_inner_rel(new_rel_num, r_inner); - func_columns[0]=new_rel_num; - return true; - } - }; - - - virtual void operator()(relation_base & rb, const relation_base & negb) { - finite_product_relation & r = get(rb); - const finite_product_relation & neg = get(negb); - - if(m_table_overlaps_only) { - handle_only_tables_overlap_case(r, neg); - return; - } - - finite_product_relation_plugin & plugin = r.get_plugin(); - if(!m_neg_intersection_join) { - } - scoped_rel intersection = get((*m_neg_intersection_join)(r, neg)); - SASSERT(&intersection->get_plugin()==&plugin); //the result of join is also finite product - SASSERT(r.get_signature()==intersection->get_signature()); - - table_base & r_table = r.get_table(); - table_plugin & tplugin = r_table.get_plugin(); - relation_manager & rmgr = r.get_manager(); - - //we need to do this before we perform the \c m_table_subtract - //the sigrature of the \c table_overlap0 relation is - //(data_columns)(original rel idx)(subtracted rel idx) - scoped_rel table_overlap0 = (*m_table_intersection_join)(r_table, - intersection->get_table()); - - //the rows that don't appear in the table of the intersection are safe to stay - (*m_table_subtract)(r_table, intersection->get_table()); - - //now we will examine the rows that do appear in the intersection - - //There are no functional columns in the \c table_overlap0 relation (because of - //the project we did). We need to make both rel idx columns functional. - //We do not lose any rows, since the data columns by themselves are unique. - - table_signature table_overlap_sig(table_overlap0->get_signature()); - table_overlap_sig.set_functional_columns(2); - scoped_rel table_overlap = tplugin.mk_empty(table_overlap_sig); - - if(!m_table_overlap_union) { - m_table_overlap_union = rmgr.mk_union_fn(*table_overlap, *table_overlap0); - SASSERT(m_table_overlap_union); - } - (*m_table_overlap_union)(*table_overlap, *table_overlap0); - - { - rel_subtractor * mutator = alloc(rel_subtractor, *this, r, *intersection); - scoped_ptr mapper = rmgr.mk_map_fn(*table_overlap, mutator); - (*mapper)(*table_overlap); - } - - if(!m_overlap_table_last_column_remover) { - unsigned removed_col = table_overlap->get_signature().size()-1; - m_overlap_table_last_column_remover = rmgr.mk_project_fn(*table_overlap, 1, &removed_col); - } - scoped_rel final_overlapping_rows_table = - (*m_overlap_table_last_column_remover)(*table_overlap); - - if(!m_r_table_union) { - m_r_table_union = rmgr.mk_union_fn(r_table, *final_overlapping_rows_table); - } - - (*m_r_table_union)(r_table, *final_overlapping_rows_table); - } - }; - - relation_intersection_filter_fn * finite_product_relation_plugin::mk_filter_by_negation_fn(const relation_base & rb, - const relation_base & negb, unsigned joined_col_cnt, - const unsigned * r_cols, const unsigned * negated_cols) { - if(&rb.get_plugin()!=this || &negb.get_plugin()!=this) { - return 0; - } - - return alloc(negation_filter_fn, get(rb), get(negb), joined_col_cnt, r_cols, negated_cols); - } - - - class finite_product_relation_plugin::filter_identical_pairs_fn : public relation_mutator_fn { - scoped_ptr m_tproject_fn; //if zero, no columns need to be projected away - unsigned m_col_cnt; - unsigned_vector m_table_cols; - unsigned_vector m_rel_cols; - - scoped_ptr m_assembling_join_project; - scoped_ptr m_updating_union; - public: - filter_identical_pairs_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * table_cols, - const unsigned * rel_cols) : - m_col_cnt(col_cnt), - m_table_cols(col_cnt, table_cols), - m_rel_cols(col_cnt, rel_cols) { - SASSERT(col_cnt>0); - const table_signature & tsig = r.m_table_sig; - unsigned t_sz = tsig.size(); - - sort_two_arrays(col_cnt, m_table_cols.begin(), m_rel_cols.begin()); - SASSERT(m_table_cols.back() tproj; - if(m_tproject_fn) { - tproj = (*m_tproject_fn)(r.get_table()); - } - else { - tproj = r.get_table().clone(); - } - SASSERT(m_col_cnt+1==tproj->get_signature().size()); - - table_signature filtered_sig = tproj->get_signature(); - filtered_sig.push_back(finite_product_relation::s_rel_idx_sort); - filtered_sig.set_functional_columns(1); - - relation_vector new_rels; - scoped_rel filtered_table = tplugin.mk_empty(filtered_sig); - table_fact f; - table_base::iterator pit = tproj->begin(); - table_base::iterator pend = tproj->end(); - for(; pit!=pend; ++pit) { - pit->get_fact(f); - unsigned old_rel_idx = static_cast(f.back()); - const relation_base & old_rel = r.get_inner_rel(old_rel_idx); - relation_base * new_rel = old_rel.clone(); - for(unsigned i=0; i filter = rmgr.mk_filter_equal_fn(*new_rel, r_el, m_rel_cols[i]); - (*filter)(*new_rel); - } - - if(new_rel->empty()) { - new_rel->deallocate(); - continue; - } - - unsigned new_rel_idx = new_rels.size(); - new_rels.push_back(new_rel); - f.push_back(new_rel_idx); - filtered_table->add_fact(f); - } - - if(!m_assembling_join_project) { - m_assembling_join_project = mk_assembler_of_filter_result(rtable, *filtered_table, m_table_cols); - } - - scoped_rel new_table = (*m_assembling_join_project)(r.get_table(), *filtered_table); - - r.reset(); - r.init(*new_table, new_rels, true); - } - }; - - relation_mutator_fn * finite_product_relation_plugin::mk_filter_identical_pairs(const finite_product_relation & r, - unsigned col_cnt, const unsigned * table_cols, const unsigned * rel_cols) { - return alloc(filter_identical_pairs_fn, r, col_cnt, table_cols, rel_cols); - } - - table_join_fn * finite_product_relation_plugin::mk_assembler_of_filter_result(const table_base & relation_table, - const table_base & filtered_table, const unsigned_vector & selected_columns) { - - table_plugin & tplugin = relation_table.get_plugin(); - const table_signature & rtable_sig = relation_table.get_signature(); - unsigned rtable_sig_sz = rtable_sig.size(); - unsigned selected_col_cnt = selected_columns.size(); - SASSERT(selected_col_cnt+2==filtered_table.get_signature().size()); - SASSERT(rtable_sig.functional_columns()==1); - SASSERT(filtered_table.get_signature().functional_columns()==1); - - unsigned_vector rtable_joined_cols; - rtable_joined_cols.append(selected_col_cnt, selected_columns.c_ptr()); //filtered table cols - rtable_joined_cols.push_back(rtable_sig_sz-1); //unfiltered relation indexes - - unsigned_vector filtered_joined_cols; - add_sequence(0, selected_col_cnt, filtered_joined_cols); //filtered table cols - filtered_joined_cols.push_back(selected_col_cnt); //unfiltered relation indexes - SASSERT(rtable_joined_cols.size()==filtered_joined_cols.size()); - - //the signature after join: - //(all relation table columns)(all filtered relation table columns)(unfiltered rel idx non-func from 'filtered') - // (unfiltered rel idx func from 'rtable')(filtered rel idx) - unsigned_vector removed_cols; - unsigned filtered_nonfunc_ofs = rtable_sig_sz-1; - add_sequence(filtered_nonfunc_ofs, selected_col_cnt, removed_cols); //data columns from 'filtered' - unsigned idx_ofs = filtered_nonfunc_ofs+selected_col_cnt; - removed_cols.push_back(idx_ofs); //unfiltered relation indexes from 'filtered' - removed_cols.push_back(idx_ofs+1); //unfiltered relation indexes from rtable - - table_join_fn * res = tplugin.get_manager().mk_join_project_fn(relation_table, filtered_table, - rtable_joined_cols, filtered_joined_cols, removed_cols); - SASSERT(res); - return res; - } - - // ----------------------------------- - // - // finite_product_relation - // - // ----------------------------------- - - finite_product_relation::finite_product_relation(finite_product_relation_plugin & p, - const relation_signature & s, const bool * table_columns, table_plugin & tplugin, - relation_plugin & oplugin, family_id other_kind) - : relation_base(p, s), - m_other_plugin(oplugin), - m_other_kind(other_kind), - m_full_rel_idx(UINT_MAX) { - const relation_signature & rel_sig = get_signature(); - unsigned sz = rel_sig.size(); - m_sig2table.resize(sz, UINT_MAX); - m_sig2other.resize(sz, UINT_MAX); - for(unsigned i=0; iclone()), - m_others(r.m_others), - m_available_rel_indexes(r.m_available_rel_indexes), - m_full_rel_idx(r.m_full_rel_idx), - m_live_rel_collection_project(), - m_empty_rel_removal_filter() { - //m_others is now just a shallow copy, we need use clone of the relations that in it now - unsigned other_sz = m_others.size(); - for(unsigned i=0; ideallocate(); - relation_vector::iterator it = m_others.begin(); - relation_vector::iterator end = m_others.end(); - for(; it!=end; ++it) { - relation_base * rel= *it; - if(rel) { - rel->deallocate(); - } - } - } - - context & finite_product_relation::get_context() const { - return get_manager().get_context(); - } - - unsigned finite_product_relation::get_next_rel_idx() const { - unsigned res; - if(!m_available_rel_indexes.empty()) { - res = m_available_rel_indexes.back(); - m_available_rel_indexes.pop_back(); - } - else { - res = m_others.size(); - m_others.push_back(0); - } - SASSERT(m_others[res]==0); - return res; - } - - void finite_product_relation::recycle_rel_idx(unsigned idx) const { - SASSERT(m_others[idx]==0); - m_available_rel_indexes.push_back(idx); - } - - unsigned finite_product_relation::get_full_rel_idx() { - if(m_full_rel_idx==UINT_MAX) { - m_full_rel_idx = get_next_rel_idx(); - relation_base * full_other = mk_full_inner(0); - m_others[m_full_rel_idx] = full_other; - } - return m_full_rel_idx; - } - - void finite_product_relation::init(const table_base & table_vals, const relation_vector & others, bool contiguous) { - SASSERT(m_table_sig.equal_up_to_fn_mark(table_vals.get_signature())); - SASSERT(empty()); - if(!m_others.empty()) { - garbage_collect(false); - } - SASSERT(m_others.empty()); - - m_others = others; - scoped_ptr table_union = get_manager().mk_union_fn(get_table(), table_vals); - (*table_union)(get_table(), table_vals); - - if(!contiguous) { - unsigned rel_cnt = m_others.size(); - for(unsigned i=0; isuggest_fact(t_f)) { - SASSERT(t_f.back()==new_rel_idx); - new_rel = mk_empty_inner(); - } else { - new_rel = get_inner_rel(t_f.back()).clone(); - - t_f[t_f.size()-1]=new_rel_idx; - m_table->ensure_fact(t_f); - } - new_rel->add_fact(o_f); - set_inner_rel(new_rel_idx, new_rel); - } - - bool finite_product_relation::contains_fact(const relation_fact & f) const { - table_fact t_f; - extract_table_fact(f, t_f); - - if(!m_table->fetch_fact(t_f)) { - return false; - } - - relation_fact o_f(get_context()); - extract_other_fact(f, o_f); - - const relation_base & other = get_inner_rel(t_f.back()); - - return other.contains_fact(o_f); - } - - bool finite_product_relation::empty() const { - garbage_collect(true); - return m_table->empty(); - } - - finite_product_relation * finite_product_relation::clone() const { - return alloc(finite_product_relation, *this); - } - - void finite_product_relation::complement_self(func_decl* p) { - unsigned other_sz = m_others.size(); - for(unsigned i=0; icomplement(p); - std::swap(m_others[i],r); - r->deallocate(); - } - table_element full_rel_idx = get_full_rel_idx(); - scoped_rel complement_table = m_table->complement(p, &full_rel_idx); - - scoped_ptr u_fn = get_manager().mk_union_fn(*m_table, *complement_table, 0); - SASSERT(u_fn); - (*u_fn)(*m_table, *complement_table, 0); - } - - finite_product_relation * finite_product_relation::complement(func_decl* p) const { - finite_product_relation * res = clone(); - res->complement_self(p); - return res; - } - - class finite_product_relation::live_rel_collection_reducer : public table_row_pair_reduce_fn { - idx_set & m_accumulator; - public: - live_rel_collection_reducer(idx_set & accumulator) : m_accumulator(accumulator) {} - - virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { - m_accumulator.insert(static_cast(merged_func_columns[0])); - } - }; - - void finite_product_relation::collect_live_relation_indexes(idx_set & res) const { - SASSERT(res.empty()); - unsigned table_data_col_cnt = m_table_sig.size()-1; - - if(table_data_col_cnt==0) { - if(!get_table().empty()) { - table_base::iterator iit = get_table().begin(); - table_base::iterator iend = get_table().end(); - - SASSERT(iit!=iend); - res.insert(static_cast((*iit)[0])); - SASSERT((++iit)==iend); - } - return; - } - - if(!m_live_rel_collection_project) { - buffer removed_cols; - removed_cols.resize(table_data_col_cnt); - for(unsigned i=0; i live_indexes_table = (*m_live_rel_collection_project)(get_table()); - res.swap(m_live_rel_collection_acc); - - SASSERT(live_indexes_table->get_signature().size()==1); - SASSERT(live_indexes_table->get_signature().functional_columns()==1); - if(!live_indexes_table->empty()) { - table_base::iterator iit = live_indexes_table->begin(); - table_base::iterator iend = live_indexes_table->end(); - - SASSERT(iit!=iend); - res.insert(static_cast((*iit)[0])); - SASSERT((++iit)==iend); - } - } - - void finite_product_relation::garbage_collect(bool remove_empty) const { - idx_set live_indexes; - collect_live_relation_indexes(live_indexes); - - scoped_rel empty_rel_indexes; //populated only if \c remove_empty==true - table_fact empty_rel_fact; - - unsigned rel_cnt = m_others.size(); -#if Z3DEBUG - unsigned encountered_live_indexes = 0; -#endif - for(unsigned rel_idx=0; rel_idxempty()) { - continue; - } - if(empty_rel_indexes==0) { - table_signature empty_rel_indexes_sig; - empty_rel_indexes_sig.push_back(s_rel_idx_sort); - empty_rel_indexes = get_table_plugin().mk_empty(empty_rel_indexes_sig); - } - empty_rel_fact.reset(); - empty_rel_fact.push_back(rel_idx); - empty_rel_indexes->add_fact(empty_rel_fact); - } - m_others[rel_idx]->deallocate(); - m_others[rel_idx] = 0; - if(rel_idx==m_full_rel_idx) { - m_full_rel_idx = UINT_MAX; - } - recycle_rel_idx(rel_idx); - } - SASSERT(encountered_live_indexes==live_indexes.num_elems()); - - if(m_available_rel_indexes.size()==m_others.size()) { - m_available_rel_indexes.reset(); - m_others.reset(); - } - - if(empty_rel_indexes) { - SASSERT(remove_empty); - - if(!m_empty_rel_removal_filter) { - unsigned t_joined_cols[] = { m_table_sig.size()-1 }; - unsigned ei_joined_cols[] = { 0 }; - m_empty_rel_removal_filter = get_manager().mk_filter_by_negation_fn(get_table(), *empty_rel_indexes, - 1, t_joined_cols, ei_joined_cols); - } - - (*m_empty_rel_removal_filter)(*m_table, *empty_rel_indexes); - } - } - - bool finite_product_relation::try_unify_specifications(ptr_vector & rels) { - if(rels.empty()) { - return true; - } - unsigned sig_sz = rels.back()->get_signature().size(); - svector table_cols(sig_sz, true); - - ptr_vector::iterator it = rels.begin(); - ptr_vector::iterator end = rels.end(); - for(; it!=end; ++it) { - finite_product_relation & rel = **it; - for(unsigned i=0; i pr_fun = get_manager().mk_project_fn(get_table(), to_project_away); - table_base * moved_cols_table = (*pr_fun)(get_table()); //gets destroyed inside moved_cols_trel - scoped_rel moved_cols_trel = - rmgr.get_table_relation_plugin(moved_cols_table->get_plugin()).mk_from_table(moved_cols_sig, moved_cols_table); - - svector moved_cols_table_flags(moved_cols_sig.size(), false); - - scoped_rel moved_cols_rel = get_plugin().mk_empty(moved_cols_sig, - moved_cols_table_flags.c_ptr()); - - scoped_ptr union_fun = - get_manager().mk_union_fn(*moved_cols_rel, *moved_cols_trel); - SASSERT(union_fun); //the table_relation should be able to be 'unioned into' any relation - - (*union_fun)(*moved_cols_rel, *moved_cols_trel); - - unsigned_vector all_moved_cols_indexes; - add_sequence(0, moved_cols_sig.size(), all_moved_cols_indexes); - - scoped_ptr join_fun = get_manager().mk_join_project_fn(*this, *moved_cols_rel, new_rel_columns, - all_moved_cols_indexes, new_rel_columns, false); - - scoped_rel unordered_rel = (*join_fun)(*this, *moved_cols_rel); - SASSERT(unordered_rel->get_signature().size()==sig_sz); //the signature size should be the same as original - - //now we need to reorder the columns in the \c new_rel to match the original table - unsigned_vector permutation; - unsigned moved_cols_cnt = new_rel_columns.size(); - unsigned next_replaced_idx = 0; - unsigned next_orig_idx = 0; - for(unsigned i=0; i perm_fun = get_manager().mk_rename_fn(*unordered_rel, cycle); - //the scoped_rel wrapper does the destruction of the old object - unordered_rel = (*perm_fun)(*unordered_rel); - cycle.reset(); - } - - finite_product_relation & new_rel = finite_product_relation_plugin::get(*unordered_rel); - - //Swap the content of the current object and new_rel. On exitting the function new_rel will be destroyed - //since it points to the content of scoped_rel unordered_rel. - swap(new_rel); - - return true; - } - - void finite_product_relation::display(std::ostream & out) const { - - garbage_collect(true); - - out << "finite_product_relation:\n"; - - out << " table:\n"; - get_table().display(out); - - unsigned other_sz = m_others.size(); - for(unsigned i=0; iget_fact(tfact); - - const table_relation & orel = static_cast(get_inner_rel(tfact[rel_idx_col])); - const table_base & otable = orel.get_table(); - table_base::iterator oit = otable.begin(); - table_base::iterator oend = otable.end(); - for(; oit!=oend; ++oit) { - oit->get_fact(ofact); - - out << "\t("; - for(unsigned i=0; iget_fact(fact); - conjs.reset(); - SASSERT(fact.size() == fact_sz); - unsigned rel_idx = static_cast(fact[fact_sz-1]); - m_others[rel_idx]->to_formula(tmp); - for (unsigned i = 0; i + 1 < fact_sz; ++i) { - conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), util.mk_numeral(fact[i], sig[i]))); - } - sh(tmp, fact_sz-1, tmp); - conjs.push_back(tmp); - disjs.push_back(m.mk_and(conjs.size(), conjs.c_ptr())); - } - bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), fml); - } - -}; - diff --git a/src/muz/dl_finite_product_relation.h b/src/muz/dl_finite_product_relation.h deleted file mode 100644 index d5181d122..000000000 --- a/src/muz/dl_finite_product_relation.h +++ /dev/null @@ -1,366 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_finite_product_relation.h - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-24. - -Revision History: - ---*/ -#ifndef _DL_FINITE_PRODUCT_RELATION_H_ -#define _DL_FINITE_PRODUCT_RELATION_H_ - - -#include "dl_base.h" -#include "dl_relation_manager.h" -#include "dl_table_relation.h" - -namespace datalog { - - class finite_product_relation; - - void universal_delete(finite_product_relation* ptr); - - - class finite_product_relation_plugin : public relation_plugin { - friend class finite_product_relation; - public: - struct rel_spec { - family_id m_inner_kind; //null_family_id means we don't care about the kind - svector m_table_cols; - - rel_spec() : m_inner_kind(null_family_id) {} - rel_spec(const svector& table_cols) - : m_inner_kind(null_family_id), m_table_cols(table_cols) {} - - bool operator==(const rel_spec & o) const { - return m_inner_kind==o.m_inner_kind && vectors_equal(m_table_cols, o.m_table_cols); - } - struct hash { - unsigned operator()(const rel_spec & o) const { - return o.m_inner_kind^svector_hash()(o.m_table_cols); - } - }; - }; - private: - - class join_fn; - class converting_join_fn; - class project_fn; - class rename_fn; - class union_fn; - class inner_singleton_union_fn; - class converting_union_fn; - class filter_identical_fn; - class filter_equal_fn; - class filter_interpreted_fn; - class negation_filter_fn; - class filter_identical_pairs_fn; - - relation_plugin & m_inner_plugin; - - rel_spec_store > m_spec_store; - - static symbol get_name(relation_plugin & inner_plugin); - family_id get_relation_kind(finite_product_relation & r, const bool * table_columns); - - static void get_all_possible_table_columns(relation_manager & rmgr, const relation_signature & s, - svector & table_columns); - void get_all_possible_table_columns(const relation_signature & s, svector & table_columns) { - get_all_possible_table_columns(get_manager(), s, table_columns); - } - - void split_signatures(const relation_signature & s, table_signature & table_sig, - relation_signature & remaining_sig); - void split_signatures(const relation_signature & s, const bool * table_columns, - table_signature & table_sig, relation_signature & remaining_sig); - public: - static finite_product_relation & get(relation_base & r); - static const finite_product_relation & get(const relation_base & r); - static finite_product_relation * get(relation_base * r); - static const finite_product_relation * get(const relation_base * r); - - static finite_product_relation_plugin & get_plugin(relation_manager & rmgr, relation_plugin & inner); - - finite_product_relation_plugin(relation_plugin & inner_plugin, relation_manager & manager); - - virtual void initialize(family_id fid); - - relation_plugin & get_inner_plugin() const { return m_inner_plugin; } - - virtual bool can_handle_signature(const relation_signature & s); - - virtual relation_base * mk_empty(const relation_signature & s); - /** - \c inner_kind==null_family_id means we don't care about the kind of the inner relation - */ - finite_product_relation * mk_empty(const relation_signature & s, const bool * table_columns, - family_id inner_kind=null_family_id); - finite_product_relation * mk_empty(const finite_product_relation & original); - virtual relation_base * mk_empty(const relation_base & original); - virtual relation_base * mk_empty(const relation_signature & s, family_id kind); - - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - - /** - \brief Return true if \c r can be converted to \c finite_product_relation_plugin either - by \c mk_from_table_relation or by \c mk_from_inner_relation. - */ - bool can_be_converted(const relation_base & r); - - /** - If the conversion cannot be performed, 0 is returned. - */ - finite_product_relation * mk_from_table_relation(const table_relation & r); - finite_product_relation * mk_from_inner_relation(const relation_base & r); - - bool can_convert_to_table_relation(const finite_product_relation & r); - table_relation * to_table_relation(const finite_product_relation & r); - - protected: - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - - private: - /** - \brief Create a filter that enforces equality between pairs of table and relation columns - - The column numbers in arrays \c table_cols and \c rel_cols must be local to the table/inner relation. - */ - relation_mutator_fn * mk_filter_identical_pairs(const finite_product_relation & r, unsigned col_cnt, - const unsigned * table_cols, const unsigned * rel_cols); - - /** - \brief Create a join-project operation that creates a table according to \c relation_table - but with references to relations updated and removed according to the content of \c filtered_table. - \c selected_columns contains sorted indexes of data columns in \c relation_table that are also in - the \c filtered_table (so that the first column in \c filtered_table corresponds to - \c selected_columns[0] -th column in \c relation_table etc...) - - Signature of \c relation_table: - (data columns)(functional column with indexes of relation objects) - - Signature of \c filtered_table: - (selected data columns)(non-functional column with original relation object indexes) - (functional column with indexes of filtered relation objects) - - */ - static table_join_fn * mk_assembler_of_filter_result(const table_base & relation_table, - const table_base & filtered_table, const unsigned_vector & selected_columns); - - }; - - class finite_product_relation : public relation_base { - friend class finite_product_relation_plugin; - friend class finite_product_relation_plugin::join_fn; - friend class finite_product_relation_plugin::project_fn; - friend class finite_product_relation_plugin::union_fn; - friend class finite_product_relation_plugin::rename_fn; - friend class finite_product_relation_plugin::inner_singleton_union_fn; - friend class finite_product_relation_plugin::filter_equal_fn; - friend class finite_product_relation_plugin::filter_identical_pairs_fn; - - class live_rel_collection_reducer; - - - public: - /** - Size of this sort determines how many different relation objects can we refer to. - */ - static const table_sort s_rel_idx_sort; - - - /** - \brief The last column in the signature is a functional column with index of the - associated inner relation. The other columns correspond to the relation signature - according to \c m_table2sig. - - It holds that \c m_table_sig.size()-1==m_table2sig.size() - */ - - table_signature m_table_sig; - unsigned_vector m_table2sig; // (ordered list) - unsigned_vector m_sig2table; //index of corresponding table column or UINT_MAX - private: - relation_signature m_other_sig; - unsigned_vector m_other2sig; // (ordered list) - public: - unsigned_vector m_sig2other; //index of corresponding other relation column or UINT_MAX - private: - relation_plugin & m_other_plugin; - family_id m_other_kind; - - mutable table_base * m_table; - public: - mutable relation_vector m_others; - private: - mutable unsigned_vector m_available_rel_indexes; - - /** - \c UINT_MAX means uninitialized. - If we can get away with it, we want to have a single full relation to refer to. - */ - mutable unsigned m_full_rel_idx; - - mutable idx_set m_live_rel_collection_acc; - mutable scoped_ptr m_live_rel_collection_project; - - mutable scoped_ptr m_empty_rel_removal_filter; - - void recycle_rel_idx(unsigned idx) const; - - // creates a full relation if it does not exist. - unsigned get_full_rel_idx(); - - - - public: - relation_base & get_inner_rel(table_element idx) - { SASSERT(idx(idx)); } - relation_base & get_inner_rel(unsigned idx) { SASSERT(m_others[idx]); return *m_others[idx]; } - const relation_base & get_inner_rel(unsigned idx) const - { return const_cast(*this).get_inner_rel(idx); } - - unsigned get_next_rel_idx() const; - - /** - The relation takes ownership of the \c inner object. - */ - void set_inner_rel(table_element idx, relation_base * inner) - { SASSERT(idx(idx), inner); } - /** - The relation takes ownership of the \c inner object. - */ - void set_inner_rel(unsigned idx, relation_base * inner) { - SASSERT(!m_others[idx]); - SASSERT(inner); - m_others[idx] = inner; - } - table_base & get_table() { return *m_table; } - - table_plugin & get_table_plugin() const { return get_table().get_plugin(); } - - void garbage_collect(bool remove_empty) const; - - /** - \brief Initialize an empty relation with table \c table_vals and relations in \c others. - - The relation object takes ownership of relations inside the \c others vector. - - If \c contiguous is true, it can be assumed that there are no zero elements in the \c others array. - */ - void init(const table_base & table_vals, const relation_vector & others, bool contiguous); - - private: - - - /** - \brief Extract the values of table non-functional columns from the relation fact. - The value of the functional column which determines index of the inner relation is undefined. - */ - void extract_table_fact(const relation_fact rf, table_fact & tf) const; - /** - \brief Extract the values of the inner relation columns from the relation fact. - */ - void extract_other_fact(const relation_fact rf, relation_fact & of) const; - - relation_base * mk_empty_inner(); - relation_base * mk_full_inner(func_decl* pred); - - - void complement_self(func_decl* pred); - - void collect_live_relation_indexes(idx_set & res) const; - - - /** - \brief Try to modify relations in \c rels so that they have the same columns corresponding to the table - and the inner relation (so that the union can be perofrmed on theim in a straightforward way). - - Relations in \c rels must all have equal signature. - - Even if the function fails and false is returned, some relations may already be modified. They are - in a valid state, but with different specification. - */ - static bool try_unify_specifications(ptr_vector & rels); - - bool try_modify_specification(const bool * table_cols); - - virtual bool can_swap(const relation_base & r) const - { return &get_plugin()==&r.get_plugin(); } - - /** - \brief Swap content of the current relation with the content of \c r. - - Both relations must come from the same plugin and be of the same signature. - */ - virtual void swap(relation_base & r); - - /** - \brief Create a \c finite_product_relation object. - */ - finite_product_relation(finite_product_relation_plugin & p, const relation_signature & s, - const bool * table_columns, table_plugin & tplugin, relation_plugin & oplugin, family_id other_kind); - finite_product_relation(const finite_product_relation & r); - virtual ~finite_product_relation(); - public: - context & get_context() const; - finite_product_relation_plugin & get_plugin() const { - return static_cast(relation_base::get_plugin()); - } - - bool is_table_column(unsigned col_idx) const { return m_sig2table[col_idx]!=UINT_MAX; } - - const table_base & get_table() const { return *m_table; } - - const relation_base & get_inner_rel(table_element idx) const - { SASSERT(idx(idx)); } - - /** - The function calls garbage_collect, so the internal state may change when it is called. - */ - virtual bool empty() const; - void reset() { m_table->reset(); garbage_collect(false); } - - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - - virtual finite_product_relation * clone() const; - virtual finite_product_relation * complement(func_decl* p) const; - - virtual void display(std::ostream & out) const; - virtual void display_tuples(func_decl & pred, std::ostream & out) const; - - virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); } - virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); } - - virtual void to_formula(expr_ref& fml) const; - }; - -}; - -#endif /* _DL_FINITE_PRODUCT_RELATION_H_ */ - diff --git a/src/muz/dl_interval_relation.cpp b/src/muz/dl_interval_relation.cpp deleted file mode 100644 index adc8cb760..000000000 --- a/src/muz/dl_interval_relation.cpp +++ /dev/null @@ -1,652 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_interval_relation.cpp - -Abstract: - - Basic interval reatlion. - -Author: - - Nikolaj Bjorner (nbjorner) 2010-2-11 - -Revision History: - ---*/ - -#include "debug.h" -#include "optional.h" -#include "ast_pp.h" -#include "dl_interval_relation.h" -#include "bool_rewriter.h" - - -namespace datalog { - // ------------------------- - // interval_relation_plugin - - interval_relation_plugin::interval_relation_plugin(relation_manager& m): - relation_plugin(interval_relation_plugin::get_name(), m), - m_empty(m_dep), - m_arith(get_ast_manager()) { - } - - bool interval_relation_plugin::can_handle_signature(const relation_signature & sig) { - for (unsigned i = 0; i < sig.size(); ++i) { - if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) { - return false; - } - } - return true; - } - - - relation_base * interval_relation_plugin::mk_empty(const relation_signature & s) { - return alloc(interval_relation, *this, s, true); - } - - relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { - return alloc(interval_relation, *this, s, false); - } - - class interval_relation_plugin::join_fn : public convenient_relation_join_fn { - public: - join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2) - : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ - } - - virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { - interval_relation const& r1 = get(_r1); - interval_relation const& r2 = get(_r2); - interval_relation_plugin& p = r1.get_plugin(); - interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); - result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); - return result; - } - }; - - relation_join_fn * interval_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (!check_kind(r1) || !check_kind(r2)) { - return 0; - } - return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); - } - - - class interval_relation_plugin::project_fn : public convenient_relation_project_fn { - public: - project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) - : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { - } - - virtual relation_base * operator()(const relation_base & _r) { - interval_relation const& r = get(_r); - interval_relation_plugin& p = r.get_plugin(); - interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); - result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); - return result; - } - }; - - relation_transformer_fn * interval_relation_plugin::mk_project_fn(const relation_base & r, - unsigned col_cnt, const unsigned * removed_cols) { - return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); - } - - class interval_relation_plugin::rename_fn : public convenient_relation_rename_fn { - public: - rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) - : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { - } - - virtual relation_base * operator()(const relation_base & _r) { - interval_relation const& r = get(_r); - interval_relation_plugin& p = r.get_plugin(); - interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); - result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); - return result; - } - }; - - relation_transformer_fn * interval_relation_plugin::mk_rename_fn(const relation_base & r, - unsigned cycle_len, const unsigned * permutation_cycle) { - if(!check_kind(r)) { - return 0; - } - return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); - } - - interval interval_relation_plugin::unite(interval const& src1, interval const& src2) { - bool l_open = src1.is_lower_open(); - bool r_open = src1.is_upper_open(); - ext_numeral low = src1.inf(); - ext_numeral high = src1.sup(); - if (src2.inf() < low || (src2.inf() == low && l_open)) { - low = src2.inf(); - l_open = src2.is_lower_open(); - } - if (src2.sup() > high || (src2.sup() == high && r_open)) { - high = src2.sup(); - r_open = src2.is_upper_open(); - } - return interval(dep(), low, l_open, 0, high, r_open, 0); - } - - interval interval_relation_plugin::widen(interval const& src1, interval const& src2) { - bool l_open = src1.is_lower_open(); - bool r_open = src1.is_upper_open(); - ext_numeral low = src1.inf(); - ext_numeral high = src1.sup(); - - if (src2.inf() < low || (low == src2.inf() && l_open && !src2.is_lower_open())) { - low = ext_numeral(false); - l_open = true; - } - if (high < src2.sup() || (src2.sup() == high && !r_open && src2.is_upper_open())) { - high = ext_numeral(true); - r_open = true; - } - return interval(dep(), low, l_open, 0, high, r_open, 0); - } - - interval interval_relation_plugin::meet(interval const& src1, interval const& src2, bool& isempty) { - isempty = false; - if (is_empty(0, src1) || is_infinite(src2)) { - return src1; - } - if (is_empty(0, src2) || is_infinite(src1)) { - return src2; - } - bool l_open = src1.is_lower_open(); - bool r_open = src1.is_upper_open(); - ext_numeral low = src1.inf(); - ext_numeral high = src1.sup(); - if (src2.inf() > low || (src2.inf() == low && !l_open)) { - low = src2.inf(); - l_open = src2.is_lower_open(); - } - if (src2.sup() < high || (src2.sup() == high && !r_open)) { - high = src2.sup(); - r_open = src2.is_upper_open(); - } - if (low > high || (low == high && (l_open || r_open))) { - isempty = true; - return interval(dep()); - } - else { - return interval(dep(), low, l_open, 0, high, r_open, 0); - } - } - - bool interval_relation_plugin::is_infinite(interval const& i) { - return i.plus_infinity() && i.minus_infinity(); - } - - bool interval_relation_plugin::is_empty(unsigned, interval const& i) { - return i.sup() < i.inf(); - } - - class interval_relation_plugin::union_fn : public relation_union_fn { - bool m_is_widen; - public: - union_fn(bool is_widen) : - m_is_widen(is_widen) { - } - - virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { - - TRACE("interval_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); - - interval_relation& r = get(_r); - interval_relation const& src = get(_src); - if (_delta) { - interval_relation& d = get(*_delta); - r.mk_union(src, &d, m_is_widen); - } - else { - r.mk_union(src, 0, m_is_widen); - } - } - }; - - relation_union_fn * interval_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; - } - return alloc(union_fn, false); - } - - relation_union_fn * interval_relation_plugin::mk_widen_fn( - const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; - } - return alloc(union_fn, true); - } - - class interval_relation_plugin::filter_identical_fn : public relation_mutator_fn { - unsigned_vector m_identical_cols; - public: - filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) - : m_identical_cols(col_cnt, identical_cols) {} - - virtual void operator()(relation_base & r) { - interval_relation & pr = get(r); - for (unsigned i = 1; i < m_identical_cols.size(); ++i) { - unsigned c1 = m_identical_cols[0]; - unsigned c2 = m_identical_cols[i]; - pr.equate(c1, c2); - } - } - }; - - relation_mutator_fn * interval_relation_plugin::mk_filter_identical_fn( - const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { - if(!check_kind(t)) { - return 0; - } - return alloc(filter_identical_fn, col_cnt, identical_cols); - } - - - class interval_relation_plugin::filter_equal_fn : public relation_mutator_fn { - unsigned m_col; - rational m_value; - public: - filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) - : m_col(col) { - arith_util arith(m.get_context().get_manager()); - VERIFY(arith.is_numeral(value, m_value)); - } - - virtual void operator()(relation_base & _r) { - interval_relation & r = get(_r); - interval_relation_plugin & p = r.get_plugin(); - r.mk_intersect(m_col, interval(p.dep(), m_value)); - TRACE("interval_relation", tout << m_value << "\n"; r.display(tout);); - } - }; - - relation_mutator_fn * interval_relation_plugin::mk_filter_equal_fn(const relation_base & r, - const relation_element & value, unsigned col) { - if(check_kind(r)) { - return alloc(filter_equal_fn, get_manager(), value, col); - } - return 0; - } - - - class interval_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { - app_ref m_cond; - public: - filter_interpreted_fn(interval_relation const& t, app* cond): - m_cond(cond, t.get_plugin().get_ast_manager()) { - } - - void operator()(relation_base& t) { - get(t).filter_interpreted(m_cond); - TRACE("interval_relation", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); - } - }; - - relation_mutator_fn * interval_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { - if (check_kind(t)) { - return alloc(filter_interpreted_fn, get(t), condition); - } - return 0; - } - - interval_relation& interval_relation_plugin::get(relation_base& r) { - return dynamic_cast(r); - } - - interval_relation const & interval_relation_plugin::get(relation_base const& r) { - return dynamic_cast(r); - } - - // ----------------------- - // interval_relation - - interval_relation::interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty): - vector_relation(p, s, is_empty, interval(p.dep())) - { - TRACE("interval_relation", tout << s.size() << "\n";); - } - - void interval_relation::add_fact(const relation_fact & f) { - interval_relation r(get_plugin(), get_signature(), false); - ast_manager& m = get_plugin().get_ast_manager(); - for (unsigned i = 0; i < f.size(); ++i) { - app_ref eq(m); - expr* e = f[i]; - eq = m.mk_eq(m.mk_var(i, m.get_sort(e)), e); - r.filter_interpreted(eq.get()); - } - mk_union(r, 0, false); - } - - bool interval_relation::contains_fact(const relation_fact & f) const { - SASSERT(f.size() == get_signature().size()); - interval_relation_plugin& p = get_plugin(); - - for (unsigned i = 0; i < f.size(); ++i) { - if (f[i] != f[find(i)]) { - return false; - } - interval const& iv = (*this)[i]; - if (p.is_infinite(iv)) { - continue; - } - rational v; - if (p.m_arith.is_numeral(f[i], v)) { - if (!iv.contains(v)) { - return false; - } - } - else { - // TBD: may or must? - } - } - return true; - } - - interval_relation * interval_relation::clone() const { - interval_relation* result = alloc(interval_relation, get_plugin(), get_signature(), empty()); - result->copy(*this); - return result; - } - - interval_relation * interval_relation::complement(func_decl*) const { - UNREACHABLE(); - return 0; - } - - void interval_relation::to_formula(expr_ref& fml) const { - ast_manager& m = get_plugin().get_ast_manager(); - arith_util& arith = get_plugin().m_arith; - expr_ref_vector conjs(m); - relation_signature const& sig = get_signature(); - for (unsigned i = 0; i < sig.size(); ++i) { - if (i != find(i)) { - conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), - m.mk_var(find(i), sig[find(i)]))); - continue; - } - interval const& iv = (*this)[i]; - sort* ty = sig[i]; - expr_ref var(m.mk_var(i, ty), m); - if (!iv.minus_infinity()) { - expr* lo = arith.mk_numeral(iv.get_lower_value(), ty); - if (iv.is_lower_open()) { - conjs.push_back(arith.mk_lt(lo, var)); - } - else { - conjs.push_back(arith.mk_le(lo, var)); - } - } - if (!iv.plus_infinity()) { - expr* hi = arith.mk_numeral(iv.get_upper_value(), ty); - if (iv.is_upper_open()) { - conjs.push_back(arith.mk_lt(var, hi)); - } - else { - conjs.push_back(arith.mk_le(var, hi)); - } - } - } - bool_rewriter br(m); - br.mk_and(conjs.size(), conjs.c_ptr(), fml); - } - - - void interval_relation::display_index(unsigned i, interval const& j, std::ostream & out) const { - out << i << " in " << j << "\n"; - } - - interval_relation_plugin& interval_relation::get_plugin() const { - return static_cast(relation_base::get_plugin()); - } - - void interval_relation::mk_intersect(unsigned idx, interval const& i) { - bool isempty; - (*this)[idx] = mk_intersect((*this)[idx], i, isempty); - if (isempty || is_empty(idx, (*this)[idx])) { - set_empty(); - } - } - - void interval_relation::mk_rename_elem(interval& i, unsigned, unsigned const* ) { - - } - - void interval_relation::filter_interpreted(app* cond) { - interval_relation_plugin& p = get_plugin(); - rational k; - unsigned x, y; - if (p.is_lt(cond, x, k, y)) { - // 0 < x - y + k - if (x == UINT_MAX) { - // y < k - mk_intersect(y, interval(p.dep(), k, true, false, 0)); - return; - } - if (y == UINT_MAX) { - // -k < x - mk_intersect(x, interval(p.dep(), -k, true, true, 0)); - return; - } - // y < x + k - ext_numeral x_hi = (*this)[x].sup(); - ext_numeral y_lo = (*this)[y].inf(); - if (!x_hi.is_infinite()) { - mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), true, false, 0)); - } - if (!y_lo.is_infinite()) { - mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, true, true, 0)); - } - return; - } - bool is_int = false; - if (p.is_le(cond, x, k, y, is_int)) { - // 0 <= x - y + k - if (x == UINT_MAX) { - // y <= k - mk_intersect(y, interval(p.dep(), k, false, false, 0)); - return; - } - if (y == UINT_MAX) { - // -k <= x - mk_intersect(x, interval(p.dep(), -k, false, true, 0)); - return; - } - ext_numeral x_hi = (*this)[x].sup(); - ext_numeral y_lo = (*this)[y].inf(); - if (!x_hi.is_infinite()) { - mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), false, false, 0)); - } - if (!y_lo.is_infinite()) { - mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, false, true, 0)); - } - return; - } - if (p.is_eq(cond, x, k, y)) { - // y = x + k - if (x == UINT_MAX) { - SASSERT(y != UINT_MAX); - mk_intersect(y, interval(p.dep(), k)); - return; - } - if (y == UINT_MAX) { - // x = - k - SASSERT(x != UINT_MAX); - mk_intersect(x, interval(p.dep(), -k)); - return; - } - interval x_i = (*this)[x]; - interval y_i = (*this)[y]; - x_i += interval(p.dep(), k); - y_i -= interval(p.dep(), k); - mk_intersect(x, y_i); - mk_intersect(y, x_i); - } - if (get_plugin().get_ast_manager().is_false(cond)) { - set_empty(); - } - } - - bool interval_relation_plugin::is_linear(expr* e, unsigned& neg, unsigned& pos, rational& k, bool is_pos) const { -#define SET_VAR(_idx_) \ - if (is_pos &&pos == UINT_MAX) { \ - pos = _idx_; \ - return true; \ - } \ - if (!is_pos && neg == UINT_MAX) { \ - neg = _idx_; \ - return true; \ - } \ - else { \ - return false; \ - } - - if (is_var(e)) { - SET_VAR(to_var(e)->get_idx()); - } - if (!is_app(e)) { - return false; - } - app* a = to_app(e); - - if (m_arith.is_add(e)) { - for (unsigned i = 0; i < a->get_num_args(); ++i) { - if (!is_linear(a->get_arg(i), neg, pos, k, is_pos)) return false; - } - return true; - } - if (m_arith.is_sub(e)) { - SASSERT(a->get_num_args() == 2); - return - is_linear(a->get_arg(0), neg, pos, k, is_pos) && - is_linear(a->get_arg(1), neg, pos, k, !is_pos); - } - rational k1; - SASSERT(!m_arith.is_mul(e) || a->get_num_args() == 2); - if (m_arith.is_mul(e) && - m_arith.is_numeral(a->get_arg(0), k1) && - k1.is_minus_one() && - is_var(a->get_arg(1))) { - SET_VAR(to_var(a->get_arg(1))->get_idx()); - } - - if (m_arith.is_numeral(e, k1)) { - if (is_pos) { - k += k1; - } - else { - k -= k1; - } - return true; - } - return false; - } - - // 0 <= x - y + k - bool interval_relation_plugin::is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const { - ast_manager& m = get_ast_manager(); - k.reset(); - x = UINT_MAX; - y = UINT_MAX; - - if (m_arith.is_le(cond)) { - is_int = m_arith.is_int(cond->get_arg(0)); - if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; - if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; - return (x != UINT_MAX || y != UINT_MAX); - } - if (m_arith.is_ge(cond)) { - is_int = m_arith.is_int(cond->get_arg(0)); - if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; - if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; - return (x != UINT_MAX || y != UINT_MAX); - } - if (m_arith.is_lt(cond) && m_arith.is_int(cond->get_arg(0))) { - is_int = true; - if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; - if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; - k -= rational::one(); - return (x != UINT_MAX || y != UINT_MAX); - } - if (m_arith.is_gt(cond) && m_arith.is_int(cond->get_arg(0))) { - is_int = true; - if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; - if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; - k += rational::one(); - return (x != UINT_MAX || y != UINT_MAX); - } - if (m.is_not(cond) && is_app(cond->get_arg(0))) { - // not (0 <= x - y + k) - // <=> - // 0 > x - y + k - // <=> - // 0 <= y - x - k - 1 - if (is_le(to_app(cond->get_arg(0)), x, k, y, is_int) && is_int) { - k.neg(); - k -= rational::one(); - std::swap(x, y); - return true; - } - // not (0 < x - y + k) - // <=> - // 0 >= x - y + k - // <=> - // 0 <= y - x - k - if (is_lt(to_app(cond->get_arg(0)), x, k, y)) { - is_int = false; - k.neg(); - std::swap(x, y); - return true; - } - } - return false; - } - - // 0 < x - y + k - bool interval_relation_plugin::is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const { - k.reset(); - x = UINT_MAX; - y = UINT_MAX; - if (m_arith.is_lt(cond) && m_arith.is_real(cond->get_arg(0))) { - if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; - if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; - return (x != UINT_MAX || y != UINT_MAX); - } - if (m_arith.is_gt(cond) && m_arith.is_real(cond->get_arg(0))) { - if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; - if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; - return (x != UINT_MAX || y != UINT_MAX); - } - return false; - } - - // 0 = x - y + k - bool interval_relation_plugin::is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const { - ast_manager& m = get_ast_manager(); - k.reset(); - x = UINT_MAX; - y = UINT_MAX; - if (m.is_eq(cond)) { - if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; - if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; - return (x != UINT_MAX || y != UINT_MAX); - } - return false; - } - -}; - diff --git a/src/muz/dl_interval_relation.h b/src/muz/dl_interval_relation.h deleted file mode 100644 index 685ef5c86..000000000 --- a/src/muz/dl_interval_relation.h +++ /dev/null @@ -1,140 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_interval_relation.h - -Abstract: - - Basic interval reatlion. - -Author: - - Nikolaj Bjorner (nbjorner) 2010-2-11 - -Revision History: - ---*/ -#ifndef _DL_INTERVAL_RELATION_H_ -#define _DL_INTERVAL_RELATION_H_ - - -#include "dl_context.h" -#include "old_interval.h" -#include "dl_vector_relation.h" -#include "arith_decl_plugin.h" -#include "basic_simplifier_plugin.h" - -namespace datalog { - - class interval_relation; - - class interval_relation_plugin : public relation_plugin { - v_dependency_manager m_dep; - interval m_empty; - arith_util m_arith; - - class join_fn; - class project_fn; - class rename_fn; - class union_fn; - class filter_equal_fn; - class filter_identical_fn; - class filter_interpreted_fn; - friend class interval_relation; - - interval unite(interval const& src1, interval const& src2); - interval widen(interval const& src1, interval const& src2); - interval meet(interval const& src1, interval const& src2, bool& is_empty); - - v_dependency_manager & dep() const { return const_cast(m_dep); } - - public: - interval_relation_plugin(relation_manager& m); - virtual bool can_handle_signature(const relation_signature & s); - static symbol get_name() { return symbol("interval_relation"); } - virtual relation_base * mk_empty(const relation_signature & s); - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - - static bool is_empty(unsigned idx, interval const& i); - static bool is_infinite(interval const& i); - - private: - static interval_relation& get(relation_base& r); - static interval_relation const & get(relation_base const& r); - - bool is_linear(expr* e, unsigned& pos, unsigned& neg, rational& k, bool is_pos) const; - - // x + k <= y - bool is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const; - // x + k < y - bool is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const; - // x + k = y - bool is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const; - }; - - - class interval_relation : public vector_relation { - friend class interval_relation_plugin; - friend class interval_relation_plugin::filter_equal_fn; - public: - interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty); - - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual interval_relation * clone() const; - virtual interval_relation * complement(func_decl*) const; - virtual void to_formula(expr_ref& fml) const; - interval_relation_plugin& get_plugin() const; - - void filter_interpreted(app* cond); - virtual bool is_precise() const { return false; } - - private: - - virtual interval mk_intersect(interval const& t1, interval const& t2, bool& is_empty) const { - return get_plugin().meet(t1, t2, is_empty); - } - - virtual interval mk_unite(interval const& t1, interval const& t2) const { return get_plugin().unite(t1,t2); } - - virtual interval mk_widen(interval const& t1, interval const& t2) const { return get_plugin().widen(t1,t2); } - - virtual bool is_subset_of(interval const& t1, interval const& t2) const { NOT_IMPLEMENTED_YET(); return false; } - - virtual bool is_full(interval const& t) const { - return interval_relation_plugin::is_infinite(t); - } - - virtual bool is_empty(unsigned idx, interval const& t) const { - return interval_relation_plugin::is_empty(idx, t); - } - - virtual void mk_rename_elem(interval& i, unsigned col_cnt, unsigned const* cycle); - - virtual void display_index(unsigned idx, interval const & i, std::ostream& out) const; - - void mk_intersect(unsigned idx, interval const& i); - - }; - -}; - -#endif - diff --git a/src/muz/dl_mk_explanations.cpp b/src/muz/dl_mk_explanations.cpp deleted file mode 100644 index 253bbbec7..000000000 --- a/src/muz/dl_mk_explanations.cpp +++ /dev/null @@ -1,880 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_mk_explanations.cpp - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-11-08. - -Revision History: - ---*/ - - -#include -#include"ast_pp.h" -#include "ast_smt_pp.h" -#include"dl_finite_product_relation.h" -#include"dl_product_relation.h" -#include"dl_sieve_relation.h" - -#include"dl_mk_explanations.h" - -namespace datalog { - - // ----------------------------------- - // - // explanation_relation_plugin declaration - // - // ----------------------------------- - - class explanation_relation; - - class explanation_relation_plugin : public relation_plugin { - friend class explanation_relation; - - class join_fn; - class project_fn; - class rename_fn; - class union_fn; - class foreign_union_fn; - class assignment_filter_fn; - class negation_filter_fn; - class intersection_filter_fn; - - bool m_relation_level_explanations; - - func_decl_ref m_union_decl; - - vector > m_pool; - - - app * mk_union(app * a1, app * a2) { - return get_ast_manager().mk_app(m_union_decl, a1, a2); - } - - public: - static symbol get_name(bool relation_level) { - return symbol(relation_level ? "relation_explanation" : "fact_explanation"); - } - - explanation_relation_plugin(bool relation_level, relation_manager & manager) - : relation_plugin(get_name(relation_level), manager), - m_relation_level_explanations(relation_level), - m_union_decl(mk_explanations::get_union_decl(get_context()), get_ast_manager()) {} - - ~explanation_relation_plugin() { - for (unsigned i = 0; i < m_pool.size(); ++i) { - for (unsigned j = 0; j < m_pool[i].size(); ++j) { - dealloc(m_pool[i][j]); - } - } - } - - virtual bool can_handle_signature(const relation_signature & s) { - unsigned n=s.size(); - for (unsigned i=0; i(relation_base::get_plugin()); - } - - virtual void to_formula(expr_ref& fml) const { - ast_manager& m = fml.get_manager(); - fml = m.mk_eq(m.mk_var(0, m.get_sort(m_data[0])), m_data[0]); - } - - bool is_undefined(unsigned col_idx) const { - return m_data[col_idx]==0; - } - bool no_undefined() const { - if (empty()) { - return true; - } - unsigned n = get_signature().size(); - for (unsigned i=0; i(get_plugin().mk_empty(get_signature())); - res->m_empty = m_empty; - SASSERT(res->m_data.empty()); - res->m_data.append(m_data); - return res; - } - - virtual relation_base * complement(func_decl* pred) const { - explanation_relation * res = static_cast(get_plugin().mk_empty(get_signature())); - if (empty()) { - res->set_undefined(); - } - return res; - } - - void display_explanation(app * expl, std::ostream & out) const { - if (expl) { - //TODO: some nice explanation output - ast_smt_pp pp(get_plugin().get_ast_manager()); - pp.display_expr_smt2(out, expl); - } - else { - out << ""; - } - } - - virtual void display(std::ostream & out) const { - if (empty()) { - out << "\n"; - return; - } - unsigned sz = get_signature().size(); - for (unsigned i=0; i s.size() && !m_pool[s.size()].empty()) { - explanation_relation* r = m_pool[s.size()].back(); - m_pool[s.size()].pop_back(); - r->m_empty = true; - r->m_data.reset(); - return r; - } - return alloc(explanation_relation, *this, s); - } - - void explanation_relation_plugin::recycle(explanation_relation* r) { - relation_signature const& sig = r->get_signature(); - if (m_pool.size() <= sig.size()) { - m_pool.resize(sig.size()+1); - } - m_pool[sig.size()].push_back(r); - } - - - class explanation_relation_plugin::join_fn : public convenient_relation_join_fn { - public: - join_fn(const relation_signature & sig1, const relation_signature & sig2) - : convenient_relation_join_fn(sig1, sig2, 0, 0, 0) {} - - virtual relation_base * operator()(const relation_base & r1_0, const relation_base & r2_0) { - const explanation_relation & r1 = static_cast(r1_0); - const explanation_relation & r2 = static_cast(r2_0); - explanation_relation_plugin & plugin = r1.get_plugin(); - - explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); - if (!r1.empty() && !r2.empty()) { - res->m_empty = false; - SASSERT(res->m_data.empty()); - res->m_data.append(r1.m_data); - res->m_data.append(r2.m_data); - } - return res; - } - }; - - relation_join_fn * explanation_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (&r1.get_plugin()!=this || &r2.get_plugin()!=this) { - return 0; - } - if (col_cnt!=0) { - return 0; - } - return alloc(join_fn, r1.get_signature(), r2.get_signature()); - } - - - class explanation_relation_plugin::project_fn : public convenient_relation_project_fn { - public: - project_fn(const relation_signature & sig, unsigned col_cnt, const unsigned * removed_cols) - : convenient_relation_project_fn(sig, col_cnt, removed_cols) {} - - virtual relation_base * operator()(const relation_base & r0) { - const explanation_relation & r = static_cast(r0); - explanation_relation_plugin & plugin = r.get_plugin(); - - explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); - if (!r.empty()) { - relation_fact proj_data = r.m_data; - project_out_vector_columns(proj_data, m_removed_cols); - res->assign_data(proj_data); - } - return res; - } - }; - - relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, - const unsigned * removed_cols) { - if (&r.get_plugin()!=this) { - return 0; - } - return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); - } - - - class explanation_relation_plugin::rename_fn : public convenient_relation_rename_fn { - public: - rename_fn(const relation_signature & sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) - : convenient_relation_rename_fn(sig, permutation_cycle_len, permutation_cycle) {} - - virtual relation_base * operator()(const relation_base & r0) { - const explanation_relation & r = static_cast(r0); - explanation_relation_plugin & plugin = r.get_plugin(); - - explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); - if (!r.empty()) { - relation_fact permutated_data = r.m_data; - permutate_by_cycle(permutated_data, m_cycle); - res->assign_data(permutated_data); - } - return res; - } - }; - - relation_transformer_fn * explanation_relation_plugin::mk_rename_fn(const relation_base & r, - unsigned permutation_cycle_len, const unsigned * permutation_cycle) { - return alloc(rename_fn, r.get_signature(), permutation_cycle_len, permutation_cycle); - } - - - class explanation_relation_plugin::union_fn : public relation_union_fn { - scoped_ptr m_delta_union_fun; - public: - virtual void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) { - explanation_relation & tgt = static_cast(tgt0); - const explanation_relation & src = static_cast(src0); - explanation_relation * delta = delta0 ? static_cast(delta0) : 0; - explanation_relation_plugin & plugin = tgt.get_plugin(); - - if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { - UNREACHABLE(); - } - if (src.empty()) { - return; - } - if (plugin.m_relation_level_explanations) { - tgt.unite_with_data(src.m_data); - if (delta) { - if (!m_delta_union_fun) { - m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src); - SASSERT(m_delta_union_fun); - } - (*m_delta_union_fun)(*delta, src); - } - } - else { - if (tgt.empty()) { - tgt.assign_data(src.m_data); - if (delta && delta->empty()) { - delta->assign_data(src.m_data); - } - } - } - } - }; - - class explanation_relation_plugin::foreign_union_fn : public relation_union_fn { - scoped_ptr m_delta_union_fun; - public: - virtual void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) { - explanation_relation & tgt = static_cast(tgt0); - explanation_relation * delta = delta0 ? static_cast(delta0) : 0; - - if (src.empty()) { - return; - } - tgt.set_undefined(); - if (delta) { - delta->set_undefined(); - } - } - }; - - relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - if (!check_kind(tgt) || (delta && !check_kind(*delta))) { - return 0; - } - if (!check_kind(src)) { - //this is to handle the product relation - return alloc(foreign_union_fn); - } - return alloc(union_fn); - } - - class explanation_relation_plugin::assignment_filter_fn : public relation_mutator_fn { - ast_manager & m_manager; - var_subst & m_subst; - unsigned m_col_idx; - app_ref m_new_rule; - public: - assignment_filter_fn(context & ctx, unsigned col_idx, app_ref new_rule) - : m_manager(ctx.get_manager()), - m_subst(ctx.get_var_subst()), - m_col_idx(col_idx), - m_new_rule(new_rule) {} - - virtual void operator()(relation_base & r0) { - explanation_relation & r = static_cast(r0); - - if (!r.is_undefined(m_col_idx)) { - UNREACHABLE(); - } - - unsigned sz = r.get_signature().size(); - ptr_vector subst_arg; - subst_arg.resize(sz, 0); - unsigned ofs = sz-1; - for (unsigned i=0; iget_arg(0); - expr * arg2 = cond->get_arg(1); - - if (is_var(arg2)) { - std::swap(arg1, arg2); - } - - if (!is_var(arg1) || !is_app(arg2)) { - return 0; - } - var * col_var = to_var(arg1); - app * new_rule = to_app(arg2); - if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { - return 0; - } - unsigned col_idx = col_var->get_idx(); - - return alloc(assignment_filter_fn, get_context(), col_idx, app_ref(new_rule, get_ast_manager())); - } - - - class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { - public: - virtual void operator()(relation_base & r, const relation_base & neg) { - if (!neg.empty()) { - r.reset(); - } - } - }; - - relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, - const relation_base & neg, unsigned joined_col_cnt, const unsigned * t_cols, - const unsigned * negated_cols) { - if (&r.get_plugin()!=this || &neg.get_plugin()!=this) { - return 0; - } - return alloc(negation_filter_fn); - } - - class explanation_relation_plugin::intersection_filter_fn : public relation_intersection_filter_fn { - func_decl_ref m_union_decl; - public: - intersection_filter_fn(explanation_relation_plugin & plugin) - : m_union_decl(plugin.m_union_decl) {} - - virtual void operator()(relation_base & tgt0, const relation_base & src0) { - explanation_relation & tgt = static_cast(tgt0); - const explanation_relation & src = static_cast(src0); - - if (src.empty()) { - tgt.reset(); - return; - } - if (tgt.empty()) { - return; - } - unsigned sz = tgt.get_signature().size(); - for (unsigned i=0; iget_decl()==m_union_decl.get()) { - if (curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) { - tgt.m_data.set(i, curr_src); - continue; - } - } - //the intersection is imprecise because we do nothing here, but it is good enough for - //the purpose of explanations - } - } - }; - - relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_intersection_fn( - const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, - const unsigned * tgt_cols, const unsigned * src_cols) { - if (&tgt.get_plugin()!=this || &src.get_plugin()!=this) { - return 0; - } - //this checks the join is one to one on all columns - if (tgt.get_signature()!=src.get_signature() - || joined_col_cnt!=tgt.get_signature().size() - || !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) { - return 0; - } - counter ctr; - ctr.count(joined_col_cnt, tgt_cols); - if (ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) { - return 0; - } - return alloc(intersection_filter_fn, *this); - } - - - // ----------------------------------- - // - // mk_explanations - // - // ----------------------------------- - - - mk_explanations::mk_explanations(context & ctx) - : plugin(50000), - m_manager(ctx.get_manager()), - m_context(ctx), - m_decl_util(ctx.get_decl_util()), - m_relation_level(ctx.explanations_on_relation_level()), - m_pinned(m_manager) { - m_e_sort = m_decl_util.mk_rule_sort(); - m_pinned.push_back(m_e_sort); - - relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); - symbol er_symbol = explanation_relation_plugin::get_name(m_relation_level); - m_er_plugin = static_cast(rmgr.get_relation_plugin(er_symbol)); - if (!m_er_plugin) { - m_er_plugin = alloc(explanation_relation_plugin, m_relation_level, rmgr); - rmgr.register_plugin(m_er_plugin); - if (!m_relation_level) { - DEBUG_CODE( - finite_product_relation_plugin * dummy; - SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); - ); - rmgr.register_plugin(alloc(finite_product_relation_plugin, *m_er_plugin, rmgr)); - } - } - DEBUG_CODE( - if (!m_relation_level) { - finite_product_relation_plugin * dummy; - SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); - } - ); - } - - func_decl * mk_explanations::get_union_decl(context & ctx) { - ast_manager & m = ctx.get_manager(); - sort_ref s(ctx.get_decl_util().mk_rule_sort(), m); - //can it happen that the function name would collide with some other symbol? - //if functions can be overloaded by their ranges, it should be fine. - return m.mk_func_decl(symbol("e_union"), s, s, s); - } - - void mk_explanations::assign_rel_level_kind(func_decl * e_decl, func_decl * orig) { - SASSERT(m_relation_level); - - relation_manager & rmgr = m_context.get_rel_context()->get_rmanager(); - unsigned sz = e_decl->get_arity(); - relation_signature sig; - rmgr.from_predicate(e_decl, sig); - - svector inner_sieve(sz-1, true); - inner_sieve.push_back(false); - - svector expl_sieve(sz-1, false); - expl_sieve.push_back(true); - - sieve_relation_plugin & sieve_plugin = sieve_relation_plugin::get_plugin(rmgr); - - family_id inner_kind = rmgr.get_requested_predicate_kind(orig); //may be null_family_id - family_id inner_sieve_kind = sieve_plugin.get_relation_kind(sig, inner_sieve, inner_kind); - family_id expl_kind = m_er_plugin->get_kind(); - family_id expl_sieve_kind = sieve_plugin.get_relation_kind(sig, expl_sieve, expl_kind); - - product_relation_plugin::rel_spec product_spec; - product_spec.push_back(inner_sieve_kind); - product_spec.push_back(expl_sieve_kind); - - family_id pred_kind = - product_relation_plugin::get_plugin(rmgr).get_relation_kind(sig, product_spec); - - rmgr.set_predicate_kind(e_decl, pred_kind); - } - - func_decl * mk_explanations::get_e_decl(func_decl * orig_decl) { - decl_map::obj_map_entry * e = m_e_decl_map.insert_if_not_there2(orig_decl, 0); - if (e->get_data().m_value==0) { - relation_signature e_domain; - e_domain.append(orig_decl->get_arity(), orig_decl->get_domain()); - e_domain.push_back(m_e_sort); - func_decl * new_decl = m_context.mk_fresh_head_predicate(orig_decl->get_name(), symbol("expl"), - e_domain.size(), e_domain.c_ptr(), orig_decl); - m_pinned.push_back(new_decl); - e->get_data().m_value = new_decl; - - if (m_relation_level) { - assign_rel_level_kind(new_decl, orig_decl); - } - } - return e->get_data().m_value; - } - - app * mk_explanations::get_e_lit(app * lit, unsigned e_var_idx) { - expr_ref_vector args(m_manager); - func_decl * e_decl = get_e_decl(lit->get_decl()); - args.append(lit->get_num_args(), lit->get_args()); - args.push_back(m_manager.mk_var(e_var_idx, m_e_sort)); - return m_manager.mk_app(e_decl, args.c_ptr()); - } - - symbol mk_explanations::get_rule_symbol(rule * r) { - if (r->name() == symbol::null) { - std::stringstream sstm; - r->display(m_context, sstm); - std::string res = sstm.str(); - res = res.substr(0, res.find_last_not_of('\n')+1); - return symbol(res.c_str()); - } - else { - return r->name(); - } - } - - rule * mk_explanations::get_e_rule(rule * r) { - rule_counter ctr; - ctr.count_rule_vars(m_manager, r); - unsigned max_var; - unsigned next_var = ctr.get_max_positive(max_var) ? (max_var+1) : 0; - unsigned head_var = next_var++; - app_ref e_head(get_e_lit(r->get_head(), head_var), m_manager); - - app_ref_vector e_tail(m_manager); - svector neg_flags; - unsigned pos_tail_sz = r->get_positive_tail_size(); - for (unsigned i=0; iget_tail(i), e_var)); - neg_flags.push_back(false); - } - unsigned tail_sz = r->get_tail_size(); - for (unsigned i=pos_tail_sz; iget_tail(i)); - neg_flags.push_back(r->is_neg_tail(i)); - } - - symbol rule_repr = get_rule_symbol(r); - - expr_ref_vector rule_expr_args(m_manager); - for (unsigned tail_idx=0; tail_idxget_arg(tail->get_num_args()-1)); - } - else { - //this adds argument values and the explanation term - //(values will be substituted for variables at runtime by the finite_product_relation) - rule_expr_args.append(tail->get_num_args(), tail->get_args()); - } - } - //rule_expr contains rule function with string representation of the rule as symbol and - //for each positive uninterpreted tail it contains its argument values and its explanation term - expr * rule_expr = m_decl_util.mk_rule(rule_repr, rule_expr_args.size(), rule_expr_args.c_ptr()); - - app_ref e_record(m_manager.mk_eq(m_manager.mk_var(head_var, m_e_sort), rule_expr), m_manager); - e_tail.push_back(e_record); - neg_flags.push_back(false); - SASSERT(e_tail.size()==neg_flags.size()); - - return m_context.get_rule_manager().mk(e_head, e_tail.size(), e_tail.c_ptr(), neg_flags.c_ptr()); - } - - void mk_explanations::transform_rules(const rule_set & src, rule_set & dst) { - rule_set::iterator rit = src.begin(); - rule_set::iterator rend = src.end(); - for (; rit!=rend; ++rit) { - rule * e_rule = get_e_rule(*rit); - dst.add_rule(e_rule); - } - - //add rules that will (for output predicates) copy facts from explained relations back to - //the original ones - expr_ref_vector lit_args(m_manager); - decl_set::iterator pit = src.get_output_predicates().begin(); - decl_set::iterator pend = src.get_output_predicates().end(); - for (; pit != pend; ++pit) { - func_decl * orig_decl = *pit; - - lit_args.reset(); - unsigned arity = orig_decl->get_arity(); - for (unsigned i=0; iget_domain(i))); - } - app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager); - app_ref e_lit(get_e_lit(orig_lit, arity), m_manager); - app * tail[] = { e_lit.get() }; - dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0)); - } - } - - void mk_explanations::translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, - relation_base & e_rel) { - SASSERT(m_e_fact_relation); - SASSERT(e_rel.get_plugin().is_product_relation()); - - product_relation & prod_rel = static_cast(e_rel); - SASSERT(prod_rel.size()==2); - SASSERT(prod_rel[0].get_plugin().is_sieve_relation()); - SASSERT(prod_rel[1].get_plugin().is_sieve_relation()); - sieve_relation * srels[] = { - static_cast(&prod_rel[0]), - static_cast(&prod_rel[1]) }; - if (&srels[0]->get_inner().get_plugin()==m_er_plugin) { - std::swap(srels[0], srels[1]); - } - SASSERT(&srels[0]->get_inner().get_plugin()==&orig.get_plugin()); - SASSERT(&srels[1]->get_inner().get_plugin()==m_er_plugin); - - relation_base & new_orig = srels[0]->get_inner(); - explanation_relation & expl_rel = static_cast(srels[1]->get_inner()); - - { - scoped_ptr orig_union_fun = rmgr.mk_union_fn(new_orig, orig); - SASSERT(orig_union_fun); - (*orig_union_fun)(new_orig, orig); - } - - { - scoped_ptr expl_union_fun = rmgr.mk_union_fn(expl_rel, *m_e_fact_relation); - SASSERT(expl_union_fun); - (*expl_union_fun)(expl_rel, *m_e_fact_relation); - } - } - - void mk_explanations::transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst) { - - if (!m_e_fact_relation) { - relation_signature expl_singleton_sig; - expl_singleton_sig.push_back(m_e_sort); - - relation_base * expl_singleton = rmgr.mk_empty_relation(expl_singleton_sig, m_er_plugin->get_kind()); - relation_fact es_fact(m_manager); - es_fact.push_back(m_decl_util.mk_fact(symbol("fact"))); - expl_singleton->add_fact(es_fact); - - SASSERT(&expl_singleton->get_plugin()==m_er_plugin); - m_e_fact_relation = static_cast(expl_singleton); - } - func_decl_set const& predicates = m_context.get_predicates(); - decl_set::iterator it = predicates.begin(); - decl_set::iterator end = predicates.end(); - for (; it!=end; ++it) { - func_decl * orig_decl = *it; - func_decl * e_decl = get_e_decl(orig_decl); - - if (!rmgr.try_get_relation(orig_decl) && - !src.contains(orig_decl)) { - // there are no facts or rules for this predicate - continue; - } - - dst.inherit_predicate(src, orig_decl, e_decl); - - relation_base & orig_rel = rmgr.get_relation(orig_decl); - relation_base & e_rel = rmgr.get_relation(e_decl); - SASSERT(e_rel.empty()); //the e_rel should be a new relation - - if (m_relation_level) { - translate_rel_level_relation(rmgr, orig_rel, e_rel); - } - else { - scoped_ptr product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, 0, 0); - SASSERT(product_fun); - scoped_rel aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation); - scoped_ptr union_fun = rmgr.mk_union_fn(e_rel, *aux_extended_rel); - SASSERT(union_fun); - (*union_fun)(e_rel, *aux_extended_rel); - } - } - } - - rule_set * mk_explanations::operator()(rule_set const & source) { - - if (source.empty()) { - return 0; - } - if (!m_context.generate_explanations()) { - return 0; - } - rule_set * res = alloc(rule_set, m_context); - transform_facts(m_context.get_rel_context()->get_rmanager(), source, *res); - transform_rules(source, *res); - return res; - } - -}; - diff --git a/src/muz/dl_mk_explanations.h b/src/muz/dl_mk_explanations.h deleted file mode 100644 index 9e4d705c3..000000000 --- a/src/muz/dl_mk_explanations.h +++ /dev/null @@ -1,86 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_mk_explanations.h - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-11-08. - -Revision History: - ---*/ - -#ifndef _DL_MK_EXPLANATIONS_H_ -#define _DL_MK_EXPLANATIONS_H_ - -#include "dl_context.h" -#include "dl_rule_transformer.h" - -namespace datalog { - - class explanation_relation; - class explanation_relation_plugin; - - class mk_explanations : public rule_transformer::plugin { - - typedef obj_map decl_map; - - ast_manager & m_manager; - context & m_context; - dl_decl_util & m_decl_util; - bool m_relation_level; - ast_ref_vector m_pinned; - explanation_relation_plugin * m_er_plugin; - sort * m_e_sort; - scoped_rel m_e_fact_relation; - - decl_map m_e_decl_map; - - symbol get_rule_symbol(rule * r); - - app * get_e_lit(app * lit, unsigned e_var_idx); - rule * get_e_rule(rule * r); - - /** - If \c m_relation_level is true, ensure \c e_decl predicate will be represented by - the right relation object. \c orig is the predicate corresponding to \c e_decl without - the explanation column. - */ - void assign_rel_level_kind(func_decl * e_decl, func_decl * orig); - void translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel); - - void transform_rules(const rule_set & src, rule_set & dst); - - void transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst); - public: - /** - If relation_level is true, the explanation will not be stored for each fact, - but we will rather store history of the whole relation. - */ - mk_explanations(context & ctx); - - /** - \brief Return explanation predicate that corresponds to \c orig_decl. - */ - func_decl * get_e_decl(func_decl * orig_decl); - - static func_decl * get_union_decl(context & ctx); - func_decl * get_union_decl() const { - return get_union_decl(m_context); - } - - rule_set * operator()(rule_set const & source); - - static expr* get_explanation(relation_base const& r); - }; -}; - -#endif /* _DL_MK_EXPLANATIONS_H_ */ - diff --git a/src/muz/dl_mk_karr_invariants.cpp b/src/muz/dl_mk_karr_invariants.cpp deleted file mode 100644 index 11bc850bb..000000000 --- a/src/muz/dl_mk_karr_invariants.cpp +++ /dev/null @@ -1,1114 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - dl_mk_karr_invariants.cpp - -Abstract: - - Extract integer linear invariants. - - The linear invariants are extracted according to Karr's method. - A short description is in - Nikolaj Bjorner, Anca Browne and Zohar Manna. Automatic Generation - of Invariants and Intermediate Assertions, in CP 95. - - The algorithm is here adapted to Horn clauses. - The idea is to maintain two data-structures for each recursive relation. - We call them R and RD - - R - set of linear congruences that are true of R. - - RD - the dual basis of of solutions for R. - - RD is updated by accumulating basis vectors for solutions - to R (the homogeneous dual of R) - R is updated from the inhomogeneous dual of RD. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-03-09 - -Revision History: - ---*/ - -#include"dl_mk_karr_invariants.h" -#include"expr_safe_replace.h" -#include"bool_rewriter.h" -#include"dl_mk_backwards.h" -#include"dl_mk_loop_counter.h" -#include "for_each_expr.h" - -namespace datalog { - - - mk_karr_invariants::mk_karr_invariants(context & ctx, unsigned priority): - rule_transformer::plugin(priority, false), - m_ctx(ctx), - m(ctx.get_manager()), - rm(ctx.get_rule_manager()), - m_inner_ctx(m, ctx.get_fparams()), - a(m), - m_pinned(m), - m_cancel(false) { - params_ref params; - params.set_sym("default_relation", symbol("karr_relation")); - params.set_sym("engine", symbol("datalog")); - params.set_bool("karr", false); - m_inner_ctx.updt_params(params); - } - - mk_karr_invariants::~mk_karr_invariants() { } - - matrix& matrix::operator=(matrix const& other) { - reset(); - append(other); - return *this; - } - - void matrix::display_row( - std::ostream& out, vector const& row, rational const& b, bool is_eq) { - for (unsigned j = 0; j < row.size(); ++j) { - out << row[j] << " "; - } - out << (is_eq?" = ":" >= ") << -b << "\n"; - } - - void matrix::display_ineq( - std::ostream& out, vector const& row, rational const& b, bool is_eq) { - bool first = true; - for (unsigned j = 0; j < row.size(); ++j) { - if (!row[j].is_zero()) { - if (!first && row[j].is_pos()) { - out << "+ "; - } - if (row[j].is_minus_one()) { - out << "- "; - } - if (row[j] > rational(1) || row[j] < rational(-1)) { - out << row[j] << "*"; - } - out << "x" << j << " "; - first = false; - } - } - out << (is_eq?"= ":">= ") << -b << "\n"; - } - - void matrix::display(std::ostream& out) const { - for (unsigned i = 0; i < A.size(); ++i) { - display_row(out, A[i], b[i], eq[i]); - } - } - - - class mk_karr_invariants::add_invariant_model_converter : public model_converter { - ast_manager& m; - arith_util a; - func_decl_ref_vector m_funcs; - expr_ref_vector m_invs; - public: - - add_invariant_model_converter(ast_manager& m): m(m), a(m), m_funcs(m), m_invs(m) {} - - virtual ~add_invariant_model_converter() { } - - void add(func_decl* p, expr* inv) { - if (!m.is_true(inv)) { - m_funcs.push_back(p); - m_invs.push_back(inv); - } - } - - virtual void operator()(model_ref & mr) { - for (unsigned i = 0; i < m_funcs.size(); ++i) { - func_decl* p = m_funcs[i].get(); - func_interp* f = mr->get_func_interp(p); - expr_ref body(m); - unsigned arity = p->get_arity(); - SASSERT(0 < arity); - if (f) { - SASSERT(f->num_entries() == 0); - if (!f->is_partial()) { - bool_rewriter(m).mk_and(f->get_else(), m_invs[i].get(), body); - } - } - else { - f = alloc(func_interp, m, arity); - mr->register_decl(p, f); - body = m.mk_false(); // fragile: assume that relation was pruned by being infeasible. - } - f->set_else(body); - } - } - - virtual model_converter * translate(ast_translation & translator) { - add_invariant_model_converter* mc = alloc(add_invariant_model_converter, m); - for (unsigned i = 0; i < m_funcs.size(); ++i) { - mc->add(translator(m_funcs[i].get()), m_invs[i].get()); - } - return mc; - } - - private: - void mk_body(matrix const& M, expr_ref& body) { - expr_ref_vector conj(m); - for (unsigned i = 0; i < M.size(); ++i) { - mk_body(M.A[i], M.b[i], M.eq[i], conj); - } - bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), body); - } - - void mk_body(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) { - expr_ref_vector sum(m); - expr_ref zero(m), lhs(m); - zero = a.mk_numeral(rational(0), true); - - for (unsigned i = 0; i < row.size(); ++i) { - if (row[i].is_zero()) { - continue; - } - var* var = m.mk_var(i, a.mk_int()); - if (row[i].is_one()) { - sum.push_back(var); - } - else { - sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); - } - } - if (!b.is_zero()) { - sum.push_back(a.mk_numeral(b, true)); - } - lhs = a.mk_add(sum.size(), sum.c_ptr()); - if (is_eq) { - conj.push_back(m.mk_eq(lhs, zero)); - } - else { - conj.push_back(a.mk_ge(lhs, zero)); - } - } - }; - - void mk_karr_invariants::cancel() { - m_cancel = true; - m_inner_ctx.cancel(); - } - - rule_set * mk_karr_invariants::operator()(rule_set const & source) { - if (!m_ctx.get_params().karr()) { - return 0; - } - rule_set::iterator it = source.begin(), end = source.end(); - for (; it != end; ++it) { - rule const& r = **it; - if (r.has_negation()) { - return 0; - } - } - mk_loop_counter lc(m_ctx); - mk_backwards bwd(m_ctx); - - scoped_ptr src_loop = lc(source); - TRACE("dl", src_loop->display(tout << "source loop\n");); - - get_invariants(*src_loop); - - if (m_cancel) { - return 0; - } - - // figure out whether to update same rules as used for saturation. - scoped_ptr rev_source = bwd(*src_loop); - get_invariants(*rev_source); - scoped_ptr src_annot = update_rules(*src_loop); - rule_set* rules = lc.revert(*src_annot); - rules->inherit_predicates(source); - TRACE("dl", rules->display(tout);); - m_pinned.reset(); - m_fun2inv.reset(); - return rules; - } - - void mk_karr_invariants::get_invariants(rule_set const& src) { - m_inner_ctx.reset(); - rel_context& rctx = *m_inner_ctx.get_rel_context(); - ptr_vector heads; - func_decl_set const& predicates = m_ctx.get_predicates(); - for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) { - m_inner_ctx.register_predicate(*fit, false); - } - m_inner_ctx.ensure_opened(); - m_inner_ctx.replace_rules(src); - m_inner_ctx.close(); - rule_set::decl2rules::iterator dit = src.begin_grouped_rules(); - rule_set::decl2rules::iterator dend = src.end_grouped_rules(); - for (; dit != dend; ++dit) { - heads.push_back(dit->m_key); - } - m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); - - // retrieve invariants. - dit = src.begin_grouped_rules(); - for (; dit != dend; ++dit) { - func_decl* p = dit->m_key; - relation_base* rb = rctx.try_get_relation(p); - if (rb) { - expr_ref fml(m); - rb->to_formula(fml); - if (m.is_true(fml)) { - continue; - } - expr* inv = 0; - if (m_fun2inv.find(p, inv)) { - fml = m.mk_and(inv, fml); - } - m_pinned.push_back(fml); - m_fun2inv.insert(p, fml); - } - } - } - - rule_set* mk_karr_invariants::update_rules(rule_set const& src) { - scoped_ptr dst = alloc(rule_set, m_ctx); - rule_set::iterator it = src.begin(), end = src.end(); - for (; it != end; ++it) { - update_body(*dst, **it); - } - if (m_ctx.get_model_converter()) { - add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m); - rule_set::decl2rules::iterator git = src.begin_grouped_rules(); - rule_set::decl2rules::iterator gend = src.end_grouped_rules(); - for (; git != gend; ++git) { - func_decl* p = git->m_key; - expr* fml = 0; - if (m_fun2inv.find(p, fml)) { - kmc->add(p, fml); - } - } - m_ctx.add_model_converter(kmc); - } - - dst->inherit_predicates(src); - return dst.detach(); - } - - void mk_karr_invariants::update_body(rule_set& rules, rule& r) { - unsigned utsz = r.get_uninterpreted_tail_size(); - unsigned tsz = r.get_tail_size(); - app_ref_vector tail(m); - expr_ref fml(m); - for (unsigned i = 0; i < tsz; ++i) { - tail.push_back(r.get_tail(i)); - } - for (unsigned i = 0; i < utsz; ++i) { - func_decl* q = r.get_decl(i); - expr* fml = 0; - if (m_fun2inv.find(q, fml)) { - expr_safe_replace rep(m); - for (unsigned j = 0; j < q->get_arity(); ++j) { - rep.insert(m.mk_var(j, q->get_domain(j)), - r.get_tail(i)->get_arg(j)); - } - expr_ref tmp(fml, m); - rep(tmp); - tail.push_back(to_app(tmp)); - } - } - rule* new_rule = &r; - if (tail.size() != tsz) { - new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), 0, r.name()); - } - rules.add_rule(new_rule); - rm.mk_rule_rewrite_proof(r, *new_rule); // should be weakening rule. - } - - - - class karr_relation : public relation_base { - friend class karr_relation_plugin; - friend class karr_relation_plugin::filter_equal_fn; - - karr_relation_plugin& m_plugin; - ast_manager& m; - mutable arith_util a; - func_decl_ref m_fn; - mutable bool m_empty; - mutable matrix m_ineqs; - mutable bool m_ineqs_valid; - mutable matrix m_basis; - mutable bool m_basis_valid; - - public: - karr_relation(karr_relation_plugin& p, func_decl* f, relation_signature const& s, bool is_empty): - relation_base(p, s), - m_plugin(p), - m(p.get_ast_manager()), - a(m), - m_fn(f, m), - m_empty(is_empty), - m_ineqs_valid(!is_empty), - m_basis_valid(false) - { - } - - virtual bool empty() const { - return m_empty; - } - - virtual bool is_precise() const { return false; } - - virtual void add_fact(const relation_fact & f) { - SASSERT(m_empty); - SASSERT(!m_basis_valid); - m_empty = false; - m_ineqs_valid = true; - for (unsigned i = 0; i < f.size(); ++i) { - rational n; - if (a.is_numeral(f[i], n) && n.is_int()) { - vector row; - row.resize(f.size()); - row[i] = rational(1); - m_ineqs.A.push_back(row); - m_ineqs.b.push_back(-n); - m_ineqs.eq.push_back(true); - } - } - } - - virtual bool contains_fact(const relation_fact & f) const { - UNREACHABLE(); - return false; - } - - virtual void display(std::ostream & out) const { - if (m_fn) { - out << m_fn->get_name() << "\n"; - } - if (empty()) { - out << "empty\n"; - } - else { - if (m_ineqs_valid) { - m_ineqs.display(out << "ineqs:\n"); - } - if (m_basis_valid) { - m_basis.display(out << "basis:\n"); - } - } - } - - virtual karr_relation * clone() const { - karr_relation* result = alloc(karr_relation, m_plugin, m_fn, get_signature(), m_empty); - result->copy(*this); - return result; - } - - virtual karr_relation * complement(func_decl*) const { - UNREACHABLE(); - return 0; - } - - virtual void to_formula(expr_ref& fml) const { - if (empty()) { - fml = m.mk_false(); - } - else { - matrix const& M = get_ineqs(); - expr_ref_vector conj(m); - for (unsigned i = 0; i < M.size(); ++i) { - to_formula(M.A[i], M.b[i], M.eq[i], conj); - } - bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), fml); - } - } - - karr_relation_plugin& get_plugin() const { return m_plugin; } - - void filter_interpreted(app* cond) { - rational one(1), mone(-1); - expr* e1, *e2, *en; - var* v, *w; - rational n1, n2; - expr_ref_vector conjs(m); - qe::flatten_and(cond, conjs); - matrix& M = get_ineqs(); - unsigned num_columns = get_signature().size(); - - for (unsigned i = 0; i < conjs.size(); ++i) { - expr* e = conjs[i].get(); - rational b(0); - vector row; - row.resize(num_columns, rational(0)); - bool processed = true; - if (m.is_eq(e, e1, e2) && is_linear(e1, row, b, one) && is_linear(e2, row, b, mone)) { - M.A.push_back(row); - M.b.push_back(b); - M.eq.push_back(true); - } - else if ((a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) && - is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { - M.A.push_back(row); - M.b.push_back(b); - M.eq.push_back(false); - } - else if ((a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) && - is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { - M.A.push_back(row); - M.b.push_back(b - rational(1)); - M.eq.push_back(false); - } - else if (m.is_not(e, en) && (a.is_lt(en, e2, e1) || a.is_gt(en, e1, e2)) && - is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { - M.A.push_back(row); - M.b.push_back(b); - M.eq.push_back(false); - } - else if (m.is_not(e, en) && (a.is_le(en, e2, e1) || a.is_ge(en, e1, e2)) && - is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { - M.A.push_back(row); - M.b.push_back(b - rational(1)); - M.eq.push_back(false); - } - else if (m.is_or(e, e1, e2) && is_eq(e1, v, n1) && is_eq(e2, w, n2) && v == w) { - if (n1 > n2) { - std::swap(n1, n2); - } - SASSERT(n1 <= n2); - row[v->get_idx()] = rational(1); - // v - n1 >= 0 - M.A.push_back(row); - M.b.push_back(-n1); - M.eq.push_back(false); - // -v + n2 >= 0 - row[v->get_idx()] = rational(-1); - M.A.push_back(row); - M.b.push_back(n2); - M.eq.push_back(false); - } - else { - processed = false; - } - TRACE("dl", tout << (processed?"+ ":"- ") << mk_pp(e, m) << "\n"; - if (processed) matrix::display_ineq(tout, row, M.b.back(), M.eq.back()); - ); - } - TRACE("dl", display(tout);); - } - - void mk_join(karr_relation const& r1, karr_relation const& r2, - unsigned col_cnt, unsigned const* cols1, unsigned const* cols2) { - if (r1.empty() || r2.empty()) { - m_empty = true; - return; - } - matrix const& M1 = r1.get_ineqs(); - matrix const& M2 = r2.get_ineqs(); - unsigned sig1_size = r1.get_signature().size(); - unsigned sig_size = get_signature().size(); - m_ineqs.reset(); - for (unsigned i = 0; i < M1.size(); ++i) { - vector row; - row.append(M1.A[i]); - row.resize(sig_size); - m_ineqs.A.push_back(row); - m_ineqs.b.push_back(M1.b[i]); - m_ineqs.eq.push_back(M1.eq[i]); - } - for (unsigned i = 0; i < M2.size(); ++i) { - vector row; - row.resize(sig_size); - for (unsigned j = 0; j < M2.A[i].size(); ++j) { - row[sig1_size + j] = M2.A[i][j]; - } - m_ineqs.A.push_back(row); - m_ineqs.b.push_back(M2.b[i]); - m_ineqs.eq.push_back(M2.eq[i]); - } - for (unsigned i = 0; i < col_cnt; ++i) { - vector row; - row.resize(sig_size); - row[cols1[i]] = rational(1); - row[sig1_size + cols2[i]] = rational(-1); - m_ineqs.A.push_back(row); - m_ineqs.b.push_back(rational(0)); - m_ineqs.eq.push_back(true); - } - m_ineqs_valid = true; - m_basis_valid = false; - m_empty = false; - if (r1.m_fn) { - m_fn = r1.m_fn; - } - if (r2.m_fn) { - m_fn = r2.m_fn; - } - } - - void mk_project(karr_relation const& r, unsigned cnt, unsigned const* cols) { - if (r.m_empty) { - m_empty = true; - return; - } - matrix const& M = r.get_basis(); - m_basis.reset(); - for (unsigned i = 0; i < M.size(); ++i) { - vector row; - unsigned k = 0; - for (unsigned j = 0; j < M.A[i].size(); ++j) { - if (k < cnt && j == cols[k]) { - ++k; - } - else { - row.push_back(M.A[i][j]); - } - } - SASSERT(row.size() + cnt == M.A[i].size()); - SASSERT(M.eq[i]); - m_basis.A.push_back(row); - m_basis.b.push_back(M.b[i]); - m_basis.eq.push_back(true); - } - m_basis_valid = true; - m_ineqs_valid = false; - m_empty = false; - m_fn = r.m_fn; - - TRACE("dl", - for (unsigned i = 0; i < cnt; ++i) { - tout << cols[i] << " "; - } - tout << "\n"; - r.display(tout); - display(tout);); - } - - void mk_rename(const karr_relation & r, unsigned col_cnt, const unsigned * cols) { - if (r.empty()) { - m_empty = true; - return; - } - m_ineqs.reset(); - m_basis.reset(); - m_ineqs_valid = r.m_ineqs_valid; - m_basis_valid = r.m_basis_valid; - if (m_ineqs_valid) { - m_ineqs.append(r.m_ineqs); - mk_rename(m_ineqs, col_cnt, cols); - } - if (m_basis_valid) { - m_basis.append(r.m_basis); - mk_rename(m_basis, col_cnt, cols); - } - m_fn = r.m_fn; - TRACE("dl", r.display(tout); display(tout);); - } - - void mk_union(karr_relation const& src, karr_relation* delta) { - if (src.empty()) { - if (delta) { - delta->m_empty = true; - } - return; - } - matrix const& M = src.get_basis(); - if (empty()) { - m_basis = M; - m_basis_valid = true; - m_empty = false; - m_ineqs_valid = false; - if (delta) { - delta->copy(*this); - } - return; - } - matrix& N = get_basis(); - unsigned N_size = N.size(); - for (unsigned i = 0; i < M.size(); ++i) { - bool found = false; - for (unsigned j = 0; !found && j < N_size; ++j) { - found = - same_row(M.A[i], N.A[j]) && - M.b[i] == N.b[j] && - M.eq[i] == N.eq[j]; - } - if (!found) { - N.A.push_back(M.A[i]); - N.b.push_back(M.b[i]); - N.eq.push_back(M.eq[i]); - } - } - m_ineqs_valid = false; - if (N_size != N.size()) { - if (delta) { - delta->copy(*this); - } - } - } - - matrix const& get_basis() const { - init_basis(); - return m_basis; - } - - matrix& get_basis() { - init_basis(); - return m_basis; - } - - matrix const& get_ineqs() const { - init_ineqs(); - return m_ineqs; - } - - matrix & get_ineqs() { - init_ineqs(); - return m_ineqs; - } - - private: - - void copy(karr_relation const& other) { - m_ineqs = other.m_ineqs; - m_basis = other.m_basis; - m_basis_valid = other.m_basis_valid; - m_ineqs_valid = other.m_ineqs_valid; - m_empty = other.m_empty; - } - - bool same_row(vector const& r1, vector const& r2) const { - SASSERT(r1.size() == r2.size()); - for (unsigned i = 0; i < r1.size(); ++i) { - if (r1[i] != r2[i]) { - return false; - } - } - return true; - } - - void mk_rename(matrix& M, unsigned col_cnt, unsigned const* cols) { - for (unsigned j = 0; j < M.size(); ++j) { - vector & row = M.A[j]; - rational tmp = row[cols[0]]; - for (unsigned i = 0; i + 1 < col_cnt; ++i) { - row[cols[i]] = row[cols[i+1]]; - } - row[cols[col_cnt-1]] = tmp; - } - } - - bool is_eq(expr* e, var*& v, rational& n) { - expr* e1, *e2; - if (!m.is_eq(e, e1, e2)) { - return false; - } - if (!is_var(e1)) { - std::swap(e1, e2); - } - if (!is_var(e1)) { - return false; - } - v = to_var(e1); - if (!a.is_numeral(e2, n)) { - return false; - } - return true; - } - - bool is_linear(expr* e, vector& row, rational& b, rational const& mul) { - if (!a.is_int(e)) { - return false; - } - if (is_var(e)) { - row[to_var(e)->get_idx()] += mul; - return true; - } - if (!is_app(e)) { - return false; - } - rational n; - if (a.is_numeral(e, n)) { - b += mul*n; - return true; - } - if (a.is_add(e)) { - for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { - if (!is_linear(to_app(e)->get_arg(i), row, b, mul)) { - return false; - } - } - return true; - } - expr* e1, *e2; - if (a.is_sub(e, e1, e2)) { - return is_linear(e1, row, b, mul) && is_linear(e2, row, b, -mul); - } - if (a.is_mul(e, e1, e2) && a.is_numeral(e1, n)) { - return is_linear(e2, row, b, mul*n); - } - if (a.is_mul(e, e1, e2) && a.is_numeral(e2, n)) { - return is_linear(e1, row, b, mul*n); - } - if (a.is_uminus(e, e1)) { - return is_linear(e1, row, b, -mul); - } - return false; - } - - void init_ineqs() const { - if (!m_ineqs_valid) { - SASSERT(m_basis_valid); - m_plugin.dualizeH(m_ineqs, m_basis); - m_ineqs_valid = true; - } - } - - void init_basis() const { - if (!m_basis_valid) { - SASSERT(m_ineqs_valid); - if (m_plugin.dualizeI(m_basis, m_ineqs)) { - m_basis_valid = true; - } - else { - m_empty = true; - } - } - } - - void to_formula(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) const { - expr_ref_vector sum(m); - expr_ref zero(m), lhs(m); - zero = a.mk_numeral(rational(0), true); - - for (unsigned i = 0; i < row.size(); ++i) { - if (row[i].is_zero()) { - continue; - } - var* var = m.mk_var(i, a.mk_int()); - if (row[i].is_one()) { - sum.push_back(var); - } - else { - sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); - } - } - if (!b.is_zero()) { - sum.push_back(a.mk_numeral(b, true)); - } - lhs = a.mk_add(sum.size(), sum.c_ptr()); - if (is_eq) { - conj.push_back(m.mk_eq(lhs, zero)); - } - else { - conj.push_back(a.mk_ge(lhs, zero)); - } - } - }; - - - karr_relation& karr_relation_plugin::get(relation_base& r) { - return dynamic_cast(r); - } - - karr_relation const & karr_relation_plugin::get(relation_base const& r) { - return dynamic_cast(r); - } - - void karr_relation_plugin::set_cancel(bool f) { - m_hb.set_cancel(f); - } - - relation_base * karr_relation_plugin::mk_empty(const relation_signature & s) { - return alloc(karr_relation, *this, 0, s, true); - } - - relation_base * karr_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { - return alloc(karr_relation, *this, p, s, false); - } - - class karr_relation_plugin::join_fn : public convenient_relation_join_fn { - public: - join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2) - : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ - } - - virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { - karr_relation const& r1 = get(_r1); - karr_relation const& r2 = get(_r2); - karr_relation_plugin& p = r1.get_plugin(); - karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); - result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); - return result; - } - }; - - relation_join_fn * karr_relation_plugin::mk_join_fn( - const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (!check_kind(t1) || !check_kind(t2)) { - return 0; - } - return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); - } - - - class karr_relation_plugin::project_fn : public convenient_relation_project_fn { - public: - project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) - : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { - } - - virtual relation_base * operator()(const relation_base & _r) { - karr_relation const& r = get(_r); - karr_relation_plugin& p = r.get_plugin(); - karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); - result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); - return result; - } - }; - - relation_transformer_fn * karr_relation_plugin::mk_project_fn(const relation_base & r, - unsigned col_cnt, const unsigned * removed_cols) { - return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); - } - - class karr_relation_plugin::rename_fn : public convenient_relation_rename_fn { - public: - rename_fn(karr_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) - : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {} - - virtual relation_base * operator()(const relation_base & _r) { - karr_relation const& r = get(_r); - karr_relation_plugin& p = r.get_plugin(); - karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); - result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); - return result; - } - }; - - relation_transformer_fn * karr_relation_plugin::mk_rename_fn(const relation_base & r, - unsigned cycle_len, const unsigned * permutation_cycle) { - if (!check_kind(r)) { - return 0; - } - return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle); - } - - bool karr_relation_plugin::dualizeI(matrix& dst, matrix const& src) { - dst.reset(); - m_hb.reset(); - for (unsigned i = 0; i < src.size(); ++i) { - if (src.eq[i]) { - m_hb.add_eq(src.A[i], -src.b[i]); - } - else { - m_hb.add_ge(src.A[i], -src.b[i]); - } - } - for (unsigned i = 0; !src.A.empty() && i < src.A[0].size(); ++i) { - m_hb.set_is_int(i); - } - lbool is_sat = l_undef; - - try { - is_sat = m_hb.saturate(); - } - catch (...) { - is_sat = l_undef; - } - TRACE("dl_verbose", m_hb.display(tout);); - if (is_sat == l_false) { - return false; - } - if (is_sat == l_undef) { - return true; - } - unsigned basis_size = m_hb.get_basis_size(); - bool first_initial = true; - for (unsigned i = 0; i < basis_size; ++i) { - bool is_initial; - vector soln; - m_hb.get_basis_solution(i, soln, is_initial); - if (is_initial && first_initial) { - dst.A.push_back(soln); - dst.b.push_back(rational(1)); - dst.eq.push_back(true); - first_initial = false; - } - else if (!is_initial) { - dst.A.push_back(soln); - dst.b.push_back(rational(0)); - dst.eq.push_back(true); - } - } - return true; - } - - void karr_relation_plugin::dualizeH(matrix& dst, matrix const& src) { - dst.reset(); - if (src.size() == 0) { - return; - } - m_hb.reset(); - for (unsigned i = 0; i < src.size(); ++i) { - vector v(src.A[i]); - v.push_back(src.b[i]); - if (src.eq[i]) { - m_hb.add_eq(v, rational(0)); - } - else { - m_hb.add_ge(v, rational(0)); - } - } - for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) { - m_hb.set_is_int(i); - } - lbool is_sat = l_undef; - try { - is_sat = m_hb.saturate(); - } - catch (...) { - is_sat = l_undef; - } - if (is_sat != l_true) { - return; - } - TRACE("dl_verbose", m_hb.display(tout);); - SASSERT(is_sat == l_true); - unsigned basis_size = m_hb.get_basis_size(); - for (unsigned i = 0; i < basis_size; ++i) { - bool is_initial; - vector soln; - m_hb.get_basis_solution(i, soln, is_initial); - if (!is_initial) { - dst.b.push_back(soln.back()); - dst.eq.push_back(true); - soln.pop_back(); - dst.A.push_back(soln); - } - } - } - - - class karr_relation_plugin::union_fn : public relation_union_fn { - public: - union_fn() {} - - virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { - - karr_relation& r = get(_r); - karr_relation const& src = get(_src); - TRACE("dl", r.display(tout << "dst:\n"); src.display(tout << "src:\n");); - - if (_delta) { - karr_relation& d = get(*_delta); - r.mk_union(src, &d); - } - else { - r.mk_union(src, 0); - } - TRACE("dl", r.display(tout << "result:\n");); - } - }; - - relation_union_fn * karr_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; - } - return alloc(union_fn); - } - - class karr_relation_plugin::filter_identical_fn : public relation_mutator_fn { - unsigned_vector m_identical_cols; - public: - filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) - : m_identical_cols(col_cnt, identical_cols) {} - - virtual void operator()(relation_base & _r) { - karr_relation & r = get(_r); - TRACE("dl", r.display(tout << "src:\n");); - r.get_ineqs(); - for (unsigned i = 1; i < m_identical_cols.size(); ++i) { - unsigned c1 = m_identical_cols[0]; - unsigned c2 = m_identical_cols[i]; - vector row; - row.resize(r.get_signature().size()); - row[c1] = rational(1); - row[c2] = rational(-1); - r.m_ineqs.A.push_back(row); - r.m_ineqs.b.push_back(rational(0)); - r.m_ineqs.eq.push_back(true); - r.m_basis_valid = false; - } - TRACE("dl", r.display(tout << "result:\n");); - } - }; - - relation_mutator_fn * karr_relation_plugin::mk_filter_identical_fn( - const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { - if(!check_kind(t)) { - return 0; - } - return alloc(filter_identical_fn, col_cnt, identical_cols); - } - - - class karr_relation_plugin::filter_equal_fn : public relation_mutator_fn { - unsigned m_col; - rational m_value; - bool m_valid; - public: - filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) - : m_col(col) { - arith_util arith(m.get_context().get_manager()); - m_valid = arith.is_numeral(value, m_value) && m_value.is_int(); - } - - virtual void operator()(relation_base & _r) { - karr_relation & r = get(_r); - if (m_valid) { - r.get_ineqs(); - vector row; - row.resize(r.get_signature().size()); - row[m_col] = rational(1); - r.m_ineqs.A.push_back(row); - r.m_ineqs.b.push_back(rational(-1)); - r.m_ineqs.eq.push_back(true); - r.m_basis_valid = false; - } - TRACE("dl", tout << m_value << "\n"; r.display(tout);); - } - }; - - relation_mutator_fn * karr_relation_plugin::mk_filter_equal_fn(const relation_base & r, - const relation_element & value, unsigned col) { - if (check_kind(r)) { - return alloc(filter_equal_fn, get_manager(), value, col); - } - return 0; - } - - - class karr_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { - app_ref m_cond; - public: - filter_interpreted_fn(karr_relation const& t, app* cond): - m_cond(cond, t.get_plugin().get_ast_manager()) { - } - - void operator()(relation_base& t) { - get(t).filter_interpreted(m_cond); - TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); - } - }; - - relation_mutator_fn * karr_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { - if (check_kind(t)) { - return alloc(filter_interpreted_fn, get(t), condition); - } - return 0; - } - -}; - diff --git a/src/muz/dl_mk_karr_invariants.h b/src/muz/dl_mk_karr_invariants.h deleted file mode 100644 index a86f726ef..000000000 --- a/src/muz/dl_mk_karr_invariants.h +++ /dev/null @@ -1,138 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - dl_mk_karr_invariants.h - -Abstract: - - Extract integer linear invariants. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-03-08 - -Revision History: - ---*/ -#ifndef _DL_MK_KARR_INVARIANTS_H_ -#define _DL_MK_KARR_INVARIANTS_H_ - -#include"dl_context.h" -#include"dl_rule_set.h" -#include"dl_rule_transformer.h" -#include"arith_decl_plugin.h" -#include"hilbert_basis.h" - -namespace datalog { - - /** - \brief Rule transformer that strengthens bodies with invariants. - */ - - struct matrix { - vector > A; - vector b; - svector eq; - unsigned size() const { return A.size(); } - void reset() { A.reset(); b.reset(); eq.reset(); } - matrix& operator=(matrix const& other); - void append(matrix const& other) { A.append(other.A); b.append(other.b); eq.append(other.eq); } - void display(std::ostream& out) const; - static void display_row( - std::ostream& out, vector const& row, rational const& b, bool is_eq); - static void display_ineq( - std::ostream& out, vector const& row, rational const& b, bool is_eq); - }; - - class mk_karr_invariants : public rule_transformer::plugin { - - class add_invariant_model_converter; - - context& m_ctx; - ast_manager& m; - rule_manager& rm; - context m_inner_ctx; - arith_util a; - obj_map m_fun2inv; - ast_ref_vector m_pinned; - volatile bool m_cancel; - - void get_invariants(rule_set const& src); - - void update_body(rule_set& result, rule& r); - rule_set* update_rules(rule_set const& src); - public: - mk_karr_invariants(context & ctx, unsigned priority); - - virtual ~mk_karr_invariants(); - - virtual void cancel(); - - rule_set * operator()(rule_set const & source); - - }; - - class karr_relation; - - class karr_relation_plugin : public relation_plugin { - arith_util a; - hilbert_basis m_hb; - - class join_fn; - class project_fn; - class rename_fn; - class union_fn; - class filter_equal_fn; - class filter_identical_fn; - class filter_interpreted_fn; - friend class karr_relation; - public: - karr_relation_plugin(relation_manager& rm): - relation_plugin(karr_relation_plugin::get_name(), rm), - a(get_ast_manager()) - {} - - virtual bool can_handle_signature(const relation_signature & sig) { - return true; - } - - static symbol get_name() { return symbol("karr_relation"); } - - virtual void set_cancel(bool f); - - virtual relation_base * mk_empty(const relation_signature & s); - - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - - static karr_relation& get(relation_base& r); - static karr_relation const & get(relation_base const& r); - - virtual relation_join_fn * mk_join_fn( - const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - - private: - bool dualizeI(matrix& dst, matrix const& src); - void dualizeH(matrix& dst, matrix const& src); - - - }; - - -}; - -#endif /* _DL_MK_KARR_INVARIANTS_H_ */ - diff --git a/src/muz/dl_mk_partial_equiv.cpp b/src/muz/dl_mk_partial_equiv.cpp deleted file mode 100644 index 4d1a1e860..000000000 --- a/src/muz/dl_mk_partial_equiv.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_partial_equiv.cpp - -Abstract: - - Rule transformer which identifies predicates that are partial equivalence relations. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-05-14 - -Revision History: - ---*/ - -#include "dl_mk_partial_equiv.h" -#include "ast_pp.h" - -namespace datalog { - - bool mk_partial_equivalence_transformer::is_symmetry(rule const* r) { - func_decl* p = r->get_decl(); - return - p->get_arity() == 2 && - p->get_domain(0) == p->get_domain(1) && - r->get_tail_size() == 1 && - r->get_tail(0)->get_decl() == p && - r->get_head()->get_arg(0) == r->get_tail(0)->get_arg(1) && - r->get_head()->get_arg(1) == r->get_tail(0)->get_arg(0) && - is_var(r->get_head()->get_arg(0)) && - is_var(r->get_head()->get_arg(1)) && - r->get_head()->get_arg(0) != r->get_head()->get_arg(1); - } - - - bool mk_partial_equivalence_transformer::is_transitivity(rule const* r) { - func_decl* p = r->get_decl(); - if (p->get_arity() != 2 || - p->get_domain(0) != p->get_domain(1) || - r->get_tail_size() != 2 || - r->get_tail(0)->get_decl() != p || - r->get_tail(1)->get_decl() != p) { - return false; - } - app* h = r->get_head(); - app* a = r->get_tail(0); - app* b = r->get_tail(1); - expr* x1 = h->get_arg(0); - expr* x2 = h->get_arg(1); - expr* a1 = a->get_arg(0); - expr* a2 = a->get_arg(1); - expr* b1 = b->get_arg(0); - expr* b2 = b->get_arg(1); - - if (!(is_var(x1) && is_var(x2) && is_var(a1) && is_var(a2) && is_var(b1) && is_var(b2))) { - return false; - } - if (x1 == x2 || a1 == a2 || b1 == b2) { - return false; - } - if (a2 == b1) { - if (x1 == b2 && x2 == a1) { - return true; - } - if (x1 == a1 && x2 == b2) { - return true; - } - return false; - } - if (a1 == b2) { - if (x1 == b1 && x2 == a2) { - return true; - } - if (x1 == a2 && x2 == b1) { - return true; - } - return false; - } - - return false; -; - } - - - rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source) { - // TODO mc - - if (source.get_num_rules() == 0) { - return 0; - } - - if (m_context.get_engine() != DATALOG_ENGINE) { - return 0; - } - - relation_manager & rm = m_context.get_rel_context()->get_rmanager(); - rule_set::decl2rules::iterator it = source.begin_grouped_rules(); - rule_set::decl2rules::iterator end = source.end_grouped_rules(); - - rule_set* res = alloc(rule_set, m_context); - - for (; it != end; ++it) { - func_decl* p = it->m_key; - rule_vector const& rv = *(it->m_value); - bool has_symmetry = false; - bool has_transitivity = false; - unsigned i_symmetry, i_transitivity; - family_id kind = rm.get_requested_predicate_kind(p); - for (unsigned i = 0; i < rv.size(); ++i) { - - if (kind != null_family_id) { - res->add_rule(rv[i]); - } - else if (is_symmetry(rv[i])) { - i_symmetry = i; - has_symmetry = true; - } - else if (is_transitivity(rv[i])) { - i_transitivity = i; - has_transitivity = true; - } - else { - res->add_rule(rv[i]); - } - } - if (has_symmetry && !has_transitivity) { - res->add_rule(rv[i_symmetry]); - } - else if (!has_symmetry && has_transitivity) { - res->add_rule(rv[i_transitivity]); - } - else if (has_symmetry && has_transitivity) { - TRACE("dl", tout << "updating predicate " << mk_pp(p, m) << " to partial equivalence\n";); - SASSERT(kind == null_family_id); - rm.set_predicate_kind(p, rm.get_table_plugin(symbol("equivalence"))->get_kind()); - } - } - - if (res->get_num_rules() == source.get_num_rules()) { - dealloc(res); - return 0; - } - res->inherit_predicates(source); - - return res; - } - -}; - - diff --git a/src/muz/dl_mk_partial_equiv.h b/src/muz/dl_mk_partial_equiv.h deleted file mode 100644 index 54a70b3c0..000000000 --- a/src/muz/dl_mk_partial_equiv.h +++ /dev/null @@ -1,50 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_partial_equiv.h - -Abstract: - - Rule transformer which identifies predicates that are partial equivalence relations. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-05-14 - -Revision History: - ---*/ - - -#ifndef _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ -#define _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ - -#include "dl_context.h" -#include "dl_rule_transformer.h" - -namespace datalog { - - class mk_partial_equivalence_transformer : public rule_transformer::plugin { - ast_manager & m; - context & m_context; - public: - mk_partial_equivalence_transformer(context & ctx, unsigned priority=45000) - : plugin(priority), - m(ctx.get_manager()), - m_context(ctx) {} - - rule_set * operator()(rule_set const & source); - - private: - - bool is_symmetry(rule const* r); - bool is_transitivity(rule const* r); - }; - -}; - -#endif /* _DL_MK_PARTIAL_EQUIV_TRANSFORMER_H_ */ - - diff --git a/src/muz/dl_mk_similarity_compressor.cpp b/src/muz/dl_mk_similarity_compressor.cpp deleted file mode 100644 index d4f410130..000000000 --- a/src/muz/dl_mk_similarity_compressor.cpp +++ /dev/null @@ -1,545 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_mk_similarity_compressor.cpp - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-10-22. - -Revision History: - ---*/ - -#include -#include -#include"dl_mk_similarity_compressor.h" - -namespace datalog { - - mk_similarity_compressor::mk_similarity_compressor(context & ctx) : - plugin(5000), - m_context(ctx), - m_manager(ctx.get_manager()), - m_threshold_count(ctx.similarity_compressor_threshold()), - m_result_rules(ctx.get_rule_manager()), - m_modified(false), - m_pinned(m_manager) { - SASSERT(m_threshold_count>1); - } - - void mk_similarity_compressor::reset() { - m_rules.reset(); - m_result_rules.reset(); - m_pinned.reset(); - } - - /** - Allows to traverse head and positive tails in a single for loop starting from -1 - */ - static app * get_by_tail_index(rule * r, int idx) { - if (idx < 0) { - return r->get_head(); - } - SASSERT(idx < static_cast(r->get_positive_tail_size())); - return r->get_tail(idx); - } - - template - static int aux_compare(T a, T b) { - return (a>b) ? 1 : ( (a==b) ? 0 : -1); - } - - template - static int aux_compare(T* a, T* b); - - static int compare_var_args(app* t1, app* t2) { - SASSERT(t1->get_num_args()==t2->get_num_args()); - int res; - unsigned n = t1->get_num_args(); - for (unsigned i = 0; i < n; i++) { - expr * a1 = t1->get_arg(i); - expr * a2 = t2->get_arg(i); - res = aux_compare(is_var(a1), is_var(a2)); - if (res != 0) { - return res; - } - if (is_var(a1)) { - res = aux_compare(to_var(a1)->get_idx(), to_var(a2)->get_idx()); - if (res != 0) { - return res; - } - } - } - return 0; - } - - static int compare_args(app* t1, app* t2, int & skip_countdown) { - SASSERT(t1->get_num_args()==t2->get_num_args()); - int res; - unsigned n = t1->get_num_args(); - for (unsigned i=0; iget_arg(i))) { - SASSERT(t1->get_arg(i) == t2->get_arg(i)); - continue; - } - if ((skip_countdown--) == 0) { - continue; - } - res = aux_compare(t1->get_arg(i)->get_id(), t2->get_arg(i)->get_id()); - if (res!=0) { return res; } - } - return 0; - } - - /** - \brief Return 0 if r1 and r2 could be similar. If the rough similarity - equaivelance class of r1 is greater than the one of r2, return 1; otherwise return -1. - - Two rules are in the same rough similarity class if they differ only in constant arguments - of positive uninterpreted predicates. - */ - static int rough_compare(rule * r1, rule * r2) { - int res = aux_compare(r1->get_tail_size(), r2->get_tail_size()); - if (res!=0) { return res; } - res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size()); - if (res!=0) { return res; } - res = aux_compare(r1->get_positive_tail_size(), r2->get_positive_tail_size()); - if (res!=0) { return res; } - - int pos_tail_sz = r1->get_positive_tail_size(); - for (int i=-1; iget_decl()->get_id(), t2->get_decl()->get_id()); - if (res!=0) { return res; } - res = compare_var_args(t1, t2); - if (res!=0) { return res; } - } - - unsigned tail_sz = r1->get_tail_size(); - for (unsigned i=pos_tail_sz; iget_tail(i)->get_id(), r2->get_tail(i)->get_id()); - if (res!=0) { return res; } - } - - return 0; - } - - /** - \c r1 and \c r2 must be equal according to the \c rough_compare function for this function - to be called. - */ - static int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) { - SASSERT(rough_compare(r1, r2)==0); - int pos_tail_sz = r1->get_positive_tail_size(); - for (int i=-1; i info_vector; - - static void collect_const_indexes(app * t, int tail_index, info_vector & res) { - unsigned n = t->get_num_args(); - for (unsigned i=0; iget_arg(i))) { - continue; - } - res.push_back(const_info(tail_index, i)); - } - } - - static void collect_const_indexes(rule * r, info_vector & res) { - collect_const_indexes(r->get_head(), -1, res); - unsigned pos_tail_sz = r->get_positive_tail_size(); - for (unsigned i=0; iget_tail(i), i, res); - } - } - - template - static void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) { - unsigned const_cnt = const_infos.size(); - tgt.reset(); - for (unsigned i=0; iget_arg(inf.arg_index()))); - SASSERT(tgt.back()->get_num_args()==0); - } - } - template - static void collect_orphan_sorts(rule * r, const info_vector & const_infos, T & tgt) { - unsigned const_cnt = const_infos.size(); - tgt.reset(); - for (unsigned i=0; iget_decl()->get_domain(inf.arg_index())); - } - } - - /** - \brief From the \c tail_indexes and \c arg_indexes remove elements corresponding to constants - that are the same in rules \c *first ... \c *(after_last-1). - */ - static void remove_stable_constants(rule_vector::iterator first, rule_vector::iterator after_last, - info_vector & const_infos) { - SASSERT(after_last-first>1); - unsigned const_cnt = const_infos.size(); - ptr_vector vals; - rule * r = *(first++); - collect_orphan_consts(r, const_infos, vals); - SASSERT(vals.size()==const_cnt); - rule_vector::iterator it = first; - for (; it!=after_last; ++it) { - for (unsigned i=0; iget_arg(const_infos[i].arg_index())); - if (vals[i]!=val) { - vals[i] = 0; - } - } - } - unsigned removed_cnt = 0; - for (unsigned i=0; i vals; - ptr_vector sorts; - rule * r = *(first++); - collect_orphan_consts(r, const_infos, vals); - collect_orphan_sorts(r, const_infos, sorts); - SASSERT(vals.size()==const_cnt); - vector possible_parents(const_cnt); - for (unsigned i=1; iget_head()->get_num_args() - count_variable_arguments(r->get_head()); - unsigned pos_tail_sz = r->get_positive_tail_size(); - for (unsigned i=0; iget_tail(i)->get_num_args() - count_variable_arguments(r->get_tail(i)); - } - return res; - } - - static bool initial_comparator(rule * r1, rule * r2) { - int res = rough_compare(r1, r2); - if (res!=0) { return res>0; } - return total_compare(r1, r2)>0; - } - - class arg_ignoring_comparator { - unsigned m_ignored_index; - public: - arg_ignoring_comparator(unsigned ignored_index) : m_ignored_index(ignored_index) {} - bool operator()(rule * r1, rule * r2) const { - return total_compare(r1, r2, m_ignored_index)>0; - } - bool eq(rule * r1, rule * r2) const { - return total_compare(r1, r2, m_ignored_index)==0; - } - }; - - void mk_similarity_compressor::merge_class(rule_vector::iterator first, - rule_vector::iterator after_last) { - SASSERT(after_last-first>1); - info_vector const_infos; - rule * r = *first; //an arbitrary representative of the class - collect_const_indexes(r, const_infos); - remove_stable_constants(first, after_last, const_infos); - - unsigned const_cnt = const_infos.size(); - SASSERT(const_cnt>0); - - detect_equal_constants(first, after_last, const_infos); - - - //The aux relation contains column for each constant which does not have an earlier constant - //that it is equal to (i.e. only has no parent) - ptr_vector aux_domain; - collect_orphan_sorts(r, const_infos, aux_domain); - - func_decl* head_pred = r->get_decl(); - symbol const& name_prefix = head_pred->get_name(); - std::string name_suffix = "sc_" + to_string(const_cnt); - func_decl * aux_pred = m_context.mk_fresh_head_predicate(name_prefix, symbol(name_suffix.c_str()), - aux_domain.size(), aux_domain.c_ptr(), head_pred); - m_pinned.push_back(aux_pred); - - relation_fact val_fact(m_manager, const_cnt); - rule_vector::iterator it = first; - for (; it!=after_last; ++it) { - collect_orphan_consts(*it, const_infos, val_fact); - m_context.add_fact(aux_pred, val_fact); - } - m_context.get_rel_context()->get_rmanager().mark_saturated(aux_pred); - - app * new_head = r->get_head(); - ptr_vector new_tail; - svector new_negs; - unsigned tail_sz = r->get_tail_size(); - for (unsigned i=0; iget_tail(i)); - new_negs.push_back(r->is_neg_tail(i)); - } - - rule_counter ctr; - ctr.count_rule_vars(m_manager, r); - unsigned max_var_idx, new_var_idx_base; - if (ctr.get_max_positive(max_var_idx)) { - new_var_idx_base = max_var_idx+1; - } - else { - new_var_idx_base = 0; - } - - ptr_vector const_vars; //variables at indexes of their corresponding constants - expr_ref_vector aux_vars(m_manager); //variables as arguments for the auxiliary predicate - - unsigned aux_column_index = 0; - - for (unsigned i=0; i mod_args(mod_tail->get_num_args(), mod_tail->get_args()); - - for (; iget_decl(), mod_args.c_ptr()); - m_pinned.push_back(upd_tail); - mod_tail = upd_tail; - } - - app_ref aux_tail(m_manager.mk_app(aux_pred, aux_vars.c_ptr()), m_manager); - new_tail.push_back(aux_tail); - new_negs.push_back(false); - - rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), - new_negs.c_ptr()); - m_result_rules.push_back(new_rule); - - //TODO: allow for a rule to have multiple parent objects - new_rule->set_accounting_parent_object(m_context, r); - m_modified = true; - } - - void mk_similarity_compressor::process_class(rule_set const& source, rule_vector::iterator first, - rule_vector::iterator after_last) { - SASSERT(first!=after_last); - //remove duplicates - { - rule_vector::iterator it = first; - rule_vector::iterator prev = it; - ++it; - while(it!=after_last) { - if (it!=after_last && total_compare(*prev, *it)==0) { - --after_last; - std::swap(*it, *after_last); - m_modified = true; - } - else { - prev = it; - ++it; - } - } - } - SASSERT(first!=after_last); - - unsigned const_cnt = get_constant_count(*first); -#if 0 - for (unsigned ignored_index=0; ignored_indexm_threshold_count) { - merge_class(grp_begin, it); - //group was processed, so we remove it from the class - if (it==after_last) { - after_last=grp_begin; - it=after_last; - } - else { - while(it!=grp_begin) { - std::swap(*--it, *--after_last); - } - } - } - grp_begin = it; - grp_size = 0; - } - } - } -#endif - //TODO: compress also rules with pairs (or tuples) of equal constants - -#if 1 - if (const_cnt>0 && !source.is_output_predicate((*first)->get_decl())) { - unsigned rule_cnt = static_cast(after_last-first); - if (rule_cnt>m_threshold_count) { - merge_class(first, after_last); - return; - } - } -#endif - - //put rules which weren't merged into result - rule_vector::iterator it = first; - for (; it!=after_last; ++it) { - m_result_rules.push_back(*it); - } - } - - rule_set * mk_similarity_compressor::operator()(rule_set const & source) { - // TODO mc - m_modified = false; - unsigned init_rule_cnt = source.get_num_rules(); - SASSERT(m_rules.empty()); - for (unsigned i=0; i(0); - if (m_modified) { - result = alloc(rule_set, m_context); - unsigned fin_rule_cnt = m_result_rules.size(); - for (unsigned i=0; iadd_rule(m_result_rules.get(i)); - } - result->inherit_predicates(source); - } - reset(); - return result; - } -}; diff --git a/src/muz/dl_mk_similarity_compressor.h b/src/muz/dl_mk_similarity_compressor.h deleted file mode 100644 index 34b76e7e1..000000000 --- a/src/muz/dl_mk_similarity_compressor.h +++ /dev/null @@ -1,78 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_mk_similarity_compressor.h - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-10-22. - -Revision History: - ---*/ -#ifndef _DL_MK_SIMILARITY_COMPRESSOR_H_ -#define _DL_MK_SIMILARITY_COMPRESSOR_H_ - -#include - -#include"map.h" -#include"obj_pair_hashtable.h" - -#include"dl_context.h" -#include"dl_rule_set.h" -#include"dl_rule_transformer.h" - -namespace datalog { - - /** - \brief Functor for merging groups of similar rules. - - A rule sequence - - P("1",x):-Q(x). - ... - P("N",x):-Q(x). - - will be replaced by - - P(y,x):-Q(x), Aux(y). - - and a set of facts - - Aux("1"). - ... - Aux("N"). - - Similar transformation is performed when the varying constant appears in the positive tail. - */ - class mk_similarity_compressor : public rule_transformer::plugin { - - context & m_context; - ast_manager & m_manager; - /** number of similar rules necessary for a group to be introduced */ - unsigned m_threshold_count; - rule_vector m_rules; - rule_ref_vector m_result_rules; - bool m_modified; - ast_ref_vector m_pinned; - - void merge_class(rule_vector::iterator first, rule_vector::iterator after_last); - void process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last); - - void reset(); - public: - mk_similarity_compressor(context & ctx); - - rule_set * operator()(rule_set const & source); - }; - -}; - -#endif /* _DL_MK_SIMILARITY_COMPRESSOR_H_ */ - diff --git a/src/muz/dl_mk_simple_joins.cpp b/src/muz/dl_mk_simple_joins.cpp deleted file mode 100644 index 300ed0879..000000000 --- a/src/muz/dl_mk_simple_joins.cpp +++ /dev/null @@ -1,741 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_mk_simple_joins.cpp - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-05-20. - -Revision History: - ---*/ - -#include -#include -#include -#include"dl_mk_simple_joins.h" -#include"ast_pp.h" -#include"trace.h" - - -namespace datalog { - - mk_simple_joins::mk_simple_joins(context & ctx): - plugin(1000), - m_context(ctx), - rm(ctx.get_rule_manager()) { - } - - class join_planner { - typedef float cost; - - class pair_info { - cost m_total_cost; - /** - \brief Number of rules longer than two that contain this pair. - - This number is being updated by \c add_rule and \remove rule. Even though between - adding a rule and removing it, the length of a rule can decrease without this pair - being notified about it, it will surely see the decrease from length 3 to 2 which - the threshold for rule being counted in this counter. - */ - unsigned m_consumers; - bool m_stratified; - unsigned m_src_stratum; - public: - var_idx_set m_all_nonlocal_vars; - rule_vector m_rules; - - pair_info() : m_consumers(0), m_stratified(true), m_src_stratum(0) {} - - bool can_be_joined() const { - return m_consumers>0; - } - - cost get_cost() const { - /*if(m_instantiated) { - return std::numeric_limits::min(); - }*/ - SASSERT(m_consumers>0); - cost amortized = m_total_cost/m_consumers; - if(m_stratified) { - return amortized * ( (amortized>0) ? (1/16.0f) : 16.0f); - } - else { - return amortized; - } - } - - /** - \brief Add rule \c r among rules interested in current predicate pair. - - The \c pl.m_rule_content entry of the rule has to be properly filled in - by the time of a call to this function - */ - void add_rule(join_planner & pl, app * t1, app * t2, rule * r, - const var_idx_set & non_local_vars_normalized) { - if(m_rules.empty()) { - m_total_cost = pl.compute_cost(t1, t2); - m_src_stratum = std::max(pl.get_stratum(t1->get_decl()), pl.get_stratum(t2->get_decl())); - } - m_rules.push_back(r); - if(pl.m_rules_content.find_core(r)->get_data().m_value.size()>2) { - m_consumers++; - } - if(m_stratified) { - unsigned head_stratum = pl.get_stratum(r->get_decl()); - SASSERT(head_stratum>=m_src_stratum); - if(head_stratum==m_src_stratum) { - m_stratified = false; - } - } - idx_set_union(m_all_nonlocal_vars, non_local_vars_normalized); - } - /** - \brief Remove rule from the pair record. Return true if no rules remain - in the pair, and so it should be removed. - */ - bool remove_rule(rule * r, unsigned original_length) { - TRUSTME( remove_from_vector(m_rules, r) ); - if(original_length>2) { - SASSERT(m_consumers>0); - m_consumers--; - } - SASSERT(!m_rules.empty() || m_consumers==0); - return m_rules.empty(); - } - private: - pair_info & operator=(const pair_info &); //to avoid the implicit one - }; - typedef std::pair app_pair; - typedef map, obj_ptr_hash >, default_eq > cost_map; - typedef map, ptr_hash, ptr_eq > rule_pred_map; - - context & m_context; - ast_manager & m; - rule_manager & rm; - var_subst & m_var_subst; - rule_set & m_rs_aux_copy; //reference to a rule_set that will allow to ask for stratum levels - - cost_map m_costs; - ptr_vector m_interpreted; - rule_pred_map m_rules_content; - rule_ref_vector m_introduced_rules; - ptr_hashtable, ptr_eq > m_modified_rules; - - ast_ref_vector m_pinned; - mutable ptr_vector m_vars; - - public: - join_planner(context & ctx, rule_set & rs_aux_copy) - : m_context(ctx), m(ctx.get_manager()), - rm(ctx.get_rule_manager()), - m_var_subst(ctx.get_var_subst()), - m_rs_aux_copy(rs_aux_copy), - m_introduced_rules(ctx.get_rule_manager()), - m_pinned(ctx.get_manager()) - { - } - - ~join_planner() - { - cost_map::iterator it = m_costs.begin(); - cost_map::iterator end = m_costs.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - m_costs.reset(); - } - private: - - void get_normalizer(app * t, unsigned & next_var, expr_ref_vector & result) const { - SASSERT(result.size()>0); - unsigned res_ofs = result.size()-1; - unsigned n=t->get_num_args(); - for(unsigned i=0; iget_arg(i))); - var * v = to_var(t->get_arg(i)); - unsigned var_idx = v->get_idx(); - if(result[res_ofs-var_idx]==0) { - result[res_ofs-var_idx]=m.mk_var(next_var, v->get_sort()); - next_var++; - } - } - } - - void get_normalizer(app * t1, app * t2, expr_ref_vector & result) const { - SASSERT(result.empty()); - if(t1->get_num_args()==0 && t2->get_num_args()==0) { - return; //nothing to normalize - } - SASSERT(!t1->is_ground() || !t2->is_ground()); - - unsigned max_var_idx = 0; - { - var_idx_set& orig_var_set = rm.collect_vars(t1, t2); - var_idx_set::iterator ovit = orig_var_set.begin(); - var_idx_set::iterator ovend = orig_var_set.end(); - for(; ovit!=ovend; ++ovit) { - unsigned var_idx = *ovit; - if(var_idx>max_var_idx) { - max_var_idx = var_idx; - } - } - } - - if(t1->get_decl()!=t2->get_decl()) { - if(t1->get_decl()->get_id()get_decl()->get_id()) { - std::swap(t1, t2); - } - } - else { - int_vector norm1(max_var_idx+1, -1); - int_vector norm2(max_var_idx+1, -1); - unsigned n=t1->get_num_args(); - SASSERT(n==t2->get_num_args()); - for(unsigned i=0; iget_arg(i)); - var * v2 = to_var(t2->get_arg(i)); - if(v1->get_sort()!=v2->get_sort()) { - //different sorts mean we can distinguish the two terms - if(v1->get_sort()->get_id()get_sort()->get_id()) { - std::swap(t1, t2); - } - break; - } - unsigned v1_idx = v1->get_idx(); - unsigned v2_idx = v2->get_idx(); - //since the rules already went through the mk_filter_rules transformer, - //variables must be linear - SASSERT(norm1[v1_idx]==-1); - SASSERT(norm2[v2_idx]==-1); - - if(norm2[v1_idx]!=norm1[v2_idx]) { - //now we can distinguish the two terms - if(norm2[v1_idx](0)); - unsigned next_var = 0; - get_normalizer(t1, next_var, result); - get_normalizer(t2, next_var, result); - } - - app_pair get_key(app * t1, app * t2) { - expr_ref_vector norm_subst(m); - get_normalizer(t1, t2, norm_subst); - expr_ref t1n_ref(m); - expr_ref t2n_ref(m); - m_var_subst(t1, norm_subst.size(), norm_subst.c_ptr(), t1n_ref); - m_var_subst(t2, norm_subst.size(), norm_subst.c_ptr(), t2n_ref); - app * t1n = to_app(t1n_ref); - app * t2n = to_app(t2n_ref); - if(t1n>t2n) { - std::swap(t1n, t2n); - } - m_pinned.push_back(t1n); - m_pinned.push_back(t2n); - - /* - IF_VERBOSE(0, - print_renaming(norm_subst, verbose_stream()); - display_predicate(m_context, t1, verbose_stream()); - display_predicate(m_context, t2, verbose_stream()); - display_predicate(m_context, t1n, verbose_stream()); - display_predicate(m_context, t2n, verbose_stream());); - */ - return app_pair(t1n, t2n); - } - - /** - \brief Add rule \c r among rules interested in predicate pair \c t1, \c t2. - - The \c m_rule_content entry of the rule \c r has to be properly filled in - by the time of a call to this function - */ - void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) { - TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << "\n"; - r->display(m_context, tout); tout << "\n";); - SASSERT(t1!=t2); - cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), 0); - pair_info * & ptr_inf = e->get_data().m_value; - if(ptr_inf==0) { - ptr_inf = alloc(pair_info); - } - pair_info & inf = *ptr_inf; - - expr_ref_vector normalizer(m); - get_normalizer(t1, t2, normalizer); - unsigned norm_ofs = normalizer.size()-1; - var_idx_set normalized_vars; - var_idx_set::iterator vit = non_local_vars.begin(); - var_idx_set::iterator vend = non_local_vars.end(); - for(; vit!=vend; ++vit) { - unsigned norm_var = to_var(normalizer.get(norm_ofs-*vit))->get_idx(); - normalized_vars.insert(norm_var); - } - - inf.add_rule(*this, t1, t2, r, normalized_vars); - } - - pair_info & get_pair(app_pair key) const { - cost_map::entry * e = m_costs.find_core(key); - SASSERT(e); - return *e->get_data().m_value; - } - - void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) { - pair_info * ptr = &get_pair(key); - if(ptr->remove_rule(r, original_len)) { - SASSERT(ptr->m_rules.empty()); - m_costs.remove(key); - dealloc(ptr); - } - } - - void register_rule(rule * r) { - rule_counter counter; - counter.count_rule_vars(m, r, 1); - - ptr_vector & rule_content = - m_rules_content.insert_if_not_there2(r, ptr_vector())->get_data().m_value; - SASSERT(rule_content.empty()); - - unsigned pos_tail_size=r->get_positive_tail_size(); - for(unsigned i=0; iget_tail(i)); - } - for(unsigned i=0; iget_tail(i); - var_idx_set t1_vars = rm.collect_vars(t1); - counter.count_vars(m, t1, -1); //temporarily remove t1 variables from counter - for(unsigned j=i+1; jget_tail(j); - counter.count_vars(m, t2, -1); //temporarily remove t2 variables from counter - var_idx_set scope_vars = rm.collect_vars(t2); - scope_vars |= t1_vars; - var_idx_set non_local_vars; - counter.collect_positive(non_local_vars); - counter.count_vars(m, t2, 1); //restore t2 variables in counter - set_intersection(non_local_vars, scope_vars); - register_pair(t1, t2, r, non_local_vars); - } - counter.count_vars(m, t1, 1); //restore t1 variables in counter - } - } - - bool extract_argument_info(unsigned var_idx, app * t, expr_ref_vector & args, - ptr_vector & domain) { - unsigned n=t->get_num_args(); - for(unsigned i=0; iget_arg(i)); - if(v->get_idx()==var_idx) { - args.push_back(v); - domain.push_back(m.get_sort(v)); - return true; - } - } - return false; - } - - void join_pair(app_pair pair_key) { - app * t1 = pair_key.first; - app * t2 = pair_key.second; - pair_info & inf = get_pair(pair_key); - SASSERT(!inf.m_rules.empty()); - var_idx_set & output_vars = inf.m_all_nonlocal_vars; - expr_ref_vector args(m); - ptr_vector domain; - - unsigned arity = output_vars.num_elems(); - idx_set::iterator ovit=output_vars.begin(); - idx_set::iterator ovend=output_vars.end(); - //TODO: improve quadratic complexity - for(;ovit!=ovend;++ovit) { - unsigned var_idx=*ovit; - - bool found=extract_argument_info(var_idx, t1, args, domain); - if(!found) { - found=extract_argument_info(var_idx, t2, args, domain); - } - SASSERT(found); - } - - SASSERT(args.size()==arity); - SASSERT(domain.size()==arity); - - rule * one_parent = inf.m_rules.back(); - - func_decl* parent_head = one_parent->get_decl(); - const char * one_parent_name = parent_head->get_name().bare_str(); - std::string parent_name; - if(inf.m_rules.size()>1) { - parent_name = one_parent_name + std::string("_and_") + to_string(inf.m_rules.size()-1); - } - else { - parent_name = one_parent_name; - } - - func_decl * decl = m_context.mk_fresh_head_predicate( - symbol(parent_name.c_str()), symbol("split"), - arity, domain.c_ptr(), parent_head); - - app_ref head(m.mk_app(decl, arity, args.c_ptr()), m); - - app * tail[] = {t1, t2}; - - rule * new_rule = m_context.get_rule_manager().mk(head, 2, tail, 0); - - //TODO: update accounting so that it can handle multiple parents - new_rule->set_accounting_parent_object(m_context, one_parent); - - m_introduced_rules.push_back(new_rule); - - //here we copy the inf.m_rules vector because inf.m_rules will get changed - //in the iteration. Also we use hashtable instead of vector because we do - //not want to process one rule twice. - typedef ptr_hashtable, default_eq > rule_hashtable; - rule_hashtable relevant_rules; - insert_into_set(relevant_rules, inf.m_rules); - rule_hashtable::iterator rit = relevant_rules.begin(); - rule_hashtable::iterator rend = relevant_rules.end(); - for(; rit!=rend; ++rit) { - apply_binary_rule(*rit, pair_key, head); - } - - // SASSERT(!m_costs.contains(pair_key)); - } - - void replace_edges(rule * r, const ptr_vector & removed_tails, - const ptr_vector & added_tails0, const ptr_vector & rule_content) { - SASSERT(removed_tails.size()>=added_tails0.size()); - unsigned len = rule_content.size(); - unsigned original_len = len+removed_tails.size()-added_tails0.size(); - ptr_vector added_tails(added_tails0); //we need a copy since we'll be modifying it - - unsigned rt_sz = removed_tails.size(); - //remove edges between removed tails - for(unsigned i=0; iget_head(); - - var_counter counter; - counter.count_vars(m, head, 1); - - unsigned tail_size=r->get_tail_size(); - unsigned pos_tail_size=r->get_positive_tail_size(); - - for(unsigned i=pos_tail_size; iget_tail(i), 1); - } - for(unsigned i=0; i & rule_content = m_rules_content.find_core(r)->get_data().m_value; - unsigned len = rule_content.size(); - if(len==1) { - return; - } - - func_decl * t1_pred = t1->get_decl(); - func_decl * t2_pred = t2->get_decl(); - ptr_vector removed_tails; - ptr_vector added_tails; - for(unsigned i1=0; i1get_decl()!=t1_pred) { - continue; - } - unsigned i2start = (t1_pred==t2_pred) ? (i1+1) : 0; - for(unsigned i2=i2start; i2get_decl()!=t2_pred) { - continue; - } - if(get_key(rt1, rt2)!=pair_key) { - continue; - } - expr_ref_vector normalizer(m); - get_normalizer(rt1, rt2, normalizer); - expr_ref_vector denormalizer(m); - reverse_renaming(m, normalizer, denormalizer); - expr_ref new_transf(m); - m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr(), new_transf); - app * new_lit = to_app(new_transf); - - m_pinned.push_back(new_lit); - rule_content[i1]=new_lit; - rule_content[i2]=rule_content.back(); - rule_content.pop_back(); - len--; //here the bound of both loops changes!!! - removed_tails.push_back(rt1); - removed_tails.push_back(rt2); - added_tails.push_back(new_lit); - //this exits the inner loop, the outer one continues in case there will - //be other matches - break; - } - } - SASSERT(!removed_tails.empty()); - SASSERT(!added_tails.empty()); - m_modified_rules.insert(r); - replace_edges(r, removed_tails, added_tails, rule_content); - } - - cost get_domain_size(func_decl * pred, unsigned arg_index) const { - relation_sort sort = pred->get_domain(arg_index); - return static_cast(m_context.get_sort_size_estimate(sort)); - //unsigned sz; - //if(!m_context.get_sort_size(sort, sz)) { - // sz=UINT_MAX; - //} - //return static_cast(sz); - } - - unsigned get_stratum(func_decl * pred) const { - return m_rs_aux_copy.get_predicate_strat(pred); - } - - cost estimate_size(app * t) const { - func_decl * pred = t->get_decl(); - unsigned n=pred->get_arity(); - rel_context* rel = m_context.get_rel_context(); - if (!rel) { - return cost(1); - } - relation_manager& rm = rel->get_rmanager(); - if( (m_context.saturation_was_run() && rm.try_get_relation(pred)) - || rm.is_saturated(pred)) { - SASSERT(rm.try_get_relation(pred)); //if it is saturated, it should exist - unsigned rel_size_int = rel->get_relation(pred).get_size_estimate_rows(); - if(rel_size_int!=0) { - cost rel_size = static_cast(rel_size_int); - cost curr_size = rel_size; - for(unsigned i=0; iget_arg(i))) { - curr_size /= get_domain_size(pred, i); - } - } - return curr_size; - } - } - cost res = 1; - for(unsigned i=0; iget_arg(i))) { - res *= get_domain_size(pred, i); - } - } - return res; - } - - cost compute_cost(app * t1, app * t2) const { - func_decl * t1_pred = t1->get_decl(); - func_decl * t2_pred = t2->get_decl(); - cost inters_size = 1; - variable_intersection vi(m_context.get_manager()); - vi.populate(t1, t2); - unsigned n = vi.size(); - for(unsigned i=0; i0) { - res /= 2; - } - else { - res *= 2; - } - } - }*/ - - TRACE("report_costs", - display_predicate(m_context, t1, tout); - display_predicate(m_context, t2, tout); - tout << res << "\n";); - return res; - } - - - bool pick_best_pair(app_pair & p) { - app_pair best; - bool found = false; - cost best_cost; - - cost_map::iterator it = m_costs.begin(); - cost_map::iterator end = m_costs.end(); - for(; it!=end; ++it) { - app_pair key = it->m_key; - pair_info & inf = *it->m_value; - if(!inf.can_be_joined()) { - continue; - } - cost c = inf.get_cost(); - if(!found || cm_key; - ptr_vector content = rcit->m_value; - SASSERT(content.size()<=2); - if(content.size()==orig_r->get_positive_tail_size()) { - //rule did not change - result->add_rule(orig_r); - continue; - } - - ptr_vector tail(content); - svector negs(tail.size(), false); - unsigned or_len = orig_r->get_tail_size(); - for(unsigned i=orig_r->get_positive_tail_size(); iget_tail(i)); - negs.push_back(orig_r->is_neg_tail(i)); - } - - rule * new_rule = m_context.get_rule_manager().mk(orig_r->get_head(), tail.size(), tail.c_ptr(), - negs.c_ptr()); - - new_rule->set_accounting_parent_object(m_context, orig_r); - m_context.get_rule_manager().mk_rule_rewrite_proof(*orig_r, *new_rule); - result->add_rule(new_rule); - } - while (!m_introduced_rules.empty()) { - result->add_rule(m_introduced_rules.back()); - m_context.get_rule_manager().mk_rule_asserted_proof(*m_introduced_rules.back()); - m_introduced_rules.pop_back(); - } - result->inherit_predicates(source); - return result; - } - }; - - rule_set * mk_simple_joins::operator()(rule_set const & source) { - rule_set rs_aux_copy(m_context); - rs_aux_copy.replace_rules(source); - if(!rs_aux_copy.is_closed()) { - rs_aux_copy.close(); - } - - join_planner planner(m_context, rs_aux_copy); - - return planner.run(source); - } - - -}; - diff --git a/src/muz/dl_mk_simple_joins.h b/src/muz/dl_mk_simple_joins.h deleted file mode 100644 index 36eb08dd5..000000000 --- a/src/muz/dl_mk_simple_joins.h +++ /dev/null @@ -1,63 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_mk_simple_joins.h - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-05-20. - -Revision History: - ---*/ -#ifndef _DL_MK_SIMPLE_JOINS_H_ -#define _DL_MK_SIMPLE_JOINS_H_ - -#include"map.h" -#include"obj_pair_hashtable.h" - -#include"dl_context.h" -#include"dl_rule_set.h" -#include"dl_rule_transformer.h" - -namespace datalog { - - /** - \brief Functor for creating rules that contain simple joins. - A simple join is the join of two tables. - - After applying this transformation, every rule has at most one join. - So, the rules will have the form - - HEAD :- TAIL. - HEAD :- TAIL_1, TAIL_2. - - We also assume a rule may contain interpreted expressions that work as filtering conditions. - So, we may also have: - - HEAD :- TAIL, C_1, ..., C_n. - HEAD :- TAIL_1, TAIL_2, C_1, ..., C_n. - - Where the C_i's are interpreted expressions. - - We say that a rule containing C_i's is a rule with a "big tail". - */ - class mk_simple_joins : public rule_transformer::plugin { - context & m_context; - rule_manager & rm; - public: - mk_simple_joins(context & ctx); - - rule_set * operator()(rule_set const & source); - }; - -}; - -#endif /* _DL_MK_SIMPLE_JOINS_H_ */ - diff --git a/src/muz/dl_product_relation.cpp b/src/muz/dl_product_relation.cpp deleted file mode 100644 index 48cd666e6..000000000 --- a/src/muz/dl_product_relation.cpp +++ /dev/null @@ -1,1117 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_product_relation.cpp - -Abstract: - - A Relation combinator. - -Author: - - Nikolaj Bjorner (nbjorner) 2010-4-11 - -Revision History: - -Notes: - - join = - more refined version lets augment the product - relation as a consequence of join. - join Q = - join = - - - u = - more refined version: - < (R u R') n (R u S') n (R' u S), (S u S') n (S u R') n (S' u R)> - - - proj = < proj R, proj S> - - & phi = - attach S to [R & phi] whenever R & phi can propagate to S - - - [rename] = - - - ---*/ - - -#include "dl_sieve_relation.h" -#include "dl_table_relation.h" -#include "dl_product_relation.h" -#include "bool_rewriter.h" -#include "ast_pp.h" - -namespace datalog { - - // ----------------------------------- - // - // product_relation_plugin - // - // ----------------------------------- - - product_relation_plugin & product_relation_plugin::get_plugin(relation_manager & rmgr) { - product_relation_plugin * res = - static_cast(rmgr.get_relation_plugin(get_name())); - if(!res) { - res = alloc(product_relation_plugin, rmgr); - rmgr.register_plugin(res); - } - return *res; - } - - product_relation_plugin::product_relation_plugin(relation_manager& m): - relation_plugin(product_relation_plugin::get_name(), m, ST_PRODUCT_RELATION), - m_spec_store(*this) { - } - - void product_relation_plugin::initialize(family_id fid) { - relation_plugin::initialize(fid); - m_spec_store.add_available_kind(get_kind()); - } - - family_id product_relation_plugin::get_relation_kind(const relation_signature & sig, const rel_spec & spec) { - return m_spec_store.get_relation_kind(sig, spec); - } - - family_id product_relation_plugin::get_relation_kind(const product_relation & r) { - return get_relation_kind(r.get_signature(), r.m_spec); - } - - bool product_relation_plugin::can_handle_signature(const relation_signature & s) { - return m_spec_store.contains_signature(s); - } - - bool product_relation_plugin::can_handle_signature(const relation_signature & s, family_id k) { - return true; - } - - product_relation& product_relation_plugin::get(relation_base& r) { - return dynamic_cast(r); - } - - product_relation const & product_relation_plugin::get(relation_base const& r) { - return dynamic_cast(r); - } - - product_relation* product_relation_plugin::get(relation_base* r) { - return dynamic_cast(r); - } - - product_relation const* product_relation_plugin::get(relation_base const* r) { - return dynamic_cast(r); - } - - bool product_relation_plugin::is_product_relation(relation_base const& r) { - return r.get_plugin().get_name() == product_relation_plugin::get_name(); - } - - bool product_relation_plugin::are_aligned(const product_relation& r1, const product_relation& r2) { - unsigned sz = r1.size(); - if(sz!=r2.size()) { - return false; - } - for(unsigned i=0; i & rels, - rel_spec & res) { - vector specs; - ptr_vector::const_iterator rit = rels.begin(); - ptr_vector::const_iterator rend = rels.end(); - for(; rit!=rend; ++rit) { - specs.push_back((*rit)->m_spec); - } - - vector::iterator sit = specs.begin(); - vector::iterator send = specs.end(); - for(; sit!=send; ++sit) { - rel_spec & s = *sit; - std::sort(s.begin(), s.end()); - } - - res.reset(); - for(;;) { - family_id next = -1; - - sit = specs.begin(); - for(; sit!=send; ++sit) { - rel_spec & s = *sit; - if(!s.empty() && s.back()>next) { - next = s.back(); - } - } - if(next==-1) { - //we're done - break; - } - res.push_back(next); - sit = specs.begin(); - for(; sit!=send; ++sit) { - rel_spec & s = *sit; - if(!s.empty() && s.back()==next) { - s.pop_back(); - } - } - } - } - - - relation_base * product_relation_plugin::mk_empty(const relation_signature & s) { - return alloc(product_relation,*this, s); - } - - relation_base * product_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { - rel_spec spec; - m_spec_store.get_relation_spec(s, kind, spec); - relation_vector inner_rels; - unsigned rel_cnt = spec.size(); - for(unsigned i=0; i m_joins; - ptr_vector m_full; - unsigned_vector m_offset1; - svector m_kind1; - unsigned_vector m_offset2; - svector m_kind2; - - const relation_base & get_nonsieve_relation(const relation_base & r) { - relation_plugin & rp = r.get_plugin(); - if(rp.is_sieve_relation()) { - return static_cast(r).get_inner(); - } - else { - return r; - } - } - - relation_plugin & get_nonsieve_plugin(const relation_base & r) { - return get_nonsieve_relation(r).get_plugin(); - } - - family_id get_nonsieve_kind(const relation_base & r) { - return get_nonsieve_relation(r).get_kind(); - } - - /** - A tableish relatio is either a table_relation or a sieve_relation with a table_relation inside. - */ - bool is_tableish_relation(const relation_base & r) { - return get_nonsieve_plugin(r).from_table(); - } - - relation_base * get_full_tableish_relation(const relation_signature & sig, func_decl* p, family_id kind) { - relation_manager& rmgr = m_plugin.get_manager(); - table_signature tsig; - if(rmgr.relation_signature_to_table(sig, tsig)) { - return rmgr.mk_table_relation(sig, rmgr.get_appropriate_plugin(tsig).mk_full(p, tsig, kind)); - } - unsigned sz = sig.size(); - tsig.reset(); - for(unsigned i=0; i relations; - unsigned sz = m_joins.size(); - relation_base* result = 0; - for (unsigned i = 0; i < sz; ++i) { - relation_base const& r1 = (m_kind1[i] == T_FULL)?(*m_full[m_offset1[i]]):access(m_offset1[i], _r1); - relation_base const& r2 = (m_kind2[i] == T_FULL)?(*m_full[m_offset2[i]]):access(m_offset2[i], _r2); - relations.push_back((*m_joins[i])(r1, r2)); - } - result = alloc(product_relation, m_plugin, get_result_signature(), sz, relations.c_ptr()); - TRACE("dl",result->display(tout);); - return result; - } - }; - - relation_join_fn * product_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (is_product_relation(r1) && is_product_relation(r2)) { - return alloc(join_fn, *this, get(r1), get(r2), col_cnt, cols1, cols2); - } - if (is_product_relation(r1)) { - return alloc(join_fn, *this, get(r1), r2, col_cnt, cols1, cols2); - } - if (is_product_relation(r2)) { - return alloc(join_fn, *this, r1, get(r2), col_cnt, cols1, cols2); - } - if (r1.get_kind() != r2.get_kind()) { - return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2); - } - return 0; - } - - - class product_relation_plugin::transform_fn : public relation_transformer_fn { - relation_signature m_sig; - ptr_vector m_transforms; - public: - transform_fn(relation_signature s, unsigned num_trans, relation_transformer_fn** trans): - m_sig(s), - m_transforms(num_trans, trans) {} - - ~transform_fn() { dealloc_ptr_vector_content(m_transforms); } - - virtual relation_base * operator()(const relation_base & _r) { - product_relation const& r = get(_r); - product_relation_plugin& p = r.get_plugin(); - SASSERT(m_transforms.size() == r.size()); - ptr_vector relations; - for (unsigned i = 0; i < r.size(); ++i) { - relations.push_back((*m_transforms[i])(r[i])); - } - relation_base* result = alloc(product_relation, p, m_sig, relations.size(), relations.c_ptr()); - TRACE("dl", _r.display(tout); result->display(tout);); - return result; - } - }; - - relation_transformer_fn * product_relation_plugin::mk_project_fn(const relation_base & _r, - unsigned col_cnt, const unsigned * removed_cols) { - if (is_product_relation(_r)) { - product_relation const& r = get(_r); - ptr_vector projs; - for (unsigned i = 0; i < r.size(); ++i) { - projs.push_back(get_manager().mk_project_fn(r[i], col_cnt, removed_cols)); - } - relation_signature s; - relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, s); - return alloc(transform_fn, s, projs.size(), projs.c_ptr()); - } - return 0; - } - - relation_transformer_fn * product_relation_plugin::mk_rename_fn(const relation_base & _r, - unsigned cycle_len, const unsigned * permutation_cycle) { - if(is_product_relation(_r)) { - ptr_vector trans; - product_relation const& r = get(_r); - for (unsigned i = 0; i < r.size(); ++i) { - trans.push_back(get_manager().mk_rename_fn(r[i], cycle_len, permutation_cycle)); - } - relation_signature s; - relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, s); - return alloc(transform_fn, s, trans.size(), trans.c_ptr()); - } - return 0; - } - - class product_relation_plugin::aligned_union_fn : public relation_union_fn { - relation_manager & m_rmgr; - bool m_is_widen; - - //m_union[i][j] is union between i-th and j-th relation. - //It can be zero which means that particular union should be skipped. - vector > m_unions; - - void mk_union_fn(unsigned i, unsigned j, relation_base const& r1, relation_base const& r2, - const relation_base* delta) { - relation_manager& rmgr = r1.get_manager(); - relation_union_fn* u = 0; - if (m_is_widen) { - u = rmgr.mk_widen_fn(r1, r2, delta); - } - else { - u = rmgr.mk_union_fn(r1, r2, delta); - } - m_unions.back().push_back(u); - } - - void init(const relation_vector & tgts, const relation_vector & srcs, const relation_vector * deltas) { - SASSERT(tgts.size()==srcs.size()); - unsigned num = tgts.size(); - for (unsigned i = 0; i < num; ++i) { - relation_base& r1 = *tgts[i]; - relation_base* delta = deltas ? (*deltas)[i] : 0; - m_unions.push_back(ptr_vector()); - for (unsigned j = 0; j < num; ++j) { - relation_base& r2 = *srcs[j]; - mk_union_fn(i, j, r1, r2, delta); - } - } - } - - bool can_do_inner_union(unsigned tgt_idx, unsigned src_idx) { - return m_unions[tgt_idx][src_idx]!=0; - } - - void do_inner_union(unsigned tgt_idx, unsigned src_idx, relation_base& tgt, - relation_base& src, relation_base * delta) { - SASSERT(m_unions[tgt_idx][src_idx]); - (*m_unions[tgt_idx][src_idx])(tgt, src, delta); - } - - /** - If tgt is zero, it is assumed to be a full relation. - */ - void do_destructive_intersection(scoped_rel& tgt, scoped_rel& src) { - if(!src) { - return; - } - if(!tgt) { - tgt=src.release(); - return; - } - do_intersection(*tgt, *src); - src = 0; - } - - void do_intersection(relation_base& tgt, relation_base& src) { - scoped_ptr intersect_fun = - m_rmgr.mk_filter_by_intersection_fn(tgt, src); - if(!intersect_fun) { - warning_msg("intersection does not exist"); - return; - } - (*intersect_fun)(tgt, src); - } - void do_delta_union(unsigned rel_idx, relation_base& tgt, relation_base& src) { - scoped_ptr union_fun = m_rmgr.mk_union_fn(tgt, src); - SASSERT(union_fun); - (*union_fun)(tgt, src); - } - public: - aligned_union_fn(product_relation const& tgt, product_relation const& src, product_relation const* delta, - bool is_widen) : - m_rmgr(tgt.get_manager()), - m_is_widen(is_widen) { - SASSERT(vectors_equal(tgt.m_spec, src.m_spec)); - SASSERT(!delta || vectors_equal(tgt.m_spec, delta->m_spec)); - init(tgt.m_relations, src.m_relations, delta ? &delta->m_relations : 0); - } - - ~aligned_union_fn() { - unsigned sz = m_unions.size(); - for(unsigned i=0; i side_results; - ptr_vector side_deltas; - - for (unsigned i = 0; i < num; ++i) { - relation_base& itgt = tgt[i]; - relation_base* idelta = delta ? &(*delta)[i] : 0; - - scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : 0; - scoped_rel side_result; - scoped_rel side_delta; - - //compute the side unions with which we will intersect the result of the basic one - for (unsigned j = 0; j < num; ++j) { - if (i == j) { - continue; //this is the basic union which we will perform later - } - if (can_do_inner_union(i, j)) { - TRACE("dl", itgt.display(tout << "tgt:\n"); src[j].display(tout << "src:\n");); - // union[i][j] - scoped_rel one_side_union = itgt.clone(); - scoped_rel one_side_delta = fresh_delta ? fresh_delta->clone() : 0; - TRACE("dl", one_side_union->display(tout << "union 1:\n"); src[j].display(tout);); - do_inner_union(i, j, *one_side_union, src[j], one_side_delta.get()); - TRACE("dl", one_side_union->display(tout << "union:\n");); - do_destructive_intersection(side_result, one_side_union); - TRACE("dl", - side_result->display(tout << "inner-union: " << i << " " << j << "\n"); - itgt.display(tout << "tgt:\n");); - if (one_side_delta) { - do_destructive_intersection(side_delta, one_side_delta); - } - - // union[j][i] - one_side_union = src[i].clone(); - one_side_delta = fresh_delta ? fresh_delta->clone() : 0; - TRACE("dl", one_side_union->display(tout << "union 2:\n"); tgt[j].display(tout);); - do_inner_union(i, j, *one_side_union, tgt[j], one_side_delta.get()); - TRACE("dl", one_side_union->display(tout << "union:\n");); - do_destructive_intersection(side_result, one_side_union); - TRACE("dl", - side_result->display(tout << "inner-union: " << i << " " << j << "\n"); - itgt.display(tout << "tgt:\n");); - if (one_side_delta) { - do_destructive_intersection(side_delta, one_side_delta); - } - } - } - side_results.push_back(side_result.release()); - side_deltas.push_back(side_delta.release()); - } - for (unsigned i = 0; i < num; ++i) { - relation_base& itgt = tgt[i]; - relation_base* idelta = delta ? &(*delta)[i] : 0; - scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : 0; - scoped_rel side_result(side_results[i]); - scoped_rel side_delta(side_deltas[i]); - - // perform the basic union - // assume a relation can always perform union with the relation of the same type - VERIFY(can_do_inner_union(i,i)); - do_inner_union(i, i, itgt, src[i], fresh_delta.get()); - - if (side_result) { - do_intersection(itgt, *side_result); - TRACE("dl", side_result->display(tout << "inner-union-end: " << i << "\n");); - } - if (fresh_delta) { - do_destructive_intersection(fresh_delta,side_delta); - SASSERT(idelta); - do_delta_union(i, *idelta, *fresh_delta); - } - } - if (num == 0) { - //we need to handle product relation of no relations separately - if (!src.m_default_empty && tgt.m_default_empty) { - tgt.m_default_empty = false; - if (delta) { - delta->m_default_empty = false; - } - } - } - TRACE("dl", _tgt.display(tout << "dst':\n"); - if (_delta) _delta->display(tout << "delta:\n"); ;); - } - }; - - class product_relation_plugin::unaligned_union_fn : public relation_union_fn { - bool m_is_widen; - rel_spec m_common_spec; - scoped_ptr m_aligned_union_fun; - public: - unaligned_union_fn(product_relation const& tgt, product_relation const& src, - product_relation const* delta, bool is_widen) : m_is_widen(is_widen) { - ptr_vector rels; - rels.push_back(&tgt); - rels.push_back(&src); - if(delta) { - rels.push_back(delta); - } - get_common_spec(rels, m_common_spec); - } - - - virtual void operator()(relation_base& _tgt, const relation_base& _src, relation_base* _delta) { - TRACE("dl", _tgt.display(tout << "dst:\n"); _src.display(tout << "src:\n");); - product_relation& tgt = get(_tgt); - product_relation const& src0 = get(_src); - product_relation* delta = _delta ? get(_delta) : 0; - - tgt.convert_spec(m_common_spec); - if(delta) { - delta->convert_spec(m_common_spec); - } - scoped_rel src_scoped; - if(src0.get_kind()!=tgt.get_kind()) { - src_scoped = src0.clone(); - src_scoped->convert_spec(m_common_spec); - } - product_relation const& src = src_scoped ? *src_scoped : src0; - - if(!m_aligned_union_fun) { - m_aligned_union_fun = alloc(aligned_union_fn, tgt, src, delta, m_is_widen); - SASSERT(m_aligned_union_fun); - } - (*m_aligned_union_fun)(tgt, src, delta); - TRACE("dl", _tgt.display(tout << "dst':\n"); - if (_delta) _delta->display(tout << "delta:\n");); - } - }; - - class product_relation_plugin::single_non_transparent_src_union_fn : public relation_union_fn { - unsigned m_single_rel_idx; - scoped_ptr m_inner_union_fun; - public: - single_non_transparent_src_union_fn(unsigned single_rel_idx, relation_union_fn* inner_union_fun) - : m_single_rel_idx(single_rel_idx), - m_inner_union_fun(inner_union_fun) {} - - virtual void operator()(relation_base& tgt, const relation_base& _src, relation_base* delta) { - product_relation const& src = get(_src); - (*m_inner_union_fun)(tgt, src[m_single_rel_idx], delta); - } - }; - - relation_union_fn * product_relation_plugin::mk_union_w_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta, bool is_widen) { - if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { - if(are_aligned(get(tgt), get(src)) && (!delta || are_aligned(get(tgt), *get(delta)))) { - return alloc(aligned_union_fn, get(tgt), get(src), get(delta), is_widen); - } - return alloc(unaligned_union_fn, get(tgt), get(src), get(delta), is_widen); - } - if(check_kind(src)) { - const product_relation & p_src = get(src); - unsigned single_idx; - if(p_src.try_get_single_non_transparent(single_idx)) { - relation_union_fn * inner; - if(is_widen) { - inner = get_manager().mk_widen_fn(tgt, p_src[single_idx], delta); - } - else { - inner = get_manager().mk_union_fn(tgt, p_src[single_idx], delta); - } - if(inner) { - return alloc(single_non_transparent_src_union_fn, single_idx, inner); - } - } - } - return 0; - } - - relation_union_fn * product_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - return mk_union_w_fn(tgt, src, delta, false); - } - - relation_union_fn * product_relation_plugin::mk_widen_fn( - const relation_base & tgt, const relation_base & src, const relation_base * delta) { - return mk_union_w_fn(tgt, src, delta, true); - } - - class product_relation_plugin::mutator_fn : public relation_mutator_fn { - ptr_vector m_mutators; - public: - mutator_fn(unsigned sz, relation_mutator_fn** muts): - m_mutators(sz, muts) {} - - ~mutator_fn() { dealloc_ptr_vector_content(m_mutators); } - - virtual void operator()(relation_base & _r) { - TRACE("dl", _r.display(tout);); - product_relation& r = get(_r); - SASSERT(m_mutators.size() == r.size()); - for (unsigned i = 0; i < r.size(); ++i) { - relation_mutator_fn* m = m_mutators[i]; - if (m) { - (*m)(r[i]); - } - } - TRACE("dl", _r.display(tout);); - } - }; - - - relation_mutator_fn * product_relation_plugin::mk_filter_identical_fn( - const relation_base & _t, unsigned col_cnt, const unsigned * identical_cols) { - - if(is_product_relation(_t)) { - bool found = false; - product_relation const& r = get(_t); - ptr_vector mutators; - for (unsigned i = 0; i < r.size(); ++i) { - relation_mutator_fn* m = get_manager().mk_filter_identical_fn(r[i], col_cnt, identical_cols); - mutators.push_back(m); - if (m) found = true; - } - if (found) { - return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); - } - } - return 0; - } - - relation_mutator_fn * product_relation_plugin::mk_filter_equal_fn(const relation_base & _t, - const relation_element & value, unsigned col) { - if(is_product_relation(_t)) { - product_relation const& r = get(_t); - ptr_vector mutators; - bool found = false; - for (unsigned i = 0; i < r.size(); ++i) { - relation_mutator_fn* m = get_manager().mk_filter_equal_fn(r[i], value, col); - mutators.push_back(m); - if (m) found = true; - } - if (found) { - return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); - } - } - return 0; - } - - class product_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { - ptr_vector m_mutators; - svector > m_attach; - public: - - filter_interpreted_fn(product_relation const& r, app* cond) { - for (unsigned i = 0; i < r.size(); ++i) { - m_mutators.push_back(r.get_manager().mk_filter_interpreted_fn(r[i], cond)); - } - for (unsigned i = 0; i < r.size(); ++i) { - relation_mutator_fn& m1 = *(m_mutators[i]); - for (unsigned j = i + 1; j < r.size(); ++j) { - relation_mutator_fn& m2 = *(m_mutators[j]); - if (m1.supports_attachment(r[j])) { - m_attach.push_back(std::make_pair(i,j)); - } - if (m2.supports_attachment(r[i])) { - m_attach.push_back(std::make_pair(j,i)); - } - } - } - } - - ~filter_interpreted_fn() { dealloc_ptr_vector_content(m_mutators); } - - void operator()(relation_base& _r) { - TRACE("dl", _r.display(tout);); - product_relation const& r = get(_r); - for (unsigned i = 0; i < m_attach.size(); ++i) { - m_mutators[m_attach[i].first]->attach(r[m_attach[i].second]); - } - for (unsigned i = 0; i < m_mutators.size(); ++i) { - (*m_mutators[i])(r[i]); - } - TRACE("dl", _r.display(tout);); - } - }; - - relation_mutator_fn * product_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { - return alloc(filter_interpreted_fn, get(t), condition); - } - - - // ----------------------------------- - // - // product_relation - // - // ----------------------------------- - - product_relation::product_relation(product_relation_plugin& p, relation_signature const& s): - relation_base(p, s), - m_default_empty(true) { - ensure_correct_kind(); - } - - product_relation::product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations) : - relation_base(p, s), - m_default_empty(true) { - for (unsigned i = 0; i < num_relations; ++i) { - SASSERT(relations[i]->get_signature()==s); - m_relations.push_back(relations[i]); - } - ensure_correct_kind(); - } - - product_relation::~product_relation() { - unsigned num_relations = m_relations.size(); - for (unsigned i = 0; i < num_relations; ++i) { - m_relations[i]->deallocate(); - } - } - - product_relation_plugin& product_relation::get_plugin() const { - return dynamic_cast(relation_base::get_plugin()); - } - - void product_relation::ensure_correct_kind() { - unsigned rel_cnt = m_relations.size(); - //the rel_cnt==0 part makes us to update the kind also when the relation is newly created - bool spec_changed = rel_cnt!=m_spec.size() || rel_cnt==0; - if(spec_changed) { - m_spec.resize(rel_cnt); - } - for(unsigned i=0;iget_kind(); - if(spec_changed || m_spec[i]!=rkind) { - spec_changed = true; - m_spec[i]=rkind; - } - } - if(spec_changed) { - family_id new_kind = get_plugin().get_relation_kind(*this); - set_kind(new_kind); - } - } - - void product_relation::convert_spec(const rel_spec & spec) { - - func_decl* p = 0; - const relation_signature & sig = get_signature(); - family_id new_kind = get_plugin().get_relation_kind(sig, spec); - if(new_kind==get_kind()) { - return; - } - - unsigned old_sz = size(); - unsigned new_sz = spec.size(); - unsigned old_remain = old_sz; - relation_vector new_rels; - - //the loop is quadratic with the number of relations, maybe we want to fix it - for(unsigned i=0; iget_kind()==ikind) { - irel = m_relations[j]; - m_relations[j] = 0; - old_remain--; - break; - } - } - if(!irel) { - if(old_sz==0 && m_default_empty) { - //The relation didn't contain any inner relations but it was empty, - //so we make the newly added relations empty as well. - irel = get_manager().mk_empty_relation(sig, new_kind); - } - else { - irel = get_manager().mk_full_relation(sig, p, new_kind); - } - } - new_rels.push_back(irel); - } - SASSERT(old_remain==0); //the new specification must be a superset of the old one - m_relations = new_rels; - - set_kind(new_kind); - DEBUG_CODE( - ensure_correct_kind(); - SASSERT(get_kind()==new_kind); - ); - } - - bool product_relation::try_get_single_non_transparent(unsigned & idx) const { - unsigned sz = size(); - bool found = false; - unsigned candidate; - for(unsigned i=0; i relations; - for (unsigned i = 0; i < size(); ++i) { - relations.push_back((*this)[i].clone()); - } - product_relation_plugin& p = get_plugin(); - return alloc(product_relation, p, get_signature(), relations.size(), relations.c_ptr()); - } - - product_relation * product_relation::complement(func_decl*) const { - if(m_relations.empty()) { - product_relation * res = clone(); - res->m_default_empty = !m_default_empty; - return res; - } - UNREACHABLE(); - return 0; - } - - bool product_relation::empty() const { - if(m_relations.empty()) { - return m_default_empty; - } - for (unsigned i = 0; i < m_relations.size(); ++i) { - if (m_relations[i]->empty()) { - return true; - } - } - return false; - } - - void product_relation::to_formula(expr_ref& fml) const { - ast_manager& m = fml.get_manager(); - expr_ref_vector conjs(m); - expr_ref tmp(m); - for (unsigned i = 0; i < m_relations.size(); ++i) { - m_relations[i]->to_formula(tmp); - conjs.push_back(tmp); - } - bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), fml); - } - - void product_relation::display(std::ostream & out) const { - out<<"Product of the following relations:\n"; - for (unsigned i = 0; i < m_relations.size(); ++i) { - m_relations[i]->display(out); - } - } - -}; - - - diff --git a/src/muz/dl_product_relation.h b/src/muz/dl_product_relation.h deleted file mode 100644 index bf37a59d0..000000000 --- a/src/muz/dl_product_relation.h +++ /dev/null @@ -1,190 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_product_relation.h - -Abstract: - - A Relation relation combinator. - -Author: - - Nikolaj Bjorner (nbjorner) 2010-4-11 - -Revision History: - ---*/ -#ifndef _DL_PRODUCT_RELATION_H_ -#define _DL_PRODUCT_RELATION_H_ - - -#include "dl_context.h" - -namespace datalog { - - class product_relation; - - class product_relation_plugin : public relation_plugin { - friend class product_relation; - public: - typedef svector rel_spec; - private: - class join_fn; - class transform_fn; - class mutator_fn; - class aligned_union_fn; - class unaligned_union_fn; - class single_non_transparent_src_union_fn; - class filter_equal_fn; - class filter_identical_fn; - class filter_interpreted_fn; - struct fid_hash { - typedef family_id data; - unsigned operator()(data x) const { return static_cast(x); } - }; - - rel_spec_store > m_spec_store; - - family_id get_relation_kind(const product_relation & r); - - bool is_product_relation(relation_base * r) { return r->get_plugin().is_product_relation(); } - - public: - static product_relation_plugin& get_plugin(relation_manager & rmgr); - - product_relation_plugin(relation_manager& m); - - virtual void initialize(family_id fid); - - virtual bool can_handle_signature(const relation_signature & s); - virtual bool can_handle_signature(const relation_signature & s, family_id kind); - - static symbol get_name() { return symbol("product_relation"); } - - family_id get_relation_kind(const relation_signature & sig, const rel_spec & spec); - - virtual relation_base * mk_empty(const relation_signature & s); - virtual relation_base * mk_empty(const relation_signature & s, family_id kind); - - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - virtual relation_base * mk_full(func_decl* p, const relation_signature & s, family_id kind); - - protected: - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - - static bool is_product_relation(relation_base const& r); - - private: - static product_relation& get(relation_base& r); - static product_relation const & get(relation_base const& r); - static product_relation* get(relation_base* r); - static product_relation const* get(relation_base const* r); - - relation_union_fn * mk_union_w_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta, bool is_widen); - - bool are_aligned(const product_relation& r1, const product_relation& r2); - static void get_common_spec(const ptr_vector & rels, rel_spec & res); - }; - - - class product_relation : public relation_base { - friend class product_relation_plugin; - - friend class product_relation_plugin::join_fn; - friend class product_relation_plugin::transform_fn; - friend class product_relation_plugin::mutator_fn; - friend class product_relation_plugin::aligned_union_fn; - friend class product_relation_plugin::unaligned_union_fn; - friend class product_relation_plugin::single_non_transparent_src_union_fn; - friend class product_relation_plugin::filter_equal_fn; - friend class product_relation_plugin::filter_identical_fn; - friend class product_relation_plugin::filter_interpreted_fn; - - - - typedef product_relation_plugin::rel_spec rel_spec; - - /** - If m_relations is empty, value of this determines whether the relation is empty or full. - */ - bool m_default_empty; - - /** - There must not be two relations of the same kind - */ - ptr_vector m_relations; - - /** - Array of kinds of inner relations. - - If two product relations have equal signature and specification, their - m_relations arrays contain corresponding relations at the same indexes. - - The value returned by get_kind() depends uniquely on the specification. - */ - rel_spec m_spec; - - /** - \brief Ensure the kind assigned to this relation reflects the types of inner relations. - */ - void ensure_correct_kind(); - /** - The current specification must be a subset of the new one. - */ - void convert_spec(const rel_spec & spec); - public: - product_relation(product_relation_plugin& p, relation_signature const& s); - product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations); - - ~product_relation(); - - virtual bool empty() const; - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual product_relation * clone() const; - virtual product_relation * complement(func_decl* p) const; - virtual void display(std::ostream & out) const; - virtual void to_formula(expr_ref& fml) const; - product_relation_plugin& get_plugin() const; - - unsigned size() const { return m_relations.size(); } - relation_base& operator[](unsigned i) const { return *m_relations[i]; } - - /** - If all relations except one are sieve_relations with no inner columns, - return true and into \c idx assign index of that relation. Otherwise return - false. - */ - bool try_get_single_non_transparent(unsigned & idx) const; - - virtual bool is_precise() const { - for (unsigned i = 0; i < m_relations.size(); ++i) { - if (!m_relations[i]->is_precise()) { - return false; - } - } - return true; - } - }; - -}; - -#endif - diff --git a/src/muz/dl_relation_manager.cpp b/src/muz/dl_relation_manager.cpp deleted file mode 100644 index 457ef28c0..000000000 --- a/src/muz/dl_relation_manager.cpp +++ /dev/null @@ -1,1702 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_relation_manager.cpp - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-14. - -Revision History: - ---*/ - - -#include -#include"ast_pp.h" -#include"dl_check_table.h" -#include"dl_context.h" -#include"dl_finite_product_relation.h" -#include"dl_product_relation.h" -#include"dl_sieve_relation.h" -#include"dl_table_relation.h" -#include"dl_relation_manager.h" - -namespace datalog { - - relation_manager::~relation_manager() { - reset(); - } - - - void relation_manager::reset_relations() { - relation_map::iterator it=m_relations.begin(); - relation_map::iterator end=m_relations.end(); - for(;it!=end;++it) { - func_decl * pred = it->m_key; - get_context().get_manager().dec_ref(pred); //inc_ref in get_relation - relation_base * r=(*it).m_value; - r->deallocate(); - } - m_relations.reset(); - } - - void relation_manager::reset() { - reset_relations(); - - m_favourite_table_plugin = static_cast(0); - m_favourite_relation_plugin = static_cast(0); - dealloc_ptr_vector_content(m_table_plugins); - m_table_plugins.reset(); - dealloc_ptr_vector_content(m_relation_plugins); - m_relation_plugins.reset(); - m_next_table_fid = 0; - m_next_relation_fid = 0; - } - - dl_decl_util & relation_manager::get_decl_util() const { - return get_context().get_decl_util(); - } - - family_id relation_manager::get_next_relation_fid(relation_plugin & claimer) { - unsigned res = m_next_relation_fid++; - m_kind2plugin.insert(res, &claimer); - return res; - } - - void relation_manager::set_predicate_kind(func_decl * pred, family_id kind) { - SASSERT(!m_relations.contains(pred)); - m_pred_kinds.insert(pred, kind); - } - - family_id relation_manager::get_requested_predicate_kind(func_decl * pred) { - family_id res; - if(m_pred_kinds.find(pred, res)) { - return res; - } - else { - return null_family_id; - } - } - - relation_base & relation_manager::get_relation(func_decl * pred) { - relation_base * res = try_get_relation(pred); - if(!res) { - relation_signature sig; - from_predicate(pred, sig); - family_id rel_kind = get_requested_predicate_kind(pred); - res = mk_empty_relation(sig, rel_kind); - store_relation(pred, res); - } - return *res; - } - - relation_base * relation_manager::try_get_relation(func_decl * pred) const { - relation_base * res = 0; - if(!m_relations.find(pred, res)) { - return 0; - } - SASSERT(res); - return res; - } - - void relation_manager::store_relation(func_decl * pred, relation_base * rel) { - SASSERT(rel); - relation_map::entry * e = m_relations.insert_if_not_there2(pred, 0); - if (e->get_data().m_value) { - e->get_data().m_value->deallocate(); - } - else { - get_context().get_manager().inc_ref(pred); //dec_ref in reset - } - e->get_data().m_value = rel; - } - - void relation_manager::collect_non_empty_predicates(decl_set & res) const { - relation_map::iterator it = m_relations.begin(); - relation_map::iterator end = m_relations.end(); - for(; it!=end; ++it) { - if(!it->m_value->empty()) { - res.insert(it->m_key); - } - } - } - - void relation_manager::restrict_predicates(const decl_set & preds) { - typedef ptr_vector fd_vector; - fd_vector to_remove; - - relation_map::iterator rit = m_relations.begin(); - relation_map::iterator rend = m_relations.end(); - for(; rit!=rend; ++rit) { - func_decl * pred = rit->m_key; - if (!preds.contains(pred)) { - to_remove.insert(pred); - } - } - - fd_vector::iterator pit = to_remove.begin(); - fd_vector::iterator pend = to_remove.end(); - for(; pit!=pend; ++pit) { - func_decl * pred = *pit; - relation_base * rel; - VERIFY( m_relations.find(pred, rel) ); - rel->deallocate(); - m_relations.remove(pred); - get_context().get_manager().dec_ref(pred); - } - - set_intersection(m_saturated_rels, preds); - } - - void relation_manager::register_plugin(table_plugin * plugin) { - plugin->initialize(get_next_table_fid()); - m_table_plugins.push_back(plugin); - - if(plugin->get_name()==get_context().default_table()) { - m_favourite_table_plugin = plugin; - } - - table_relation_plugin * tr_plugin = alloc(table_relation_plugin, *plugin, *this); - register_relation_plugin_impl(tr_plugin); - m_table_relation_plugins.insert(plugin, tr_plugin); - - symbol checker_name = get_context().default_table_checker(); - if(get_context().default_table_checked() && get_table_plugin(checker_name)) { - if( m_favourite_table_plugin && - (plugin==m_favourite_table_plugin || plugin->get_name()==checker_name) ) { - symbol checked_name = get_context().default_table(); - //the plugins we need to create the checking plugin were just added - SASSERT(m_favourite_table_plugin->get_name()==get_context().default_table()); - table_plugin * checking_plugin = alloc(check_table_plugin, *this, checker_name, checked_name); - register_plugin(checking_plugin); - m_favourite_table_plugin = checking_plugin; - } - if(m_favourite_relation_plugin && m_favourite_relation_plugin->from_table()) { - table_relation_plugin * fav_rel_plugin = - static_cast(m_favourite_relation_plugin); - if(&fav_rel_plugin->get_table_plugin()==plugin || plugin->get_name()==checker_name) { - //the plugins we need to create the checking table_relation_plugin were just added - SASSERT(m_favourite_relation_plugin->get_name() == - get_context().default_relation()); - symbol checked_name = fav_rel_plugin->get_table_plugin().get_name(); - table_plugin * checking_plugin = alloc(check_table_plugin, *this, checker_name, checked_name); - register_plugin(checking_plugin); - - table_relation_plugin * checking_tr_plugin = - alloc(table_relation_plugin, *checking_plugin, *this); - register_relation_plugin_impl(checking_tr_plugin); - m_table_relation_plugins.insert(checking_plugin, checking_tr_plugin); - m_favourite_relation_plugin = checking_tr_plugin; - } - } - } - - } - - void relation_manager::register_relation_plugin_impl(relation_plugin * plugin) { - m_relation_plugins.push_back(plugin); - plugin->initialize(get_next_relation_fid(*plugin)); - if (plugin->get_name() == get_context().default_relation()) { - m_favourite_relation_plugin = plugin; - } - if(plugin->is_finite_product_relation()) { - finite_product_relation_plugin * fprp = static_cast(plugin); - relation_plugin * inner = &fprp->get_inner_plugin(); - m_finite_product_relation_plugins.insert(inner, fprp); - } - } - - relation_plugin * relation_manager::try_get_appropriate_plugin(const relation_signature & s) { - if(m_favourite_relation_plugin && m_favourite_relation_plugin->can_handle_signature(s)) { - return m_favourite_relation_plugin; - } - relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); - relation_plugin_vector::iterator rpend = m_relation_plugins.end(); - for(; rpit!=rpend; ++rpit) { - if((*rpit)->can_handle_signature(s)) { - return *rpit; - } - } - return 0; - } - - relation_plugin & relation_manager::get_appropriate_plugin(const relation_signature & s) { - relation_plugin * res = try_get_appropriate_plugin(s); - if (!res) { - throw default_exception("no suitable plugin found for given relation signature"); - } - return *res; - } - - table_plugin * relation_manager::try_get_appropriate_plugin(const table_signature & t) { - if (m_favourite_table_plugin && m_favourite_table_plugin->can_handle_signature(t)) { - return m_favourite_table_plugin; - } - table_plugin_vector::iterator tpit = m_table_plugins.begin(); - table_plugin_vector::iterator tpend = m_table_plugins.end(); - for(; tpit!=tpend; ++tpit) { - if((*tpit)->can_handle_signature(t)) { - return *tpit; - } - } - return 0; - } - - table_plugin & relation_manager::get_appropriate_plugin(const table_signature & t) { - table_plugin * res = try_get_appropriate_plugin(t); - if(!res) { - throw default_exception("no suitable plugin found for given table signature"); - } - return *res; - } - - relation_plugin * relation_manager::get_relation_plugin(symbol const& s) { - relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); - relation_plugin_vector::iterator rpend = m_relation_plugins.end(); - for(; rpit!=rpend; ++rpit) { - if((*rpit)->get_name()==s) { - return *rpit; - } - } - return 0; - } - - relation_plugin & relation_manager::get_relation_plugin(family_id kind) { - SASSERT(kind>=0); - SASSERT(kindget_name()==k) { - return *tpit; - } - } - return 0; - } - - table_relation_plugin & relation_manager::get_table_relation_plugin(table_plugin & tp) { - table_relation_plugin * res; - VERIFY( m_table_relation_plugins.find(&tp, res) ); - return *res; - } - - bool relation_manager::try_get_finite_product_relation_plugin(const relation_plugin & inner, - finite_product_relation_plugin * & res) { - return m_finite_product_relation_plugins.find(&inner, res); - } - - table_base * relation_manager::mk_empty_table(const table_signature & s) { - return get_appropriate_plugin(s).mk_empty(s); - } - - - bool relation_manager::is_non_explanation(relation_signature const& s) const { - dl_decl_util & decl_util = get_context().get_decl_util(); - unsigned n = s.size(); - for(unsigned i = 0; i < n; i++) { - if(decl_util.is_rule_sort(s[i])) { - return false; - } - } - return true; - } - - relation_base * relation_manager::mk_empty_relation(const relation_signature & s, func_decl* pred) { - return mk_empty_relation(s, get_requested_predicate_kind(pred)); - } - - relation_base * relation_manager::mk_empty_relation(const relation_signature & s, family_id kind) { - if (kind != null_family_id) { - relation_plugin & plugin = get_relation_plugin(kind); - if (plugin.can_handle_signature(s, kind)) - return plugin.mk_empty(s, kind); - } - relation_base * res; - relation_plugin* p = m_favourite_relation_plugin; - - if (p && p->can_handle_signature(s)) { - return p->mk_empty(s); - } - - if (mk_empty_table_relation(s, res)) { - return res; - } - - for (unsigned i = 0; i < m_relation_plugins.size(); ++i) { - p = m_relation_plugins[i]; - if (p->can_handle_signature(s)) { - return p->mk_empty(s); - } - } - - //If there is no plugin to handle the signature, we just create an empty product relation and - //stuff will be added to it by later operations. - return product_relation_plugin::get_plugin(*this).mk_empty(s); - } - - - relation_base * relation_manager::mk_table_relation(const relation_signature & s, table_base * table) { - SASSERT(s.size()==table->get_signature().size()); - return get_table_relation_plugin(table->get_plugin()).mk_from_table(s, table); - } - - bool relation_manager::mk_empty_table_relation(const relation_signature & s, relation_base * & result) { - table_signature tsig; - if(!relation_signature_to_table(s, tsig)) { - return false; - } - table_base * table = mk_empty_table(tsig); - result = mk_table_relation(s, table); - return true; - } - - - relation_base * relation_manager::mk_full_relation(const relation_signature & s, func_decl* p, family_id kind) { - if (kind != null_family_id) { - relation_plugin & plugin = get_relation_plugin(kind); - if (plugin.can_handle_signature(s, kind)) { - return plugin.mk_full(p, s, kind); - } - } - return get_appropriate_plugin(s).mk_full(p, s, null_family_id); - } - - relation_base * relation_manager::mk_full_relation(const relation_signature & s, func_decl* pred) { - family_id kind = get_requested_predicate_kind(pred); - return mk_full_relation(s, pred, kind); - } - - void relation_manager::relation_to_table(const relation_sort & sort, const relation_element & from, - table_element & to) { - SASSERT(from->get_num_args()==0); - VERIFY(get_context().get_decl_util().is_numeral_ext(from, to)); - } - - void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, - relation_element & to) { - to = get_decl_util().mk_numeral(from, sort); - } - - void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, - relation_element_ref & to) { - relation_element rel_el; - table_to_relation(sort, from, rel_el); - to = rel_el; - } - - void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, - const relation_fact::el_proxy & to) { - relation_element rel_el; - table_to_relation(sort, from, rel_el); - to = rel_el; - } - - bool relation_manager::relation_sort_to_table(const relation_sort & from, table_sort & to) { - return get_context().get_decl_util().try_get_size(from, to); - } - - void relation_manager::from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result) { - result = pred->get_domain(arg_index); - } - - void relation_manager::from_predicate(func_decl * pred, relation_signature & result) { - result.reset(); - unsigned arg_num=pred->get_arity(); - for(unsigned i=0;iset_cancel(f); - } - } - - std::string relation_manager::to_nice_string(const relation_element & el) const { - uint64 val; - std::stringstream stm; - if(get_context().get_decl_util().is_numeral_ext(el, val)) { - stm << val; - } - else { - stm << mk_pp(el, get_context().get_manager()); - } - return stm.str(); - } - - std::string relation_manager::to_nice_string(const relation_sort & s, const relation_element & el) const { - std::stringstream stm; - uint64 val; - if(get_context().get_decl_util().is_numeral_ext(el, val)) { - get_context().print_constant_name(s, val, stm); - } - else { - stm << mk_pp(el, get_context().get_manager()); - } - return stm.str(); - } - - std::string relation_manager::to_nice_string(const relation_sort & s) const { - return std::string(s->get_name().bare_str()); - } - - std::string relation_manager::to_nice_string(const relation_signature & s) const { - std::string res("["); - bool first = true; - relation_signature::const_iterator it = s.begin(); - relation_signature::const_iterator end = s.end(); - for(; it!=end; ++it) { - if(first) { - first = false; - } - else { - res+=','; - } - res+=to_nice_string(*it); - } - res+=']'; - - return res; - } - - void relation_manager::display(std::ostream & out) const { - relation_map::iterator it=m_relations.begin(); - relation_map::iterator end=m_relations.end(); - for(;it!=end;++it) { - out << "Table " << it->m_key->get_name() << "\n"; - it->m_value->display(out); - } - } - - void relation_manager::display_relation_sizes(std::ostream & out) const { - relation_map::iterator it=m_relations.begin(); - relation_map::iterator end=m_relations.end(); - for(;it!=end;++it) { - out << "Relation " << it->m_key->get_name() << " has size " - << it->m_value->get_size_estimate_rows() << "\n"; - } - } - - void relation_manager::display_output_tables(rule_set const& rules, std::ostream & out) const { - const decl_set & output_preds = rules.get_output_predicates(); - decl_set::iterator it=output_preds.begin(); - decl_set::iterator end=output_preds.end(); - for(; it!=end; ++it) { - func_decl * pred = *it; - relation_base * rel = try_get_relation(pred); - if(!rel) { - out << "Tuples in " << pred->get_name() << ": \n"; - continue; - } - rel->display_tuples(*pred, out); - } - } - - - // ----------------------------------- - // - // relation operations - // - // ----------------------------------- - - class relation_manager::empty_signature_relation_join_fn : public relation_join_fn { - public: - virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { - TRACE("dl", tout << r1.get_plugin().get_name() << " " << r2.get_plugin().get_name() << "\n";); - if(r1.get_signature().empty()) { - if(r1.empty()) { - return r2.get_manager().mk_empty_relation(r2.get_signature(), r2.get_kind()); - } - else { - return r2.clone(); - } - } - else { - SASSERT(r2.get_signature().empty()); - if(r2.empty()) { - return r1.get_manager().mk_empty_relation(r1.get_signature(), r1.get_kind()); - } - else { - return r1.clone(); - } - } - } - }; - - relation_join_fn * relation_manager::mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation) { - relation_plugin * p1 = &t1.get_plugin(); - relation_plugin * p2 = &t2.get_plugin(); - - relation_join_fn * res = p1->mk_join_fn(t1, t2, col_cnt, cols1, cols2); - if(!res && p1!=p2) { - res = p2->mk_join_fn(t1, t2, col_cnt, cols1, cols2); - } - - if(!res && (t1.get_signature().empty() || t2.get_signature().empty())) { - res = alloc(empty_signature_relation_join_fn); - } - - finite_product_relation_plugin * fprp; - if(!res && p1->from_table() && try_get_finite_product_relation_plugin(*p2, fprp)) { - //we downcast here to relation_plugin so that we don't have to declare - //relation_manager as a friend class of finite_product_relation_plugin - res = static_cast(fprp)->mk_join_fn(t1, t2, col_cnt, cols1, cols2); - } - if(!res && p2->from_table() && try_get_finite_product_relation_plugin(*p1, fprp)) { - res = static_cast(fprp)->mk_join_fn(t1, t2, col_cnt, cols1, cols2); - } - - if(!res && allow_product_relation) { - relation_plugin & product_plugin = product_relation_plugin::get_plugin(*this); - res = product_plugin.mk_join_fn(t1, t2, col_cnt, cols1, cols2); - } - - return res; - } - - relation_transformer_fn * relation_manager::mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols) { - return t.get_plugin().mk_project_fn(t, col_cnt, removed_cols); - } - - class relation_manager::default_relation_filter_interpreted_and_project_fn : public relation_transformer_fn { - scoped_ptr m_filter; - scoped_ptr m_project; - unsigned_vector m_removed_cols; - public: - /** - This constructor should be used only if we know that the projection operation - exists for the result of the join. - */ - default_relation_filter_interpreted_and_project_fn( - relation_mutator_fn* filter, - unsigned removed_col_cnt, - const unsigned * removed_cols) - : m_filter(filter), - m_project(0), - m_removed_cols(removed_col_cnt, removed_cols) {} - - virtual relation_base * operator()(const relation_base & t) { - scoped_rel t1 = t.clone(); - (*m_filter)(*t1); - if( !m_project) { - relation_manager & rmgr = t1->get_plugin().get_manager(); - m_project = rmgr.mk_project_fn(*t1, m_removed_cols.size(), m_removed_cols.c_ptr()); - if (!m_project) { - throw default_exception("projection does not exist"); - } - } - return (*m_project)(*t1); - } - }; - - relation_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn( - const relation_base & t, app * condition, - unsigned removed_col_cnt, const unsigned * removed_cols) { - - relation_transformer_fn* res = - t.get_plugin().mk_filter_interpreted_and_project_fn( - t, - condition, - removed_col_cnt, - removed_cols); - - if (!res) { - relation_mutator_fn* filter_fn = mk_filter_interpreted_fn(t, condition); - if (filter_fn) { - res = alloc(default_relation_filter_interpreted_and_project_fn, - filter_fn, - removed_col_cnt, - removed_cols); - } - } - return res; - } - - - class relation_manager::default_relation_join_project_fn : public relation_join_fn { - scoped_ptr m_join; - scoped_ptr m_project; - - unsigned_vector m_removed_cols; - public: - /** - This constructor should be used only if we know that the projection operation - exists for the result of the join. - */ - default_relation_join_project_fn(join_fn * join, unsigned removed_col_cnt, - const unsigned * removed_cols) - : m_join(join), m_project(0), m_removed_cols(removed_col_cnt, removed_cols) {} - - virtual relation_base * operator()(const relation_base & t1, const relation_base & t2) { - scoped_rel aux = (*m_join)(t1, t2); - if(!m_project) { - relation_manager & rmgr = aux->get_plugin().get_manager(); - m_project = rmgr.mk_project_fn(*aux, m_removed_cols.size(), m_removed_cols.c_ptr()); - if(!m_project) { - throw default_exception("projection does not exist"); - } - } - relation_base * res = (*m_project)(*aux); - return res; - } - }; - - - relation_join_fn * relation_manager::mk_join_project_fn(const relation_base & t1, const relation_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join) { - relation_join_fn * res = t1.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, - removed_col_cnt, removed_cols); - if(!res && &t1.get_plugin()!=&t2.get_plugin()) { - res = t2.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, - removed_cols); - } - if(!res) { - relation_join_fn * join = mk_join_fn(t1, t2, joined_col_cnt, cols1, cols2, allow_product_relation_join); - if(join) { - res = alloc(default_relation_join_project_fn, join, removed_col_cnt, removed_cols); - } - } - return res; - - } - - relation_transformer_fn * relation_manager::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle) { - return t.get_plugin().mk_rename_fn(t, permutation_cycle_len, permutation_cycle); - } - - relation_transformer_fn * relation_manager::mk_permutation_rename_fn(const relation_base & t, - const unsigned * permutation) { - relation_transformer_fn * res = t.get_plugin().mk_permutation_rename_fn(t, permutation); - if(!res) { - res = alloc(default_relation_permutation_rename_fn, t, permutation); - } - return res; - } - - - relation_union_fn * relation_manager::mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - relation_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); - if(!res && &tgt.get_plugin()!=&src.get_plugin()) { - res = src.get_plugin().mk_union_fn(tgt, src, delta); - } - if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { - res = delta->get_plugin().mk_union_fn(tgt, src, delta); - } - return res; - } - - relation_union_fn * relation_manager::mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - relation_union_fn * res = tgt.get_plugin().mk_widen_fn(tgt, src, delta); - if(!res && &tgt.get_plugin()!=&src.get_plugin()) { - res = src.get_plugin().mk_widen_fn(tgt, src, delta); - } - if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { - res = delta->get_plugin().mk_widen_fn(tgt, src, delta); - } - if(!res) { - res = mk_union_fn(tgt, src, delta); - } - return res; - } - - relation_mutator_fn * relation_manager::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols) { - return t.get_plugin().mk_filter_identical_fn(t, col_cnt, identical_cols); - } - - relation_mutator_fn * relation_manager::mk_filter_equal_fn(const relation_base & t, - const relation_element & value, unsigned col) { - - return t.get_plugin().mk_filter_equal_fn(t, value, col); - } - - relation_mutator_fn * relation_manager::mk_filter_interpreted_fn(const relation_base & t, app * condition) { - return t.get_plugin().mk_filter_interpreted_fn(t, condition); - } - - class relation_manager::default_relation_select_equal_and_project_fn : public relation_transformer_fn { - scoped_ptr m_filter; - scoped_ptr m_project; - public: - default_relation_select_equal_and_project_fn(relation_mutator_fn * filter, relation_transformer_fn * project) - : m_filter(filter), m_project(project) {} - - virtual relation_base * operator()(const relation_base & t1) { - TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); - scoped_rel aux = t1.clone(); - (*m_filter)(*aux); - relation_base * res = (*m_project)(*aux); - return res; - } - }; - - relation_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const relation_base & t, - const relation_element & value, unsigned col) { - relation_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); - if(!res) { - relation_mutator_fn * selector = mk_filter_equal_fn(t, value, col); - if(selector) { - relation_transformer_fn * projector = mk_project_fn(t, 1, &col); - if(projector) { - res = alloc(default_relation_select_equal_and_project_fn, selector, projector); - } - else { - dealloc(selector); - } - } - } - return res; - } - - - class relation_manager::default_relation_intersection_filter_fn : public relation_intersection_filter_fn { - scoped_ptr m_join_fun; - scoped_ptr m_union_fun; - public: - - default_relation_intersection_filter_fn(relation_join_fn * join_fun, relation_union_fn * union_fun) - : m_join_fun(join_fun), m_union_fun(union_fun) {} - - virtual void operator()(relation_base & tgt, const relation_base & intersected_obj) { - scoped_rel filtered_rel = (*m_join_fun)(tgt, intersected_obj); - TRACE("dl", - tgt.display(tout << "tgt:\n"); - intersected_obj.display(tout << "intersected:\n"); - filtered_rel->display(tout << "filtered:\n"); - ); - if(!m_union_fun) { - SASSERT(tgt.can_swap(*filtered_rel)); - tgt.swap(*filtered_rel); - } - tgt.reset(); - TRACE("dl", tgt.display(tout << "target reset:\n"); ); - (*m_union_fun)(tgt, *filtered_rel); - TRACE("dl", tgt.display(tout << "intersected target:\n"); ); - } - - }; - - relation_intersection_filter_fn * relation_manager::try_mk_default_filter_by_intersection_fn( - const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, - const unsigned * tgt_cols, const unsigned * src_cols) { - TRACE("dl_verbose", tout << tgt.get_plugin().get_name() << "\n";); - unsigned_vector join_removed_cols; - add_sequence(tgt.get_signature().size(), src.get_signature().size(), join_removed_cols); - scoped_rel join_fun = mk_join_project_fn(tgt, src, joined_col_cnt, tgt_cols, src_cols, - join_removed_cols.size(), join_removed_cols.c_ptr(), false); - if(!join_fun) { - return 0; - } - //we perform the join operation here to see what the result is - scoped_rel join_res = (*join_fun)(tgt, src); - if(tgt.can_swap(*join_res)) { - return alloc(default_relation_intersection_filter_fn, join_fun.release(), 0); - } - if(join_res->get_plugin().is_product_relation()) { - //we cannot have the product relation here, since it uses the intersection operation - //for unions and therefore we would get into an infinite recursion - return 0; - } - scoped_rel union_fun = mk_union_fn(tgt, *join_res); - if(!union_fun) { - return 0; - } - return alloc(default_relation_intersection_filter_fn, join_fun.release(), union_fun.release()); - } - - - relation_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const relation_base & t, - const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { - TRACE("dl_verbose", tout << t.get_plugin().get_name() << "\n";); - relation_intersection_filter_fn * res = t.get_plugin().mk_filter_by_intersection_fn(t, src, joined_col_cnt, - t_cols, src_cols); - if(!res && &t.get_plugin()!=&src.get_plugin()) { - res = src.get_plugin().mk_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); - } - if(!res) { - res = try_mk_default_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); - } - return res; - } - - relation_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const relation_base & tgt, - const relation_base & src) { - TRACE("dl_verbose", tout << tgt.get_plugin().get_name() << "\n";); - SASSERT(tgt.get_signature()==src.get_signature()); - unsigned sz = tgt.get_signature().size(); - unsigned_vector cols; - add_sequence(0, sz, cols); - return mk_filter_by_intersection_fn(tgt, src, cols, cols); - } - - - relation_intersection_filter_fn * relation_manager::mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols) { - TRACE("dl", tout << t.get_plugin().get_name() << "\n";); - relation_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, - t_cols, negated_cols); - if(!res && &t.get_plugin()!=&negated_obj.get_plugin()) { - res = negated_obj.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, - negated_cols); - } - return res; - } - - - - - - // ----------------------------------- - // - // table operations - // - // ----------------------------------- - - class relation_manager::default_table_join_fn : public convenient_table_join_fn { - unsigned m_col_cnt; - public: - default_table_join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2) - : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), m_col_cnt(col_cnt) {} - - virtual table_base * operator()(const table_base & t1, const table_base & t2) { - table_plugin * plugin = &t1.get_plugin(); - - const table_signature & res_sign = get_result_signature(); - if (!plugin->can_handle_signature(res_sign)) { - plugin = &t2.get_plugin(); - if (!plugin->can_handle_signature(res_sign)) { - plugin = &t1.get_manager().get_appropriate_plugin(res_sign); - } - } - SASSERT(plugin->can_handle_signature(res_sign)); - table_base * res = plugin->mk_empty(res_sign); - - unsigned t1cols = t1.get_signature().size(); - unsigned t2cols = t2.get_signature().size(); - unsigned t1first_func = t1.get_signature().first_functional(); - unsigned t2first_func = t2.get_signature().first_functional(); - - table_base::iterator els1it = t1.begin(); - table_base::iterator els1end = t1.end(); - table_base::iterator els2end = t2.end(); - - table_fact acc; - - for(; els1it!=els1end; ++els1it) { - const table_base::row_interface & row1 = *els1it; - - table_base::iterator els2it = t2.begin(); - for(; els2it!=els2end; ++els2it) { - const table_base::row_interface & row2 = *els2it; - - bool match=true; - for(unsigned i=0; iadd_fact(acc); - } - } - return res; - } - }; - - table_join_fn * relation_manager::mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - table_join_fn * res = t1.get_plugin().mk_join_fn(t1, t2, col_cnt, cols1, cols2); - if(!res && &t1.get_plugin()!=&t2.get_plugin()) { - res = t2.get_plugin().mk_join_fn(t1, t2, col_cnt, cols1, cols2); - } - if(!res) { - table_signature sig; - table_signature::from_join(t1.get_signature(), t2.get_signature(), - col_cnt, cols1, cols2, sig); - res = alloc(default_table_join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); - } - return res; - } - - - class relation_manager::auxiliary_table_transformer_fn { - table_fact m_row; - public: - virtual ~auxiliary_table_transformer_fn() {} - virtual const table_signature & get_result_signature() const = 0; - virtual void modify_fact(table_fact & f) const = 0; - - table_base * operator()(const table_base & t) { - table_plugin & plugin = t.get_plugin(); - const table_signature & res_sign = get_result_signature(); - SASSERT(plugin.can_handle_signature(res_sign)); - table_base * res = plugin.mk_empty(res_sign); - - table_base::iterator it = t.begin(); - table_base::iterator end = t.end(); - - for(; it!=end; ++it) { - it->get_fact(m_row); - modify_fact(m_row); - res->add_fact(m_row); - } - return res; - } - }; - - class relation_manager::default_table_project_fn - : public convenient_table_project_fn, auxiliary_table_transformer_fn { - public: - default_table_project_fn(const table_signature & orig_sig, unsigned removed_col_cnt, - const unsigned * removed_cols) - : convenient_table_project_fn(orig_sig, removed_col_cnt, removed_cols) { - SASSERT(removed_col_cnt>0); - } - - virtual const table_signature & get_result_signature() const { - return convenient_table_project_fn::get_result_signature(); - } - - virtual void modify_fact(table_fact & f) const { - project_out_vector_columns(f, m_removed_cols); - } - - virtual table_base * operator()(const table_base & t) { - return auxiliary_table_transformer_fn::operator()(t); - } - }; - - class relation_manager::null_signature_table_project_fn : public table_transformer_fn { - const table_signature m_empty_sig; - public: - null_signature_table_project_fn() : m_empty_sig() {} - virtual table_base * operator()(const table_base & t) { - relation_manager & m = t.get_plugin().get_manager(); - table_base * res = m.mk_empty_table(m_empty_sig); - if(!t.empty()) { - table_fact el; - res->add_fact(el); - } - return res; - } - }; - - - - table_transformer_fn * relation_manager::mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols) { - table_transformer_fn * res = t.get_plugin().mk_project_fn(t, col_cnt, removed_cols); - if(!res && col_cnt==t.get_signature().size()) { - //all columns are projected out - res = alloc(null_signature_table_project_fn); - } - if(!res) { - res = alloc(default_table_project_fn, t.get_signature(), col_cnt, removed_cols); - } - return res; - } - - - class relation_manager::default_table_join_project_fn : public convenient_table_join_project_fn { - scoped_ptr m_join; - scoped_ptr m_project; - - unsigned_vector m_removed_cols; - public: - default_table_join_project_fn(join_fn * join, const table_base & t1, const table_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols) - : convenient_table_join_project_fn(t1.get_signature(), t2.get_signature(), joined_col_cnt, cols1, - cols2, removed_col_cnt, removed_cols), - m_join(join), - m_removed_cols(removed_col_cnt, removed_cols) {} - - class unreachable_reducer : public table_row_pair_reduce_fn { - virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { - //we do project_with_reduce only if we are sure there will be no reductions - //(see code of the table_signature::from_join_project function) - UNREACHABLE(); - } - }; - - virtual table_base * operator()(const table_base & t1, const table_base & t2) { - table_base * aux = (*m_join)(t1, t2); - if(m_project==0) { - relation_manager & rmgr = aux->get_plugin().get_manager(); - if(get_result_signature().functional_columns()!=0) { - //to preserve functional columns we need to do the project_with_reduction - unreachable_reducer * reducer = alloc(unreachable_reducer); - m_project = rmgr.mk_project_with_reduce_fn(*aux, m_removed_cols.size(), m_removed_cols.c_ptr(), reducer); - } - else { - m_project = rmgr.mk_project_fn(*aux, m_removed_cols); - } - if(!m_project) { - throw default_exception("projection for table does not exist"); - } - } - table_base * res = (*m_project)(*aux); - aux->deallocate(); - return res; - } - }; - - table_join_fn * relation_manager::mk_join_project_fn(const table_base & t1, const table_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols) { - table_join_fn * res = t1.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, - removed_col_cnt, removed_cols); - if(!res && &t1.get_plugin()!=&t2.get_plugin()) { - res = t2.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, - removed_cols); - } - if(!res) { - table_join_fn * join = mk_join_fn(t1, t2, joined_col_cnt, cols1, cols2); - if(join) { - res = alloc(default_table_join_project_fn, join, t1, t2, joined_col_cnt, cols1, cols2, - removed_col_cnt, removed_cols); - } - } - return res; - - } - - class relation_manager::default_table_rename_fn - : public convenient_table_rename_fn, auxiliary_table_transformer_fn { - public: - default_table_rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, - const unsigned * permutation_cycle) - : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { - SASSERT(permutation_cycle_len>=2); - } - - virtual const table_signature & get_result_signature() const { - return convenient_table_rename_fn::get_result_signature(); - } - - virtual void modify_fact(table_fact & f) const { - permutate_by_cycle(f, m_cycle); - } - - virtual table_base * operator()(const table_base & t) { - return auxiliary_table_transformer_fn::operator()(t); - } - - }; - - table_transformer_fn * relation_manager::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle) { - table_transformer_fn * res = t.get_plugin().mk_rename_fn(t, permutation_cycle_len, permutation_cycle); - if(!res) { - res = alloc(default_table_rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); - } - return res; - } - - table_transformer_fn * relation_manager::mk_permutation_rename_fn(const table_base & t, - const unsigned * permutation) { - table_transformer_fn * res = t.get_plugin().mk_permutation_rename_fn(t, permutation); - if(!res) { - res = alloc(default_table_permutation_rename_fn, t, permutation); - } - return res; - } - - - class relation_manager::default_table_union_fn : public table_union_fn { - table_fact m_row; - public: - virtual void operator()(table_base & tgt, const table_base & src, table_base * delta) { - table_base::iterator it = src.begin(); - table_base::iterator iend = src.end(); - - for(; it!=iend; ++it) { - it->get_fact(m_row); - - if(delta) { - if(!tgt.contains_fact(m_row)) { - tgt.add_new_fact(m_row); - delta->add_fact(m_row); - } - } - else { - //if there's no delta, we don't need to know whether we are actually adding a new fact - tgt.add_fact(m_row); - } - } - } - }; - - table_union_fn * relation_manager::mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta) { - table_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); - if(!res && &tgt.get_plugin()!=&src.get_plugin()) { - res = src.get_plugin().mk_union_fn(tgt, src, delta); - } - if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { - res = delta->get_plugin().mk_union_fn(tgt, src, delta); - } - if(!res) { - res = alloc(default_table_union_fn); - } - return res; - } - - table_union_fn * relation_manager::mk_widen_fn(const table_base & tgt, const table_base & src, - const table_base * delta) { - table_union_fn * res = tgt.get_plugin().mk_widen_fn(tgt, src, delta); - if(!res && &tgt.get_plugin()!=&src.get_plugin()) { - res = src.get_plugin().mk_widen_fn(tgt, src, delta); - } - if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { - res = delta->get_plugin().mk_widen_fn(tgt, src, delta); - } - if(!res) { - res = mk_union_fn(tgt, src, delta); - } - return res; - } - - - /** - An auixiliary class for functors that perform filtering. It performs the table traversal - and only asks for each individual row whether it should be removed. - - When using this class in multiple inheritance, this class should not be inherited publicly - and should be mentioned as last. This should ensure that deteletion of the object will - go well when initiated from a pointer to the first ancestor. - */ - class relation_manager::auxiliary_table_filter_fn { - table_fact m_row; - svector m_to_remove; - public: - virtual ~auxiliary_table_filter_fn() {} - virtual bool should_remove(const table_fact & f) const = 0; - - void operator()(table_base & r) { - m_to_remove.reset(); - unsigned sz = 0; - table_base::iterator it = r.begin(); - table_base::iterator iend = r.end(); - for(; it!=iend; ++it) { - it->get_fact(m_row); - if(should_remove(m_row)) { - m_to_remove.append(m_row.size(), m_row.c_ptr()); - ++sz; - } - } - r.remove_facts(sz, m_to_remove.c_ptr()); - } - }; - - class relation_manager::default_table_filter_identical_fn : public table_mutator_fn, auxiliary_table_filter_fn { - const unsigned m_col_cnt; - const unsigned_vector m_identical_cols; - public: - default_table_filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) - : m_col_cnt(col_cnt), - m_identical_cols(col_cnt, identical_cols) { - SASSERT(col_cnt>=2); - } - - virtual bool should_remove(const table_fact & f) const { - table_element val=f[m_identical_cols[0]]; - for(unsigned i=1; iget_arg(0); - if (!m.is_eq(condition)) { - return 0; - } - expr* x = to_app(condition)->get_arg(0); - expr* y = to_app(condition)->get_arg(1); - if (!is_var(x)) { - std::swap(x, y); - } - if (!is_var(x)) { - return 0; - } - dl_decl_util decl_util(m); - uint64 value = 0; - if (!decl_util.is_numeral_ext(y, value)) { - return 0; - } - return alloc(default_table_filter_not_equal_fn, ctx, to_var(x)->get_idx(), value); - } - }; - - - - class relation_manager::default_table_filter_interpreted_fn - : public table_mutator_fn, auxiliary_table_filter_fn { - ast_manager & m_ast_manager; - var_subst & m_vs; - dl_decl_util & m_decl_util; - th_rewriter & m_simp; - app_ref m_condition; - ptr_vector m_var_sorts; - expr_ref_vector m_args; - public: - default_table_filter_interpreted_fn(context & ctx, unsigned col_cnt, app* condition) - : m_ast_manager(ctx.get_manager()), - m_vs(ctx.get_var_subst()), - m_decl_util(ctx.get_decl_util()), - m_simp(ctx.get_rewriter()), - m_condition(condition, ctx.get_manager()), - m_args(ctx.get_manager()) { - m_var_sorts.resize(col_cnt); - get_free_vars(m_condition, m_var_sorts); - } - - virtual bool should_remove(const table_fact & f) const { - expr_ref_vector& args = const_cast(m_args); - - args.reset(); - //arguments need to be in reverse order for the substitution - unsigned col_cnt = f.size(); - for(int i=col_cnt-1;i>=0;i--) { - sort * var_sort = m_var_sorts[i]; - if(!var_sort) { - args.push_back(0); - continue; //this variable does not occur in the condition; - } - - table_element el = f[i]; - args.push_back(m_decl_util.mk_numeral(el, var_sort)); - } - - expr_ref ground(m_ast_manager); - m_vs(m_condition.get(), args.size(), args.c_ptr(), ground); - m_simp(ground); - - return m_ast_manager.is_false(ground); - } - - virtual void operator()(table_base & t) { - auxiliary_table_filter_fn::operator()(t); - } - }; - - table_mutator_fn * relation_manager::mk_filter_interpreted_fn(const table_base & t, app * condition) { - context & ctx = get_context(); - table_mutator_fn * res = t.get_plugin().mk_filter_interpreted_fn(t, condition); - if (!res) { - res = default_table_filter_not_equal_fn::mk(ctx, condition); - } - if(!res) { - res = alloc(default_table_filter_interpreted_fn, ctx, t.get_signature().size(), condition); - } - return res; - } - - - class relation_manager::default_table_filter_interpreted_and_project_fn - : public table_transformer_fn { - scoped_ptr m_filter; - scoped_ptr m_project; - app_ref m_condition; - unsigned_vector m_removed_cols; - public: - default_table_filter_interpreted_and_project_fn(context & ctx, table_mutator_fn * filter, - app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) - : m_filter(filter), m_condition(condition, ctx.get_manager()), - m_removed_cols(removed_col_cnt, removed_cols) {} - - virtual table_base* operator()(const table_base & tb) { - table_base *t2 = tb.clone(); - (*m_filter)(*t2); - if (!m_project) { - relation_manager & rmgr = t2->get_plugin().get_manager(); - m_project = rmgr.mk_project_fn(*t2, m_removed_cols.size(), m_removed_cols.c_ptr()); - if (!m_project) { - throw default_exception("projection does not exist"); - } - } - return (*m_project)(*t2); - } - }; - - table_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn(const table_base & t, - app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { - table_transformer_fn * res = t.get_plugin().mk_filter_interpreted_and_project_fn(t, condition, removed_col_cnt, removed_cols); - if (res) - return res; - - table_mutator_fn * filter = mk_filter_interpreted_fn(t, condition); - SASSERT(filter); - res = alloc(default_table_filter_interpreted_and_project_fn, get_context(), filter, condition, removed_col_cnt, removed_cols); - return res; - } - - - table_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const table_base & t, - const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { - table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, - t_cols, src_cols); - if(!res && &t.get_plugin()!=&src.get_plugin()) { - res = src.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, t_cols, src_cols); - } - return res; - } - - - - class relation_manager::default_table_negation_filter_fn : public convenient_table_negation_filter_fn, - auxiliary_table_filter_fn { - const table_base * m_negated_table; - mutable table_fact m_aux_fact; - public: - default_table_negation_filter_fn(const table_base & tgt, const table_base & neg_t, - unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) - : convenient_table_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), - m_negated_table(0) { - m_aux_fact.resize(neg_t.get_signature().size()); - } - - virtual bool should_remove(const table_fact & f) const { - if(!m_all_neg_bound || m_overlap) { - table_base::iterator nit = m_negated_table->begin(); - table_base::iterator nend = m_negated_table->end(); - for(; nit!=nend; ++nit) { - const table_base::row_interface & nrow = *nit; - if(bindings_match(nrow, f)) { - return true; - } - } - return false; - } - else { - make_neg_bindings(m_aux_fact, f); - return m_negated_table->contains_fact(m_aux_fact); - } - } - - virtual void operator()(table_base & tgt, const table_base & negated_table) { - SASSERT(m_negated_table==0); - flet flet_neg_table(m_negated_table, &negated_table); - auxiliary_table_filter_fn::operator()(tgt); - } - - }; - - table_intersection_filter_fn * relation_manager::mk_filter_by_negation_fn(const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols) { - table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, - t_cols, negated_cols); - if(!res && &t.get_plugin()!=&negated_obj.get_plugin()) { - res = negated_obj.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, - negated_cols); - } - if(!res) { - res = alloc(default_table_negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); - } - return res; - } - - - class relation_manager::default_table_select_equal_and_project_fn : public table_transformer_fn { - scoped_ptr m_filter; - scoped_ptr m_project; - public: - default_table_select_equal_and_project_fn(table_mutator_fn * filter, table_transformer_fn * project) - : m_filter(filter), m_project(project) {} - - virtual table_base * operator()(const table_base & t1) { - TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); - scoped_rel aux = t1.clone(); - (*m_filter)(*aux); - table_base * res = (*m_project)(*aux); - return res; - } - }; - - table_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const table_base & t, - const table_element & value, unsigned col) { - table_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); - if(!res) { - table_mutator_fn * selector = mk_filter_equal_fn(t, value, col); - SASSERT(selector); - table_transformer_fn * projector = mk_project_fn(t, 1, &col); - SASSERT(projector); - res = alloc(default_table_select_equal_and_project_fn, selector, projector); - } - return res; - } - - - class relation_manager::default_table_map_fn : public table_mutator_fn { - scoped_ptr m_mapper; - unsigned m_first_functional; - scoped_rel m_aux_table; - scoped_ptr m_union_fn; - table_fact m_curr_fact; - public: - default_table_map_fn(const table_base & t, table_row_mutator_fn * mapper) - : m_mapper(mapper), m_first_functional(t.get_signature().first_functional()) { - SASSERT(t.get_signature().functional_columns()>0); - table_plugin & plugin = t.get_plugin(); - m_aux_table = plugin.mk_empty(t.get_signature()); - m_union_fn = plugin.mk_union_fn(t, *m_aux_table, static_cast(0)); - } - - virtual void operator()(table_base & t) { - SASSERT(t.get_signature()==m_aux_table->get_signature()); - if(!m_aux_table->empty()) { - m_aux_table->reset(); - } - - - table_base::iterator it = t.begin(); - table_base::iterator iend = t.end(); - for(; it!=iend; ++it) { - it->get_fact(m_curr_fact); - if((*m_mapper)(m_curr_fact.c_ptr()+m_first_functional)) { - m_aux_table->add_fact(m_curr_fact); - } - } - - t.reset(); - (*m_union_fn)(t, *m_aux_table, static_cast(0)); - } - }; - - table_mutator_fn * relation_manager::mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { - SASSERT(t.get_signature().functional_columns()>0); - table_mutator_fn * res = t.get_plugin().mk_map_fn(t, mapper); - if(!res) { - res = alloc(default_table_map_fn, t, mapper); - } - return res; - } - - - class relation_manager::default_table_project_with_reduce_fn : public convenient_table_transformer_fn { - unsigned_vector m_removed_cols; - const unsigned m_inp_col_cnt; - const unsigned m_removed_col_cnt; - const unsigned m_result_col_cnt; - scoped_ptr m_reducer; - unsigned m_res_first_functional; - table_fact m_row; - table_fact m_former_row; - public: - default_table_project_with_reduce_fn(const table_signature & orig_sig, unsigned removed_col_cnt, - const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) - : m_removed_cols(removed_col_cnt, removed_cols), - m_inp_col_cnt(orig_sig.size()), - m_removed_col_cnt(removed_col_cnt), - m_result_col_cnt(orig_sig.size()-removed_col_cnt), - m_reducer(reducer) { - SASSERT(removed_col_cnt>0); - table_signature::from_project_with_reduce(orig_sig, removed_col_cnt, removed_cols, - get_result_signature()); - m_res_first_functional = get_result_signature().first_functional(); - m_row.resize(get_result_signature().size()); - m_former_row.resize(get_result_signature().size()); - } - - virtual void modify_fact(table_fact & f) const { - unsigned ofs=1; - unsigned r_i=1; - for(unsigned i=m_removed_cols[0]+1; isuggest_fact(m_former_row)) { - (*m_reducer)(m_former_row.c_ptr()+m_res_first_functional, m_row.c_ptr()+m_res_first_functional); - res->ensure_fact(m_former_row); - } - } - return res; - } - }; - - table_transformer_fn * relation_manager::mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { - SASSERT(t.get_signature().functional_columns()>0); - table_transformer_fn * res = t.get_plugin().mk_project_with_reduce_fn(t, col_cnt, removed_cols, reducer); - if(!res) { - res = alloc(default_table_project_with_reduce_fn, t.get_signature(), col_cnt, removed_cols, reducer); - } - return res; - } - -}; - diff --git a/src/muz/dl_relation_manager.h b/src/muz/dl_relation_manager.h deleted file mode 100644 index 9f12b4bb6..000000000 --- a/src/muz/dl_relation_manager.h +++ /dev/null @@ -1,688 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_relation_manager.h - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-24. - -Revision History: - ---*/ -#ifndef _DL_RELATION_MANAGER_H_ -#define _DL_RELATION_MANAGER_H_ - - -#include"map.h" -#include"vector.h" -#include"dl_base.h" - -namespace datalog { - - class context; - class dl_decl_util; - class table_relation; - class table_relation_plugin; - class finite_product_relation; - class finite_product_relation_plugin; - class sieve_relation; - class sieve_relation_plugin; - class rule_set; - - - class relation_manager { - class empty_signature_relation_join_fn; - class default_relation_join_project_fn; - class default_relation_select_equal_and_project_fn; - class default_relation_intersection_filter_fn; - class default_relation_filter_interpreted_and_project_fn; - - class auxiliary_table_transformer_fn; - class auxiliary_table_filter_fn; - - class default_table_join_fn; - class default_table_project_fn; - class null_signature_table_project_fn; - class default_table_join_project_fn; - class default_table_rename_fn; - class default_table_union_fn; - class default_table_filter_equal_fn; - class default_table_filter_identical_fn; - class default_table_filter_interpreted_fn; - class default_table_filter_interpreted_and_project_fn; - class default_table_negation_filter_fn; - class default_table_filter_not_equal_fn; - class default_table_select_equal_and_project_fn; - class default_table_map_fn; - class default_table_project_with_reduce_fn; - - typedef obj_map decl2kind_map; - - typedef u_map kind2plugin_map; - - typedef map, - ptr_eq > tp2trp_map; - typedef map, - ptr_eq > rp2fprp_map; - - typedef map, ptr_eq > relation_map; - typedef ptr_vector table_plugin_vector; - typedef ptr_vector relation_plugin_vector; - - context & m_context; - table_plugin_vector m_table_plugins; - relation_plugin_vector m_relation_plugins; - //table_relation_plugins corresponding to table_plugins - tp2trp_map m_table_relation_plugins; - rp2fprp_map m_finite_product_relation_plugins; - - kind2plugin_map m_kind2plugin; - - table_plugin * m_favourite_table_plugin; - - relation_plugin * m_favourite_relation_plugin; - - relation_map m_relations; - - decl_set m_saturated_rels; - - family_id m_next_table_fid; - family_id m_next_relation_fid; - - /** - Map specifying what kind of relation should be used to represent particular predicate. - */ - decl2kind_map m_pred_kinds; - - void register_relation_plugin_impl(relation_plugin * plugin); - - relation_manager(const relation_manager &); //private and undefined copy constructor - relation_manager & operator=(const relation_manager &); //private and undefined operator= - public: - relation_manager(context & ctx) : - m_context(ctx), - m_favourite_table_plugin(0), - m_favourite_relation_plugin(0), - m_next_table_fid(0), - m_next_relation_fid(0) {} - - virtual ~relation_manager(); - - void reset(); - void reset_relations(); - - context & get_context() const { return m_context; } - dl_decl_util & get_decl_util() const; - - family_id get_next_table_fid() { return m_next_table_fid++; } - family_id get_next_relation_fid(relation_plugin & claimer); - - - /** - Set what kind of relation is going to be used to represent the predicate \c pred. - - This function can be called only before the relation object for \c pred is created - (i.e. before the \c get_relation function is called with \c pred as argument for the - first time). - */ - void set_predicate_kind(func_decl * pred, family_id kind); - /** - Return the relation kind that was requested to represent the predicate \c pred by - \c set_predicate_kind. If there was no such request, return \c null_family_id. - */ - family_id get_requested_predicate_kind(func_decl * pred); - relation_base & get_relation(func_decl * pred); - relation_base * try_get_relation(func_decl * pred) const; - /** - \brief Store the relation \c rel under the predicate \c pred. The \c relation_manager - takes over the relation object. - */ - void store_relation(func_decl * pred, relation_base * rel); - - bool is_saturated(func_decl * pred) const { return m_saturated_rels.contains(pred); } - void mark_saturated(func_decl * pred) { m_saturated_rels.insert(pred); } - void reset_saturated_marks() { - if(!m_saturated_rels.empty()) { - m_saturated_rels.reset(); - } - } - - void collect_non_empty_predicates(decl_set & res) const; - void restrict_predicates(const decl_set & preds); - - void register_plugin(table_plugin * plugin); - /** - table_relation_plugins should not be passed to this function since they are - created automatically when registering a table plugin. - */ - void register_plugin(relation_plugin * plugin) { - SASSERT(!plugin->from_table()); - register_relation_plugin_impl(plugin); - } - - table_plugin & get_appropriate_plugin(const table_signature & t); - relation_plugin & get_appropriate_plugin(const relation_signature & t); - table_plugin * try_get_appropriate_plugin(const table_signature & t); - relation_plugin * try_get_appropriate_plugin(const relation_signature & t); - - table_plugin * get_table_plugin(symbol const& s); - relation_plugin * get_relation_plugin(symbol const& s); - relation_plugin & get_relation_plugin(family_id kind); - table_relation_plugin & get_table_relation_plugin(table_plugin & tp); - bool try_get_finite_product_relation_plugin(const relation_plugin & inner, - finite_product_relation_plugin * & res); - - table_base * mk_empty_table(const table_signature & s); - relation_base * mk_implicit_relation(const relation_signature & s, app * expr); - - relation_base * mk_empty_relation(const relation_signature & s, family_id kind); - relation_base * mk_empty_relation(const relation_signature & s, func_decl* pred); - - relation_base * mk_full_relation(const relation_signature & s, func_decl* pred, family_id kind); - relation_base * mk_full_relation(const relation_signature & s, func_decl* pred); - - relation_base * mk_table_relation(const relation_signature & s, table_base * table); - bool mk_empty_table_relation(const relation_signature & s, relation_base * & result); - - bool is_non_explanation(relation_signature const& s) const; - - - /** - \brief Convert relation value to table one. - - This function can be called only for the relation sorts that have a table counterpart. - */ - void relation_to_table(const relation_sort & sort, const relation_element & from, table_element & to); - - void table_to_relation(const relation_sort & sort, const table_element & from, relation_element & to); - void table_to_relation(const relation_sort & sort, const table_element & from, - const relation_fact::el_proxy & to); - void table_to_relation(const relation_sort & sort, const table_element & from, - relation_element_ref & to); - - bool relation_sort_to_table(const relation_sort & from, table_sort & to); - void from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result); - void from_predicate(func_decl * pred, relation_signature & result); - - /** - \brief Convert relation signature to table signature and return true if successful. If false - is returned, the value of \c to is undefined. - */ - bool relation_signature_to_table(const relation_signature & from, table_signature & to); - - void relation_fact_to_table(const relation_signature & s, const relation_fact & from, - table_fact & to); - void table_fact_to_relation(const relation_signature & s, const table_fact & from, - relation_fact & to); - - - void set_cancel(bool f); - - - // ----------------------------------- - // - // relation operations - // - // ----------------------------------- - - //TODO: If multiple operation implementations are available, we may want to do something to - //select the best one here. - - /** - If \c allow_product_relation is true, we will create a join that builds a product relation, - if there is no other way to do the join. If \c allow_product_relation is false, we will return - zero in that case. - */ - relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation=true); - - relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - const unsigned_vector & cols1, const unsigned_vector & cols2, bool allow_product_relation=true) { - SASSERT(cols1.size()==cols2.size()); - return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), allow_product_relation); - } - - /** - \brief Return functor that transforms a table into one that lacks columns listed in - \c removed_cols array. - - The \c removed_cols cotains columns of table \c t in strictly ascending order. - */ - relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - - relation_transformer_fn * mk_project_fn(const relation_base & t, const unsigned_vector & removed_cols) { - return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr()); - } - - /** - \brief Return an operation that is a composition of a join an a project operation. - */ - relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join=true); - - relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, - const unsigned_vector & cols1, const unsigned_vector & cols2, - const unsigned_vector & removed_cols, bool allow_product_relation_join=true) { - return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(), - removed_cols.c_ptr(), allow_product_relation_join); - } - - relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - relation_transformer_fn * mk_rename_fn(const relation_base & t, const unsigned_vector & permutation_cycle) { - return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr()); - } - - /** - Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array - of column number. - */ - relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, - const unsigned * permutation); - relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, - const unsigned_vector permutation) { - SASSERT(t.get_signature().size()==permutation.size()); - return mk_permutation_rename_fn(t, permutation.c_ptr()); - } - - - /** - The post-condition for an ideal union operation is be - - Union(tgt, src, delta): - tgt_1==tgt_0 \union src - delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 ) - - A required post-condition is - - Union(tgt, src, delta): - tgt_1==tgt_0 \union src - tgt_1==tgt_0 => delta_1==delta_0 - delta_0 \subset delta_1 - delta_1 \subset (delta_0 \union tgt_1) - ( tgt_1 \setminus tgt_0 ) \subset delta_1 - - So that a sufficient implementation is - - Union(tgt, src, delta) { - oldTgt:=tgt.clone(); - tgt:=tgt \union src - if(tgt!=oldTgt) { - delta:=delta \union src //also ?delta \union tgt? would work - } - } - - If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient - post-condition is - Union(tgt, src, delta): - tgt_1==tgt_0 \union src - (tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty - */ - relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - - relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src) { - return mk_union_fn(tgt, src, static_cast(0)); - } - - /** - Similar to union, but this one should be used inside loops to allow for abstract - domain convergence. - */ - relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - - relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, const unsigned_vector identical_cols) { - return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); - } - - relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - - relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - - relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, - unsigned removed_col_cnt, const unsigned * removed_cols); - - /** - \brief Operations that returns all rows of \c t for which is column \c col equal to \c value - with the column \c col removed. - - This operation can often be efficiently implemented and is useful for evaluating rules - of the form - - F(x):-P("c",x). - */ - relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, - const relation_element & value, unsigned col); - - - relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, - const relation_base & src, unsigned joined_col_cnt, - const unsigned * tgt_cols, const unsigned * src_cols); - relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, - const relation_base & src, const unsigned_vector & tgt_cols, const unsigned_vector & src_cols) { - SASSERT(tgt_cols.size()==src_cols.size()); - return mk_filter_by_intersection_fn(tgt, src, tgt_cols.size(), tgt_cols.c_ptr(), src_cols.c_ptr()); - } - relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, - const relation_base & src); - - /** - The filter_by_negation postcondition: - filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, - corresponding columns in neg: d1,...,dN): - tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } - */ - relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, const unsigned_vector & t_cols, - const unsigned_vector & negated_cols) { - SASSERT(t_cols.size()==negated_cols.size()); - return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); - } - - - // ----------------------------------- - // - // table operations - // - // ----------------------------------- - - - table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - - table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - const unsigned_vector & cols1, const unsigned_vector & cols2) { - SASSERT(cols1.size()==cols2.size()); - return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr()); - } - - /** - \brief Return functor that transforms a table into one that lacks columns listed in - \c removed_cols array. - - The \c removed_cols cotains columns of table \c t in strictly ascending order. - - If a project operation removes a non-functional column, all functional columns become - non-functional (so that none of the values in functional columns are lost) - */ - table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols); - - table_transformer_fn * mk_project_fn(const table_base & t, const unsigned_vector & removed_cols) { - return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr()); - } - - /** - \brief Return an operation that is a composition of a join an a project operation. - - This operation is equivalent to the two operations performed separately, unless functional - columns are involved. - - The ordinary project would make all of the functional columns into non-functional if any - non-functional column was removed. In function, however, we group columns into equivalence - classes (according to the equalities in \c cols1 and \c cols2) and make everything non-functional - only if some equivalence class of non-functional columns would have no non-functional columns - remain after the removal. - - This behavior is implemented in the \c table_signature::from_join_project function. - */ - table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols); - - table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, - const unsigned_vector & cols1, const unsigned_vector & cols2, - const unsigned_vector & removed_cols) { - return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(), - removed_cols.c_ptr()); - } - - table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - table_transformer_fn * mk_rename_fn(const table_base & t, const unsigned_vector & permutation_cycle) { - return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr()); - } - - /** - Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array - of column number. - */ - table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned * permutation); - table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned_vector permutation) { - SASSERT(t.get_signature().size()==permutation.size()); - return mk_permutation_rename_fn(t, permutation.c_ptr()); - } - - - /** - The post-condition for an ideal union operation is be - - Union(tgt, src, delta): - tgt_1==tgt_0 \union src - delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 ) - - A required post-condition is - - Union(tgt, src, delta): - tgt_1==tgt_0 \union src - tgt_1==tgt_0 => delta_1==delta_0 - delta_0 \subset delta_1 - delta_1 \subset (delta_0 \union tgt_1) - ( tgt_1 \setminus tgt_0 ) \subset delta_1 - - So that a sufficient implementation is - - Union(tgt, src, delta) { - oldTgt:=tgt.clone(); - tgt:=tgt \union src - if(tgt!=oldTgt) { - delta:=delta \union src //also ?delta \union tgt? would work - } - } - - If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient - post-condition is - Union(tgt, src, delta): - tgt_1==tgt_0 \union src - (tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty - */ - table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta); - - table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src) { - return mk_union_fn(tgt, src, static_cast(0)); - } - - /** - Similar to union, but this one should be used inside loops to allow for abstract - domain convergence. - */ - table_union_fn * mk_widen_fn(const table_base & tgt, const table_base & src, - const table_base * delta); - - table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, - const unsigned * identical_cols); - table_mutator_fn * mk_filter_identical_fn(const table_base & t, const unsigned_vector identical_cols) { - return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); - } - - table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, - unsigned col); - - table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); - - table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, - unsigned removed_col_cnt, const unsigned * removed_cols); - - /** - \brief Operations that returns all rows of \c t for which is column \c col equal to \c value - with the column \c col removed. - - This operation can often be efficiently implemented and is useful for evaluating rules - of the form - - F(x):-P("c",x). - */ - table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, - const table_element & value, unsigned col); - - table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t, - const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); - table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t, - const table_base & src, const unsigned_vector & t_cols, const unsigned_vector & src_cols) { - SASSERT(t_cols.size()==src_cols.size()); - return mk_filter_by_intersection_fn(t, src, t_cols.size(), t_cols.c_ptr(), src_cols.c_ptr()); - } - - /** - The filter_by_negation postcondition: - filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, - corresponding columns in neg: d1,...,dN): - tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } - */ - table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, - unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); - table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, - const unsigned_vector & t_cols, const unsigned_vector & negated_cols) { - SASSERT(t_cols.size()==negated_cols.size()); - return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); - } - - /** - \c t must contain at least one functional column. - - Created object takes ownership of the \c mapper object. - */ - virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper); - - /** - \c t must contain at least one functional column. - - Created object takes ownership of the \c mapper object. - */ - virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols, table_row_pair_reduce_fn * reducer); - - - - - // ----------------------------------- - // - // output functions - // - // ----------------------------------- - - - std::string to_nice_string(const relation_element & el) const; - /** - This one may give a nicer representation of \c el than the - \c to_nice_string(const relation_element & el) function, by unsing the information about the sort - of the element. - */ - std::string to_nice_string(const relation_sort & s, const relation_element & el) const; - std::string to_nice_string(const relation_sort & s) const; - std::string to_nice_string(const relation_signature & s) const; - - void display(std::ostream & out) const; - void display_relation_sizes(std::ostream & out) const; - void display_output_tables(rule_set const& rules, std::ostream & out) const; - - private: - relation_intersection_filter_fn * try_mk_default_filter_by_intersection_fn(const relation_base & t, - const relation_base & src, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * src_cols); - - }; - - /** - This is a helper class for relation_plugins whose relations can be of various kinds. - */ - template > - class rel_spec_store { - typedef relation_signature::hash r_hash; - typedef relation_signature::eq r_eq; - - typedef map family_id_idx_store; - typedef map sig2store; - - typedef u_map family_id2spec; - typedef map sig2spec_store; - - relation_plugin & m_parent; - svector m_allocated_kinds; - sig2store m_kind_assignment; - sig2spec_store m_kind_specs; - - - relation_manager & get_manager() { return m_parent.get_manager(); } - - void add_new_kind() { - add_available_kind(get_manager().get_next_relation_fid(m_parent)); - } - - public: - rel_spec_store(relation_plugin & parent) : m_parent(parent) {} - - ~rel_spec_store() { - reset_dealloc_values(m_kind_assignment); - reset_dealloc_values(m_kind_specs); - } - - void add_available_kind(family_id k) { - m_allocated_kinds.push_back(k); - } - - bool contains_signature(relation_signature const& sig) const { - return m_kind_assignment.contains(sig); - } - - family_id get_relation_kind(const relation_signature & sig, const Spec & spec) { - typename sig2store::entry * e = m_kind_assignment.find_core(sig); - if(!e) { - e = m_kind_assignment.insert_if_not_there2(sig, alloc(family_id_idx_store)); - m_kind_specs.insert(sig, alloc(family_id2spec)); - } - family_id_idx_store & ids = *e->get_data().m_value; - - unsigned res_idx; - if(!ids.find(spec, res_idx)) { - res_idx = ids.size(); - if(res_idx==m_allocated_kinds.size()) { - add_new_kind(); - } - SASSERT(res_idxinsert(m_allocated_kinds[res_idx], spec); - } - return m_allocated_kinds[res_idx]; - } - - void get_relation_spec(const relation_signature & sig, family_id kind, Spec & spec) { - family_id2spec * idspecs = m_kind_specs.find(sig); - spec = idspecs->find(kind); - } - - }; - -}; - -#endif /* _DL_RELATION_MANAGER_H_ */ - diff --git a/src/muz/dl_sieve_relation.cpp b/src/muz/dl_sieve_relation.cpp deleted file mode 100644 index 9f9419089..000000000 --- a/src/muz/dl_sieve_relation.cpp +++ /dev/null @@ -1,666 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_mk_explanations.cpp - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-11-08. - -Revision History: - ---*/ - -#include -#include"ast_pp.h" -#include"dl_sieve_relation.h" - -namespace datalog { - - // ----------------------------------- - // - // sieve_relation - // - // ----------------------------------- - - sieve_relation::sieve_relation(sieve_relation_plugin & p, const relation_signature & s, - const bool * inner_columns, relation_base * inner) - : relation_base(p, s), m_inner_cols(s.size(), inner_columns), m_inner(inner) { - unsigned n = s.size(); - for(unsigned i=0; i 0; ) { - --i; - unsigned idx = m_inner2sig[i]; - s.push_back(m.mk_var(idx, sig[i])); - } - get_inner().to_formula(tmp); - get_plugin().get_context().get_var_subst()(tmp, sz, s.c_ptr(), fml); - } - - - void sieve_relation::display(std::ostream & out) const { - out << "Sieve relation "; - print_container(m_inner_cols, out); - out <<"\n"; - get_inner().display(out); - } - - - // ----------------------------------- - // - // sieve_relation_plugin - // - // ----------------------------------- - - sieve_relation_plugin & sieve_relation_plugin::get_plugin(relation_manager & rmgr) { - sieve_relation_plugin * res = static_cast(rmgr.get_relation_plugin(get_name())); - if(!res) { - res = alloc(sieve_relation_plugin, rmgr); - rmgr.register_plugin(res); - } - return *res; - } - - sieve_relation& sieve_relation_plugin::get(relation_base& r) { - return dynamic_cast(r); - } - - sieve_relation const & sieve_relation_plugin::get(relation_base const& r) { - return dynamic_cast(r); - } - - sieve_relation* sieve_relation_plugin::get(relation_base* r) { - return dynamic_cast(r); - } - - sieve_relation const* sieve_relation_plugin::get(relation_base const* r) { - return dynamic_cast(r); - } - - sieve_relation_plugin::sieve_relation_plugin(relation_manager & manager) - : relation_plugin(get_name(), manager, ST_SIEVE_RELATION), - m_spec_store(*this) {} - - void sieve_relation_plugin::initialize(family_id fid) { - relation_plugin::initialize(fid); - m_spec_store.add_available_kind(get_kind()); - } - - family_id sieve_relation_plugin::get_relation_kind(const relation_signature & sig, - const bool * inner_columns, family_id inner_kind) { - rel_spec spec(sig.size(), inner_columns, inner_kind); - return m_spec_store.get_relation_kind(sig, spec); - } - - family_id sieve_relation_plugin::get_relation_kind(sieve_relation & r, const bool * inner_columns) { - const relation_signature & sig = r.get_signature(); - return get_relation_kind(sig, inner_columns, r.get_inner().get_kind()); - } - - void sieve_relation_plugin::extract_inner_columns(const relation_signature & s, relation_plugin & inner, - svector & inner_columns) { - SASSERT(inner_columns.size()==s.size()); - unsigned n = s.size(); - relation_signature inner_sig_singleton; - for(unsigned i=0; i & inner_columns, relation_signature & inner_sig) { - SASSERT(inner_columns.size()==s.size()); - inner_sig.reset(); - unsigned n = s.size(); - for(unsigned i=0; i inner_cols(s.size()); - extract_inner_columns(s, inner_cols.c_ptr()); - collect_inner_signature(s, inner_cols, inner_sig); -#endif - } - - bool sieve_relation_plugin::can_handle_signature(const relation_signature & s) { - //we do not want this plugin to handle anything by default - return false; -#if 0 - relation_signature inner_sig; - extract_inner_signature(s, inner_sig); - SASSERT(inner_sig.size()<=s.size()); - return !inner_sig.empty() && inner_sig.size()!=s.size(); -#endif - } - - sieve_relation * sieve_relation_plugin::mk_from_inner(const relation_signature & s, const bool * inner_columns, - relation_base * inner_rel) { - SASSERT(!inner_rel->get_plugin().is_sieve_relation()); //it does not make sense to make a sieve of a sieve - return alloc(sieve_relation, *this, s, inner_columns, inner_rel); - } - - sieve_relation * sieve_relation_plugin::mk_empty(const sieve_relation & original) { - return static_cast(mk_empty(original.get_signature(), original.get_kind())); - } - - relation_base * sieve_relation_plugin::mk_empty(const relation_base & original) { - return mk_empty(static_cast(original)); - } - - relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { - rel_spec spec; - m_spec_store.get_relation_spec(s, kind, spec); - relation_signature inner_sig; - collect_inner_signature(s, spec.m_inner_cols, inner_sig); - relation_base * inner = get_manager().mk_empty_relation(inner_sig, spec.m_inner_kind); - return mk_from_inner(s, spec.m_inner_cols.c_ptr(), inner); - } - - - relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s) { - UNREACHABLE(); - return 0; -#if 0 - svector inner_cols(s.size()); - extract_inner_columns(s, inner_cols.c_ptr()); - return mk_empty(s, inner_cols.c_ptr()); -#endif - } - - sieve_relation * sieve_relation_plugin::mk_empty(const relation_signature & s, relation_plugin & inner_plugin) { - SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve - svector inner_cols(s.size()); - extract_inner_columns(s, inner_plugin, inner_cols); - relation_signature inner_sig; - collect_inner_signature(s, inner_cols, inner_sig); - relation_base * inner_rel = inner_plugin.mk_empty(inner_sig); - return mk_from_inner(s, inner_cols, inner_rel); - } - - relation_base * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { - relation_signature empty_sig; - relation_plugin& plugin = get_manager().get_appropriate_plugin(s); - relation_base * inner = plugin.mk_full(p, empty_sig, null_family_id); - svector inner_cols; - inner_cols.resize(s.size(), false); - return mk_from_inner(s, inner_cols, inner); - } - - sieve_relation * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin) { - SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve - svector inner_cols(s.size()); - extract_inner_columns(s, inner_plugin, inner_cols); - relation_signature inner_sig; - collect_inner_signature(s, inner_cols, inner_sig); - relation_base * inner_rel = inner_plugin.mk_full(p, inner_sig, null_family_id); - return mk_from_inner(s, inner_cols, inner_rel); - } - - class sieve_relation_plugin::join_fn : public convenient_relation_join_fn { - sieve_relation_plugin & m_plugin; - unsigned_vector m_inner_cols_1; - unsigned_vector m_inner_cols_2; - svector m_result_inner_cols; - - scoped_ptr m_inner_join_fun; - public: - join_fn(sieve_relation_plugin & p, const relation_base & r1, const relation_base & r2, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2, relation_join_fn * inner_join_fun) - : convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2), - m_plugin(p), - m_inner_join_fun(inner_join_fun) { - bool r1_sieved = r1.get_plugin().is_sieve_relation(); - bool r2_sieved = r2.get_plugin().is_sieve_relation(); - const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; - const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; - if(r1_sieved) { - m_result_inner_cols.append(sr1->m_inner_cols); - } - else { - m_result_inner_cols.resize(r1.get_signature().size(), true); - } - if(r2_sieved) { - m_result_inner_cols.append(sr2->m_inner_cols); - } - else { - m_result_inner_cols.resize(m_result_inner_cols.size() + r2.get_signature().size(), true); - } - } - - virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { - bool r1_sieved = r1.get_plugin().is_sieve_relation(); - bool r2_sieved = r2.get_plugin().is_sieve_relation(); - SASSERT(r1_sieved || r2_sieved); - const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; - const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; - const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; - const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; - - relation_base * inner_res = (*m_inner_join_fun)(inner1, inner2); - - return m_plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.c_ptr(), inner_res); - } - }; - - relation_join_fn * sieve_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if( &r1.get_plugin()!=this && &r2.get_plugin()!=this ) { - //we create just operations that involve the current plugin - return 0; - } - bool r1_sieved = r1.get_plugin().is_sieve_relation(); - bool r2_sieved = r2.get_plugin().is_sieve_relation(); - const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; - const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; - const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; - const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; - - unsigned_vector inner_cols1; - unsigned_vector inner_cols2; - - for(unsigned i=0; iis_inner_col(cols1[i])) { - continue; - } - if(r2_sieved && !sr2->is_inner_col(cols2[i])) { - continue; - } - inner_cols1.push_back( r1_sieved ? sr1->get_inner_col(cols1[i]) : cols1[i] ); - inner_cols2.push_back( r2_sieved ? sr2->get_inner_col(cols2[i]) : cols2[i] ); - } - - relation_join_fn * inner_join_fun = get_manager().mk_join_fn(inner1, inner2, inner_cols1, inner_cols2, false); - if(!inner_join_fun) { - return 0; - } - return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2, inner_join_fun); - } - - - class sieve_relation_plugin::transformer_fn : public convenient_relation_transformer_fn { - svector m_result_inner_cols; - - scoped_ptr m_inner_fun; - public: - transformer_fn(relation_transformer_fn * inner_fun, const relation_signature & result_sig, - const bool * result_inner_cols) - : m_result_inner_cols(result_sig.size(), result_inner_cols), m_inner_fun(inner_fun) { - get_result_signature() = result_sig; - } - - virtual relation_base * operator()(const relation_base & r0) { - SASSERT(r0.get_plugin().is_sieve_relation()); - const sieve_relation & r = static_cast(r0); - sieve_relation_plugin & plugin = r.get_plugin(); - - relation_base * inner_res = (*m_inner_fun)(r.get_inner()); - - return plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.c_ptr(), inner_res); - } - }; - - relation_transformer_fn * sieve_relation_plugin::mk_project_fn(const relation_base & r0, unsigned col_cnt, - const unsigned * removed_cols) { - if(&r0.get_plugin()!=this) { - return 0; - } - const sieve_relation & r = static_cast(r0); - unsigned_vector inner_removed_cols; - - for(unsigned i=0; i result_inner_cols = r.m_inner_cols; - project_out_vector_columns(result_inner_cols, col_cnt, removed_cols); - - relation_signature result_sig; - relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, result_sig); - - relation_transformer_fn * inner_fun; - if(inner_removed_cols.empty()) { - inner_fun = alloc(identity_relation_transformer_fn); - } - else { - inner_fun = get_manager().mk_project_fn(r.get_inner(), inner_removed_cols); - } - - if(!inner_fun) { - return 0; - } - return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); - } - - relation_transformer_fn * sieve_relation_plugin::mk_rename_fn(const relation_base & r0, - unsigned cycle_len, const unsigned * permutation_cycle) { - if(&r0.get_plugin()!=this) { - return 0; - } - const sieve_relation & r = static_cast(r0); - - unsigned sig_sz = r.get_signature().size(); - unsigned_vector permutation; - add_sequence(0, sig_sz, permutation); - permutate_by_cycle(permutation, cycle_len, permutation_cycle); - - bool inner_identity; - unsigned_vector inner_permutation; - collect_sub_permutation(permutation, r.m_sig2inner, inner_permutation, inner_identity); - - svector result_inner_cols = r.m_inner_cols; - permutate_by_cycle(result_inner_cols, cycle_len, permutation_cycle); - - relation_signature result_sig; - relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, result_sig); - - relation_transformer_fn * inner_fun = - get_manager().mk_permutation_rename_fn(r.get_inner(), inner_permutation); - if(!inner_fun) { - return 0; - } - return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); - } - - - class sieve_relation_plugin::union_fn : public relation_union_fn { - scoped_ptr m_union_fun; - public: - union_fn(relation_union_fn * union_fun) : m_union_fun(union_fun) {} - - virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { - bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); - bool src_sieved = src.get_plugin().is_sieve_relation(); - bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); - sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : 0; - const sieve_relation * ssrc = src_sieved ? static_cast(&src) : 0; - sieve_relation * sdelta = delta_sieved ? static_cast(delta) : 0; - relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; - const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; - relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; - - (*m_union_fun)(itgt, isrc, idelta); - } - }; - - relation_union_fn * sieve_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - if(&tgt.get_plugin()!=this && &src.get_plugin()!=this && (delta && &delta->get_plugin()!=this)) { - //we create the operation only if it involves this plugin - return 0; - } - - bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); - bool src_sieved = src.get_plugin().is_sieve_relation(); - bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); - const sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : 0; - const sieve_relation * ssrc = src_sieved ? static_cast(&src) : 0; - const sieve_relation * sdelta = delta_sieved ? static_cast(delta) : 0; - const relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; - const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; - const relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; - - //Now we require that the sieved and inner columns must match on all relations. - //We may want to allow for some cases of misalignment even though it could introcude imprecision - if( tgt_sieved && src_sieved && (!delta || delta_sieved) ) { - if( !vectors_equal(stgt->m_inner_cols, ssrc->m_inner_cols) - || (delta && !vectors_equal(stgt->m_inner_cols, sdelta->m_inner_cols)) ) { - return 0; - } - } - else { - if( (stgt && !stgt->no_sieved_columns()) - || (ssrc && !ssrc->no_sieved_columns()) - || (sdelta && !sdelta->no_sieved_columns()) ) { - //We have an unsieved relation and then some relation with some sieved columns, - //which means there is an misalignment. - return 0; - } - } - - relation_union_fn * union_fun = get_manager().mk_union_fn(itgt, isrc, idelta); - if(!union_fun) { - return 0; - } - - return alloc(union_fn, union_fun); - } - - - class sieve_relation_plugin::filter_fn : public relation_mutator_fn { - scoped_ptr m_inner_fun; - public: - filter_fn(relation_mutator_fn * inner_fun) - : m_inner_fun(inner_fun) {} - - virtual void operator()(relation_base & r0) { - SASSERT(r0.get_plugin().is_sieve_relation()); - sieve_relation & r = static_cast(r0); - - (*m_inner_fun)(r.get_inner()); - } - }; - - relation_mutator_fn * sieve_relation_plugin::mk_filter_identical_fn(const relation_base & r0, - unsigned col_cnt, const unsigned * identical_cols) { - if(&r0.get_plugin()!=this) { - return 0; - } - const sieve_relation & r = static_cast(r0); - unsigned_vector inner_icols; - - //we ignore the columns which do not belong to the inner relation (which introduces imprecision) - for(unsigned i=0; i(r0); - if(!r.is_inner_col(col)) { - //if the column which do not belong to the inner relation, we do nothing (which introduces imprecision) - return alloc(identity_relation_mutator_fn); - } - unsigned inner_col = r.get_inner_col(col); - - relation_mutator_fn * inner_fun = get_manager().mk_filter_equal_fn(r.get_inner(), value, inner_col); - if(!inner_fun) { - return 0; - } - return alloc(filter_fn, inner_fun); - } - - relation_mutator_fn * sieve_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, - app * condition) { - if(&rb.get_plugin()!=this) { - return 0; - } - ast_manager & m = get_ast_manager(); - const sieve_relation & r = static_cast(rb); - const relation_signature sig = r.get_signature(); - unsigned sz = sig.size(); - - var_idx_set& cond_vars = get_context().get_rule_manager().collect_vars(condition); - expr_ref_vector subst_vect(m); - subst_vect.resize(sz); - unsigned subst_ofs = sz-1; - for(unsigned i=0; i m_inner_fun; - public: - negation_filter_fn(relation_intersection_filter_fn * inner_fun) - : m_inner_fun(inner_fun) {} - - virtual void operator()(relation_base & r, const relation_base & neg) { - bool r_sieved = r.get_plugin().is_sieve_relation(); - bool neg_sieved = neg.get_plugin().is_sieve_relation(); - SASSERT(r_sieved || neg_sieved); - sieve_relation * sr = r_sieved ? static_cast(&r) : 0; - const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : 0; - relation_base & inner_r = r_sieved ? sr->get_inner() : r; - const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; - - (*m_inner_fun)(inner_r, inner_neg); - } - }; - - relation_intersection_filter_fn * sieve_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, - const relation_base & neg, unsigned col_cnt, const unsigned * r_cols, - const unsigned * neg_cols) { - if(&r.get_plugin()!=this && &neg.get_plugin()!=this) { - //we create just operations that involve the current plugin - return 0; - } - bool r_sieved = r.get_plugin().is_sieve_relation(); - bool neg_sieved = neg.get_plugin().is_sieve_relation(); - SASSERT(r_sieved || neg_sieved); - const sieve_relation * sr = r_sieved ? static_cast(&r) : 0; - const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : 0; - const relation_base & inner_r = r_sieved ? sr->get_inner() : r; - const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; - - unsigned_vector ir_cols; - unsigned_vector ineg_cols; - - for(unsigned i=0; iis_inner_col(r_cols[i]); - bool neg_col_inner = neg_sieved && !sneg->is_inner_col(neg_cols[i]); - if(r_col_inner && neg_col_inner) { - ir_cols.push_back( r_sieved ? sr->get_inner_col(i) : i ); - ineg_cols.push_back( neg_sieved ? sneg->get_inner_col(i) : i ); - } - else if(!r_col_inner && neg_col_inner) { - //Sieved (i.e. full) column in r is matched on an inner column in neg. - //If we assume the column in neg is not full, no rows from the inner relation of - //r would be removed. So in this case we perform no operation at cost of a little - //impresicion. - return alloc(identity_relation_intersection_filter_fn); - } - else { - //Inner or sieved column in r must match a sieved column in neg. - //Since sieved columns are full, this is always true so we can skip the equality. - continue; - } - } - - relation_intersection_filter_fn * inner_fun = - get_manager().mk_filter_by_negation_fn(inner_r, inner_neg, ir_cols, ineg_cols); - if(!inner_fun) { - return 0; - } - return alloc(negation_filter_fn, inner_fun); - } - - -}; diff --git a/src/muz/dl_sieve_relation.h b/src/muz/dl_sieve_relation.h deleted file mode 100644 index 551f5d705..000000000 --- a/src/muz/dl_sieve_relation.h +++ /dev/null @@ -1,197 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_sieve_relation.h - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-11-11. - -Revision History: - ---*/ - -#ifndef _DL_SIEVE_RELATION_H_ -#define _DL_SIEVE_RELATION_H_ - -#include "dl_context.h" - -namespace datalog { - - class sieve_relation; - - class sieve_relation_plugin : public relation_plugin { - friend class sieve_relation; - public: - struct rel_spec { - svector m_inner_cols; - family_id m_inner_kind; - - /** - Create uninitialized rel_spec. - */ - rel_spec() {} - /** - \c inner_kind==null_family_id means we will not specify a relation kind when requesting - the relation object from the relation_manager. - - \c inner_kind==null_family_id cannot hold in a specification of existing relation object. - */ - rel_spec(unsigned sig_sz, const bool * inner_cols, family_id inner_kind=null_family_id) - : m_inner_cols(sig_sz, inner_cols), m_inner_kind(inner_kind) {} - - bool operator==(const rel_spec & o) const { - return m_inner_kind==o.m_inner_kind && vectors_equal(m_inner_cols, o.m_inner_cols); - } - - struct hash { - unsigned operator()(const rel_spec & s) const { - return svector_hash()(s.m_inner_cols)^s.m_inner_kind; - } - }; - }; - private: - - class join_fn; - class transformer_fn; - class union_fn; - class filter_fn; - class negation_filter_fn; - - rel_spec_store > m_spec_store; - - family_id get_relation_kind(sieve_relation & r, const bool * inner_columns); - - void extract_inner_columns(const relation_signature & s, relation_plugin & inner, - svector & inner_columns); - void extract_inner_signature(const relation_signature & s, relation_signature & inner_sig); - void collect_inner_signature(const relation_signature & s, const svector & inner_columns, - relation_signature & inner_sig); - public: - static symbol get_name() { return symbol("sieve_relation"); } - static sieve_relation_plugin& get_plugin(relation_manager & rmgr); - - static sieve_relation& get(relation_base& r); - static sieve_relation const & get(relation_base const& r); - static sieve_relation* get(relation_base* r); - static sieve_relation const* get(relation_base const* r); - - sieve_relation_plugin(relation_manager & manager); - - virtual void initialize(family_id fid); - - family_id get_relation_kind(const relation_signature & sig, const bool * inner_columns, - family_id inner_kind); - family_id get_relation_kind(const relation_signature & sig, const svector & inner_columns, - family_id inner_kind) { - SASSERT(sig.size()==inner_columns.size()); - return get_relation_kind(sig, inner_columns.c_ptr(), inner_kind); - } - - virtual bool can_handle_signature(const relation_signature & s); - - virtual relation_base * mk_empty(const relation_signature & s); - sieve_relation * mk_empty(const sieve_relation & original); - virtual relation_base * mk_empty(const relation_base & original); - virtual relation_base * mk_empty(const relation_signature & s, family_id kind); - sieve_relation * mk_empty(const relation_signature & s, relation_plugin & inner_plugin); - - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - sieve_relation * mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin); - - - sieve_relation * mk_from_inner(const relation_signature & s, const bool * inner_columns, - relation_base * inner_rel); - sieve_relation * mk_from_inner(const relation_signature & s, const svector inner_columns, - relation_base * inner_rel) { - SASSERT(inner_columns.size()==s.size()); - return mk_from_inner(s, inner_columns.c_ptr(), inner_rel); - } - - protected: - - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - }; - - - // ----------------------------------- - // - // sieve_relation - // - // ----------------------------------- - - class sieve_relation : public relation_base { - friend class sieve_relation_plugin; - friend class sieve_relation_plugin::join_fn; - friend class sieve_relation_plugin::transformer_fn; - friend class sieve_relation_plugin::union_fn; - friend class sieve_relation_plugin::filter_fn; - - svector m_inner_cols; - - unsigned_vector m_sig2inner; - unsigned_vector m_inner2sig; - unsigned_vector m_ignored_cols; //in ascending order, so that it can be used in project-like functions - - scoped_rel m_inner; - - - sieve_relation(sieve_relation_plugin & p, const relation_signature & s, - const bool * inner_columns, relation_base * inner); - - public: - sieve_relation_plugin & get_plugin() const { - return static_cast(relation_base::get_plugin()); - } - - bool is_inner_col(unsigned idx) const { return m_sig2inner[idx]!=UINT_MAX; } - unsigned get_inner_col(unsigned idx) const { - SASSERT(is_inner_col(idx)); - return m_sig2inner[idx]; - } - bool no_sieved_columns() const { return m_ignored_cols.size()==0; } - bool no_inner_columns() const { return m_ignored_cols.size()==get_signature().size(); } - - relation_base & get_inner() { return *m_inner; } - const relation_base & get_inner() const { return *m_inner; } - - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual sieve_relation * clone() const; - virtual relation_base * complement(func_decl*p) const; - virtual void to_formula(expr_ref& fml) const; - - virtual bool empty() const { return get_inner().empty(); } - virtual void reset() { get_inner().reset(); } - virtual unsigned get_size_estimate_rows() const { return get_inner().get_size_estimate_rows(); } - virtual unsigned get_size_estimate_bytes() const { return get_inner().get_size_estimate_bytes(); } - - virtual void display(std::ostream & out) const; - }; - - -}; - -#endif /* _DL_SIEVE_RELATION_H_ */ - diff --git a/src/muz/dl_sparse_table.cpp b/src/muz/dl_sparse_table.cpp deleted file mode 100644 index 52d9618b8..000000000 --- a/src/muz/dl_sparse_table.cpp +++ /dev/null @@ -1,1246 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_sparse_table.cpp - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-24. - -Revision History: - ---*/ - -#include -#include"dl_context.h" -#include"dl_util.h" -#include"dl_sparse_table.h" - -namespace datalog { - - // ----------------------------------- - // - // entry_storage - // - // ----------------------------------- - - entry_storage::store_offset entry_storage::insert_or_get_reserve_content() { - SASSERT(has_reserve()); - store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); - if (m_reserve == entry_ofs) { - //entry inserted, so reserve is no longer a reserve - m_reserve = NO_RESERVE; - } - return entry_ofs; - } - bool entry_storage::insert_reserve_content() { - SASSERT(has_reserve()); - store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); - if (m_reserve == entry_ofs) { - //entry inserted, so reserve is no longer a reserve - m_reserve = NO_RESERVE; - return true; - } - return false; - } - - bool entry_storage::remove_reserve_content() { - SASSERT(has_reserve()); - store_offset entry_ofs; - if (!find_reserve_content(entry_ofs)) { - //the fact was not in the table - return false; - } - remove_offset(entry_ofs); - return true; - } - - void entry_storage::remove_offset(store_offset ofs) { - m_data_indexer.remove(ofs); - store_offset last_ofs = after_last_offset() - m_entry_size; - if (ofs!=last_ofs) { - SASSERT(ofs + m_entry_size <= last_ofs); - //we don't want any holes, so we put the last element at the place - //of the removed one - m_data_indexer.remove(last_ofs); - char * base = &m_data.get(0); - memcpy(base+ofs, base+last_ofs, m_entry_size); - m_data_indexer.insert(ofs); - } - if (has_reserve()) { - //we already have a reserve, so we need to shrink a little to keep having just one - resize_data(m_data_size-m_entry_size); - } - m_reserve=last_ofs; - } - - unsigned entry_storage::get_size_estimate_bytes() const { - unsigned sz = m_data.capacity(); - sz += m_data_indexer.capacity()*sizeof(storage_indexer::entry); - return sz; - } - - // ----------------------------------- - // - // sparse_table::column_layout - // - // ----------------------------------- - - unsigned get_domain_length(uint64 dom_size) { - SASSERT(dom_size>0); - - unsigned length = 0; - - unsigned dom_size_sm; - if (dom_size>UINT_MAX) { - dom_size_sm = static_cast(dom_size>>32); - length += 32; - if ( (dom_size&UINT_MAX)!=0 && dom_size_sm!=UINT_MAX ) { - dom_size_sm++; - } - } - else { - dom_size_sm=static_cast(dom_size); - } - if (dom_size_sm == 1) { - length += 1; //unary domains - } - else if (dom_size_sm > 0x80000000u) { - length += 32; - } - else { - length += get_num_1bits(next_power_of_two(dom_size_sm)-1); //ceil(log2(dom_size)) - } - return length; - } - - sparse_table::column_layout::column_layout(const table_signature & sig) - : m_functional_col_cnt(sig.functional_columns()) { - SASSERT(sig.size() > 0); - unsigned ofs = 0; - unsigned sig_sz = sig.size(); - unsigned first_functional = sig_sz-m_functional_col_cnt; - for (unsigned i=0; i0); - SASSERT(length<=64); - - if (size() > 0 && (length > 54 || i == first_functional)) { - //large domains must start byte-aligned, as well as functional columns - make_byte_aligned_end(size()-1); - ofs = back().next_ofs(); - } - - push_back(column_info(ofs, length)); - ofs += length; - } - make_byte_aligned_end(size()-1); - SASSERT(back().next_ofs()%8 == 0);//the entries must be aligned to whole bytes - m_entry_size = back().next_ofs()/8; - if (m_functional_col_cnt) { - SASSERT((*this)[first_functional].m_offset%8 == 0); - m_functional_part_size = m_entry_size - (*this)[first_functional].m_offset/8; - } - else { - m_functional_part_size = 0; - } - } - - void sparse_table::column_layout::make_byte_aligned_end(unsigned col_index0) { - unsigned ofs = (*this)[col_index0].next_ofs(); - unsigned ofs_bit_part = ofs%8; - unsigned rounded_ofs = (ofs_bit_part == 0) ? ofs : (ofs+8-ofs_bit_part); - - if (rounded_ofs!=ofs) { - SASSERT(rounded_ofs>ofs); - int diff = rounded_ofs-ofs; - unsigned col_idx = col_index0+1; - while(diff!=0) { - //we should always be able to fix the alignment by the time we reach zero - SASSERT(col_idx>0); - col_idx--; - column_info & ci = (*this)[col_idx]; - unsigned new_length = ci.m_length; - if (ci.m_length < 64) { - unsigned swallowed = std::min(64-static_cast(ci.m_length), diff); - diff -= swallowed; - new_length += swallowed; - } - unsigned new_ofs = ci.m_offset+diff; - ci = column_info(new_ofs, new_length); - } - } - - SASSERT(rounded_ofs%8 == 0); - SASSERT((*this)[col_index0].next_ofs()%8 == 0); - } - - // ----------------------------------- - // - // sparse_table - // - // ----------------------------------- - - class sparse_table::our_iterator_core : public iterator_core { - - class our_row : public row_interface { - const our_iterator_core & m_parent; - public: - our_row(const sparse_table & t, const our_iterator_core & parent) : - row_interface(t), - m_parent(parent) {} - - virtual table_element operator[](unsigned col) const { - return m_parent.m_layout.get(m_parent.m_ptr, col); - } - - }; - - const char * m_end; - const char * m_ptr; - unsigned m_fact_size; - our_row m_row_obj; - const column_layout & m_layout; - - public: - our_iterator_core(const sparse_table & t, bool finished) : - m_end(t.m_data.after_last()), - m_ptr(finished ? m_end : t.m_data.begin()), - m_fact_size(t.m_fact_size), - m_row_obj(t, *this), - m_layout(t.m_column_layout) {} - - virtual bool is_finished() const { - return m_ptr == m_end; - } - - virtual row_interface & operator*() { - SASSERT(!is_finished()); - return m_row_obj; - } - virtual void operator++() { - SASSERT(!is_finished()); - m_ptr+=m_fact_size; - } - }; - - class sparse_table::key_indexer { - protected: - unsigned_vector m_key_cols; - public: - typedef const store_offset * offset_iterator; - - /** - Iterators returned by \c begin() and \c end() are valid only as long as the \c query_result - object that returned them exists. - */ - struct query_result { - private: - bool m_singleton; - union { - store_offset m_single_result; - struct { - offset_iterator begin; - offset_iterator end; - } m_many; - }; - public: - /** - \brief Empty result. - */ - query_result() : m_singleton(false) { - m_many.begin = 0; - m_many.end = 0; - } - query_result(offset_iterator begin, offset_iterator end) : m_singleton(false) { - m_many.begin = begin; - m_many.end = end; - } - query_result(store_offset single_result) : m_singleton(true), m_single_result(single_result) {} - - offset_iterator begin() const { return m_singleton ? &m_single_result : m_many.begin; } - offset_iterator end() const { return m_singleton ? (&m_single_result+1) : m_many.end; } - bool empty() const { return begin() == end(); } - }; - - key_indexer(unsigned key_len, const unsigned * key_cols) - : m_key_cols(key_len, key_cols) {} - - virtual ~key_indexer() {} - - virtual void update(const sparse_table & t) {} - - virtual query_result get_matching_offsets(const key_value & key) const = 0; - }; - - - class sparse_table::general_key_indexer : public key_indexer { - typedef svector offset_vector; - typedef u_map index_map; - - index_map m_map; - mutable entry_storage m_keys; - store_offset m_first_nonindexed; - - - void key_to_reserve(const key_value & key) const { - m_keys.ensure_reserve(); - m_keys.write_into_reserve(reinterpret_cast(key.c_ptr())); - } - - offset_vector & get_matching_offset_vector(const key_value & key) { - key_to_reserve(key); - store_offset ofs = m_keys.insert_or_get_reserve_content(); - index_map::entry * e = m_map.find_core(ofs); - if (!e) { - TRACE("dl_table_relation", tout << "inserting\n";); - e = m_map.insert_if_not_there2(ofs, offset_vector()); - } - return e->get_data().m_value; - } - public: - general_key_indexer(unsigned key_len, const unsigned * key_cols) - : key_indexer(key_len, key_cols), - m_keys(key_len*sizeof(table_element)), - m_first_nonindexed(0) {} - - virtual void update(const sparse_table & t) { - if (m_first_nonindexed == t.m_data.after_last_offset()) { - return; - } - SASSERT(m_first_nonindexedinsert(ofs); - } - - m_first_nonindexed = t.m_data.after_last_offset(); - } - - virtual query_result get_matching_offsets(const key_value & key) const { - key_to_reserve(key); - store_offset ofs; - if (!m_keys.find_reserve_content(ofs)) { - return query_result(); - } - index_map::entry * e = m_map.find_core(ofs); - if (!e) { - return query_result(); - } - const offset_vector & res = e->get_data().m_value; - return query_result(res.begin(), res.end()); - } - }; - - /** - When doing lookup using this index, the content of the reserve in sparse_table::m_data changes. - */ - class sparse_table::full_signature_key_indexer : public key_indexer { - const sparse_table & m_table; - - /** - Permutation of key columns to make it into table facts. If empty, no permutation is necessary. - */ - unsigned_vector m_permutation; - mutable table_fact m_key_fact; - public: - - static bool can_handle(unsigned key_len, const unsigned * key_cols, const sparse_table & t) { - unsigned non_func_cols = t.get_signature().first_functional(); - if (key_len!=non_func_cols) { - return false; - } - counter ctr; - ctr.count(key_len, key_cols); - if (ctr.get_max_counter_value()!=1 || ctr.get_max_positive()!=non_func_cols-1) { - return false; - } - SASSERT(ctr.get_positive_count() == non_func_cols); - return true; - } - - full_signature_key_indexer(unsigned key_len, const unsigned * key_cols, const sparse_table & t) - : key_indexer(key_len, key_cols), - m_table(t) { - SASSERT(can_handle(key_len, key_cols, t)); - - m_permutation.resize(key_len); - for (unsigned i=0; i(m_table); - t.write_into_reserve(m_key_fact.c_ptr()); - - store_offset res; - if (!t.m_data.find_reserve_content(res)) { - return query_result(); - } - return query_result(res); - } - }; - - sparse_table::sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity) - : table_base(p, sig), - m_column_layout(sig), - m_fact_size(m_column_layout.m_entry_size), - m_data(m_fact_size, m_column_layout.m_functional_part_size, init_capacity) {} - - sparse_table::sparse_table(const sparse_table & t) - : table_base(t.get_plugin(), t.get_signature()), - m_column_layout(t.m_column_layout), - m_fact_size(t.m_fact_size), - m_data(t.m_data) {} - - table_base * sparse_table::clone() const { - return get_plugin().mk_clone(*this); - } - - sparse_table::~sparse_table() { - reset_indexes(); - } - - void sparse_table::reset() { - reset_indexes(); - m_data.reset(); - } - - table_base::iterator sparse_table::begin() const { - return mk_iterator(alloc(our_iterator_core, *this, false)); - } - - table_base::iterator sparse_table::end() const { - return mk_iterator(alloc(our_iterator_core, *this, true)); - } - - sparse_table::key_indexer& sparse_table::get_key_indexer(unsigned key_len, - const unsigned * key_cols) const { -#if Z3DEBUG - //We allow indexes only on non-functional columns because we want to be able to modify them - //without having to worry about updating indexes. - //Maybe we might keep a list of indexes that contain functional columns and on an update reset - //only those. - SASSERT(key_len == 0 || - counter().count(key_len, key_cols).get_max_positive()get_data().m_value) { - if (full_signature_key_indexer::can_handle(key_len, key_cols, *this)) { - key_map_entry->get_data().m_value = alloc(full_signature_key_indexer, key_len, key_cols, *this); - } - else { - key_map_entry->get_data().m_value = alloc(general_key_indexer, key_len, key_cols); - } - } - key_indexer & indexer = *key_map_entry->get_data().m_value; - indexer.update(*this); - return indexer; - } - - void sparse_table::reset_indexes() { - key_index_map::iterator kmit = m_key_indexes.begin(); - key_index_map::iterator kmend = m_key_indexes.end(); - for (; kmit!=kmend; ++kmit) { - dealloc((*kmit).m_value); - } - m_key_indexes.reset(); - } - - void sparse_table::write_into_reserve(const table_element* f) { - TRACE("dl_table_relation", tout << "\n";); - m_data.ensure_reserve(); - char * reserve = m_data.get_reserve_ptr(); - unsigned col_cnt = m_column_layout.size(); - for (unsigned i = 0; i < col_cnt; ++i) { - SASSERT(f[i] < get_signature()[i]); //the value fits into the table signature - m_column_layout.set(reserve, i, f[i]); - } - } - - bool sparse_table::add_fact(const char * data) { - m_data.write_into_reserve(data); - return add_reserve_content(); - } - - void sparse_table::add_fact(const table_fact & f) { - write_into_reserve(f.c_ptr()); - add_reserve_content(); - } - - bool sparse_table::add_reserve_content() { - return m_data.insert_reserve_content(); - } - - bool sparse_table::contains_fact(const table_fact & f) const { - sparse_table & t = const_cast(*this); - t.write_into_reserve(f.c_ptr()); - unsigned func_col_cnt = get_signature().functional_columns(); - if (func_col_cnt == 0) { - return t.m_data.reserve_content_already_present(); - } - else { - store_offset ofs; - if (!t.m_data.find_reserve_content(ofs)) { - return false; - } - unsigned sz = get_signature().size(); - for (unsigned i=func_col_cnt; i(*this); - t.write_into_reserve(f.c_ptr()); - store_offset ofs; - if (!t.m_data.find_reserve_content(ofs)) { - return false; - } - unsigned sz = sig.size(); - for (unsigned i=sig.first_functional(); ipre_projection_idx); - dest_layout.set(dest, dest_idx++, src_layout.get(src, i)); - } - } - - void sparse_table::concatenate_rows(const column_layout & layout1, const column_layout & layout2, - const column_layout & layout_res, const char * ptr1, const char * ptr2, char * res, - const unsigned * removed_cols) { - unsigned t1non_func = layout1.size()-layout1.m_functional_col_cnt; - unsigned t2non_func = layout2.size()-layout2.m_functional_col_cnt; - unsigned t1cols = layout1.size(); - unsigned t2cols = layout2.size(); - unsigned orig_i = 0; - unsigned res_i = 0; - const unsigned * next_removed = removed_cols; - copy_columns(layout1, layout_res, 0, t1non_func, ptr1, res, res_i, orig_i, next_removed); - copy_columns(layout2, layout_res, 0, t2non_func, ptr2, res, res_i, orig_i, next_removed); - copy_columns(layout1, layout_res, t1non_func, t1cols, ptr1, res, res_i, orig_i, next_removed); - copy_columns(layout2, layout_res, t2non_func, t2cols, ptr2, res, res_i, orig_i, next_removed); - } - - void sparse_table::garbage_collect() { - if (memory::above_high_watermark()) { - get_plugin().garbage_collect(); - } - if (memory::above_high_watermark()) { - IF_VERBOSE(1, verbose_stream() << "Ran out of memory while filling table of size: " << get_size_estimate_rows() << " rows " << get_size_estimate_bytes() << " bytes\n";); - throw out_of_memory_error(); - } - } - - void sparse_table::self_agnostic_join_project(const sparse_table & t1, const sparse_table & t2, - unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, - const unsigned * removed_cols, bool tables_swapped, sparse_table & result) { - - unsigned t1_entry_size = t1.m_fact_size; - unsigned t2_entry_size = t2.m_fact_size; - - unsigned t1idx = 0; - unsigned t1end = t1.m_data.after_last_offset(); - - TRACE("dl_table_relation", - tout << "joined_col_cnt: " << joined_col_cnt << "\n"; - tout << "t1_entry_size: " << t1_entry_size << "\n"; - tout << "t2_entry_size: " << t2_entry_size << "\n"; - t1.display(tout); - t2.display(tout); - tout << (&t1) << " " << (&t2) << " " << (&result) << "\n"; - ); - - if (joined_col_cnt == 0) { - unsigned t2idx = 0; - unsigned t2end = t2.m_data.after_last_offset(); - - for (; t1idx!=t1end; t1idx+=t1_entry_size) { - for (t2idx = 0; t2idx != t2end; t2idx += t2_entry_size) { - result.m_data.ensure_reserve(); - result.garbage_collect(); - char * res_reserve = result.m_data.get_reserve_ptr(); - char const* t1ptr = t1.get_at_offset(t1idx); - char const* t2ptr = t2.get_at_offset(t2idx); - if (tables_swapped) { - concatenate_rows(t2.m_column_layout, t1.m_column_layout, result.m_column_layout, - t2ptr, t1ptr, res_reserve, removed_cols); - } else { - concatenate_rows(t1.m_column_layout, t2.m_column_layout, result.m_column_layout, - t1ptr, t2ptr, res_reserve, removed_cols); - } - result.add_reserve_content(); - } - } - return; - } - - key_value t1_key; - t1_key.resize(joined_col_cnt); - key_indexer& t2_indexer = t2.get_key_indexer(joined_col_cnt, t2_joined_cols); - - bool key_modified = true; - key_indexer::query_result t2_offsets; - - for (; t1idx != t1end; t1idx += t1_entry_size) { - for (unsigned i = 0; i < joined_col_cnt; i++) { - table_element val = t1.m_column_layout.get(t1.get_at_offset(t1idx), t1_joined_cols[i]); - TRACE("dl_table_relation", tout << "val: " << val << " " << t1idx << " " << t1_joined_cols[i] << "\n";); - if (t1_key[i] != val) { - t1_key[i] = val; - key_modified = true; - } - } - if (key_modified) { - t2_offsets = t2_indexer.get_matching_offsets(t1_key); - key_modified = false; - } - - if (t2_offsets.empty()) { - continue; - } - - key_indexer::offset_iterator t2ofs_it = t2_offsets.begin(); - key_indexer::offset_iterator t2ofs_end = t2_offsets.end(); - for (; t2ofs_it != t2ofs_end; ++t2ofs_it) { - store_offset t2ofs = *t2ofs_it; - result.m_data.ensure_reserve(); - result.garbage_collect(); - char * res_reserve = result.m_data.get_reserve_ptr(); - char const * t1ptr = t1.get_at_offset(t1idx); - char const * t2ptr = t2.get_at_offset(t2ofs); - if (tables_swapped) { - concatenate_rows(t2.m_column_layout, t1.m_column_layout, result.m_column_layout, - t2ptr, t1ptr, res_reserve, removed_cols); - } else { - concatenate_rows(t1.m_column_layout, t2.m_column_layout, result.m_column_layout, - t1ptr, t2ptr, res_reserve, removed_cols); - } - result.add_reserve_content(); - } - } - } - - - // ----------------------------------- - // - // sparse_table_plugin - // - // ----------------------------------- - - sparse_table_plugin::sparse_table_plugin(relation_manager & manager) - : table_plugin(symbol("sparse"), manager) {} - - sparse_table_plugin::~sparse_table_plugin() { - reset(); - } - - void sparse_table_plugin::reset() { - table_pool::iterator it = m_pool.begin(); - table_pool::iterator end = m_pool.end(); - for (; it!=end; ++it) { - sp_table_vector * vect = it->m_value; - sp_table_vector::iterator it = vect->begin(); - sp_table_vector::iterator end = vect->end(); - for (; it!=end; ++it) { - (*it)->destroy(); //calling deallocate() would only put the table back into the pool - } - dealloc(vect); - } - m_pool.reset(); - } - - void sparse_table_plugin::garbage_collect() { - IF_VERBOSE(2, verbose_stream() << "garbage collecting "<< memory::get_allocation_size() << " bytes down to ";); - reset(); - IF_VERBOSE(2, verbose_stream() << memory::get_allocation_size() << " bytes\n";); - } - - void sparse_table_plugin::recycle(sparse_table * t) { - const table_signature & sig = t->get_signature(); - t->reset(); - - table_pool::entry * e = m_pool.insert_if_not_there2(sig, 0); - sp_table_vector * & vect = e->get_data().m_value; - if (vect == 0) { - vect = alloc(sp_table_vector); - } - IF_VERBOSE(12, verbose_stream() << "Recycle: " << t->get_size_estimate_bytes() << "\n";); - - vect->push_back(t); - } - - table_base * sparse_table_plugin::mk_empty(const table_signature & s) { - SASSERT(can_handle_signature(s)); - - sp_table_vector * vect; - if (!m_pool.find(s, vect) || vect->empty()) { - return alloc(sparse_table, *this, s); - } - sparse_table * res = vect->back(); - vect->pop_back(); - return res; - } - - sparse_table * sparse_table_plugin::mk_clone(const sparse_table & t) { - sparse_table * res = static_cast(mk_empty(t.get_signature())); - res->m_data = t.m_data; - return res; - } - - - bool sparse_table_plugin::join_involves_functional(const table_signature & s1, const table_signature & s2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (col_cnt == 0) { - return false; - } - return counter().count(col_cnt, cols1).get_max_positive()>=s1.first_functional() - || counter().count(col_cnt, cols2).get_max_positive()>=s2.first_functional(); - } - - - class sparse_table_plugin::join_project_fn : public convenient_table_join_project_fn { - public: - join_project_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols) - : convenient_table_join_project_fn(t1_sig, t2_sig, col_cnt, cols1, cols2, - removed_col_cnt, removed_cols) { - m_removed_cols.push_back(UINT_MAX); - } - - virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { - - const sparse_table & t1 = static_cast(tb1); - const sparse_table & t2 = static_cast(tb2); - - sparse_table_plugin & plugin = t1.get_plugin(); - - sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); - - //If we join with some intersection, want to iterate over the smaller table and - //do indexing into the bigger one. If we simply do a product, we want the bigger - //one to be at the outer iteration (then the small one will hopefully fit into - //the cache) - if ( (t1.row_count() > t2.row_count()) == (!m_cols1.empty()) ) { - sparse_table::self_agnostic_join_project(t2, t1, m_cols1.size(), m_cols2.c_ptr(), - m_cols1.c_ptr(), m_removed_cols.c_ptr(), true, *res); - } - else { - sparse_table::self_agnostic_join_project(t1, t2, m_cols1.size(), m_cols1.c_ptr(), - m_cols2.c_ptr(), m_removed_cols.c_ptr(), false, *res); - } - TRACE("dl_table_relation", tb1.display(tout); tb2.display(tout); res->display(tout); ); - return res; - } - }; - - table_join_fn * sparse_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - const table_signature & sig1 = t1.get_signature(); - const table_signature & sig2 = t2.get_signature(); - if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() - || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { - //We also don't allow indexes on functional columns (and they are needed for joins) - return 0; - } - return mk_join_project_fn(t1, t2, col_cnt, cols1, cols2, 0, static_cast(0)); - } - - table_join_fn * sparse_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols) { - const table_signature & sig1 = t1.get_signature(); - const table_signature & sig2 = t2.get_signature(); - if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() - || removed_col_cnt == t1.get_signature().size()+t2.get_signature().size() - || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { - //We don't allow sparse tables with zero signatures (and project on all columns leads to such) - //We also don't allow indexes on functional columns. - return 0; - } - return alloc(join_project_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, - removed_col_cnt, removed_cols); - } - - class sparse_table_plugin::union_fn : public table_union_fn { - public: - virtual void operator()(table_base & tgt0, const table_base & src0, table_base * delta0) { - - sparse_table & tgt = static_cast(tgt0); - const sparse_table & src = static_cast(src0); - sparse_table * delta = static_cast(delta0); - - unsigned fact_size = tgt.m_fact_size; - const char* ptr = src.m_data.begin(); - const char* after_last=src.m_data.after_last(); - for (; ptradd_fact(ptr); - } - } - } - }; - - table_union_fn * sparse_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta) { - if (tgt.get_kind()!=get_kind() || src.get_kind()!=get_kind() - || (delta && delta->get_kind()!=get_kind()) - || tgt.get_signature()!=src.get_signature() - || (delta && delta->get_signature()!=tgt.get_signature())) { - return 0; - } - return alloc(union_fn); - } - - class sparse_table_plugin::project_fn : public convenient_table_project_fn { - const unsigned m_inp_col_cnt; - const unsigned m_removed_col_cnt; - const unsigned m_result_col_cnt; - public: - project_fn(const table_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) - : convenient_table_project_fn(orig_sig, removed_col_cnt, removed_cols), - m_inp_col_cnt(orig_sig.size()), - m_removed_col_cnt(removed_col_cnt), - m_result_col_cnt(orig_sig.size()-removed_col_cnt) { - SASSERT(removed_col_cnt>0); - } - - virtual void transform_row(const char * src, char * tgt, - const sparse_table::column_layout & src_layout, - const sparse_table::column_layout & tgt_layout) { - unsigned r_idx=0; - unsigned tgt_i=0; - for (unsigned i=0; i(tb); - - unsigned t_fact_size = t.m_fact_size; - - sparse_table_plugin & plugin = t.get_plugin(); - sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); - - const sparse_table::column_layout & src_layout = t.m_column_layout; - const sparse_table::column_layout & tgt_layout = res->m_column_layout; - - const char* t_ptr = t.m_data.begin(); - const char* t_end = t.m_data.after_last(); - for (; t_ptr!=t_end; t_ptr+=t_fact_size) { - SASSERT(t_ptrm_data.ensure_reserve(); - char * res_ptr = res->m_data.get_reserve_ptr(); - transform_row(t_ptr, res_ptr, src_layout, tgt_layout); - res->m_data.insert_reserve_content(); - } - return res; - } - }; - - table_transformer_fn * sparse_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols) { - if (col_cnt == t.get_signature().size()) { - return 0; - } - return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); - } - - - class sparse_table_plugin::select_equal_and_project_fn : public convenient_table_transformer_fn { - const unsigned m_col; - sparse_table::key_value m_key; - public: - select_equal_and_project_fn(const table_signature & orig_sig, table_element val, unsigned col) - : m_col(col) { - table_signature::from_project(orig_sig, 1, &col, get_result_signature()); - m_key.push_back(val); - } - - virtual table_base * operator()(const table_base & tb) { - const sparse_table & t = static_cast(tb); - - sparse_table_plugin & plugin = t.get_plugin(); - sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); - - const sparse_table::column_layout & t_layout = t.m_column_layout; - const sparse_table::column_layout & res_layout = res->m_column_layout; - unsigned t_cols = t_layout.size(); - - sparse_table::key_indexer & indexer = t.get_key_indexer(1, &m_col); - sparse_table::key_indexer::query_result t_offsets = indexer.get_matching_offsets(m_key); - if (t_offsets.empty()) { - //no matches - return res; - } - sparse_table::key_indexer::offset_iterator ofs_it=t_offsets.begin(); - sparse_table::key_indexer::offset_iterator ofs_end=t_offsets.end(); - - for (; ofs_it!=ofs_end; ++ofs_it) { - sparse_table::store_offset t_ofs = *ofs_it; - const char * t_ptr = t.get_at_offset(t_ofs); - - res->m_data.ensure_reserve(); - char * res_reserve = res->m_data.get_reserve_ptr(); - - unsigned res_i = 0; - for (unsigned i=0; iadd_reserve_content(); - } - return res; - } - }; - - table_transformer_fn * sparse_table_plugin::mk_select_equal_and_project_fn(const table_base & t, - const table_element & value, unsigned col) { - if (t.get_kind()!=get_kind() || t.get_signature().size() == 1 || col>=t.get_signature().first_functional()) { - //We don't allow sparse tables with zero signatures (and project on a single - //column table produces one). - //We also don't allow indexes on functional columns. And our implementation of - //select_equal_and_project uses index on \c col. - return 0; - } - return alloc(select_equal_and_project_fn, t.get_signature(), value, col); - } - - - class sparse_table_plugin::rename_fn : public convenient_table_rename_fn { - unsigned_vector m_out_of_cycle; - public: - rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) - : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { - SASSERT(permutation_cycle_len>=2); - idx_set cycle_cols; - for (unsigned i=0; i < permutation_cycle_len; ++i) { - cycle_cols.insert(permutation_cycle[i]); - } - for (unsigned i=0; i < orig_sig.size(); ++i) { - if (!cycle_cols.contains(i)) { - m_out_of_cycle.push_back(i); - } - } - } - - void transform_row(const char * src, char * tgt, - const sparse_table::column_layout & src_layout, - const sparse_table::column_layout & tgt_layout) { - - for (unsigned i=1; i < m_cycle.size(); ++i) { - tgt_layout.set(tgt, m_cycle[i-1], src_layout.get(src, m_cycle[i])); - } - tgt_layout.set(tgt, m_cycle[m_cycle.size()-1], src_layout.get(src, m_cycle[0])); - - unsigned_vector::const_iterator it = m_out_of_cycle.begin(); - unsigned_vector::const_iterator end = m_out_of_cycle.end(); - for (; it!=end; ++it) { - unsigned col = *it; - tgt_layout.set(tgt, col, src_layout.get(src, col)); - } - } - - virtual table_base * operator()(const table_base & tb) { - - const sparse_table & t = static_cast(tb); - - unsigned t_fact_size = t.m_fact_size; - - sparse_table_plugin & plugin = t.get_plugin(); - sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); - - unsigned res_fact_size = res->m_fact_size; - unsigned res_data_size = res_fact_size*t.row_count(); - - res->m_data.resize_data(res_data_size); - - //here we can separate data creatin and insertion into hashmap, since we know - //that no row will become duplicit - - //create the data - const char* t_ptr = t.m_data.begin(); - char* res_ptr = res->m_data.begin(); - char* res_end = res_ptr+res_data_size; - for (; res_ptr!=res_end; t_ptr+=t_fact_size, res_ptr+=res_fact_size) { - transform_row(t_ptr, res_ptr, t.m_column_layout, res->m_column_layout); - } - - //and insert them into the hash-map - for (unsigned i=0; i!=res_data_size; i+=res_fact_size) { - TRUSTME(res->m_data.insert_offset(i)); - } - - return res; - } - }; - - table_transformer_fn * sparse_table_plugin::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle) { - if (t.get_kind()!=get_kind()) { - return 0; - } - return alloc(rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); - } - - class sparse_table_plugin::negation_filter_fn : public convenient_table_negation_filter_fn { - typedef sparse_table::store_offset store_offset; - typedef sparse_table::key_value key_value; - typedef sparse_table::key_indexer key_indexer; - - bool m_joining_neg_non_functional; - - /** - Used by \c collect_intersection_offsets function. - If tgt_is_first is false, contains the same items as \c res. - */ - idx_set m_intersection_content; - - public: - negation_filter_fn(const table_base & tgt, const table_base & neg, - unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) - : convenient_table_negation_filter_fn(tgt, neg, joined_col_cnt, t_cols, negated_cols) { - unsigned neg_fisrt_func = neg.get_signature().first_functional(); - counter ctr; - ctr.count(m_cols2); - m_joining_neg_non_functional = ctr.get_max_counter_value() == 1 - && ctr.get_positive_count() == neg_fisrt_func - && (neg_fisrt_func == 0 || ctr.get_max_positive() == neg_fisrt_func-1); - } - - /** - Collect offsets of rows in \c t1 or \c t2 (depends on whether \c tgt_is_first is true or false) - that have a match in the other table into \c res. Offsets in \c res are in ascending order. - */ - void collect_intersection_offsets(const sparse_table & t1, const sparse_table & t2, - bool tgt_is_first, svector & res) { - SASSERT(res.empty()); - - if (!tgt_is_first) { - m_intersection_content.reset(); - } - - unsigned joined_col_cnt = m_cols1.size(); - unsigned t1_entry_size = t1.m_data.entry_size(); - - const unsigned * cols1 = tgt_is_first ? m_cols1.c_ptr() : m_cols2.c_ptr(); - const unsigned * cols2 = tgt_is_first ? m_cols2.c_ptr() : m_cols1.c_ptr(); - - key_value t1_key; - t1_key.resize(joined_col_cnt); - key_indexer & t2_indexer = t2.get_key_indexer(joined_col_cnt, cols2); - - bool key_modified=true; - key_indexer::query_result t2_offsets; - store_offset t1_after_last = t1.m_data.after_last_offset(); - for (store_offset t1_ofs=0; t1_ofs(tgt0); - const sparse_table & neg = static_cast(neg0); - - if (m_cols1.size() == 0) { - if (!neg.empty()) { - tgt.reset(); - } - return; - } - - svector to_remove; //offsets here are in increasing order - - //We don't do just the simple tgt.row_count()>neg.row_count() because the swapped case is - //more expensive. The constant 4 is, however, just my guess what the ratio might be. - if (tgt.row_count()/4>neg.row_count()) { - collect_intersection_offsets(neg, tgt, false, to_remove); - } - else { - collect_intersection_offsets(tgt, neg, true, to_remove); - } - - if (to_remove.empty()) { - return; - } - - //the largest offsets are at the end, so we can remove them one by one - while(!to_remove.empty()) { - store_offset removed_ofs = to_remove.back(); - to_remove.pop_back(); - tgt.m_data.remove_offset(removed_ofs); - } - tgt.reset_indexes(); - } - - }; - - table_intersection_filter_fn * sparse_table_plugin::mk_filter_by_negation_fn(const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols) { - if (!check_kind(t) || !check_kind(negated_obj) - || join_involves_functional(t.get_signature(), negated_obj.get_signature(), joined_col_cnt, - t_cols, negated_cols) ) { - return 0; - } - return alloc(negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); - } - - unsigned sparse_table::get_size_estimate_bytes() const { - unsigned sz = 0; - sz += m_data.get_size_estimate_bytes(); - sz += m_key_indexes.capacity()*8; // TBD - return sz; - } - - -}; - diff --git a/src/muz/dl_sparse_table.h b/src/muz/dl_sparse_table.h deleted file mode 100644 index 010277b6b..000000000 --- a/src/muz/dl_sparse_table.h +++ /dev/null @@ -1,480 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_table.h - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-01. - -Revision History: - ---*/ - -#ifndef _DL_SPARSE_TABLE_H_ -#define _DL_SPARSE_TABLE_H_ - -#include -#include -#include - -#include "ast.h" -#include "bit_vector.h" -#include "buffer.h" -#include "hashtable.h" -#include "map.h" -#include "ref_vector.h" -#include "vector.h" - -#include "dl_base.h" - - -namespace datalog { - class sparse_table; - - class sparse_table_plugin : public table_plugin { - friend class sparse_table; - protected: - class join_project_fn; - class union_fn; - class transformer_fn; - class rename_fn; - class project_fn; - class negation_filter_fn; - class select_equal_and_project_fn; - - typedef ptr_vector sp_table_vector; - typedef map table_pool; - - table_pool m_pool; - - void recycle(sparse_table * t); - - void garbage_collect(); - - void reset(); - - static bool join_involves_functional(const table_signature & s1, const table_signature & s2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - - public: - typedef sparse_table table; - - sparse_table_plugin(relation_manager & manager); - ~sparse_table_plugin(); - - virtual bool can_handle_signature(const table_signature & s) - { return s.size()>0; } - - virtual table_base * mk_empty(const table_signature & s); - sparse_table * mk_clone(const sparse_table & t); - - protected: - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols); - virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta); - virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, - const table_element & value, unsigned col); - virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - }; - - class entry_storage { - public: - typedef unsigned store_offset; - private: - typedef svector storage; - - class offset_hash_proc { - storage & m_storage; - unsigned m_unique_entry_size; - public: - offset_hash_proc(storage & s, unsigned unique_entry_sz) - : m_storage(s), m_unique_entry_size(unique_entry_sz) {} - unsigned operator()(store_offset ofs) const { - return string_hash(m_storage.c_ptr()+ofs, m_unique_entry_size, 0); - } - }; - - class offset_eq_proc { - storage & m_storage; - unsigned m_unique_entry_size; - public: - offset_eq_proc(storage & s, unsigned unique_entry_sz) - : m_storage(s), m_unique_entry_size(unique_entry_sz) {} - bool operator()(store_offset o1, store_offset o2) const { - const char * base = m_storage.c_ptr(); - return memcmp(base+o1, base+o2, m_unique_entry_size)==0; - } - }; - - typedef hashtable storage_indexer; - - static const store_offset NO_RESERVE = UINT_MAX; - - unsigned m_entry_size; - unsigned m_unique_part_size; - unsigned m_data_size; - /** - Invariant: Every or all but one blocks of length \c m_entry_size in the \c m_data vector - are unique sequences of bytes and have their offset stored in the \c m_data_indexer hashtable. - If the offset of the last block is not stored in the hashtable, it is stored in the \c m_reserve - variable. Otherwise \c m_reserve==NO_RESERVE. - - The size of m_data is actually 8 bytes larger than stated in m_data_size, so that we may - deref an uint64 pointer at the end of the array. - */ - storage m_data; - storage_indexer m_data_indexer; - store_offset m_reserve; - public: - entry_storage(unsigned entry_size, unsigned functional_size = 0, unsigned init_size = 0) - : m_entry_size(entry_size), - m_unique_part_size(entry_size-functional_size), - m_data_indexer(next_power_of_two(std::max(8u,init_size)), - offset_hash_proc(m_data, m_unique_part_size), offset_eq_proc(m_data, m_unique_part_size)), - m_reserve(NO_RESERVE) { - SASSERT(entry_size>0); - SASSERT(functional_size<=entry_size); - resize_data(init_size); - resize_data(0); - } - entry_storage(const entry_storage &s) - : m_entry_size(s.m_entry_size), - m_unique_part_size(s.m_unique_part_size), - m_data_size(s.m_data_size), - m_data(s.m_data), - m_data_indexer(next_power_of_two(std::max(8u,s.entry_count())), - offset_hash_proc(m_data, m_unique_part_size), offset_eq_proc(m_data, m_unique_part_size)), - m_reserve(s.m_reserve) { - store_offset after_last=after_last_offset(); - for(store_offset i=0; i(this)->get(ofs); } - - unsigned entry_count() const { return m_data_indexer.size(); } - - store_offset after_last_offset() const { - return (m_reserve==NO_RESERVE) ? m_data_size : m_reserve; - } - - char * begin() { return get(0); } - const char * begin() const { return get(0); } - const char * after_last() const { return get(after_last_offset()); } - - - bool has_reserve() const { return m_reserve!=NO_RESERVE; } - store_offset reserve() const { SASSERT(has_reserve()); return m_reserve; } - - void ensure_reserve() { - if(has_reserve()) { - SASSERT(m_reserve==m_data_size-m_entry_size); - return; - } - m_reserve=m_data_size; - resize_data(m_data_size+m_entry_size); - } - - /** - \brief Return pointer to the reserve. - - The reserve must exist when the function is called. - */ - char * get_reserve_ptr() { - SASSERT(has_reserve()); - return &m_data.get(reserve()); - } - - bool reserve_content_already_present() const { - SASSERT(has_reserve()); - return m_data_indexer.contains(reserve()); - } - - bool find_reserve_content(store_offset & result) const { - SASSERT(has_reserve()); - storage_indexer::entry * indexer_entry = m_data_indexer.find_core(reserve()); - if(!indexer_entry) { - return false; - } - result = indexer_entry->get_data(); - return true; - } - - /** - \brief Write fact \c f into the reserve at the end of the \c m_data storage. - - If the reserve does not exist, this function creates it. - */ - void write_into_reserve(const char * data) { - ensure_reserve(); - memcpy(get_reserve_ptr(), data, m_entry_size); - } - - /** - \brief If the fact in reserve is not in the table, insert it there and return true; - otherwise return false. - - When a fact is inserted into the table, the reserve becomes part of the table and - is no longer a reserve. - */ - bool insert_reserve_content(); - store_offset insert_or_get_reserve_content(); - bool remove_reserve_content(); - /** - Remove data at the offset \c ofs. - - Data with offset lower than \c ofs are not be modified by this function, data with - higher offset may be moved. - */ - void remove_offset(store_offset ofs); - - - //the following two operations allow breaking of the object invariant! - void resize_data(unsigned sz) { - m_data_size = sz; - m_data.resize(sz + sizeof(uint64)); - } - - bool insert_offset(store_offset ofs) { - return m_data_indexer.insert_if_not_there(ofs)==ofs; - } - }; - - class sparse_table : public table_base { - friend class sparse_table_plugin; - friend class sparse_table_plugin::join_project_fn; - friend class sparse_table_plugin::union_fn; - friend class sparse_table_plugin::transformer_fn; - friend class sparse_table_plugin::rename_fn; - friend class sparse_table_plugin::project_fn; - friend class sparse_table_plugin::negation_filter_fn; - friend class sparse_table_plugin::select_equal_and_project_fn; - - class our_iterator_core; - class key_indexer; - class general_key_indexer; - class full_signature_key_indexer; - typedef entry_storage::store_offset store_offset; - - - class column_info { - unsigned m_big_offset; - unsigned m_small_offset; - uint64 m_mask; - uint64 m_write_mask; - public: - unsigned m_offset; //!< in bits - unsigned m_length; //!< in bits - - column_info(unsigned offset, unsigned length) \ - : m_big_offset(offset/8), - m_small_offset(offset%8), - m_mask( length==64 ? ULLONG_MAX : (static_cast(1)<(rec+m_big_offset); - uint64 res = *ptr; - res>>=m_small_offset; - res&=m_mask; - return res; - } - void set(char * rec, table_element val) const { - SASSERT( (val&~m_mask)==0 ); //the value fits into the column - uint64 * ptr = reinterpret_cast(rec+m_big_offset); - *ptr&=m_write_mask; - *ptr|=val< { - - void make_byte_aligned_end(unsigned col_index); - public: - - unsigned m_entry_size; - /** - Number of last bytes which correspond to functional columns in the signature. - */ - unsigned m_functional_part_size; - unsigned m_functional_col_cnt; - - column_layout(const table_signature & sig); - - table_element get(const char * rec, unsigned col) const { - return (*this)[col].get(rec); - } - void set(char * rec, unsigned col, table_element val) const { - return (*this)[col].set(rec, val); - } - }; - - - typedef svector key_spec; //sequence of columns in a key - typedef svector key_value; //values of key columns - typedef map, - vector_eq_proc > key_index_map; - - static const store_offset NO_RESERVE = UINT_MAX; - - column_layout m_column_layout; - unsigned m_fact_size; - entry_storage m_data; - mutable key_index_map m_key_indexes; - - - const char * get_at_offset(store_offset i) const { - return m_data.get(i); - } - - table_element get_cell(store_offset ofs, unsigned column) const { - return m_column_layout.get(m_data.get(ofs), column); - } - - void set_cell(store_offset ofs, unsigned column, table_element val) { - m_column_layout.set(m_data.get(ofs), column, val); - } - - void write_into_reserve(const table_element* f); - - /** - \brief Return reference to an indexer over columns in \c key_cols. - - An indexer can retrieve a sequence of offsets that with \c key_cols columns equal to - the specified key. Indexers are populated lazily -- they remember the position of the - last fact they contain, and when an indexer is retrieved by the \c get_key_indexer function, - all the new facts are added into the indexer. - - When a fact is removed from the table, all indexers are destroyed. This is not an extra - expense in the current use scenario, because we first perform all fact removals and do the - joins only after that (joins are the only operations that lead to index construction). - */ - key_indexer& get_key_indexer(unsigned key_len, const unsigned * key_cols) const; - - void reset_indexes(); - - static void copy_columns(const column_layout & src_layout, const column_layout & dest_layout, - unsigned start_index, unsigned after_last, const char * src, char * dest, - unsigned & dest_idx, unsigned & pre_projection_idx, const unsigned * & next_removed); - - /** - \c array \c removed_cols contains column indexes to be removed in ascending order and - is terminated by a number greated than the highest column index of a join the the two tables. - This is to simplify the traversal of the array when building facts. - */ - static void concatenate_rows(const column_layout & layout1, const column_layout & layout2, - const column_layout & layout_res, const char * ptr1, const char * ptr2, char * res, - const unsigned * removed_cols); - - /** - \brief Perform join-project between t1 and t2 iterating through t1 and retrieving relevant - columns from t2 using indexing. - - \c array \c removed_cols contains column indexes to be removed in ascending order and - is terminated by a number greated than the highest column index of a join the the two tables. - This is to simplify the traversal of the array when building facts. - - \c tables_swapped value means that the resulting facts should contain facts from t2 first, - instead of the default behavior that would concatenate the two facts as \c (t1,t2). - - \remark The function is called \c self_agnostic_join since, unlike the virtual method - \c join, it is static and therefore allows to easily swap the roles of the two joined - tables (the indexed and iterated one) in a way that is expected to give better performance. - */ - static void self_agnostic_join_project(const sparse_table & t1, const sparse_table & t2, - unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, - const unsigned * removed_cols, bool tables_swapped, sparse_table & result); - - - /** - If the fact at \c data (in table's native representation) is not in the table, - add it and return true. Otherwise return false. - */ - bool add_fact(const char * data); - - bool add_reserve_content(); - - void garbage_collect(); - - sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity=0); - sparse_table(const sparse_table & t); - virtual ~sparse_table(); - public: - - virtual void deallocate() { - get_plugin().recycle(this); - } - - unsigned row_count() const { return m_data.entry_count(); } - - sparse_table_plugin & get_plugin() const - { return static_cast(table_base::get_plugin()); } - - virtual bool empty() const { return row_count()==0; } - virtual void add_fact(const table_fact & f); - virtual bool contains_fact(const table_fact & f) const; - virtual bool fetch_fact(table_fact & f) const; - virtual void ensure_fact(const table_fact & f); - virtual void remove_fact(const table_element* fact); - virtual void reset(); - - virtual table_base * clone() const; - - virtual table_base::iterator begin() const; - virtual table_base::iterator end() const; - - virtual unsigned get_size_estimate_rows() const { return row_count(); } - virtual unsigned get_size_estimate_bytes() const; - virtual bool knows_exact_size() const { return true; } - }; - - }; - - #endif /* _DL_SPARSE_TABLE_H_ */ diff --git a/src/muz/dl_table.cpp b/src/muz/dl_table.cpp deleted file mode 100644 index 99a868bea..000000000 --- a/src/muz/dl_table.cpp +++ /dev/null @@ -1,772 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_table.cpp - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-01. - -Revision History: - ---*/ - -#include"dl_context.h" -#include"dl_util.h" -#include"dl_table.h" - -namespace datalog { - - // ----------------------------------- - // - // hashtable_table - // - // ----------------------------------- - - table_base * hashtable_table_plugin::mk_empty(const table_signature & s) { - SASSERT(can_handle_signature(s)); - return alloc(hashtable_table, *this, s); - } - - - class hashtable_table_plugin::join_fn : public convenient_table_join_fn { - unsigned m_joined_col_cnt; - public: - join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) - : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), - m_joined_col_cnt(col_cnt) {} - - virtual table_base * operator()(const table_base & t1, const table_base & t2) { - - const hashtable_table & ht1 = static_cast(t1); - const hashtable_table & ht2 = static_cast(t2); - - hashtable_table_plugin & plugin = ht1.get_plugin(); - - hashtable_table * res = static_cast(plugin.mk_empty(get_result_signature())); - - hashtable_table::storage::iterator els1it = ht1.m_data.begin(); - hashtable_table::storage::iterator els1end = ht1.m_data.end(); - hashtable_table::storage::iterator els2end = ht2.m_data.end(); - - table_fact acc; - - for(; els1it!=els1end; ++els1it) { - const table_fact & row1 = *els1it; - - hashtable_table::storage::iterator els2it = ht2.m_data.begin(); - for(; els2it!=els2end; ++els2it) { - const table_fact & row2 = *els2it; - - bool match=true; - for(unsigned i=0; im_data.insert(acc); - } - } - return res; - } - }; - - table_join_fn * hashtable_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if(t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind()) { - return 0; - } - return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); - } - - - class hashtable_table::our_iterator_core : public iterator_core { - const hashtable_table & m_parent; - storage::iterator m_inner; - storage::iterator m_end; - - class our_row : public row_interface { - const our_iterator_core & m_parent; - public: - our_row(const our_iterator_core & parent) : row_interface(parent.m_parent), m_parent(parent) {} - - virtual void get_fact(table_fact & result) const { - result = *m_parent.m_inner; - } - virtual table_element operator[](unsigned col) const { - return (*m_parent.m_inner)[col]; - } - - }; - - our_row m_row_obj; - - public: - our_iterator_core(const hashtable_table & t, bool finished) : - m_parent(t), m_inner(finished ? t.m_data.end() : t.m_data.begin()), - m_end(t.m_data.end()), m_row_obj(*this) {} - - virtual bool is_finished() const { - return m_inner==m_end; - } - - virtual row_interface & operator*() { - SASSERT(!is_finished()); - return m_row_obj; - } - virtual void operator++() { - SASSERT(!is_finished()); - ++m_inner; - } - }; - - - - table_base::iterator hashtable_table::begin() const { - return mk_iterator(alloc(our_iterator_core, *this, false)); - } - - table_base::iterator hashtable_table::end() const { - return mk_iterator(alloc(our_iterator_core, *this, true)); - } - - // ----------------------------------- - // - // bitvector_table - // - // ----------------------------------- - - bool bitvector_table_plugin::can_handle_signature(const table_signature & sig) { - if(sig.functional_columns()!=0) { - return false; - } - unsigned cols = sig.size(); - unsigned shift = 0; - for (unsigned i = 0; i < cols; ++i) { - unsigned s = static_cast(sig[i]); - if (s != sig[i] || !is_power_of_two(s)) { - return false; - } - unsigned num_bits = 0; - unsigned bit_pos = 1; - for (num_bits = 1; num_bits < 32; ++num_bits) { - if (bit_pos & s) { - break; - } - bit_pos <<= 1; - } - shift += num_bits; - if (shift >= 32) { - return false; - } - } - return true; - } - - table_base * bitvector_table_plugin::mk_empty(const table_signature & s) { - SASSERT(can_handle_signature(s)); - return alloc(bitvector_table, *this, s); - } - - class bitvector_table::bv_iterator : public iterator_core { - - bitvector_table const& m_bv; - unsigned m_offset; - - class our_row : public caching_row_interface { - const bv_iterator& m_parent; - public: - our_row(const bv_iterator & p) : caching_row_interface(p.m_bv), m_parent(p) {} - virtual void get_fact(table_fact& result) const { - if (result.size() < size()) { - result.resize(size(), 0); - } - m_parent.m_bv.offset2fact(m_parent.m_offset, result); - } - }; - our_row m_row_obj; - - public: - bv_iterator(const bitvector_table& bv, bool end): - m_bv(bv), m_offset(end?m_bv.m_bv.size():0), m_row_obj(*this) - { - if (!is_finished() && !m_bv.m_bv.get(m_offset)) { - ++(*this); - } - } - - virtual bool is_finished() const { - return m_offset == m_bv.m_bv.size(); - } - - virtual row_interface & operator*() { - SASSERT(!is_finished()); - return m_row_obj; - } - virtual void operator++() { - SASSERT(!is_finished()); - ++m_offset; - while (!is_finished() && !m_bv.m_bv.get(m_offset)) { - ++m_offset; - } - m_row_obj.reset(); - } - }; - - bitvector_table::bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig) - : table_base(plugin, sig) { - SASSERT(plugin.can_handle_signature(sig)); - - m_num_cols = sig.size(); - unsigned shift = 0; - for (unsigned i = 0; i < m_num_cols; ++i) { - unsigned s = static_cast(sig[i]); - if (s != sig[i] || !is_power_of_two(s)) { - throw default_exception("bit-vector table is specialized to small domains that are powers of two"); - } - m_shift.push_back(shift); - m_mask.push_back(s - 1); - unsigned num_bits = 0; - unsigned bit_pos = 1; - for (num_bits = 1; num_bits < 32; ++num_bits) { - if (bit_pos & s) { - break; - } - bit_pos <<= 1; - } - shift += num_bits; - if (shift >= 32) { - throw default_exception("bit-vector table is specialized to small domains that are powers of two"); - } - m_bv.reserve(1 << shift); - } - } - - unsigned bitvector_table::fact2offset(const table_element* f) const { - unsigned result = 0; - for (unsigned i = 0; i < m_num_cols; ++i) { - SASSERT(f[i]> m_shift[i]); - } - } - - void bitvector_table::add_fact(const table_fact & f) { - m_bv.set(fact2offset(f.c_ptr())); - } - - void bitvector_table::remove_fact(const table_element* fact) { - m_bv.unset(fact2offset(fact)); - } - - bool bitvector_table::contains_fact(const table_fact & f) const { - return m_bv.get(fact2offset(f.c_ptr())); - } - - table_base::iterator bitvector_table::begin() const { - return mk_iterator(alloc(bv_iterator, *this, false)); - } - - table_base::iterator bitvector_table::end() const { - return mk_iterator(alloc(bv_iterator, *this, true)); - } - - - - - // ----------------------------------- - // - // equivalence_table - // - // ----------------------------------- - - bool equivalence_table_plugin::can_handle_signature(const table_signature & sig) { - return sig.functional_columns() == 0 && sig.size() == 2 && sig[0] < UINT_MAX && sig[0] == sig[1]; - } - - bool equivalence_table_plugin::is_equivalence_table(table_base const& tbl) const { - if (tbl.get_kind() != get_kind()) return false; - equivalence_table const& t = static_cast(tbl); - return !t.is_sparse(); - } - - table_base * equivalence_table_plugin::mk_empty(const table_signature & s) { - TRACE("dl", for (unsigned i = 0; i < s.size(); ++i) tout << s[i] << " "; tout << "\n";); - SASSERT(can_handle_signature(s)); - return alloc(equivalence_table, *this, s); - } - - class equivalence_table_plugin::select_equal_and_project_fn : public table_transformer_fn { - unsigned m_val; - table_sort m_sort; - public: - select_equal_and_project_fn(const table_signature & sig, table_element val, unsigned col) - : m_val(static_cast(val)), - m_sort(sig[0]) { - SASSERT(val <= UINT_MAX); - SASSERT(col == 0 || col == 1); - SASSERT(sig.functional_columns() == 0); - SASSERT(sig.size() == 2); - SASSERT(sig[0] < UINT_MAX && sig[0] == sig[1]); - } - - virtual table_base* operator()(const table_base& tb) { - TRACE("dl", tout << "\n";); - table_plugin & plugin = tb.get_plugin(); - table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse")); - SASSERT(rp); - table_signature sig; - sig.push_back(m_sort); - table_base* result = rp->mk_empty(sig); - equivalence_table const& eq_table = static_cast(tb); - if (eq_table.is_valid(m_val)) { - table_fact fact; - fact.resize(1); - unsigned r = m_val; - do { - fact[0] = r; - result->add_fact(fact); - r = eq_table.m_uf.next(r); - } - while (r != m_val); - } - TRACE("dl", tb.display(tout << "src:\n"); result->display(tout << "result\n");); - return result; - } - }; - - table_transformer_fn * equivalence_table_plugin::mk_select_equal_and_project_fn( - const table_base & t, const table_element & value, unsigned col) { - return alloc(select_equal_and_project_fn, t.get_signature(), value, col); - } - - class equivalence_table_plugin::union_fn : public table_union_fn { - - equivalence_table_plugin& m_plugin; - - - void mk_union1(equivalence_table & tgt, const equivalence_table & src, table_base * delta) { - unsigned num_vars = src.m_uf.get_num_vars(); - table_fact fact; - fact.resize(2); - for (unsigned i = 0; i < num_vars; ++i) { - if (src.is_valid(i) && src.m_uf.find(i) == i) { - fact[0] = i; - equivalence_table::class_iterator it = src.class_begin(i); - equivalence_table::class_iterator end = src.class_end(i); - for (; it != end; ++it) { - fact[1] = *it; - if (!tgt.contains_fact(fact)) { - tgt.add_fact(fact); - if (delta) { - delta->add_fact(fact); - } - } - } - } - } - } - - void mk_union2(equivalence_table & tgt, const table_base & src, table_base * delta) { - table_fact fact; - table_base::iterator it = src.begin(), end = src.end(); - for (; it != end; ++it) { - it->get_fact(fact); - if (!tgt.contains_fact(fact)) { - tgt.add_fact(fact); - if (delta) { - delta->add_fact(fact); - TRACE("dl", - tout << "Add: "; - for (unsigned i = 0; i < fact.size(); ++i) tout << fact[i] << " "; - tout << "\n";); - } - } - } - } - - public: - union_fn(equivalence_table_plugin& p) : m_plugin(p) {} - - virtual void operator()(table_base & tgt0, const table_base & src, table_base * delta) { - TRACE("dl", tout << "union\n";); - equivalence_table & tgt = static_cast(tgt0); - if (m_plugin.is_equivalence_table(src)) { - mk_union1(tgt, static_cast(src), delta); - } - else { - mk_union2(tgt, src, delta); - } - TRACE("dl", src.display(tout << "src\n"); tgt.display(tout << "tgt\n"); - if (delta) delta->display(tout << "delta\n");); - } - }; - - table_union_fn * equivalence_table_plugin::mk_union_fn( - const table_base & tgt, const table_base & src, const table_base * delta) { - if (!is_equivalence_table(tgt) || - tgt.get_signature() != src.get_signature() || - (delta && delta->get_signature() != tgt.get_signature())) { - return 0; - } - return alloc(union_fn,*this); - } - - class equivalence_table_plugin::join_project_fn : public convenient_table_join_project_fn { - equivalence_table_plugin& m_plugin; - public: - join_project_fn( - equivalence_table_plugin& plugin, const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols) - : convenient_table_join_project_fn(t1_sig, t2_sig, col_cnt, cols1, cols2, removed_col_cnt, removed_cols), - m_plugin(plugin) { - m_removed_cols.push_back(UINT_MAX); - } - - virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { - SASSERT(m_cols1.size() == 1); - const table_signature & res_sign = get_result_signature(); - table_plugin * plugin = &tb1.get_plugin(); - if (!plugin->can_handle_signature(res_sign)) { - plugin = &tb2.get_plugin(); - if (!plugin->can_handle_signature(res_sign)) { - plugin = &tb1.get_manager().get_appropriate_plugin(res_sign); - } - } - SASSERT(plugin->can_handle_signature(res_sign)); - table_base * result = plugin->mk_empty(res_sign); - - if (m_plugin.is_equivalence_table(tb1)) { - mk_join(0, m_cols1[0], static_cast(tb1), - 2, m_cols2[0], tb2, result); - } - else if (m_plugin.is_equivalence_table(tb2)) { - mk_join(tb1.get_signature().size(), m_cols2[0], static_cast(tb2), - 0, m_cols1[0], tb1, result); - } - else { - UNREACHABLE(); - } - TRACE("dl", tb1.display(tout << "tb1\n"); tb2.display(tout << "tb2\n"); result->display(tout << "result\n");); - return result; - } - - private: - table_base * mk_join(unsigned offs1, unsigned col1, equivalence_table const & t1, - unsigned offs2, unsigned col2, table_base const& t2, table_base* res) { - table_base::iterator els2it = t2.begin(); - table_base::iterator els2end = t2.end(); - - table_fact acc, proj; - acc.resize(t1.get_signature().size() + t2.get_signature().size()); - - for(; els2it != els2end; ++els2it) { - const table_base::row_interface & row2 = *els2it; - table_element const& e2 = row2[col2]; - equivalence_table::class_iterator it = t1.class_begin(e2); - equivalence_table::class_iterator end = t1.class_end(e2); - if (it != end) { - for (unsigned i = 0; i < row2.size(); ++i) { - acc[i+offs2] = row2[i]; - } - } - for (; it != end; ++it) { - acc[offs1+col1] = e2; - acc[offs1+1-col1] = *it; - mk_project(acc, proj); - TRACE("dl", for (unsigned i = 0; i < proj.size(); ++i) tout << proj[i] << " "; tout << "\n";); - res->add_fact(proj); - } - } - return res; - } - - virtual void mk_project(table_fact const & f, table_fact & p) const { - unsigned sz = f.size(); - p.reset(); - for (unsigned i = 0, r = 0; i < sz; ++i) { - if (r < m_removed_cols.size() && m_removed_cols[r] == i) { - ++r; - } - else { - p.push_back(f[i]); - } - } - } - - - }; - - table_join_fn * equivalence_table_plugin::mk_join_project_fn( - const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols) { - if (col_cnt != 1) { - TRACE("dl", tout << "WARNING: join_project on multiple columns is not implemented\n";); - return 0; - } - if (is_equivalence_table(t1) || is_equivalence_table(t2)) { - return alloc(join_project_fn, *this, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, - removed_col_cnt, removed_cols); - } - return 0; - } - - class equivalence_table::eq_iterator : public iterator_core { - - equivalence_table const& m_eq; - unsigned m_last; - unsigned m_current; - unsigned m_next; - - class our_row : public caching_row_interface { - const eq_iterator& m_parent; - public: - our_row(const eq_iterator & p) : caching_row_interface(p.m_eq), m_parent(p) {} - - virtual void get_fact(table_fact& result) const { - if (result.size() < size()) { - result.resize(size(), 0); - } - result[0] = m_parent.m_current; - result[1] = m_parent.m_next; - } - - virtual table_element operator[](unsigned col) const { - if (col == 0) return m_parent.m_current; - if (col == 1) return m_parent.m_next; - UNREACHABLE(); - return 0; - } - - }; - our_row m_row_obj; - - public: - eq_iterator(const equivalence_table& eq, bool end): - m_eq(eq), - m_last(eq.m_uf.get_num_vars()), - m_current(end?m_last:0), - m_next(0), - m_row_obj(*this) - { - while (m_current < m_last && !m_eq.is_valid(m_current)) { - m_current++; - m_next = m_current; - } - } - - virtual bool is_finished() const { - return m_current == m_last; - } - - virtual row_interface & operator*() { - SASSERT(!is_finished()); - return m_row_obj; - } - - virtual void operator++() { - SASSERT(!is_finished()); - m_next = m_eq.m_uf.next(m_next); - if (m_next == m_current) { - do { - m_current++; - m_next = m_current; - } - while (m_current < m_last && !m_eq.is_valid(m_current)); - } - } - }; - - equivalence_table::equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig) - : table_base(plugin, sig), m_uf(m_ctx), m_sparse(0) { - SASSERT(plugin.can_handle_signature(sig)); - } - - equivalence_table::~equivalence_table() { - if (is_sparse()) { - m_sparse->deallocate(); - } - } - - - void equivalence_table::add_fact(const table_fact & f) { - if (is_sparse()) { - add_fact_sparse(f); - } - else { - TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";); - while (first(f) >= m_uf.get_num_vars()) m_uf.mk_var(); - while (second(f) >= m_uf.get_num_vars()) m_uf.mk_var(); - m_uf.merge(first(f), second(f)); - m_valid.reserve(m_uf.get_num_vars()); - m_valid.set(first(f)); - m_valid.set(second(f)); - } - } - - void equivalence_table::remove_fact(const table_element* fact) { - mk_sparse(); - m_sparse->remove_fact(fact); - } - - void equivalence_table::mk_sparse() { - if (m_sparse) return; - - TRACE("dl",tout << "\n";); - table_plugin & plugin = get_plugin(); - table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse")); - SASSERT(rp); - table_base* result = rp->mk_empty(get_signature()); - table_base::iterator it = begin(), e = end(); - table_fact fact; - for (; it != e; ++it) { - it->get_fact(fact); - result->add_fact(fact); - } - m_sparse = result; - } - - void equivalence_table::add_fact_sparse(table_fact const& f) { - table_base::iterator it = m_sparse->begin(), end = m_sparse->end(); - vector to_add; - to_add.push_back(f); - table_fact f1(f); - - f1[0] = f[1]; - f1[1] = f[0]; - to_add.push_back(f1); - - f1[0] = f[1]; - f1[1] = f[1]; - to_add.push_back(f1); - - f1[0] = f[0]; - f1[1] = f[0]; - to_add.push_back(f1); - - for (; it != end; ++it) { - if ((*it)[0] == f[0]) { - f1[0] = f[1]; - f1[1] = (*it)[1]; - to_add.push_back(f1); - std::swap(f1[0],f1[1]); - to_add.push_back(f1); - } - } - for (unsigned i = 0; i < to_add.size(); ++i) { - m_sparse->add_fact(to_add[i]); - } - } - - bool equivalence_table::contains_fact(const table_fact & f) const { - TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";); - if (is_sparse()) { - return m_sparse->contains_fact(f); - } - return - is_valid(first(f)) && - is_valid(second(f)) && - m_uf.find(first(f)) == m_uf.find(second(f)); - } - - table_base* equivalence_table::clone() const { - if (is_sparse()) { - return m_sparse->clone(); - } - TRACE("dl",tout << "\n";); - table_plugin & plugin = get_plugin(); - table_base* result = plugin.mk_empty(get_signature()); - table_fact fact; - fact.resize(2); - for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) { - if (m_valid.get(i) && m_uf.find(i) == i) { - unsigned n = m_uf.next(i); - fact[0] = i; - while (n != i) { - fact[1] = n; - result->add_fact(fact); - n = m_uf.next(n); - } - } - } - return result; - } - - table_base::iterator equivalence_table::begin() const { - if (is_sparse()) return m_sparse->begin(); - return mk_iterator(alloc(eq_iterator, *this, false)); - } - - table_base::iterator equivalence_table::end() const { - if (is_sparse()) return m_sparse->end(); - return mk_iterator(alloc(eq_iterator, *this, true)); - } - - equivalence_table::class_iterator equivalence_table::class_begin(table_element const& _e) const { - SASSERT(!is_sparse()); - unsigned e = static_cast(_e); - return class_iterator(*this, e, !is_valid(e)); - } - - equivalence_table::class_iterator equivalence_table::class_end(table_element const& _e) const { - SASSERT(!is_sparse()); - unsigned e = static_cast(_e); - return class_iterator(*this, e, true); - } - - void equivalence_table::display(std::ostream& out) const { - if (is_sparse()) { - m_sparse->display(out); - return; - } - for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) { - if (is_valid(i) && m_uf.find(i) == i) { - unsigned j = i, last = i; - do { - out << "<" << i << " " << j << ">\n"; - j = m_uf.next(j); - } - while (last != j); - } - } - } - - unsigned equivalence_table::get_size_estimate_rows() const { - if (is_sparse()) return m_sparse->get_size_estimate_rows(); - return static_cast(get_signature()[0]); - } - - unsigned equivalence_table::get_size_estimate_bytes() const { - if (is_sparse()) return m_sparse->get_size_estimate_bytes(); - return static_cast(get_signature()[0]); - } - - bool equivalence_table::knows_exact_size() const { - return (!is_sparse() || m_sparse->knows_exact_size()); - } - -}; - diff --git a/src/muz/dl_table.h b/src/muz/dl_table.h deleted file mode 100644 index 3a240c337..000000000 --- a/src/muz/dl_table.h +++ /dev/null @@ -1,265 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_table.h - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-01. - -Revision History: - ---*/ -#ifndef _DL_TABLE_H_ -#define _DL_TABLE_H_ - -#include -#include -#include - -#include "ast.h" -#include "bit_vector.h" -#include "buffer.h" -#include "hashtable.h" -#include "map.h" -#include "ref_vector.h" -#include "vector.h" -#include "union_find.h" -#include "dl_base.h" -#include "dl_util.h" -#include "bit_vector.h" - - -namespace datalog { - - class context; - class variable_intersection; - - - - // ----------------------------------- - // - // hashtable_table - // - // ----------------------------------- - - class hashtable_table; - - class hashtable_table_plugin : public table_plugin { - friend class hashtable_table; - protected: - class join_fn; - public: - typedef hashtable_table table; - - hashtable_table_plugin(relation_manager & manager) - : table_plugin(symbol("hashtable"), manager) {} - - virtual table_base * mk_empty(const table_signature & s); - - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - }; - - class hashtable_table : public table_base { - friend class hashtable_table_plugin; - friend class hashtable_table_plugin::join_fn; - - class our_iterator_core; - - typedef hashtable, - vector_eq_proc > storage; - - storage m_data; - - hashtable_table(hashtable_table_plugin & plugin, const table_signature & sig) - : table_base(plugin, sig) {} - public: - hashtable_table_plugin & get_plugin() const - { return static_cast(table_base::get_plugin()); } - - virtual void add_fact(const table_fact & f) { - m_data.insert(f); - } - virtual void remove_fact(const table_element* fact) { - table_fact f(get_signature().size(), fact); - m_data.remove(f); - } - virtual bool contains_fact(const table_fact & f) const { - return m_data.contains(f); - } - - virtual iterator begin() const; - virtual iterator end() const; - - virtual unsigned get_size_estimate_rows() const { return m_data.size(); } - virtual unsigned get_size_estimate_bytes() const { return m_data.size()*get_signature().size()*8; } - virtual bool knows_exact_size() const { return true; } - }; - - // ----------------------------------- - // - // bitvector_table - // - // ----------------------------------- - - class bitvector_table; - - class bitvector_table_plugin : public table_plugin { - public: - typedef bitvector_table table; - - bitvector_table_plugin(relation_manager & manager) - : table_plugin(symbol("bitvector"), manager) {} - - virtual bool can_handle_signature(const table_signature & s); - - virtual table_base * mk_empty(const table_signature & s); - }; - - class bitvector_table : public table_base { - friend class bitvector_table_plugin; - - class bv_iterator; - bit_vector m_bv; - unsigned m_num_cols; - unsigned_vector m_shift; - unsigned_vector m_mask; - - unsigned fact2offset(const table_element* f) const; - void offset2fact(unsigned offset, table_fact& f) const; - - bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig); - public: - virtual void add_fact(const table_fact & f); - virtual void remove_fact(const table_element* fact); - virtual bool contains_fact(const table_fact & f) const; - virtual iterator begin() const; - virtual iterator end() const; - }; - - // ------------------------------------------- - // Equivalence table. - // Really: partial equivalence relation table. - // ------------------------------------------- - - class equivalence_table; - - class equivalence_table_plugin : public table_plugin { - class union_fn; - class select_equal_and_project_fn; - class join_project_fn; - - bool is_equivalence_table(table_base const& tbl) const; - - public: - typedef equivalence_table table; - - equivalence_table_plugin(relation_manager & manager) - : table_plugin(symbol("equivalence"), manager) {} - - virtual bool can_handle_signature(const table_signature & s); - - virtual table_base * mk_empty(const table_signature & s); - - protected: - virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta); - virtual table_transformer_fn * mk_select_equal_and_project_fn( - const table_base & t, - const table_element & value, unsigned col); - virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols); - - -#if 0 - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - const table_element & value, unsigned col); - virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); -#endif - }; - - class equivalence_table : public table_base { - friend class equivalence_table_plugin; - - class eq_iterator; - union_find_default_ctx m_ctx; - bit_vector m_valid; - union_find<> m_uf; - table_base* m_sparse; - - equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig); - virtual ~equivalence_table(); - - unsigned first(table_fact const& f) const { return static_cast(f[0]); } - unsigned second(table_fact const& f) const { return static_cast(f[1]); } - - bool is_valid(unsigned entry) const { return entry < m_valid.size() && m_valid.get(entry); } - bool is_sparse() const { return m_sparse != 0; } - - // iterator over equivalence class of 'n'. - class class_iterator { - equivalence_table const& m_parent; - unsigned m_current; - unsigned m_last; - bool m_end; - public: - class_iterator(equivalence_table const& s, unsigned n, bool end): - m_parent(s), m_current(n), m_last(n), m_end(end) {} - - unsigned operator*() { return m_current; } - - class_iterator& operator++() { - m_current = m_parent.m_uf.next(m_current); - m_end = (m_current == m_last); - return *this; - } - - bool operator==(const class_iterator & it) const { - return - (m_end && it.m_end) || - (!m_end && !it.m_end && m_current == it.m_current); - } - bool operator!=(const class_iterator & it) const { return !operator==(it); } - - }; - class_iterator class_begin(table_element const& e) const; - class_iterator class_end(table_element const& e) const; - - void add_fact_sparse(table_fact const& f); - void mk_sparse(); - - - public: - virtual void add_fact(const table_fact & f); - virtual void remove_fact(const table_element* fact); - virtual bool contains_fact(const table_fact & f) const; - virtual table_base* clone() const; - virtual iterator begin() const; - virtual iterator end() const; - virtual unsigned get_size_estimate_rows() const; - virtual unsigned get_size_estimate_bytes() const; - virtual bool knows_exact_size() const; - virtual void display(std::ostream & out) const; - - }; - - -}; - -#endif /* _DL_TABLE_H_ */ - diff --git a/src/muz/dl_table_plugin.h b/src/muz/dl_table_plugin.h deleted file mode 100644 index 134389b61..000000000 --- a/src/muz/dl_table_plugin.h +++ /dev/null @@ -1,193 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_table_plugin.h - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-23. - -Revision History: - ---*/ -#ifndef _DL_TABLE_PLUGIN_H_ -#define _DL_TABLE_PLUGIN_H_ - -#include"ast.h" -#include"map.h" -#include"vector.h" - -#include"dl_table_ops.h" - -namespace datalog { - - /** - Termplate class containing common infrastructure for relations and tables - */ - template - struct tr_infrastructure { - - typedef typename Traits::base_object base_object; - typedef typename Traits::signature signature; - typedef typename Traits::element element; - typedef typename Traits::fact fact; - typedef typename Traits::kind kind; - - class base_fn { - public: - virtual ~base_fn() {} - }; - - class join_fn : public base_fn { - public: - virtual base_object * operator()(const base_object & t1, const base_object & t2); - }; - - class transformer_fn : public base_fn { - public: - virtual base_object * operator()(const base_object & t); - }; - - class union_fn : public base_fn { - public: - virtual void operator()(base_object & tgt, const base_object & src, base_object * delta); - }; - - class mutator_fn : public base_fn { - public: - virtual void operator()(base_object & t); - }; - - class negation_filter_fn : public base_fn { - public: - virtual void operator()(base_object & t, const base_object & negated_obj); - }; - - class plugin_object { - const kind m_kind; - protected: - plugin_object(kind k) : m_kind(k) {} - public: - kind get_kind(); - - virtual base_object * mk_empty(const signature & s) = 0; - - virtual join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - NOT_IMPLEMENTED_YET(); - } - - virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, - const unsigned * removed_cols) = 0 - - virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle) = 0; - - virtual union_fn * mk_union_fn(base_object & tgt, const base_object & src, base_object * delta) = 0; - - virtual mutator_fn * mk_filter_identical_fn(base_object & t, unsigned col_cnt, - const unsigned * identical_cols) = 0; - - virtual mutator_fn * mk_filter_equal_fn(base_object & t, const element & value, - unsigned col) = 0; - - virtual mutator_fn * mk_filter_interpreted_fn(base_object & t, app * condition) = 0; - - virtual negation_filter_fn * mk_filter_interpreted_fn(base_object & t, - const base_object & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols) = 0; - - }; - - class base_ancestor { - const kind m_kind; - protected: - relation_manager & m_manager; - signature m_signature; - - base_ancestor(kind k, relation_manager & m, const signature & s) - : m_kind(k), m_manager(m), m_signature(s) {} - public: - virtual ~base_ancestor() {} - - kind get_kind() const { return m_kind; } - relation_manager & get_manager() const { return m_manager; } - const signature & get_signature() const { return m_signature; } - - virtual bool empty() const = 0; - virtual void add_fact(const fact & f) = 0; - virtual bool contains_fact(const fact & f) const = 0; - - /** - \brief Return table that contains the same data as the current one. - */ - virtual base_object * clone() const; - - }; - }; - - - // ----------------------------------- - // - // relation_base - // - // ----------------------------------- - - class relation_base1; - - enum relation_kind { - RK_UNKNOWN, - RK_TABLE - }; - - struct relation_traits { - typedef relation_base1 base_object; - typedef relation_signature signature; - typedef app * element; - typedef ptr_vector fact; - typedef relation_kind kind; - }; - - typedef tr_infrastructure relation_infrastructure; - - typedef relation_infrastructure::plugin_object relation_plugin_base; - - class relation_base1 : public relation_infrastructure::base_ancestor { - - }; - - - // ----------------------------------- - // - // table_base - // - // ----------------------------------- - - class table_base1; - - struct table_traits { - typedef table_base1 base_object; - typedef table_signature signature; - typedef unsigned element; - typedef unsigned_vector fact; - typedef table_kind kind; - }; - - typedef tr_infrastructure table_infrastructure; - - typedef table_infrastructure::plugin_object table_plugin_base; - - class table_base1 : public table_infrastructure::base_ancestor { - - }; - -}; - -#endif /* _DL_TABLE_PLUGIN_H_ */ - diff --git a/src/muz/dl_table_relation.cpp b/src/muz/dl_table_relation.cpp deleted file mode 100644 index 3c30c58bb..000000000 --- a/src/muz/dl_table_relation.cpp +++ /dev/null @@ -1,490 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_table_relation.cpp - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-14. - -Revision History: - ---*/ - - -#include -#include"dl_context.h" -#include"dl_relation_manager.h" -#include"dl_table_relation.h" - - -namespace datalog { - - // ----------------------------------- - // - // table_relation_plugin - // - // ----------------------------------- - - symbol table_relation_plugin::create_plugin_name(const table_plugin &p) { - std::string name = std::string("tr_") + p.get_name().bare_str(); - return symbol(name.c_str()); - } - - bool table_relation_plugin::can_handle_signature(const relation_signature & s) { - table_signature tsig; - if(!get_manager().relation_signature_to_table(s, tsig)) { - return false; - } - return m_table_plugin.can_handle_signature(tsig); - } - - - relation_base * table_relation_plugin::mk_empty(const relation_signature & s) { - table_signature tsig; - if(!get_manager().relation_signature_to_table(s, tsig)) { - return 0; - } - table_base * t = m_table_plugin.mk_empty(tsig); - return alloc(table_relation, *this, s, t); - } - - relation_base * table_relation_plugin::mk_full(const relation_signature & s, func_decl* p, family_id kind) { - table_signature tsig; - if(!get_manager().relation_signature_to_table(s, tsig)) { - return 0; - } - table_base * t = m_table_plugin.mk_full(p, tsig, kind); - return alloc(table_relation, *this, s, t); - } - - relation_base * table_relation_plugin::mk_from_table(const relation_signature & s, table_base * t) { - if (&t->get_plugin() == &m_table_plugin) - return alloc(table_relation, *this, s, t); - table_relation_plugin& other = t->get_manager().get_table_relation_plugin(t->get_plugin()); - return alloc(table_relation, other, s, t); - } - - class table_relation_plugin::tr_join_project_fn : public convenient_relation_join_project_fn { - scoped_ptr m_tfun; - public: - tr_join_project_fn(const relation_signature & s1, const relation_signature & s2, unsigned col_cnt, - const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols, table_join_fn * tfun) - : convenient_relation_join_project_fn(s1, s2, col_cnt, cols1, cols2, removed_col_cnt, - removed_cols), m_tfun(tfun) {} - - virtual relation_base * operator()(const relation_base & t1, const relation_base & t2) { - SASSERT(t1.from_table()); - SASSERT(t2.from_table()); - table_relation_plugin & plugin = static_cast(t1.get_plugin()); - - const table_relation & tr1 = static_cast(t1); - const table_relation & tr2 = static_cast(t2); - - table_base * tres = (*m_tfun)(tr1.get_table(), tr2.get_table()); - - TRACE("dl_table_relation", tout << "# join => "; tres->display(tout);); - if(&tres->get_plugin()!=&plugin.m_table_plugin) { - //Operation returned a table of different type than the one which is associated with - //this plugin. We need to get a correct table_relation_plugin and create the relation - //using it. - return plugin.get_manager().get_table_relation_plugin(tres->get_plugin()) - .mk_from_table(get_result_signature(), tres); - } - return plugin.mk_from_table(get_result_signature(), tres); - } - }; - - relation_join_fn * table_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if(!r1.from_table() || !r2.from_table()) { - return 0; - } - const table_relation & tr1 = static_cast(r1); - const table_relation & tr2 = static_cast(r2); - - table_join_fn * tfun = get_manager().mk_join_fn(tr1.get_table(), tr2.get_table(), col_cnt, cols1, cols2); - if(!tfun) { - return 0; - } - - return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, - cols2, 0, static_cast(0), tfun); - } - - relation_join_fn * table_relation_plugin::mk_join_project_fn(const relation_base & r1, - const relation_base & r2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols) { - if(!r1.from_table() || !r2.from_table()) { - return 0; - } - const table_relation & tr1 = static_cast(r1); - const table_relation & tr2 = static_cast(r2); - - table_join_fn * tfun = get_manager().mk_join_project_fn(tr1.get_table(), tr2.get_table(), joined_col_cnt, - cols1, cols2, removed_col_cnt, removed_cols); - SASSERT(tfun); - - return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), joined_col_cnt, cols1, - cols2, removed_col_cnt, removed_cols, tfun); - } - - - class table_relation_plugin::tr_transformer_fn : public convenient_relation_transformer_fn { - scoped_ptr m_tfun; - public: - tr_transformer_fn(const relation_signature & rsig, table_transformer_fn * tfun) - : m_tfun(tfun) { get_result_signature() = rsig; } - - virtual relation_base * operator()(const relation_base & t) { - SASSERT(t.from_table()); - table_relation_plugin & plugin = static_cast(t.get_plugin()); - - const table_relation & tr = static_cast(t); - - table_base * tres = (*m_tfun)(tr.get_table()); - - TRACE("dl_table_relation", tout << "# transform => "; tres->display(tout);); - if(&tres->get_plugin()!=&plugin.m_table_plugin) { - //Transformation returned a table of different type than the one which is associated with this plugin. - //We need to get a correct table_relation_plugin and create the relation using it. - return plugin.get_manager().get_table_relation_plugin(tres->get_plugin()) - .mk_from_table(get_result_signature(), tres); - } - return plugin.mk_from_table(get_result_signature(), tres); - } - }; - - relation_transformer_fn * table_relation_plugin::mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols) { - if(!t.from_table()) { - return 0; - } - const table_relation & tr = static_cast(t); - - table_transformer_fn * tfun = get_manager().mk_project_fn(tr.get_table(), col_cnt, removed_cols); - SASSERT(tfun); - - relation_signature sig; - relation_signature::from_project(t.get_signature(), col_cnt, removed_cols, sig); - - return alloc(tr_transformer_fn, sig, tfun); - } - - relation_transformer_fn * table_relation_plugin::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle) { - if(!t.from_table()) { - return 0; - } - const table_relation & tr = static_cast(t); - - table_transformer_fn * tfun = get_manager().mk_rename_fn(tr.get_table(), permutation_cycle_len, permutation_cycle); - SASSERT(tfun); - - relation_signature sig; - relation_signature::from_rename(t.get_signature(), permutation_cycle_len, permutation_cycle, sig); - - return alloc(tr_transformer_fn, sig, tfun); - } - - relation_transformer_fn * table_relation_plugin::mk_permutation_rename_fn(const relation_base & t, - const unsigned * permutation) { - if(!t.from_table()) { - return 0; - } - const table_relation & tr = static_cast(t); - - table_transformer_fn * tfun = get_manager().mk_permutation_rename_fn(tr.get_table(), permutation); - SASSERT(tfun); - - relation_signature sig; - relation_signature::from_permutation_rename(t.get_signature(), permutation, sig); - - return alloc(tr_transformer_fn, sig, tfun); - } - - relation_transformer_fn * table_relation_plugin::mk_select_equal_and_project_fn(const relation_base & t, - const relation_element & value, unsigned col) { - if(!t.from_table()) { - return 0; - } - const table_relation & tr = static_cast(t); - - table_element tvalue; - get_manager().relation_to_table(tr.get_signature()[col], value, tvalue); - - table_transformer_fn * tfun = get_manager().mk_select_equal_and_project_fn(tr.get_table(), tvalue, col); - SASSERT(tfun); - relation_signature res_sig; - relation_signature::from_project(t.get_signature(), 1, &col, res_sig); - return alloc(tr_transformer_fn, res_sig, tfun); - } - - /** - Union functor that can unite table relation into any other relation (using any delta relation) - by iterating through the table and calling \c add_fact of the target relation. - */ - class table_relation_plugin::universal_target_union_fn : public relation_union_fn { - virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { - SASSERT(src.from_table()); - - const table_relation & tr_src = static_cast(src); - relation_manager & rmgr = tr_src.get_manager(); - relation_signature sig = tr_src.get_signature(); - SASSERT(tgt.get_signature()==sig); - SASSERT(!delta || delta->get_signature()==sig); - - table_base::iterator it = tr_src.get_table().begin(); - table_base::iterator end = tr_src.get_table().end(); - - table_fact tfact; - relation_fact rfact(rmgr.get_context()); - for (; it != end; ++it) { - it->get_fact(tfact); - rmgr.table_fact_to_relation(sig, tfact, rfact); - if(delta) { - if(!tgt.contains_fact(rfact)) { - tgt.add_new_fact(rfact); - delta->add_fact(rfact); - } - } - else { - tgt.add_fact(rfact); - } - } - TRACE("dl_table_relation", tout << "# universal union => "; tgt.display(tout);); - } - }; - - class table_relation_plugin::tr_union_fn : public relation_union_fn { - scoped_ptr m_tfun; - public: - tr_union_fn(table_union_fn * tfun) : m_tfun(tfun) {} - - virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { - SASSERT(tgt.from_table()); - SASSERT(src.from_table()); - SASSERT(!delta || delta->from_table()); - - table_relation & tr_tgt = static_cast(tgt); - const table_relation & tr_src = static_cast(src); - table_relation * tr_delta = static_cast(delta); - - (*m_tfun)(tr_tgt.get_table(), tr_src.get_table(), tr_delta ? &tr_delta->get_table() : 0); - - TRACE("dl_table_relation", tout << "# union => "; tr_tgt.get_table().display(tout);); - } - }; - - relation_union_fn * table_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - if(!src.from_table()) { - return 0; - } - if(!tgt.from_table() || (delta && !delta->from_table())) { - return alloc(universal_target_union_fn); - } - const table_relation & tr_tgt = static_cast(tgt); - const table_relation & tr_src = static_cast(src); - const table_relation * tr_delta = static_cast(delta); - - table_union_fn * tfun = get_manager().mk_union_fn(tr_tgt.get_table(), tr_src.get_table(), - tr_delta ? &tr_delta->get_table() : 0); - SASSERT(tfun); - - return alloc(tr_union_fn, tfun); - } - - - class table_relation_plugin::tr_mutator_fn : public relation_mutator_fn { - scoped_ptr m_tfun; - public: - tr_mutator_fn(table_mutator_fn * tfun) : m_tfun(tfun) {} - - virtual void operator()(relation_base & r) { - SASSERT(r.from_table()); - table_relation & tr = static_cast(r); - (*m_tfun)(tr.get_table()); - TRACE("dl_table_relation", tout << "# mutator => "; tr.get_table().display(tout);); - } - }; - - relation_mutator_fn * table_relation_plugin::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols) { - if(!t.from_table()) { - return 0; - } - const table_relation & tr = static_cast(t); - - table_mutator_fn * tfun = get_manager().mk_filter_identical_fn(tr.get_table(), col_cnt, identical_cols); - SASSERT(tfun); - return alloc(tr_mutator_fn, tfun); - } - - relation_mutator_fn * table_relation_plugin::mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col) { - if(!t.from_table()) { - return 0; - } - const table_relation & tr = static_cast(t); - - table_element tvalue; - get_manager().relation_to_table(tr.get_signature()[col], value, tvalue); - - table_mutator_fn * tfun = get_manager().mk_filter_equal_fn(tr.get_table(), tvalue, col); - SASSERT(tfun); - return alloc(tr_mutator_fn, tfun); - } - - relation_mutator_fn * table_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { - bool condition_needs_transforming = false; - if(!t.from_table() || condition_needs_transforming) { - return 0; - } - const table_relation & tr = static_cast(t); - table_mutator_fn * tfun = get_manager().mk_filter_interpreted_fn(tr.get_table(), condition); - SASSERT(tfun); - return alloc(tr_mutator_fn, tfun); - } - - relation_transformer_fn * table_relation_plugin::mk_filter_interpreted_and_project_fn(const relation_base & t, - app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { - if (!t.from_table()) - return 0; - - const table_relation & tr = static_cast(t); - table_transformer_fn * tfun = get_manager().mk_filter_interpreted_and_project_fn(tr.get_table(), - condition, removed_col_cnt, removed_cols); - SASSERT(tfun); - - relation_signature sig; - relation_signature::from_project(t.get_signature(), removed_col_cnt, removed_cols, sig); - return alloc(tr_transformer_fn, sig, tfun); - } - - class table_relation_plugin::tr_intersection_filter_fn : public relation_intersection_filter_fn { - scoped_ptr m_tfun; - public: - tr_intersection_filter_fn(table_intersection_filter_fn * tfun) : m_tfun(tfun) {} - - virtual void operator()(relation_base & r, const relation_base & src) { - SASSERT(r.from_table()); - SASSERT(src.from_table()); - - table_relation & tr = static_cast(r); - const table_relation & tr_src = static_cast(src); - - (*m_tfun)(tr.get_table(), tr_src.get_table()); - TRACE("dl_table_relation", tout << "# negation_filter => "; tr.get_table().display(tout);); - } - }; - - relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_intersection_fn(const relation_base & r, - const relation_base & src, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * src_cols) { - if(!r.from_table() || !src.from_table()) { - return 0; - } - const table_relation & tr = static_cast(r); - const table_relation & tr_neg = static_cast(src); - table_intersection_filter_fn * tfun = get_manager().mk_filter_by_intersection_fn(tr.get_table(), - tr_neg.get_table(), joined_col_cnt, r_cols, src_cols); - if(!tfun) { - return 0; - } - - return alloc(tr_intersection_filter_fn, tfun); - } - - - relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, - const relation_base & negated_rel, unsigned joined_col_cnt, - const unsigned * r_cols, const unsigned * negated_cols) { - if(!r.from_table() || !negated_rel.from_table()) { - return 0; - } - const table_relation & tr = static_cast(r); - const table_relation & tr_neg = static_cast(negated_rel); - table_intersection_filter_fn * tfun = get_manager().mk_filter_by_negation_fn(tr.get_table(), - tr_neg.get_table(), joined_col_cnt, r_cols, negated_cols); - SASSERT(tfun); - - return alloc(tr_intersection_filter_fn, tfun); - } - - - // ----------------------------------- - // - // table_relation - // - // ----------------------------------- - - void table_relation::add_table_fact(const table_fact & f) { - get_table().add_fact(f); - } - - void table_relation::add_fact(const relation_fact & f) { - SASSERT(f.size()==get_signature().size()); - table_fact vals; - get_manager().relation_fact_to_table(get_signature(), f, vals); - get_table().add_fact(vals); - TRACE("dl_table_relation", tout << "# add fact => "; get_table().display(tout);); - } - - bool table_relation::contains_fact(const relation_fact & f) const { - table_fact vals; - get_manager().relation_fact_to_table(get_signature(), f, vals); - return get_table().contains_fact(vals); - } - - relation_base * table_relation::clone() const { - table_base * tres = get_table().clone(); - return get_plugin().mk_from_table(get_signature(), tres); - } - - relation_base * table_relation::complement(func_decl* p) const { - table_base * tres = get_table().complement(p); - return get_plugin().mk_from_table(get_signature(), tres); - } - - void table_relation::display_tuples(func_decl & pred, std::ostream & out) const { - context & ctx = get_manager().get_context(); - unsigned arity = pred.get_arity(); - - out << "Tuples in " << pred.get_name() << ": \n"; - - table_base::iterator it = get_table().begin(); - table_base::iterator end = get_table().end(); - - table_fact fact; - for (; it != end; ++it) { - it->get_fact(fact); - - out << "\t("; - - for(unsigned i=0;i - -Author: - - Krystof Hoder (t-khoder) 2010-09-24. - -Revision History: - ---*/ -#ifndef _DL_TABLE_RELATION_H_ -#define _DL_TABLE_RELATION_H_ - - -#include "dl_base.h" -#include "dl_util.h" - -namespace datalog { - - class table_relation; - - class table_relation_plugin : public relation_plugin { - friend class table_relation; - - class tr_join_project_fn; - class tr_transformer_fn; - class universal_target_union_fn; - class tr_union_fn; - class tr_mutator_fn; - class tr_intersection_filter_fn; - - table_plugin & m_table_plugin; - - static symbol create_plugin_name(const table_plugin & p); - public: - table_relation_plugin(table_plugin & tp, relation_manager & manager) - : relation_plugin(create_plugin_name(tp), manager, ST_TABLE_RELATION), m_table_plugin(tp) {} - - table_plugin & get_table_plugin() { return m_table_plugin; } - - virtual bool can_handle_signature(const relation_signature & s); - - virtual relation_base * mk_empty(const relation_signature & s); - virtual relation_base * mk_full(const relation_signature & s, func_decl* p, family_id kind); - relation_base * mk_from_table(const relation_signature & s, table_base * t); - - protected: - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, - const unsigned * permutation); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, - app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); - virtual relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t, - const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); - virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - virtual relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, - const relation_element & value, unsigned col); - }; - - class table_relation : public relation_base { - friend class table_relation_plugin; - friend class table_relation_plugin::tr_join_project_fn; - friend class table_relation_plugin::tr_transformer_fn; - - scoped_rel m_table; - - /** - \brief Create a \c table_relation object. - - The newly created object takes ownership of the \c table object. - */ - table_relation(table_relation_plugin & p, const relation_signature & s, table_base * table) - : relation_base(p, s), m_table(table) { - SASSERT(s.size()==table->get_signature().size()); - } - public: - - table_relation_plugin & get_plugin() const { - return static_cast(relation_base::get_plugin()); - } - - table_base & get_table() { return *m_table; } - const table_base & get_table() const { return *m_table; } - - virtual bool empty() const { return m_table->empty(); } - - void add_table_fact(const table_fact & f); - - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual relation_base * clone() const; - virtual relation_base * complement(func_decl* p) const; - virtual void to_formula(expr_ref& fml) const { get_table().to_formula(get_signature(), fml); } - - virtual void display(std::ostream & out) const { - get_table().display(out); - } - virtual void display_tuples(func_decl & pred, std::ostream & out) const; - - virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); } - virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); } - virtual bool knows_exact_size() const { return m_table->knows_exact_size(); } - }; - -}; - -#endif /* _DL_TABLE_RELATION_H_ */ - diff --git a/src/muz/dl_util.cpp b/src/muz/dl_util.cpp index 0b88136e2..e32999ddc 100644 --- a/src/muz/dl_util.cpp +++ b/src/muz/dl_util.cpp @@ -32,14 +32,6 @@ Revision History: namespace datalog { - void universal_delete(relation_base* ptr) { - ptr->deallocate(); - } - - void universal_delete(table_base* ptr) { - ptr->deallocate(); - } - bool contains_var(expr * trm, unsigned var_idx) { ptr_vector vars; @@ -420,18 +412,6 @@ namespace datalog { } } - void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig, - expr_ref_vector & renaming_arg) { - ast_manager & m = renaming_arg.get_manager(); - unsigned sz = map.size(); - unsigned ofs = sz-1; - renaming_arg.resize(sz, static_cast(0)); - for(unsigned i=0; i & v) { - ptr_vector::iterator it = v.begin(); - ptr_vector::iterator end = v.end(); - for(; it!=end; ++it) { - (*it)->deallocate(); - } - } // ----------------------------------- diff --git a/src/muz/dl_util.h b/src/muz/dl_util.h index debb4d3e2..d805e683b 100644 --- a/src/muz/dl_util.h +++ b/src/muz/dl_util.h @@ -50,52 +50,6 @@ namespace datalog { LAST_CACHE_MODE }; - enum DL_ENGINE { - DATALOG_ENGINE, - PDR_ENGINE, - QPDR_ENGINE, - BMC_ENGINE, - QBMC_ENGINE, - TAB_ENGINE, - CLP_ENGINE, - LAST_ENGINE - }; - - class engine_base { - ast_manager& m; - std::string m_name; - public: - engine_base(ast_manager& m, char const* name): m(m), m_name(name) {} - virtual ~engine_base() {} - - virtual expr_ref get_answer() = 0; - virtual lbool query(expr* q) = 0; - - virtual void reset_statistics() {} - virtual void display_profile(std::ostream& out) const {} - virtual void collect_statistics(statistics& st) const {} - virtual unsigned get_num_levels(func_decl* pred) { - throw default_exception(std::string("get_num_levels is not supported for ") + m_name); - } - virtual expr_ref get_cover_delta(int level, func_decl* pred) { - throw default_exception(std::string("operation is not supported for ") + m_name); - } - virtual void add_cover(int level, func_decl* pred, expr* property) { - throw default_exception(std::string("operation is not supported for ") + m_name); - } - virtual void display_certificate(std::ostream& out) const { - throw default_exception(std::string("certificates are not supported for ") + m_name); - } - virtual model_ref get_model() { - return model_ref(alloc(model, m)); - } - virtual proof_ref get_proof() { - return proof_ref(m.mk_asserted(m.mk_true()), m); - } - virtual void updt_params() {} - virtual void cancel() {} - virtual void cleanup() {} - }; struct std_string_hash_proc { unsigned operator()(const std::string & s) const @@ -435,14 +389,6 @@ namespace datalog { void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt); - /** - \brief Populate vector \c renaming_args so that it can be used as an argument to \c var_subst. - The renaming we want is one that transforms variables with numbers of indexes of \c map into the - values of at those indexes. If a value if \c UINT_MAX, it means we do not transform the index - corresponding to it. - */ - void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig, - expr_ref_vector & renaming_arg); void print_renaming(const expr_ref_vector & cont, std::ostream & out); @@ -634,8 +580,6 @@ namespace datalog { } } - void dealloc_ptr_vector_content(ptr_vector & v); - /** \brief Add elements from an iterable object \c src into the vector \c vector. */ @@ -792,9 +736,6 @@ namespace datalog { dealloc(ptr); } - void universal_delete(relation_base* ptr); - void universal_delete(table_base* ptr); - template class scoped_rel { T* m_t; diff --git a/src/muz/dl_vector_relation.h b/src/muz/dl_vector_relation.h deleted file mode 100644 index 114f4ca43..000000000 --- a/src/muz/dl_vector_relation.h +++ /dev/null @@ -1,407 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_vector_relation.h - -Abstract: - - Basic relation with equivalences. - -Author: - - Nikolaj Bjorner (nbjorner) 2010-2-11 - -Revision History: - ---*/ -#ifndef _DL_VECTOR_RELATION_H_ -#define _DL_VECTOR_RELATION_H_ - -#include "ast_pp.h" -#include "dl_context.h" -#include "union_find.h" - -namespace datalog { - - typedef std::pair u_pair; - - template - class vector_relation_helper { - public: - static void mk_project_t(T& t, unsigned_vector const& renaming) {} - }; - - template > - class vector_relation : public relation_base { - protected: - T m_default; - vector* m_elems; - bool m_empty; - union_find_default_ctx m_ctx; - union_find<>* m_eqs; - - friend class vector_relation_plugin; - - public: - vector_relation(relation_plugin& p, relation_signature const& s, bool is_empty, T const& t = T()): - relation_base(p, s), - m_default(t), - m_elems(alloc(vector)), - m_empty(is_empty), - m_eqs(alloc(union_find<>, m_ctx)) { - m_elems->resize(s.size(), t); - for (unsigned i = 0; i < s.size(); ++i) { - m_eqs->mk_var(); - } - } - - virtual ~vector_relation() { - dealloc(m_eqs); - dealloc(m_elems); - } - - virtual bool can_swap() const { return true; } - - virtual void swap(relation_base& other) { - vector_relation& o = dynamic_cast(other); - if (&o == this) return; - std::swap(o.m_eqs, m_eqs); - std::swap(o.m_empty, m_empty); - std::swap(o.m_elems, m_elems); - } - - void copy(vector_relation const& other) { - SASSERT(get_signature() == other.get_signature()); - if (other.empty()) { - set_empty(); - return; - } - m_empty = false; - for (unsigned i = 0; i < m_elems->size(); ++i) { - (*this)[i] = other[i]; - SASSERT(find(i) == i); - } - for (unsigned i = 0; i < m_elems->size(); ++i) { - merge(i, find(i)); - } - } - - - virtual bool empty() const { return m_empty; } - - T& operator[](unsigned i) { return (*m_elems)[find(i)]; } - - T const& operator[](unsigned i) const { return (*m_elems)[find(i)]; } - - virtual void display_index(unsigned i, T const& t, std::ostream& out) const = 0; - - virtual void display(std::ostream & out) const { - if (empty()) { - out << "empty\n"; - return; - } - for (unsigned i = 0; i < m_elems->size(); ++i) { - if (i == find(i)) { - display_index(i, (*m_elems)[i], out); - } - else { - out << i << " = " << find(i) << "\n"; - } - } - } - - - bool is_subset_of(vector_relation const& other) const { - if (empty()) return true; - if (other.empty()) return false; - for (unsigned i = 0; i < get_signature().size(); ++i) { - if (!is_subset_of((*this)[i], other[i])) { - return false; - } - } - return true; - } - - void set_empty() { - unsigned sz = m_elems->size(); - m_empty = true; - m_elems->reset(); - m_elems->resize(sz, m_default); - dealloc(m_eqs); - m_eqs = alloc(union_find<>,m_ctx); - for (unsigned i = 0; i < sz; ++i) { - m_eqs->mk_var(); - } - } - - - virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const = 0; - - virtual T mk_widen(T const& t1, T const& t2) const = 0; - - virtual T mk_unite(T const& t1, T const& t2) const = 0; - - virtual bool is_subset_of(T const& t1, T const& t2) const = 0; - - virtual bool is_full(T const& t) const = 0; - - virtual bool is_empty(unsigned i, T const& t) const = 0; - - virtual void mk_rename_elem(T& t, unsigned col_cnt, unsigned const* cycle) = 0; - - virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& neq_eqs, T const& t) const { return t; } - - void equate(unsigned i, unsigned j) { - SASSERT(i < get_signature().size()); - SASSERT(j < get_signature().size()); - if (!empty() && find(i) != find(j)) { - bool isempty; - T r = mk_intersect((*this)[i], (*this)[j], isempty); - if (isempty || is_empty(find(i),r)) { - m_empty = true; - } - else { - merge(i, j); - (*this)[i] = r; - } - } - } - - bool is_full() const { - for (unsigned i = 0; i < m_elems->size(); ++i) { - if (!is_full((*this)[i])) { - return false; - } - } - return true; - } - - void mk_join(vector_relation const& r1, vector_relation const& r2, - unsigned num_cols, unsigned const* cols1, unsigned const* cols2) { - SASSERT(is_full()); - bool is_empty = r1.empty() || r2.empty(); - if (is_empty) { - m_empty = true; - return; - } - unsigned sz1 = r1.get_signature().size(); - unsigned sz2 = r2.get_signature().size(); - for (unsigned i = 0; i < sz1; ++i) { - (*this)[i] = r1[i]; - } - for (unsigned i = 0; i < sz2; ++i) { - (*this)[sz1+i] = r2[i]; - } - for (unsigned i = 0; i < num_cols; ++i) { - unsigned col1 = cols1[i]; - unsigned col2 = cols2[i]; - equate(col1, sz1 + col2); - } - - TRACE("dl_relation", - r1.display(tout << "r1:\n"); - r2.display(tout << "r2:\n"); - display(tout << "dst:\n"); - ); - } - - void mk_project(vector_relation const& r, unsigned col_cnt, unsigned const* removed_cols) { - SASSERT(is_full()); - unsigned_vector classRep, repNode; - unsigned result_size = get_signature().size(); - unsigned input_size = r.get_signature().size(); - repNode.resize(input_size, UINT_MAX); - - // initialize vector entries and set class representatives. - for (unsigned i = 0, j = 0, c = 0; i < input_size; ++i) { - if (c < col_cnt && removed_cols[c] == i) { - ++c; - } - else { - (*this)[j] = r[i]; - classRep.push_back(r.find(i)); - ++j; - } - } - - // merge remaining equivalence classes. - for (unsigned i = 0; i < result_size; ++i) { - unsigned rep = classRep[i]; - if (repNode[rep] == UINT_MAX) { - repNode[rep] = i; - } - else { - merge(repNode[rep], i); - } - } - - // rename columns in image of vector relation. - unsigned_vector renaming; - for (unsigned i = 0, j = 0, c = 0; i < input_size; ++i) { - if (c < col_cnt && removed_cols[c] == i) { - renaming.push_back(UINT_MAX); - ++c; - } - else { - renaming.push_back(find(j)); - ++j; - } - } - for (unsigned k = 0; k < result_size; ++k) { - Helper::mk_project_t((*this)[k], renaming); - } - - - TRACE("dl_relation", - ast_manager& m = r.get_plugin().get_ast_manager(); - tout << "Signature: "; - for (unsigned i = 0; i < r.get_signature().size(); ++i) { - tout << mk_pp(r.get_signature()[i], m) << " "; - } - tout << "Remove: "; - for (unsigned i = 0; i < col_cnt; ++i) { - tout << removed_cols[i] << " "; - } - tout << "\n"; - r.display(tout); - tout << " --> \n"; - display(tout);); - } - - void mk_rename(vector_relation const& r, unsigned col_cnt, unsigned const* cycle) { - unsigned col1, col2; - SASSERT(is_full()); - - // roundabout way of creating permuted relation. - unsigned_vector classRep, repNode; - for (unsigned i = 0; i < r.m_elems->size(); ++i) { - classRep.push_back(r.find(i)); - repNode.push_back(UINT_MAX); - (*this)[i] = r[i]; - } - for (unsigned i = 0; i + 1 < col_cnt; ++i) { - col1 = cycle[i]; - col2 = cycle[i+1]; - (*this)[col2] = (*r.m_elems)[col1]; - classRep[col2] = r.find(col1); - } - col1 = cycle[col_cnt-1]; - col2 = cycle[0]; - (*this)[col2] = (*r.m_elems)[col1]; - classRep[col2] = r.find(col1); - - for (unsigned i = 0; i < r.m_elems->size(); ++i) { - unsigned rep = classRep[i]; - if (repNode[rep] == UINT_MAX) { - repNode[rep] = i; - } - else { - merge(repNode[rep], i); - } - } - - for (unsigned i = 0; i < r.m_elems->size(); ++i) { - mk_rename_elem((*m_elems)[i], col_cnt, cycle); - } - - TRACE("dl_relation", - ast_manager& m = r.get_plugin().get_ast_manager(); - tout << "cycle: "; - for (unsigned i = 0; i < col_cnt; ++i) { - tout << cycle[i] << " "; - } - tout << "\nold_sig: "; - for (unsigned i = 0; i < r.get_signature().size(); ++i) { - tout << mk_pp(r.get_signature()[i], m) << " "; - } - tout << "\nnew_sig: "; - for (unsigned i = 0; i < get_signature().size(); ++i) { - tout << mk_pp(get_signature()[i], m) << " "; - } - tout << "\n"; - r.display(tout << "src:\n"); - ); - } - - void mk_union(vector_relation const& src, vector_relation* delta, bool is_widen) { - TRACE("dl_relation", display(tout << "dst:\n"); src.display(tout << "src:\n");); - - if (src.empty()) { - if (delta) { - delta->copy(src); - } - return; - } - - if (empty()) { - copy(src); - if (delta) { - delta->copy(src); - } - return; - } - - // find coarsest equivalence class containing joint equalities - union_find<>* uf = alloc(union_find<>, m_ctx); - unsigned size = get_signature().size(); - map, default_eq > mp; - bool change = false; - bit_vector finds; - finds.resize(size, false); - for (unsigned i = 0; i < size; ++i) { - uf->mk_var(); - unsigned w; - u_pair p(std::make_pair(find(i), src.find(i))); - if (mp.find(p, w)) { - uf->merge(i, w); - } - else { - mp.insert(p, i); - // detect change - if (finds.get(find(i))) { - change = true; - } - else { - finds.set(find(i), true); - } - } - } - vector* elems = alloc(vector); - for (unsigned i = 0; i < size; ++i) { - T t1 = mk_eq(*m_eqs, *uf, (*this)[i]); - T t2 = mk_eq(*src.m_eqs, *uf, src[i]); - if (is_widen) { - elems->push_back(mk_widen(t1, t2)); - } - else { - elems->push_back(mk_unite(t1, t2)); - } - TRACE("dl_relation", tout << t1 << " u " << t2 << " = " << elems->back() << "\n";); - change = delta && (change || !((*elems)[i] == (*this)[i])); - } - dealloc(m_eqs); - dealloc(m_elems); - m_eqs = uf; - m_elems = elems; - if (delta && change) { - delta->copy(*this); - } - TRACE("dl_relation", display(tout << "dst':\n");); - } - - unsigned find(unsigned i) const { - return m_eqs->find(i); - } - - void merge(unsigned i, unsigned j) { - m_eqs->merge(i, j); - } - - }; - -}; - -#endif - diff --git a/src/muz/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp similarity index 98% rename from src/muz/dl_cmds.cpp rename to src/muz/fp/dl_cmds.cpp index dcdf3ebb4..7f73b0895 100644 --- a/src/muz/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -19,6 +19,7 @@ Notes: #include"dl_cmds.h" #include"dl_external_relation.h" #include"dl_context.h" +#include"dl_register_engine.h" #include"dl_decl_plugin.h" #include"dl_instruction.h" #include"dl_compiler.h" @@ -37,6 +38,7 @@ struct dl_context { params_ref m_params_ref; fixedpoint_params m_params; cmd_context & m_cmd; + datalog::register_engine m_register_engine; dl_collected_cmds* m_collected_cmds; unsigned m_ref_count; datalog::dl_decl_plugin* m_decl_plugin; @@ -70,7 +72,7 @@ struct dl_context { void init() { ast_manager& m = m_cmd.m(); if (!m_context) { - m_context = alloc(datalog::context, m, m_fparams, m_params_ref); + m_context = alloc(datalog::context, m, m_register_engine, m_fparams, m_params_ref); } if (!m_decl_plugin) { symbol name("datalog_relation"); diff --git a/src/muz/fp/dl_register_engine.cpp b/src/muz/fp/dl_register_engine.cpp new file mode 100644 index 000000000..84b64fafa --- /dev/null +++ b/src/muz/fp/dl_register_engine.cpp @@ -0,0 +1,51 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_register_engine.cpp + +Abstract: + + Class for creating Datalog engines. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-08-28 + +Revision History: + +--*/ +#include "dl_register_engine.h" +#include "dl_bmc_engine.h" +#include "clp_context.h" +#include "tab_context.h" +#include "rel_context.h" +#include "pdr_dl_interface.h" + +namespace datalog { + register_engine::register_engine(): m_ctx(0) {} + + engine_base* register_engine::mk_engine(DL_ENGINE engine_type) { + switch(engine_type) { + case PDR_ENGINE: + case QPDR_ENGINE: + return alloc(pdr::dl_interface, *m_ctx); + case DATALOG_ENGINE: + return alloc(rel_context, *m_ctx); + case BMC_ENGINE: + case QBMC_ENGINE: + return alloc(bmc, *m_ctx); + case TAB_ENGINE: + return alloc(tab, *m_ctx); + case CLP_ENGINE: + return alloc(clp, *m_ctx); + case LAST_ENGINE: + UNREACHABLE(); + return 0; + } + UNREACHABLE(); + return 0; + } + +} diff --git a/src/muz/fp/dl_register_engine.h b/src/muz/fp/dl_register_engine.h new file mode 100644 index 000000000..44f5090e6 --- /dev/null +++ b/src/muz/fp/dl_register_engine.h @@ -0,0 +1,36 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_register_engine.h + +Abstract: + + Class for creating Datalog engines. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-08-28 + +Revision History: + +--*/ +#ifndef _DL_REGISTER_ENGINE_H_ +#define _DL_REGISTER_ENGINE_H_ + +#include "dl_context.h" + +namespace datalog { + + class register_engine : public register_engine_base { + context* m_ctx; + public: + register_engine(); + engine_base* mk_engine(DL_ENGINE engine_type); + void set_context(context* ctx) { m_ctx = ctx; } + }; + +} + +#endif diff --git a/src/muz/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp similarity index 98% rename from src/muz/horn_tactic.cpp rename to src/muz/fp/horn_tactic.cpp index 07a0e2568..ca6cbc2fb 100644 --- a/src/muz/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -21,22 +21,25 @@ Revision History: #include"proof_converter.h" #include"horn_tactic.h" #include"dl_context.h" +#include"dl_register_engine.h" #include"expr_replacer.h" #include"dl_rule_transformer.h" #include"dl_mk_slice.h" #include"filter_model_converter.h" +#include"dl_transforms.h" class horn_tactic : public tactic { struct imp { ast_manager& m; bool m_is_simplify; + datalog::register_engine m_register_engine; datalog::context m_ctx; smt_params m_fparams; imp(bool t, ast_manager & m, params_ref const & p): m(m), m_is_simplify(t), - m_ctx(m, m_fparams) { + m_ctx(m, m_register_engine, m_fparams) { updt_params(p); } @@ -310,7 +313,7 @@ class horn_tactic : public tactic { func_decl* query_pred = to_app(q)->get_decl(); m_ctx.set_output_predicate(query_pred); m_ctx.get_rules(); // flush adding rules. - m_ctx.apply_default_transformation(); + apply_default_transformation(m_ctx); if (m_ctx.get_params().slice()) { datalog::rule_transformer transformer(m_ctx); diff --git a/src/muz/horn_tactic.h b/src/muz/fp/horn_tactic.h similarity index 100% rename from src/muz/horn_tactic.h rename to src/muz/fp/horn_tactic.h diff --git a/src/muz/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp similarity index 100% rename from src/muz/pdr_context.cpp rename to src/muz/pdr/pdr_context.cpp diff --git a/src/muz/pdr_context.h b/src/muz/pdr/pdr_context.h similarity index 99% rename from src/muz/pdr_context.h rename to src/muz/pdr/pdr_context.h index 3501ca7cd..57238abb3 100644 --- a/src/muz/pdr_context.h +++ b/src/muz/pdr/pdr_context.h @@ -26,7 +26,6 @@ Revision History: #endif #include #include "pdr_manager.h" -#include "dl_base.h" #include "pdr_prop_solver.h" #include "pdr_reachable_cache.h" diff --git a/src/muz/pdr_dl_interface.cpp b/src/muz/pdr/pdr_dl_interface.cpp similarity index 98% rename from src/muz/pdr_dl_interface.cpp rename to src/muz/pdr/pdr_dl_interface.cpp index 05a13dfc7..ccd22d57f 100644 --- a/src/muz/pdr_dl_interface.cpp +++ b/src/muz/pdr/pdr_dl_interface.cpp @@ -17,7 +17,6 @@ Revision History: --*/ -#include "dl_cmds.h" #include "dl_context.h" #include "dl_mk_coi_filter.h" #include "dl_mk_interp_tail_simplifier.h" @@ -33,6 +32,7 @@ Revision History: #include "dl_mk_unfold.h" #include "dl_mk_coalesce.h" #include "model_smt2_pp.h" +#include "dl_transforms.h" using namespace pdr; @@ -102,7 +102,7 @@ lbool dl_interface::query(expr * query) { ); - m_ctx.apply_default_transformation(); + apply_default_transformation(m_ctx); if (m_ctx.get_params().slice()) { datalog::rule_transformer transformer(m_ctx); diff --git a/src/muz/pdr_dl_interface.h b/src/muz/pdr/pdr_dl_interface.h similarity index 98% rename from src/muz/pdr_dl_interface.h rename to src/muz/pdr/pdr_dl_interface.h index 2075dff47..610e7fe06 100644 --- a/src/muz/pdr_dl_interface.h +++ b/src/muz/pdr/pdr_dl_interface.h @@ -24,6 +24,7 @@ Revision History: #include "dl_rule.h" #include "dl_rule_set.h" #include "dl_util.h" +#include "dl_engine_base.h" #include "statistics.h" namespace datalog { diff --git a/src/muz/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp similarity index 100% rename from src/muz/pdr_farkas_learner.cpp rename to src/muz/pdr/pdr_farkas_learner.cpp diff --git a/src/muz/pdr_farkas_learner.h b/src/muz/pdr/pdr_farkas_learner.h similarity index 100% rename from src/muz/pdr_farkas_learner.h rename to src/muz/pdr/pdr_farkas_learner.h diff --git a/src/muz/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp similarity index 99% rename from src/muz/pdr_generalizers.cpp rename to src/muz/pdr/pdr_generalizers.cpp index 883429315..f1ab01070 100644 --- a/src/muz/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -194,7 +194,7 @@ namespace pdr { } conv1.append(fmls); expr_ref fml = n.pt().get_formulas(n.level(), false); - expr_ref_vector fmls(m); + fmls.reset(); qe::flatten_and(fml, fmls); for (unsigned i = 0; i < fmls.size(); ++i) { fml = m.mk_not(fmls[i].get()); diff --git a/src/muz/pdr_generalizers.h b/src/muz/pdr/pdr_generalizers.h similarity index 100% rename from src/muz/pdr_generalizers.h rename to src/muz/pdr/pdr_generalizers.h diff --git a/src/muz/pdr_manager.cpp b/src/muz/pdr/pdr_manager.cpp similarity index 100% rename from src/muz/pdr_manager.cpp rename to src/muz/pdr/pdr_manager.cpp diff --git a/src/muz/pdr_manager.h b/src/muz/pdr/pdr_manager.h similarity index 100% rename from src/muz/pdr_manager.h rename to src/muz/pdr/pdr_manager.h diff --git a/src/muz/pdr_prop_solver.cpp b/src/muz/pdr/pdr_prop_solver.cpp similarity index 100% rename from src/muz/pdr_prop_solver.cpp rename to src/muz/pdr/pdr_prop_solver.cpp diff --git a/src/muz/pdr_prop_solver.h b/src/muz/pdr/pdr_prop_solver.h similarity index 100% rename from src/muz/pdr_prop_solver.h rename to src/muz/pdr/pdr_prop_solver.h diff --git a/src/muz/pdr_reachable_cache.cpp b/src/muz/pdr/pdr_reachable_cache.cpp similarity index 100% rename from src/muz/pdr_reachable_cache.cpp rename to src/muz/pdr/pdr_reachable_cache.cpp diff --git a/src/muz/pdr_reachable_cache.h b/src/muz/pdr/pdr_reachable_cache.h similarity index 100% rename from src/muz/pdr_reachable_cache.h rename to src/muz/pdr/pdr_reachable_cache.h diff --git a/src/muz/pdr_smt_context_manager.cpp b/src/muz/pdr/pdr_smt_context_manager.cpp similarity index 100% rename from src/muz/pdr_smt_context_manager.cpp rename to src/muz/pdr/pdr_smt_context_manager.cpp diff --git a/src/muz/pdr_smt_context_manager.h b/src/muz/pdr/pdr_smt_context_manager.h similarity index 100% rename from src/muz/pdr_smt_context_manager.h rename to src/muz/pdr/pdr_smt_context_manager.h diff --git a/src/muz/pdr_sym_mux.cpp b/src/muz/pdr/pdr_sym_mux.cpp similarity index 100% rename from src/muz/pdr_sym_mux.cpp rename to src/muz/pdr/pdr_sym_mux.cpp diff --git a/src/muz/pdr_sym_mux.h b/src/muz/pdr/pdr_sym_mux.h similarity index 100% rename from src/muz/pdr_sym_mux.h rename to src/muz/pdr/pdr_sym_mux.h diff --git a/src/muz/pdr_util.cpp b/src/muz/pdr/pdr_util.cpp similarity index 100% rename from src/muz/pdr_util.cpp rename to src/muz/pdr/pdr_util.cpp diff --git a/src/muz/pdr_util.h b/src/muz/pdr/pdr_util.h similarity index 100% rename from src/muz/pdr_util.h rename to src/muz/pdr/pdr_util.h diff --git a/src/muz/aig_exporter.cpp b/src/muz/rel/aig_exporter.cpp similarity index 100% rename from src/muz/aig_exporter.cpp rename to src/muz/rel/aig_exporter.cpp diff --git a/src/muz/aig_exporter.h b/src/muz/rel/aig_exporter.h similarity index 100% rename from src/muz/aig_exporter.h rename to src/muz/rel/aig_exporter.h index 20b31f01b..78ab9fe17 100644 --- a/src/muz/aig_exporter.h +++ b/src/muz/rel/aig_exporter.h @@ -16,9 +16,9 @@ Abstract: #include "aig.h" #include "dl_rule_set.h" -#include "rel_context.h" #include #include +#include "rel_context.h" namespace datalog { class aig_exporter { diff --git a/src/muz/dl_compiler.cpp b/src/muz/rel/dl_compiler.cpp similarity index 99% rename from src/muz/dl_compiler.cpp rename to src/muz/rel/dl_compiler.cpp index 7a2b47c78..931846c35 100644 --- a/src/muz/dl_compiler.cpp +++ b/src/muz/rel/dl_compiler.cpp @@ -21,6 +21,7 @@ Revision History: #include #include"ref_vector.h" #include"dl_context.h" +#include"rel_context.h" #include"dl_rule.h" #include"dl_util.h" #include"dl_compiler.h" diff --git a/src/muz/dl_compiler.h b/src/muz/rel/dl_compiler.h similarity index 100% rename from src/muz/dl_compiler.h rename to src/muz/rel/dl_compiler.h diff --git a/src/muz/dl_instruction.cpp b/src/muz/rel/dl_instruction.cpp similarity index 98% rename from src/muz/dl_instruction.cpp rename to src/muz/rel/dl_instruction.cpp index 55327f55b..a702c27ce 100644 --- a/src/muz/dl_instruction.cpp +++ b/src/muz/rel/dl_instruction.cpp @@ -22,6 +22,7 @@ Revision History: #include"dl_context.h" #include"dl_util.h" #include"dl_instruction.h" +#include"rel_context.h" #include"debug.h" #include"warning.h" @@ -59,7 +60,7 @@ namespace datalog { } rel_context& execution_context::get_rel_context() { - return *m_context.get_rel_context(); + return dynamic_cast(*m_context.get_rel_context()); } struct compare_size_proc { @@ -140,8 +141,9 @@ namespace datalog { process_costs(); } - void instruction::display_indented(rel_context const & ctx, std::ostream & out, std::string indentation) const { + void instruction::display_indented(rel_context_base const & _ctx, std::ostream & out, std::string indentation) const { out << indentation; + rel_context const& ctx = dynamic_cast(_ctx); display_head_impl(ctx, out); if (ctx.output_profile()) { out << " {"; @@ -313,7 +315,7 @@ namespace datalog { out << "while"; print_container(m_controls, out); } - virtual void display_body_impl(rel_context const & ctx, std::ostream & out, std::string indentation) const { + virtual void display_body_impl(rel_context_base const & ctx, std::ostream & out, std::string indentation) const { m_body->display_indented(ctx, out, indentation+" "); } }; @@ -1102,7 +1104,8 @@ namespace datalog { } } - void instruction_block::display_indented(rel_context const& ctx, std::ostream & out, std::string indentation) const { + void instruction_block::display_indented(rel_context_base const& _ctx, std::ostream & out, std::string indentation) const { + rel_context const& ctx = dynamic_cast(_ctx); instr_seq_type::const_iterator it = m_data.begin(); instr_seq_type::const_iterator end = m_data.end(); for(; it!=end; ++it) { diff --git a/src/muz/dl_instruction.h b/src/muz/rel/dl_instruction.h similarity index 96% rename from src/muz/dl_instruction.h rename to src/muz/rel/dl_instruction.h index 97622c6f3..fa705a172 100644 --- a/src/muz/dl_instruction.h +++ b/src/muz/rel/dl_instruction.h @@ -26,6 +26,7 @@ Revision History: #include "vector.h" #include "dl_base.h" #include "dl_costs.h" +#include "dl_context.h" namespace datalog { @@ -231,10 +232,10 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) = 0; - void display(rel_context const& ctx, std::ostream & out) const { + void display(rel_context_base const& ctx, std::ostream & out) const { display_indented(ctx, out, ""); } - void display_indented(rel_context const & ctx, std::ostream & out, std::string indentation) const; + void display_indented(rel_context_base const & ctx, std::ostream & out, std::string indentation) const; static instruction * mk_load(ast_manager & m, func_decl * pred, reg_idx tgt); /** @@ -335,10 +336,10 @@ namespace datalog { void make_annotations(execution_context & ctx); - void display(rel_context const & ctx, std::ostream & out) const { + void display(rel_context_base const & ctx, std::ostream & out) const { display_indented(ctx, out, ""); } - void display_indented(rel_context const & ctx, std::ostream & out, std::string indentation) const; + void display_indented(rel_context_base const & ctx, std::ostream & out, std::string indentation) const; }; diff --git a/src/muz/rel_context.cpp b/src/muz/rel/rel_context.cpp similarity index 89% rename from src/muz/rel_context.cpp rename to src/muz/rel/rel_context.cpp index 75f68c7f1..c57ef2606 100644 --- a/src/muz/rel_context.cpp +++ b/src/muz/rel/rel_context.cpp @@ -29,12 +29,23 @@ Revision History: #include"dl_product_relation.h" #include"dl_bound_relation.h" #include"dl_interval_relation.h" -#include"dl_mk_karr_invariants.h" +#include"karr_relation.h" #include"dl_finite_product_relation.h" #include"dl_sparse_table.h" #include"dl_table.h" #include"dl_table_relation.h" #include"aig_exporter.h" +#include"dl_mk_simple_joins.h" +#include"dl_mk_similarity_compressor.h" +#include"dl_mk_unbound_compressor.h" +#include"dl_mk_subsumption_checker.h" +#include"dl_mk_partial_equiv.h" +#include"dl_mk_coi_filter.h" +#include"dl_mk_filter_rules.h" +#include"dl_mk_rule_inliner.h" +#include"dl_mk_interp_tail_simplifier.h" +#include"dl_mk_bit_blast.h" + namespace datalog { @@ -75,7 +86,7 @@ namespace datalog { }; rel_context::rel_context(context& ctx) - : engine_base(ctx.get_manager(), "datalog"), + : rel_context_base(ctx.get_manager(), "datalog"), m_context(ctx), m(ctx.get_manager()), m_rmanager(ctx), @@ -127,7 +138,7 @@ namespace datalog { m_code.reset(); termination_code.reset(); m_context.ensure_closed(); - m_context.transform_rules(); + transform_rules(); if (m_context.canceled()) { result = l_undef; break; @@ -252,6 +263,39 @@ namespace datalog { return res; } + void rel_context::transform_rules() { + rule_transformer transf(m_context); + transf.register_plugin(alloc(mk_coi_filter, m_context)); + transf.register_plugin(alloc(mk_filter_rules, m_context)); + transf.register_plugin(alloc(mk_simple_joins, m_context)); + if (m_context.unbound_compressor()) { + transf.register_plugin(alloc(mk_unbound_compressor, m_context)); + } + if (m_context.similarity_compressor()) { + transf.register_plugin(alloc(mk_similarity_compressor, m_context)); + } + transf.register_plugin(alloc(mk_partial_equivalence_transformer, m_context)); + transf.register_plugin(alloc(mk_rule_inliner, m_context)); + transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context)); + + if (m_context.get_params().bit_blast()) { + transf.register_plugin(alloc(mk_bit_blast, m_context, 22000)); + transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context, 21000)); + } + m_context.transform_rules(transf); + } + + bool rel_context::try_get_size(func_decl* p, unsigned& rel_size) const { + relation_base* rb = try_get_relation(p); + if (rb && rb->knows_exact_size()) { + rel_size = rb->get_size_estimate_rows(); + return true; + } + else { + return false; + } + } + lbool rel_context::query(expr* query) { get_rmanager().reset_saturated_marks(); scoped_query _scoped_query(m_context); @@ -372,6 +416,20 @@ namespace datalog { relation_base & rel_context::get_relation(func_decl * pred) { return get_rmanager().get_relation(pred); } relation_base * rel_context::try_get_relation(func_decl * pred) const { return get_rmanager().try_get_relation(pred); } + expr_ref rel_context::try_get_formula(func_decl* p) const { + expr_ref result(m); + relation_base* rb = try_get_relation(p); + if (rb) { + rb->to_formula(result); + } + return result; + } + + bool rel_context::is_empty_relation(func_decl* pred) const { + relation_base* rb = try_get_relation(pred); + return !rb || rb->empty(); + } + relation_manager & rel_context::get_rmanager() { return m_rmanager; } const relation_manager & rel_context::get_rmanager() const { return m_rmanager; } diff --git a/src/muz/rel_context.h b/src/muz/rel/rel_context.h similarity index 55% rename from src/muz/rel_context.h rename to src/muz/rel/rel_context.h index 20da827ce..68a6fbd90 100644 --- a/src/muz/rel_context.h +++ b/src/muz/rel/rel_context.h @@ -23,6 +23,8 @@ Revision History: #include "ast.h" #include "dl_relation_manager.h" #include "dl_instruction.h" +#include "dl_engine_base.h" +#include "dl_context.h" #include "lbool.h" namespace datalog { @@ -30,7 +32,7 @@ namespace datalog { class context; typedef vector > fact_vector; - class rel_context : public engine_base { + class rel_context : public rel_context_base { context& m_context; ast_manager& m; relation_manager m_rmanager; @@ -50,30 +52,33 @@ namespace datalog { lbool saturate(scoped_query& sq); + void set_cancel(bool f); + public: rel_context(context& ctx); - ~rel_context(); + virtual ~rel_context(); - relation_manager & get_rmanager(); - const relation_manager & get_rmanager() const; + virtual relation_manager & get_rmanager(); + virtual const relation_manager & get_rmanager() const; ast_manager& get_manager() const { return m; } context& get_context() const { return m_context; } - relation_base & get_relation(func_decl * pred); - relation_base * try_get_relation(func_decl * pred) const; + virtual relation_base & get_relation(func_decl * pred); + virtual relation_base * try_get_relation(func_decl * pred) const; + virtual bool is_empty_relation(func_decl* pred) const; + virtual expr_ref try_get_formula(func_decl * pred) const; virtual expr_ref get_answer() { return m_answer; } - bool output_profile() const; + virtual bool output_profile() const; virtual lbool query(expr* q); - lbool query(unsigned num_rels, func_decl * const* rels); + virtual lbool query(unsigned num_rels, func_decl * const* rels); - void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, + virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names); - void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred); + virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred); - void set_cancel(bool f); virtual void cancel() { set_cancel(true); } virtual void cleanup() { set_cancel(false);} @@ -83,35 +88,41 @@ namespace datalog { The function deallocates unsused relations, it does not deal with rules. */ - void restrict_predicates(func_decl_set const& predicates); + virtual void restrict_predicates(func_decl_set const& predicates); + virtual void transform_rules(); + virtual bool try_get_size(func_decl* pred, unsigned& rel_size) const; /** \brief query result if it contains fact. */ - bool result_contains_fact(relation_fact const& f); + virtual bool result_contains_fact(relation_fact const& f); + + virtual void collect_non_empty_predicates(func_decl_set& ps) { + return get_rmanager().collect_non_empty_predicates(ps); + } /** \brief add facts to relation */ - void add_fact(func_decl* pred, relation_fact const& fact); - void add_fact(func_decl* pred, table_fact const& fact); + virtual void add_fact(func_decl* pred, relation_fact const& fact); + virtual void add_fact(func_decl* pred, table_fact const& fact); /** \brief check if facts were added to relation */ - bool has_facts(func_decl * pred) const; + virtual bool has_facts(func_decl * pred) const; /** \brief Store the relation \c rel under the predicate \c pred. The \c context object takes over the ownership of the relation object. */ - void store_relation(func_decl * pred, relation_base * rel); + virtual void store_relation(func_decl * pred, relation_base * rel); - void display_output_facts(rule_set const& rules, std::ostream & out) const; - void display_facts(std::ostream & out) const; + virtual void display_output_facts(rule_set const& rules, std::ostream & out) const; + virtual void display_facts(std::ostream & out) const; - void display_profile(std::ostream& out); + virtual void display_profile(std::ostream& out); - lbool saturate(); + virtual lbool saturate(); }; }; diff --git a/src/muz/tab_context.cpp b/src/muz/tab/tab_context.cpp similarity index 100% rename from src/muz/tab_context.cpp rename to src/muz/tab/tab_context.cpp diff --git a/src/muz/tab_context.h b/src/muz/tab/tab_context.h similarity index 96% rename from src/muz/tab_context.h rename to src/muz/tab/tab_context.h index c23ccb7d3..4689598c0 100644 --- a/src/muz/tab_context.h +++ b/src/muz/tab/tab_context.h @@ -22,7 +22,7 @@ Revision History: #include "ast.h" #include "lbool.h" #include "statistics.h" -#include "dl_util.h" +#include "dl_engine_base.h" namespace datalog { class context; diff --git a/src/muz/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp similarity index 100% rename from src/muz/dl_mk_array_blast.cpp rename to src/muz/transforms/dl_mk_array_blast.cpp diff --git a/src/muz/dl_mk_array_blast.h b/src/muz/transforms/dl_mk_array_blast.h similarity index 100% rename from src/muz/dl_mk_array_blast.h rename to src/muz/transforms/dl_mk_array_blast.h diff --git a/src/muz/dl_mk_backwards.cpp b/src/muz/transforms/dl_mk_backwards.cpp similarity index 100% rename from src/muz/dl_mk_backwards.cpp rename to src/muz/transforms/dl_mk_backwards.cpp diff --git a/src/muz/dl_mk_backwards.h b/src/muz/transforms/dl_mk_backwards.h similarity index 100% rename from src/muz/dl_mk_backwards.h rename to src/muz/transforms/dl_mk_backwards.h diff --git a/src/muz/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp similarity index 100% rename from src/muz/dl_mk_bit_blast.cpp rename to src/muz/transforms/dl_mk_bit_blast.cpp diff --git a/src/muz/dl_mk_bit_blast.h b/src/muz/transforms/dl_mk_bit_blast.h similarity index 100% rename from src/muz/dl_mk_bit_blast.h rename to src/muz/transforms/dl_mk_bit_blast.h diff --git a/src/muz/dl_mk_coalesce.cpp b/src/muz/transforms/dl_mk_coalesce.cpp similarity index 100% rename from src/muz/dl_mk_coalesce.cpp rename to src/muz/transforms/dl_mk_coalesce.cpp diff --git a/src/muz/dl_mk_coalesce.h b/src/muz/transforms/dl_mk_coalesce.h similarity index 100% rename from src/muz/dl_mk_coalesce.h rename to src/muz/transforms/dl_mk_coalesce.h diff --git a/src/muz/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp similarity index 92% rename from src/muz/dl_mk_coi_filter.cpp rename to src/muz/transforms/dl_mk_coi_filter.cpp index a0fc845bf..fc4c411ff 100644 --- a/src/muz/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -48,7 +48,7 @@ namespace datalog { } rule_set * mk_coi_filter::bottom_up(rule_set const & source) { - decl_set all, reached; + func_decl_set all, reached; ptr_vector todo; rule_set::decl2rules body2rules; // initialization for reachability @@ -117,7 +117,7 @@ namespace datalog { // set to false each unreached predicate if (m_context.get_model_converter()) { extension_model_converter* mc0 = alloc(extension_model_converter, m); - for (decl_set::iterator it = all.begin(); it != all.end(); ++it) { + for (func_decl_set::iterator it = all.begin(); it != all.end(); ++it) { if (!reached.contains(*it)) { mc0->insert(*it, m.mk_false()); } @@ -134,13 +134,13 @@ namespace datalog { rule_set * mk_coi_filter::top_down(rule_set const & source) { - decl_set interesting_preds; - decl_set pruned_preds; + func_decl_set interesting_preds; + func_decl_set pruned_preds; ptr_vector todo; { - const decl_set& output_preds = source.get_output_predicates(); - decl_set::iterator oend = output_preds.end(); - for (decl_set::iterator it = output_preds.begin(); it!=oend; ++it) { + const func_decl_set& output_preds = source.get_output_predicates(); + func_decl_set::iterator oend = output_preds.end(); + for (func_decl_set::iterator it = output_preds.begin(); it!=oend; ++it) { todo.push_back(*it); interesting_preds.insert(*it); } @@ -185,8 +185,8 @@ namespace datalog { } if (res && m_context.get_model_converter()) { - decl_set::iterator end = pruned_preds.end(); - decl_set::iterator it = pruned_preds.begin(); + func_decl_set::iterator end = pruned_preds.end(); + func_decl_set::iterator it = pruned_preds.begin(); extension_model_converter* mc0 = alloc(extension_model_converter, m); for (; it != end; ++it) { mc0->insert(*it, m.mk_true()); diff --git a/src/muz/dl_mk_coi_filter.h b/src/muz/transforms/dl_mk_coi_filter.h similarity index 100% rename from src/muz/dl_mk_coi_filter.h rename to src/muz/transforms/dl_mk_coi_filter.h diff --git a/src/muz/dl_mk_different.h b/src/muz/transforms/dl_mk_different.h similarity index 100% rename from src/muz/dl_mk_different.h rename to src/muz/transforms/dl_mk_different.h diff --git a/src/muz/dl_mk_filter_rules.cpp b/src/muz/transforms/dl_mk_filter_rules.cpp similarity index 100% rename from src/muz/dl_mk_filter_rules.cpp rename to src/muz/transforms/dl_mk_filter_rules.cpp diff --git a/src/muz/dl_mk_filter_rules.h b/src/muz/transforms/dl_mk_filter_rules.h similarity index 100% rename from src/muz/dl_mk_filter_rules.h rename to src/muz/transforms/dl_mk_filter_rules.h diff --git a/src/muz/dl_mk_interp_tail_simplifier.cpp b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp similarity index 100% rename from src/muz/dl_mk_interp_tail_simplifier.cpp rename to src/muz/transforms/dl_mk_interp_tail_simplifier.cpp diff --git a/src/muz/dl_mk_interp_tail_simplifier.h b/src/muz/transforms/dl_mk_interp_tail_simplifier.h similarity index 100% rename from src/muz/dl_mk_interp_tail_simplifier.h rename to src/muz/transforms/dl_mk_interp_tail_simplifier.h diff --git a/src/muz/transforms/dl_mk_karr_invariants.cpp b/src/muz/transforms/dl_mk_karr_invariants.cpp new file mode 100644 index 000000000..4c0a24b65 --- /dev/null +++ b/src/muz/transforms/dl_mk_karr_invariants.cpp @@ -0,0 +1,325 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_karr_invariants.cpp + +Abstract: + + Extract integer linear invariants. + + The linear invariants are extracted according to Karr's method. + A short description is in + Nikolaj Bjorner, Anca Browne and Zohar Manna. Automatic Generation + of Invariants and Intermediate Assertions, in CP 95. + + The algorithm is here adapted to Horn clauses. + The idea is to maintain two data-structures for each recursive relation. + We call them R and RD + - R - set of linear congruences that are true of R. + - RD - the dual basis of of solutions for R. + + RD is updated by accumulating basis vectors for solutions + to R (the homogeneous dual of R) + R is updated from the inhomogeneous dual of RD. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-09 + +Revision History: + +--*/ + +#include"expr_safe_replace.h" +#include"bool_rewriter.h" +#include"for_each_expr.h" + +#include"dl_mk_karr_invariants.h" +#include"dl_mk_backwards.h" +#include"dl_mk_loop_counter.h" + +namespace datalog { + + + mk_karr_invariants::mk_karr_invariants(context & ctx, unsigned priority): + rule_transformer::plugin(priority, false), + m_ctx(ctx), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_inner_ctx(m, ctx.get_register_engine(), ctx.get_fparams()), + a(m), + m_pinned(m), + m_cancel(false) { + params_ref params; + params.set_sym("default_relation", symbol("karr_relation")); + params.set_sym("engine", symbol("datalog")); + params.set_bool("karr", false); + m_inner_ctx.updt_params(params); + } + + mk_karr_invariants::~mk_karr_invariants() { } + + matrix& matrix::operator=(matrix const& other) { + reset(); + append(other); + return *this; + } + + void matrix::display_row( + std::ostream& out, vector const& row, rational const& b, bool is_eq) { + for (unsigned j = 0; j < row.size(); ++j) { + out << row[j] << " "; + } + out << (is_eq?" = ":" >= ") << -b << "\n"; + } + + void matrix::display_ineq( + std::ostream& out, vector const& row, rational const& b, bool is_eq) { + bool first = true; + for (unsigned j = 0; j < row.size(); ++j) { + if (!row[j].is_zero()) { + if (!first && row[j].is_pos()) { + out << "+ "; + } + if (row[j].is_minus_one()) { + out << "- "; + } + if (row[j] > rational(1) || row[j] < rational(-1)) { + out << row[j] << "*"; + } + out << "x" << j << " "; + first = false; + } + } + out << (is_eq?"= ":">= ") << -b << "\n"; + } + + void matrix::display(std::ostream& out) const { + for (unsigned i = 0; i < A.size(); ++i) { + display_row(out, A[i], b[i], eq[i]); + } + } + + + class mk_karr_invariants::add_invariant_model_converter : public model_converter { + ast_manager& m; + arith_util a; + func_decl_ref_vector m_funcs; + expr_ref_vector m_invs; + public: + + add_invariant_model_converter(ast_manager& m): m(m), a(m), m_funcs(m), m_invs(m) {} + + virtual ~add_invariant_model_converter() { } + + void add(func_decl* p, expr* inv) { + if (!m.is_true(inv)) { + m_funcs.push_back(p); + m_invs.push_back(inv); + } + } + + virtual void operator()(model_ref & mr) { + for (unsigned i = 0; i < m_funcs.size(); ++i) { + func_decl* p = m_funcs[i].get(); + func_interp* f = mr->get_func_interp(p); + expr_ref body(m); + unsigned arity = p->get_arity(); + SASSERT(0 < arity); + if (f) { + SASSERT(f->num_entries() == 0); + if (!f->is_partial()) { + bool_rewriter(m).mk_and(f->get_else(), m_invs[i].get(), body); + } + } + else { + f = alloc(func_interp, m, arity); + mr->register_decl(p, f); + body = m.mk_false(); // fragile: assume that relation was pruned by being infeasible. + } + f->set_else(body); + } + } + + virtual model_converter * translate(ast_translation & translator) { + add_invariant_model_converter* mc = alloc(add_invariant_model_converter, m); + for (unsigned i = 0; i < m_funcs.size(); ++i) { + mc->add(translator(m_funcs[i].get()), m_invs[i].get()); + } + return mc; + } + + private: + void mk_body(matrix const& M, expr_ref& body) { + expr_ref_vector conj(m); + for (unsigned i = 0; i < M.size(); ++i) { + mk_body(M.A[i], M.b[i], M.eq[i], conj); + } + bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), body); + } + + void mk_body(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) { + expr_ref_vector sum(m); + expr_ref zero(m), lhs(m); + zero = a.mk_numeral(rational(0), true); + + for (unsigned i = 0; i < row.size(); ++i) { + if (row[i].is_zero()) { + continue; + } + var* var = m.mk_var(i, a.mk_int()); + if (row[i].is_one()) { + sum.push_back(var); + } + else { + sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); + } + } + if (!b.is_zero()) { + sum.push_back(a.mk_numeral(b, true)); + } + lhs = a.mk_add(sum.size(), sum.c_ptr()); + if (is_eq) { + conj.push_back(m.mk_eq(lhs, zero)); + } + else { + conj.push_back(a.mk_ge(lhs, zero)); + } + } + }; + + void mk_karr_invariants::cancel() { + m_cancel = true; + m_inner_ctx.cancel(); + } + + rule_set * mk_karr_invariants::operator()(rule_set const & source) { + if (!m_ctx.get_params().karr()) { + return 0; + } + rule_set::iterator it = source.begin(), end = source.end(); + for (; it != end; ++it) { + rule const& r = **it; + if (r.has_negation()) { + return 0; + } + } + mk_loop_counter lc(m_ctx); + mk_backwards bwd(m_ctx); + + scoped_ptr src_loop = lc(source); + TRACE("dl", src_loop->display(tout << "source loop\n");); + + get_invariants(*src_loop); + + if (m_cancel) { + return 0; + } + + // figure out whether to update same rules as used for saturation. + scoped_ptr rev_source = bwd(*src_loop); + get_invariants(*rev_source); + scoped_ptr src_annot = update_rules(*src_loop); + rule_set* rules = lc.revert(*src_annot); + rules->inherit_predicates(source); + TRACE("dl", rules->display(tout);); + m_pinned.reset(); + m_fun2inv.reset(); + return rules; + } + + void mk_karr_invariants::get_invariants(rule_set const& src) { + m_inner_ctx.reset(); + rel_context_base& rctx = *m_inner_ctx.get_rel_context(); + ptr_vector heads; + func_decl_set const& predicates = m_ctx.get_predicates(); + for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) { + m_inner_ctx.register_predicate(*fit, false); + } + m_inner_ctx.ensure_opened(); + m_inner_ctx.replace_rules(src); + m_inner_ctx.close(); + rule_set::decl2rules::iterator dit = src.begin_grouped_rules(); + rule_set::decl2rules::iterator dend = src.end_grouped_rules(); + for (; dit != dend; ++dit) { + heads.push_back(dit->m_key); + } + m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); + + // retrieve invariants. + dit = src.begin_grouped_rules(); + for (; dit != dend; ++dit) { + func_decl* p = dit->m_key; + expr_ref fml = rctx.try_get_formula(p); + if (fml && !m.is_true(fml)) { + expr* inv = 0; + if (m_fun2inv.find(p, inv)) { + fml = m.mk_and(inv, fml); + } + m_pinned.push_back(fml); + m_fun2inv.insert(p, fml); + } + } + } + + rule_set* mk_karr_invariants::update_rules(rule_set const& src) { + scoped_ptr dst = alloc(rule_set, m_ctx); + rule_set::iterator it = src.begin(), end = src.end(); + for (; it != end; ++it) { + update_body(*dst, **it); + } + if (m_ctx.get_model_converter()) { + add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m); + rule_set::decl2rules::iterator git = src.begin_grouped_rules(); + rule_set::decl2rules::iterator gend = src.end_grouped_rules(); + for (; git != gend; ++git) { + func_decl* p = git->m_key; + expr* fml = 0; + if (m_fun2inv.find(p, fml)) { + kmc->add(p, fml); + } + } + m_ctx.add_model_converter(kmc); + } + + dst->inherit_predicates(src); + return dst.detach(); + } + + void mk_karr_invariants::update_body(rule_set& rules, rule& r) { + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + app_ref_vector tail(m); + expr_ref fml(m); + for (unsigned i = 0; i < tsz; ++i) { + tail.push_back(r.get_tail(i)); + } + for (unsigned i = 0; i < utsz; ++i) { + func_decl* q = r.get_decl(i); + expr* fml = 0; + if (m_fun2inv.find(q, fml)) { + expr_safe_replace rep(m); + for (unsigned j = 0; j < q->get_arity(); ++j) { + rep.insert(m.mk_var(j, q->get_domain(j)), + r.get_tail(i)->get_arg(j)); + } + expr_ref tmp(fml, m); + rep(tmp); + tail.push_back(to_app(tmp)); + } + } + rule* new_rule = &r; + if (tail.size() != tsz) { + new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), 0, r.name()); + } + rules.add_rule(new_rule); + rm.mk_rule_rewrite_proof(r, *new_rule); // should be weakening rule. + } + + + + +}; + diff --git a/src/muz/transforms/dl_mk_karr_invariants.h b/src/muz/transforms/dl_mk_karr_invariants.h new file mode 100644 index 000000000..378a7e587 --- /dev/null +++ b/src/muz/transforms/dl_mk_karr_invariants.h @@ -0,0 +1,81 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_karr_invariants.h + +Abstract: + + Extract integer linear invariants. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-08 + +Revision History: + +--*/ +#ifndef _DL_MK_KARR_INVARIANTS_H_ +#define _DL_MK_KARR_INVARIANTS_H_ + +#include"dl_context.h" +#include"dl_rule_set.h" +#include"dl_rule_transformer.h" +#include"arith_decl_plugin.h" +#include"hilbert_basis.h" + +namespace datalog { + + /** + \brief Rule transformer that strengthens bodies with invariants. + */ + + struct matrix { + vector > A; + vector b; + svector eq; + unsigned size() const { return A.size(); } + void reset() { A.reset(); b.reset(); eq.reset(); } + matrix& operator=(matrix const& other); + void append(matrix const& other) { A.append(other.A); b.append(other.b); eq.append(other.eq); } + void display(std::ostream& out) const; + static void display_row( + std::ostream& out, vector const& row, rational const& b, bool is_eq); + static void display_ineq( + std::ostream& out, vector const& row, rational const& b, bool is_eq); + }; + + class mk_karr_invariants : public rule_transformer::plugin { + + class add_invariant_model_converter; + + context& m_ctx; + ast_manager& m; + rule_manager& rm; + context m_inner_ctx; + arith_util a; + obj_map m_fun2inv; + ast_ref_vector m_pinned; + volatile bool m_cancel; + + void get_invariants(rule_set const& src); + + void update_body(rule_set& result, rule& r); + rule_set* update_rules(rule_set const& src); + public: + mk_karr_invariants(context & ctx, unsigned priority); + + virtual ~mk_karr_invariants(); + + virtual void cancel(); + + rule_set * operator()(rule_set const & source); + + }; + + +}; + +#endif /* _DL_MK_KARR_INVARIANTS_H_ */ + diff --git a/src/muz/dl_mk_loop_counter.cpp b/src/muz/transforms/dl_mk_loop_counter.cpp similarity index 100% rename from src/muz/dl_mk_loop_counter.cpp rename to src/muz/transforms/dl_mk_loop_counter.cpp diff --git a/src/muz/dl_mk_loop_counter.h b/src/muz/transforms/dl_mk_loop_counter.h similarity index 100% rename from src/muz/dl_mk_loop_counter.h rename to src/muz/transforms/dl_mk_loop_counter.h diff --git a/src/muz/dl_mk_magic_sets.cpp b/src/muz/transforms/dl_mk_magic_sets.cpp similarity index 99% rename from src/muz/dl_mk_magic_sets.cpp rename to src/muz/transforms/dl_mk_magic_sets.cpp index 24d9d01cb..48bd69255 100644 --- a/src/muz/dl_mk_magic_sets.cpp +++ b/src/muz/transforms/dl_mk_magic_sets.cpp @@ -362,7 +362,7 @@ namespace datalog { rule * r = *it; transform_rule(task.m_adornment, r, *result); } - if (!m_context.get_rel_context()->get_relation(task.m_pred).empty()) { + if (!m_context.get_rel_context()->is_empty_relation(task.m_pred)) { //we need a rule to copy facts that are already in a relation into the adorned //relation (since out intentional predicates can have facts, not only rules) create_transfer_rule(task, *result); diff --git a/src/muz/dl_mk_magic_sets.h b/src/muz/transforms/dl_mk_magic_sets.h similarity index 100% rename from src/muz/dl_mk_magic_sets.h rename to src/muz/transforms/dl_mk_magic_sets.h diff --git a/src/muz/dl_mk_magic_symbolic.cpp b/src/muz/transforms/dl_mk_magic_symbolic.cpp similarity index 100% rename from src/muz/dl_mk_magic_symbolic.cpp rename to src/muz/transforms/dl_mk_magic_symbolic.cpp diff --git a/src/muz/dl_mk_magic_symbolic.h b/src/muz/transforms/dl_mk_magic_symbolic.h similarity index 100% rename from src/muz/dl_mk_magic_symbolic.h rename to src/muz/transforms/dl_mk_magic_symbolic.h diff --git a/src/muz/dl_mk_quantifier_abstraction.cpp b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp similarity index 100% rename from src/muz/dl_mk_quantifier_abstraction.cpp rename to src/muz/transforms/dl_mk_quantifier_abstraction.cpp diff --git a/src/muz/dl_mk_quantifier_abstraction.h b/src/muz/transforms/dl_mk_quantifier_abstraction.h similarity index 100% rename from src/muz/dl_mk_quantifier_abstraction.h rename to src/muz/transforms/dl_mk_quantifier_abstraction.h diff --git a/src/muz/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp similarity index 100% rename from src/muz/dl_mk_quantifier_instantiation.cpp rename to src/muz/transforms/dl_mk_quantifier_instantiation.cpp diff --git a/src/muz/dl_mk_quantifier_instantiation.h b/src/muz/transforms/dl_mk_quantifier_instantiation.h similarity index 100% rename from src/muz/dl_mk_quantifier_instantiation.h rename to src/muz/transforms/dl_mk_quantifier_instantiation.h diff --git a/src/muz/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp similarity index 99% rename from src/muz/dl_mk_rule_inliner.cpp rename to src/muz/transforms/dl_mk_rule_inliner.cpp index 5e0d6446b..65ce44b8b 100644 --- a/src/muz/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -49,9 +49,6 @@ Subsumption transformation (remove rule): #include #include "ast_pp.h" -#include "dl_finite_product_relation.h" -#include "dl_product_relation.h" -#include "dl_sieve_relation.h" #include "rewriter.h" #include "rewriter_def.h" #include "dl_mk_rule_inliner.h" @@ -206,9 +203,9 @@ namespace datalog { void mk_rule_inliner::count_pred_occurrences(rule_set const & orig) { - rel_context* rel = m_context.get_rel_context(); + rel_context_base* rel = m_context.get_rel_context(); if (rel) { - rel->get_rmanager().collect_non_empty_predicates(m_preds_with_facts); + rel->collect_non_empty_predicates(m_preds_with_facts); } rule_set::iterator rend = orig.end(); diff --git a/src/muz/dl_mk_rule_inliner.h b/src/muz/transforms/dl_mk_rule_inliner.h similarity index 98% rename from src/muz/dl_mk_rule_inliner.h rename to src/muz/transforms/dl_mk_rule_inliner.h index 3a933f990..ed555492a 100644 --- a/src/muz/dl_mk_rule_inliner.h +++ b/src/muz/transforms/dl_mk_rule_inliner.h @@ -106,9 +106,9 @@ namespace datalog { context & m_context; th_rewriter& m_simp; rule_ref_vector m_pinned; - decl_set m_forbidden_preds; - decl_set m_preds_with_facts; - decl_set m_preds_with_neg_occurrence; + func_decl_set m_forbidden_preds; + func_decl_set m_preds_with_facts; + func_decl_set m_preds_with_neg_occurrence; ast_counter m_head_pred_ctr; ast_counter m_head_pred_non_empty_tails_ctr; ast_counter m_tail_pred_ctr; diff --git a/src/muz/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp similarity index 100% rename from src/muz/dl_mk_scale.cpp rename to src/muz/transforms/dl_mk_scale.cpp diff --git a/src/muz/dl_mk_scale.h b/src/muz/transforms/dl_mk_scale.h similarity index 100% rename from src/muz/dl_mk_scale.h rename to src/muz/transforms/dl_mk_scale.h diff --git a/src/muz/dl_mk_slice.cpp b/src/muz/transforms/dl_mk_slice.cpp similarity index 100% rename from src/muz/dl_mk_slice.cpp rename to src/muz/transforms/dl_mk_slice.cpp diff --git a/src/muz/dl_mk_slice.h b/src/muz/transforms/dl_mk_slice.h similarity index 100% rename from src/muz/dl_mk_slice.h rename to src/muz/transforms/dl_mk_slice.h diff --git a/src/muz/dl_mk_subsumption_checker.cpp b/src/muz/transforms/dl_mk_subsumption_checker.cpp similarity index 96% rename from src/muz/dl_mk_subsumption_checker.cpp rename to src/muz/transforms/dl_mk_subsumption_checker.cpp index 0d32a5c3b..9b2c5627b 100644 --- a/src/muz/dl_mk_subsumption_checker.cpp +++ b/src/muz/transforms/dl_mk_subsumption_checker.cpp @@ -24,7 +24,6 @@ Revision History: #include "rewriter.h" #include "rewriter_def.h" #include"dl_mk_subsumption_checker.h" -#include"dl_table_relation.h" namespace datalog { @@ -249,23 +248,21 @@ namespace datalog { } void mk_subsumption_checker::scan_for_relations_total_due_to_facts(rule_set const& source) { - rel_context* rel = m_context.get_rel_context(); + rel_context_base* rel = m_context.get_rel_context(); if (!rel) { return; } relation_manager& rm = rel->get_rmanager(); - decl_set const& candidate_preds = m_context.get_predicates(); + func_decl_set const& candidate_preds = m_context.get_predicates(); - decl_set::iterator end = candidate_preds.end(); - for(decl_set::iterator it = candidate_preds.begin(); it!=end; ++it) { + func_decl_set::iterator end = candidate_preds.end(); + for(func_decl_set::iterator it = candidate_preds.begin(); it!=end; ++it) { func_decl * pred = *it; + unsigned rel_sz; - if (m_total_relations.contains(pred)) { continue; } //already total - - relation_base * rel = rm.try_get_relation(pred); - - if (!rel || !rel->knows_exact_size()) { continue; } + if (m_total_relations.contains(pred)) { continue; } // already total + if (!rel->try_get_size(pred, rel_sz)) { continue; } unsigned arity = pred->get_arity(); if (arity > 30) { continue; } @@ -280,7 +277,6 @@ namespace datalog { { unsigned total_size = 1<get_size_estimate_rows(); obj_hashtable * head_store; if(m_ground_unconditional_rule_heads.find(pred, head_store)) { diff --git a/src/muz/dl_mk_subsumption_checker.h b/src/muz/transforms/dl_mk_subsumption_checker.h similarity index 100% rename from src/muz/dl_mk_subsumption_checker.h rename to src/muz/transforms/dl_mk_subsumption_checker.h diff --git a/src/muz/dl_mk_unbound_compressor.cpp b/src/muz/transforms/dl_mk_unbound_compressor.cpp similarity index 99% rename from src/muz/dl_mk_unbound_compressor.cpp rename to src/muz/transforms/dl_mk_unbound_compressor.cpp index 71d4d5479..68c35e2c9 100644 --- a/src/muz/dl_mk_unbound_compressor.cpp +++ b/src/muz/transforms/dl_mk_unbound_compressor.cpp @@ -334,9 +334,9 @@ namespace datalog { // TODO mc m_modified = false; - rel_context* rel = m_context.get_rel_context(); + rel_context_base* rel = m_context.get_rel_context(); if (rel) { - rel->get_rmanager().collect_non_empty_predicates(m_non_empty_rels); + rel->collect_non_empty_predicates(m_non_empty_rels); } unsigned init_rule_cnt = source.get_num_rules(); SASSERT(m_rules.empty()); diff --git a/src/muz/dl_mk_unbound_compressor.h b/src/muz/transforms/dl_mk_unbound_compressor.h similarity index 98% rename from src/muz/dl_mk_unbound_compressor.h rename to src/muz/transforms/dl_mk_unbound_compressor.h index 4e2ff0b3c..44877e646 100644 --- a/src/muz/dl_mk_unbound_compressor.h +++ b/src/muz/transforms/dl_mk_unbound_compressor.h @@ -62,7 +62,7 @@ namespace datalog { /** Relations that contain facts */ - decl_set m_non_empty_rels; + func_decl_set m_non_empty_rels; ast_counter m_head_occurrence_ctr; diff --git a/src/muz/dl_mk_unfold.cpp b/src/muz/transforms/dl_mk_unfold.cpp similarity index 100% rename from src/muz/dl_mk_unfold.cpp rename to src/muz/transforms/dl_mk_unfold.cpp diff --git a/src/muz/dl_mk_unfold.h b/src/muz/transforms/dl_mk_unfold.h similarity index 100% rename from src/muz/dl_mk_unfold.h rename to src/muz/transforms/dl_mk_unfold.h diff --git a/src/muz/transforms/dl_transforms.cpp b/src/muz/transforms/dl_transforms.cpp new file mode 100644 index 000000000..7c5707ccf --- /dev/null +++ b/src/muz/transforms/dl_transforms.cpp @@ -0,0 +1,81 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_transforms.cpp + +Abstract: + + Default transformations. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-08-28. + +Revision History: + + Extracted from dl_context + +--*/ + +#include"dl_transforms.h" +#include"dl_rule_transformer.h" +#include"dl_mk_coi_filter.h" +#include"dl_mk_filter_rules.h" +#include"dl_mk_interp_tail_simplifier.h" +#include"dl_mk_rule_inliner.h" +#include"dl_mk_bit_blast.h" +#include"dl_mk_array_blast.h" +#include"dl_mk_karr_invariants.h" +#include"dl_mk_magic_symbolic.h" +#include"dl_mk_quantifier_abstraction.h" +#include"dl_mk_quantifier_instantiation.h" +#include"dl_mk_subsumption_checker.h" +#include"dl_mk_scale.h" + +namespace datalog { + + void apply_default_transformation(context& ctx) { + rule_transformer transf(ctx); + ctx.ensure_closed(); + transf.reset(); + transf.register_plugin(alloc(datalog::mk_coi_filter, ctx)); + transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx)); + + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 35005)); + transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 35000)); + transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34990)); + transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx, 34980)); + + //and another round of inlining + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34975)); + transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34970)); + transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34960)); + transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx, 34950)); + + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34940)); + transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34930)); + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34920)); + transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34910)); + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34900)); + transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34890)); + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34880)); + + + if (ctx.get_params().quantify_arrays()) { + transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, ctx, 33000)); + transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 32500)); + } + transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, ctx, 32000)); + + transf.register_plugin(alloc(datalog::mk_bit_blast, ctx, 35000)); + transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 36000)); + transf.register_plugin(alloc(datalog::mk_karr_invariants, ctx, 36010)); + if (ctx.get_params().magic()) { + transf.register_plugin(alloc(datalog::mk_magic_symbolic, ctx, 36020)); + } + transf.register_plugin(alloc(datalog::mk_scale, ctx, 36030)); + ctx.transform_rules(transf); + } +} diff --git a/src/muz/transforms/dl_transforms.h b/src/muz/transforms/dl_transforms.h new file mode 100644 index 000000000..4f9a92dd8 --- /dev/null +++ b/src/muz/transforms/dl_transforms.h @@ -0,0 +1,30 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_transforms.h + +Abstract: + + Default transformations. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-08-28. + +Revision History: + + Extracted from dl_context + +--*/ +#ifndef _DL_TRANSFORMS_H_ +#define _DL_TRANSFORMS_H_ + +#include "dl_context.h" + +namespace datalog { + void apply_default_transformation(context& ctx); +} + +#endif diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index 56f60bfa0..15681ea36 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -31,6 +31,8 @@ Revision History: #include"dl_mk_filter_rules.h" #include"dl_finite_product_relation.h" #include"dl_context.h" +#include"rel_context.h" +#include"dl_register_engine.h" #include"datalog_parser.h" #include"datalog_frontend.h" #include"timeout.h" @@ -118,13 +120,14 @@ unsigned read_datalog(char const * file) { IF_VERBOSE(1, verbose_stream() << "Z3 Datalog Engine\n";); smt_params s_params; ast_manager m; + datalog::register_engine re; g_overall_time.start(); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); params_ref params; params.set_sym("engine", symbol("datalog")); - datalog::context ctx(m, s_params, params); + datalog::context ctx(m, re, s_params, params); datalog::relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); datalog::relation_plugin & inner_plg = *rmgr.get_relation_plugin(symbol("tr_hashtable")); SASSERT(&inner_plg); @@ -179,11 +182,11 @@ unsigned read_datalog(char const * file) { bool early_termination; unsigned timeout = ctx.initial_restart_timeout(); - if(timeout == 0) { + if (timeout == 0) { timeout = UINT_MAX; } do { - ctx.transform_rules(); + ctx.get_rel_context()->transform_rules(); datalog::compiler::compile(ctx, ctx.get_rules(), rules_code, termination_code); diff --git a/src/test/datalog_parser.cpp b/src/test/datalog_parser.cpp index 64ee4201e..e23650e3b 100644 --- a/src/test/datalog_parser.cpp +++ b/src/test/datalog_parser.cpp @@ -2,6 +2,7 @@ #include "ast_pp.h" #include "arith_decl_plugin.h" #include "dl_context.h" +#include "dl_register_engine.h" #include "smt_params.h" #include "reg_decl_plugins.h" @@ -12,8 +13,8 @@ static void dparse_string(char const* str) { ast_manager m; smt_params params; reg_decl_plugins(m); - - context ctx(m, params); + register_engine re; + context ctx(m, re, params); parser* p = parser::create(ctx,m); bool res=p->parse_string(str); @@ -39,8 +40,9 @@ static void dparse_file(char const* file) { ast_manager m; smt_params params; reg_decl_plugins(m); + register_engine re; - context ctx(m, params); + context ctx(m, re, params); parser* p = parser::create(ctx,m); if (!p->parse_file(file)) { diff --git a/src/test/dl_context.cpp b/src/test/dl_context.cpp index 9e8a37974..5c70aa8b5 100644 --- a/src/test/dl_context.cpp +++ b/src/test/dl_context.cpp @@ -3,6 +3,7 @@ #include "arith_decl_plugin.h" #include "dl_context.h" #include "smt_params.h" +#include "dl_register_engine.h" using namespace datalog; @@ -26,9 +27,9 @@ static lbool dl_context_eval_unary_predicate(ast_manager & m, context & ctx, cha static void dl_context_simple_query_test(params_ref & params) { ast_manager m; dl_decl_util decl_util(m); - + register_engine re; smt_params fparams; - context ctx(m, fparams); + context ctx(m, re, fparams); ctx.updt_params(params); /* lbool status = */ dl_context_eval_unary_predicate(m, ctx, "Z 64\n\nP(x:Z)\nP(\"a\").", "P"); @@ -50,7 +51,8 @@ void dl_context_saturate_file(params_ref & params, const char * f) { ast_manager m; dl_decl_util decl_util(m); smt_params fparams; - context ctx(m, fparams); + register_engine re; + context ctx(m, re, fparams); ctx.updt_params(params); datalog::parser * parser = datalog::parser::create(ctx, m); diff --git a/src/test/dl_product_relation.cpp b/src/test/dl_product_relation.cpp index d58603be1..357ccd604 100644 --- a/src/test/dl_product_relation.cpp +++ b/src/test/dl_product_relation.cpp @@ -1,7 +1,9 @@ #ifdef _WINDOWS #include "dl_context.h" +#include "dl_register_engine.h" #include "dl_finite_product_relation.h" #include "dl_sparse_table.h" +#include "rel_context.h" namespace datalog { @@ -21,8 +23,9 @@ namespace datalog { void test_functional_columns(smt_params fparams, params_ref& params) { ast_manager m; - context ctx(m, fparams); - rel_context& rctx = *ctx.get_rel_context(); + register_engine re; + context ctx(m, re, fparams); + rel_context_base& rctx = *ctx.get_rel_context(); ctx.updt_params(params); relation_manager & rmgr(rctx.get_rmanager()); @@ -124,7 +127,8 @@ namespace datalog { void test_finite_product_relation(smt_params fparams, params_ref& params) { ast_manager m; - context ctx(m, fparams); + register_engine re; + context ctx(m, re, fparams); ctx.updt_params(params); dl_decl_util dl_util(m); relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); diff --git a/src/test/dl_query.cpp b/src/test/dl_query.cpp index 17781d7bb..bb991c65f 100644 --- a/src/test/dl_query.cpp +++ b/src/test/dl_query.cpp @@ -2,9 +2,11 @@ #include "ast_pp.h" #include "dl_table_relation.h" #include "dl_context.h" +#include "dl_register_engine.h" #include "smt_params.h" #include "stopwatch.h" #include "reg_decl_plugins.h" +#include "dl_relation_manager.h" using namespace datalog; @@ -50,7 +52,8 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, dl_decl_util decl_util(m); random_gen ran(0); - context ctx_q(m, fparams); + register_engine re; + context ctx_q(m, re, fparams); params.set_bool("magic_sets_for_queries", use_magic_sets); ctx_q.updt_params(params); { @@ -135,7 +138,8 @@ void dl_query_test_wpa(smt_params & fparams, params_ref& params) { dl_decl_util dl_util(m); std::cerr << "Testing queries on " << problem_dir <<"\n"; - context ctx(m, fparams); + register_engine re; + context ctx(m, re, fparams); ctx.updt_params(params); { wpa_parser* p = wpa_parser::create(ctx, m); @@ -204,7 +208,8 @@ void tst_dl_query() { std::cerr << "Testing queries on " << problem_file <<"\n"; - context ctx_base(m, fparams); + register_engine re; + context ctx_base(m, re, fparams); ctx_base.updt_params(params); { parser* p = parser::create(ctx_base,m); diff --git a/src/test/dl_relation.cpp b/src/test/dl_relation.cpp index 609dca3da..bb1c8614c 100644 --- a/src/test/dl_relation.cpp +++ b/src/test/dl_relation.cpp @@ -1,5 +1,7 @@ #ifdef _WINDOWS #include "dl_context.h" +#include "dl_register_engine.h" +#include "dl_relation_manager.h" #include "dl_interval_relation.h" #include "dl_bound_relation.h" #include "dl_product_relation.h" @@ -10,7 +12,8 @@ namespace datalog { static void test_interval_relation() { smt_params params; ast_manager ast_m; - context ctx(ast_m, params); + register_engine re; + context ctx(ast_m, re, params); arith_util autil(ast_m); relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(interval_relation_plugin, m)); @@ -113,7 +116,8 @@ namespace datalog { smt_params params; ast_manager ast_m; - context ctx(ast_m, params); + register_engine re; + context ctx(ast_m, re, params); arith_util autil(ast_m); relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(bound_relation_plugin, m)); diff --git a/src/test/dl_table.cpp b/src/test/dl_table.cpp index d7025ed48..d24200f9b 100644 --- a/src/test/dl_table.cpp +++ b/src/test/dl_table.cpp @@ -1,6 +1,8 @@ #ifdef _WINDOWS #include "dl_context.h" #include "dl_table.h" +#include "dl_register_engine.h" +#include "dl_relation_manager.h" typedef datalog::table_base* (*mk_table_fn)(datalog::relation_manager& m, datalog::table_signature& sig); @@ -18,7 +20,8 @@ static void test_table(mk_table_fn mk_table) { sig.push_back(4); smt_params params; ast_manager ast_m; - datalog::context ctx(ast_m, params); + datalog::register_engine re; + datalog::context ctx(ast_m, re, params); datalog::relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(datalog::bitvector_table_plugin, m)); From c8f953525105a2c98f55e402faf6c2eb66b96059 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2013 21:23:16 -0700 Subject: [PATCH 129/509] re-organize muz_qe into separate units Signed-off-by: Nikolaj Bjorner --- src/muz/rel/dl_base.cpp | 490 ++++ src/muz/rel/dl_base.h | 1231 ++++++++++ src/muz/rel/dl_bound_relation.cpp | 707 ++++++ src/muz/rel/dl_bound_relation.h | 178 ++ src/muz/rel/dl_check_table.cpp | 439 ++++ src/muz/rel/dl_check_table.h | 135 ++ src/muz/rel/dl_external_relation.cpp | 456 ++++ src/muz/rel/dl_external_relation.h | 154 ++ src/muz/rel/dl_finite_product_relation.cpp | 2377 +++++++++++++++++++ src/muz/rel/dl_finite_product_relation.h | 366 +++ src/muz/rel/dl_interval_relation.cpp | 653 +++++ src/muz/rel/dl_interval_relation.h | 142 ++ src/muz/rel/dl_mk_explanations.cpp | 879 +++++++ src/muz/rel/dl_mk_explanations.h | 86 + src/muz/rel/dl_mk_partial_equiv.cpp | 155 ++ src/muz/rel/dl_mk_partial_equiv.h | 50 + src/muz/rel/dl_mk_similarity_compressor.cpp | 546 +++++ src/muz/rel/dl_mk_similarity_compressor.h | 78 + src/muz/rel/dl_mk_simple_joins.cpp | 742 ++++++ src/muz/rel/dl_mk_simple_joins.h | 63 + src/muz/rel/dl_product_relation.cpp | 1117 +++++++++ src/muz/rel/dl_product_relation.h | 191 ++ src/muz/rel/dl_relation_manager.cpp | 1702 +++++++++++++ src/muz/rel/dl_relation_manager.h | 688 ++++++ src/muz/rel/dl_sieve_relation.cpp | 666 ++++++ src/muz/rel/dl_sieve_relation.h | 198 ++ src/muz/rel/dl_sparse_table.cpp | 1246 ++++++++++ src/muz/rel/dl_sparse_table.h | 480 ++++ src/muz/rel/dl_table.cpp | 773 ++++++ src/muz/rel/dl_table.h | 265 +++ src/muz/rel/dl_table_plugin.h | 193 ++ src/muz/rel/dl_table_relation.cpp | 490 ++++ src/muz/rel/dl_table_relation.h | 133 ++ src/muz/rel/dl_vector_relation.h | 407 ++++ src/muz/rel/karr_relation.cpp | 790 ++++++ src/muz/rel/karr_relation.h | 88 + 36 files changed, 19354 insertions(+) create mode 100644 src/muz/rel/dl_base.cpp create mode 100644 src/muz/rel/dl_base.h create mode 100644 src/muz/rel/dl_bound_relation.cpp create mode 100644 src/muz/rel/dl_bound_relation.h create mode 100644 src/muz/rel/dl_check_table.cpp create mode 100644 src/muz/rel/dl_check_table.h create mode 100644 src/muz/rel/dl_external_relation.cpp create mode 100644 src/muz/rel/dl_external_relation.h create mode 100644 src/muz/rel/dl_finite_product_relation.cpp create mode 100644 src/muz/rel/dl_finite_product_relation.h create mode 100644 src/muz/rel/dl_interval_relation.cpp create mode 100644 src/muz/rel/dl_interval_relation.h create mode 100644 src/muz/rel/dl_mk_explanations.cpp create mode 100644 src/muz/rel/dl_mk_explanations.h create mode 100644 src/muz/rel/dl_mk_partial_equiv.cpp create mode 100644 src/muz/rel/dl_mk_partial_equiv.h create mode 100644 src/muz/rel/dl_mk_similarity_compressor.cpp create mode 100644 src/muz/rel/dl_mk_similarity_compressor.h create mode 100644 src/muz/rel/dl_mk_simple_joins.cpp create mode 100644 src/muz/rel/dl_mk_simple_joins.h create mode 100644 src/muz/rel/dl_product_relation.cpp create mode 100644 src/muz/rel/dl_product_relation.h create mode 100644 src/muz/rel/dl_relation_manager.cpp create mode 100644 src/muz/rel/dl_relation_manager.h create mode 100644 src/muz/rel/dl_sieve_relation.cpp create mode 100644 src/muz/rel/dl_sieve_relation.h create mode 100644 src/muz/rel/dl_sparse_table.cpp create mode 100644 src/muz/rel/dl_sparse_table.h create mode 100644 src/muz/rel/dl_table.cpp create mode 100644 src/muz/rel/dl_table.h create mode 100644 src/muz/rel/dl_table_plugin.h create mode 100644 src/muz/rel/dl_table_relation.cpp create mode 100644 src/muz/rel/dl_table_relation.h create mode 100644 src/muz/rel/dl_vector_relation.h create mode 100644 src/muz/rel/karr_relation.cpp create mode 100644 src/muz/rel/karr_relation.h diff --git a/src/muz/rel/dl_base.cpp b/src/muz/rel/dl_base.cpp new file mode 100644 index 000000000..dc10b5f8e --- /dev/null +++ b/src/muz/rel/dl_base.cpp @@ -0,0 +1,490 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_base.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-14. + +Revision History: + +--*/ + + +#include"ast_pp.h" +#include"union_find.h" +#include"vector.h" +#include"dl_context.h" +#include"dl_base.h" +#include"bool_rewriter.h" +#include"dl_relation_manager.h" +#include + + +namespace datalog { + + void universal_delete(relation_base* ptr) { + ptr->deallocate(); + } + + void universal_delete(table_base* ptr) { + ptr->deallocate(); + } + + void dealloc_ptr_vector_content(ptr_vector & v) { + ptr_vector::iterator it = v.begin(); + ptr_vector::iterator end = v.end(); + for(; it!=end; ++it) { + (*it)->deallocate(); + } + } + + void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig, + expr_ref_vector & renaming_arg) { + ast_manager & m = renaming_arg.get_manager(); + unsigned sz = map.size(); + unsigned ofs = sz-1; + renaming_arg.resize(sz, static_cast(0)); + for(unsigned i=0; i reset_fn = + get_manager().mk_filter_interpreted_fn(static_cast(*this), bottom_ref); + if(!reset_fn) { + NOT_IMPLEMENTED_YET(); + } + (*reset_fn)(*this); + } + + + + void table_signature::from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, table_signature & result) { + result.reset(); + + unsigned s1sz=s1.size(); + unsigned s2sz=s2.size(); + unsigned s1first_func=s1sz-s1.functional_columns(); + unsigned s2first_func=s2sz-s2.functional_columns(); + for(unsigned i=0; i=col_cnt); + result.set_functional_columns(func_cnt-col_cnt); + } + } + + void table_signature::from_project_with_reduce(const table_signature & src, unsigned col_cnt, + const unsigned * removed_cols, table_signature & result) { + signature_base::from_project(src, col_cnt, removed_cols, result); + + unsigned remaining_fun = src.functional_columns(); + unsigned first_src_fun = src.first_functional(); + for(int i=col_cnt-1; i>=0; i--) { + if(removed_cols[i] remaining_in_equivalence_class; + remaining_in_equivalence_class.resize(join_sig_sz, 0); + bool merging_rows_can_happen = false; + + union_find_default_ctx uf_ctx; + union_find<> uf(uf_ctx); //the numbers in uf correspond to column indexes after the join + for(unsigned i=0; icols1[i]) ? cols1[i] : (first_func_ofs+cols1[i]-s1_first_func); + unsigned idx2 = (s2_first_func>cols2[i]) ? (second_ofs+cols2[i]) : (second_func_ofs+cols2[i]-s2_first_func); + uf.merge(idx1, idx2); + } + for(unsigned i=0; i=first_func_ofs) { + //removing functional columns won't make us merge rows + continue; + } + unsigned eq_class_idx = uf.find(rc); + if(remaining_in_equivalence_class[eq_class_idx]>1) { + remaining_in_equivalence_class[eq_class_idx]--; + } + else { + merging_rows_can_happen = true; + break; + } + } + + if(merging_rows_can_happen) { + //this one marks all columns as non-functional + from_project(aux, removed_col_cnt, removed_cols, result); + SASSERT(result.functional_columns()==0); + } + else { + //this one preserves columns to be functional + from_project_with_reduce(aux, removed_col_cnt, removed_cols, result); + } + } + + + + + // ----------------------------------- + // + // table_base + // + // ----------------------------------- + + //here we give generic implementation of table operations using iterators + + bool table_base::empty() const { + return begin()==end(); + } + + void table_base::remove_facts(unsigned fact_cnt, const table_fact * facts) { + for(unsigned i=0; i to_remove; + table_base::iterator it = begin(); + table_base::iterator iend = end(); + table_fact row; + for(; it!=iend; ++it) { + it->get_fact(row); + to_remove.push_back(row); + } + remove_facts(to_remove.size(), to_remove.c_ptr()); + } + + bool table_base::contains_fact(const table_fact & f) const { + iterator it = begin(); + iterator iend = end(); + + table_fact row; + + for(; it!=iend; ++it) { + it->get_fact(row); + if(vectors_equal(row, f)) { + return true; + } + } + return false; + } + + bool table_base::fetch_fact(table_fact & f) const { + if(get_signature().functional_columns()==0) { + return contains_fact(f); + } + else { + unsigned sig_sz = get_signature().size(); + unsigned non_func_cnt = sig_sz-get_signature().functional_columns(); + table_base::iterator it = begin(); + table_base::iterator iend = end(); + table_fact row; + for(; it!=iend; ++it) { + it->get_fact(row); + bool differs = false; + for(unsigned i=0; iget_fact(row); + res->add_new_fact(row); + } + return res; + } + + /** + \brief Default method for complementation. + + It assumes that the compiler creates only tables with + at most one column (0 or 1 columns). + Complementation of tables with more than one columns + is transformed into a cross product of complements and/or + difference. + + */ + table_base * table_base::complement(func_decl* p, const table_element * func_columns) const { + const table_signature & sig = get_signature(); + SASSERT(sig.functional_columns()==0 || func_columns!=0); + SASSERT(sig.first_functional() <= 1); + + table_base * res = get_plugin().mk_empty(sig); + + table_fact fact; + fact.resize(sig.first_functional()); + fact.append(sig.functional_columns(), func_columns); + + if (sig.first_functional() == 0) { + if (empty()) { + res->add_fact(fact); + } + return res; + } + + VERIFY(sig.first_functional() == 1); + + uint64 upper_bound = get_signature()[0]; + bool empty_table = empty(); + + if (upper_bound > (1 << 18)) { + std::ostringstream buffer; + buffer << "creating large table of size " << upper_bound; + if (p) buffer << " for relation " << p->get_name(); + warning_msg(buffer.str().c_str()); + } + + for(table_element i = 0; i < upper_bound; i++) { + fact[0] = i; + if(empty_table || !contains_fact(fact)) { + res->add_fact(fact); + } + } + return res; + } + + void table_base::display(std::ostream & out) const { + out << "table with signature "; + print_container(get_signature(), out); + out << ":\n"; + + iterator it = begin(); + iterator iend = end(); + for(; it!=iend; ++it) { + const row_interface & r = *it; + r.display(out); + } + + out << "\n"; + } + + + class table_base::row_interface::fact_row_iterator : public table_base::row_iterator_core { + const row_interface & m_parent; + unsigned m_index; + protected: + virtual bool is_finished() const { return m_index==m_parent.size(); } + public: + fact_row_iterator(const row_interface & row, bool finished) + : m_parent(row), m_index(finished ? row.size() : 0) {} + + virtual table_element operator*() { + SASSERT(!is_finished()); + return m_parent[m_index]; + } + + virtual void operator++() { + m_index++; + SASSERT(m_index<=m_parent.size()); + } + }; + + table_base::row_iterator table_base::row_interface::begin() const { + return row_iterator(alloc(fact_row_iterator, *this, false)); + } + table_base::row_iterator table_base::row_interface::end() const { + return row_iterator(alloc(fact_row_iterator, *this, true)); + } + + void table_base::row_interface::get_fact(table_fact & result) const { + result.reset(); + unsigned n=size(); + for(unsigned i=0; i + +Author: + + Krystof Hoder (t-khoder) 2010-09-23. + +Revision History: + +--*/ +#ifndef _DL_BASE_H_ +#define _DL_BASE_H_ + +#define DL_LEAK_HUNTING 0 + +#include + +#include"ast.h" +#include"map.h" +#include"vector.h" +#include"ref.h" +#include"dl_util.h" +#include"dl_context.h" + +namespace datalog { + + class context; + class relation_manager; + + ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm); + context & get_context_from_rel_manager(const relation_manager & rm); + + typedef func_decl_set decl_set; + +#if DL_LEAK_HUNTING + void leak_guard_check(const symbol & s); +#endif + + void universal_delete(relation_base* ptr); + void universal_delete(table_base* ptr); + void dealloc_ptr_vector_content(ptr_vector & v); + + + /** + Termplate class containing common infrastructure for relations and tables + */ + template + struct tr_infrastructure { + + typedef typename Traits::plugin plugin; + typedef typename Traits::base_object base_object; + typedef typename Traits::element element; + typedef typename Traits::fact fact; + typedef typename Traits::sort sort; + typedef typename Traits::signature_base_base signature_base_base; //this must be a vector-like type + typedef typename Traits::signature signature; //this must be a vector-like type + + /** + The client submits an initial class to be used as a base for signature. Then we excend it by + the common signature methods into a signature_base class which then the client inherits from + to obtain the actual signature class. + */ + class signature_base : public signature_base_base { + public: + bool operator==(const signature & o) const { + unsigned n=signature_base_base::size(); + if(n!=o.size()) { + return false; + } + return memcmp(this->c_ptr(), o.c_ptr(), n*sizeof(sort))==0; + /*for(unsigned i=0; i=2); + result=src; + + permutate_by_cycle(result, cycle_len, permutation_cycle); + } + + /** + \brief Into \c result assign signature \c src with reordered columns. + */ + static void from_permutation_rename(const signature & src, + const unsigned * permutation, signature & result) { + result.reset(); + unsigned n = src.size(); + for(unsigned i=0; i(0)); + } + }; + + /** + \brief Mutator for relations that propagate constraints as a consequence of + combination. + + - supports_attachment + is used to query the mutator if it allows communicating + constraints to relations of the kind of the relation. + + - attach + is used to associate downstream clients. + It assumes that the relation kind is supported (supports_kind returns true) + */ + class mutator_fn : public base_fn { + public: + virtual void operator()(base_object & t) = 0; + + virtual bool supports_attachment(base_object& other) { return false; } + + virtual void attach(base_object& other) { UNREACHABLE(); } + }; + + class intersection_filter_fn : public base_fn { + public: + virtual void operator()(base_object & t, const base_object & intersected_obj) = 0; + }; + + class default_join_project_fn; + + /** + \brief Plugin class providing factory functions for a table and operations on it. + + The functor factory functions (mk_*_fn) may return 0. It means that the plugin + is unable to perform the operation on relations/tables of the particular kind. + */ + class plugin_object { + friend class relation_manager; + friend class check_table_plugin; + + family_id m_kind; + symbol m_name; + relation_manager & m_manager; + protected: + plugin_object(symbol const& name, relation_manager & manager) + : m_kind(null_family_id), m_name(name), m_manager(manager) {} + + /** + \brief Check \c r is of the same kind as the plugin. + */ + bool check_kind(base_object const& r) const { return &r.get_plugin()==this; } + public: + virtual ~plugin_object() {} + + virtual void initialize(family_id fid) { m_kind = fid; } + + family_id get_kind() const { return m_kind; } + + symbol const& get_name() const { return m_name; } + + virtual void set_cancel(bool f) {} + + relation_manager & get_manager() const { return m_manager; } + ast_manager& get_ast_manager() const { return datalog::get_ast_manager_from_rel_manager(m_manager); } + context& get_context() const { return datalog::get_context_from_rel_manager(m_manager); } + + virtual bool can_handle_signature(const signature & s) = 0; + + virtual bool can_handle_signature(const signature & s, family_id kind) { + return can_handle_signature(s); + } + + /** + \brief Create empty table/relation with given signature and return pointer to it. + + Precondition: can_handle_signature(s)==true + */ + virtual base_object * mk_empty(const signature & s) = 0; + + /** + \brief Create empty table/relation with signature \c s and kind \c kind. + + Precondition: &orig.get_plugin()==this + */ + virtual base_object * mk_empty(const signature & s, family_id kind) { + SASSERT(kind==get_kind()); //if plugin uses multiple kinds, this function needs to be overriden + return mk_empty(s); + } + + /** + \brief Create empty table/relation of the same specification as \c orig and return pointer to it. + + Precondition: &orig.get_plugin()==this + */ + virtual base_object * mk_empty(const base_object & orig) { + return mk_empty(orig.get_signature(), orig.get_kind()); + } + + /** + \brief Create full table/relation with given signature and return pointer to it. + + Precondition: can_handle_signature(s)==true + */ + virtual base_object * mk_full(func_decl* p, const signature & s) { + base_object * aux = mk_empty(s); + base_object * res = aux->complement(p); + aux->deallocate(); + return res; + } + + virtual base_object * mk_full(func_decl* p, const signature & s, family_id kind) { + if (kind == get_kind() || kind == null_family_id) { + return mk_full(p, s); + } + base_object * aux = mk_empty(s, kind); + base_object * res = aux->complement(p); + aux->deallocate(); + return res; + } + + protected: + //see \c relation_manager for documentation of the operations + + virtual join_fn * mk_join_fn(const base_object & t1, const base_object & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { return 0; } + + virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, + const unsigned * removed_cols) { return 0; } + + virtual join_fn * mk_join_project_fn(const base_object & t1, const base_object & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } + + virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { return 0; } + + virtual transformer_fn * mk_permutation_rename_fn(const base_object & t, + const unsigned * permutation) { return 0; } + + public: + virtual union_fn * mk_union_fn(const base_object & tgt, const base_object & src, + const base_object * delta) { return 0; } + protected: + + virtual union_fn * mk_widen_fn(const base_object & tgt, const base_object & src, + const base_object * delta) { return 0; } + + virtual mutator_fn * mk_filter_identical_fn(const base_object & t, unsigned col_cnt, + const unsigned * identical_cols) { return 0; } + + virtual mutator_fn * mk_filter_equal_fn(const base_object & t, const element & value, + unsigned col) { return 0; } + + virtual mutator_fn * mk_filter_interpreted_fn(const base_object & t, app * condition) + { return 0; } + + virtual transformer_fn * mk_filter_interpreted_and_project_fn(const base_object & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) + { return 0; } + + virtual transformer_fn * mk_select_equal_and_project_fn(const base_object & t, + const element & value, unsigned col) { return 0; } + + virtual intersection_filter_fn * mk_filter_by_intersection_fn(const base_object & t, + const base_object & src, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * src_cols) + { return 0; } + + virtual intersection_filter_fn * mk_filter_by_negation_fn(const base_object & t, + const base_object & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) + { return 0; } + }; + + class base_ancestor { + plugin & m_plugin; + signature m_signature; + family_id m_kind; +#if DL_LEAK_HUNTING + sort_ref m_leak_guard; +#endif + protected: + base_ancestor(plugin & p, const signature & s) + : m_plugin(p), m_signature(s), m_kind(p.get_kind()) +#if DL_LEAK_HUNTING + , m_leak_guard(p.get_ast_manager().mk_fresh_sort(p.get_name().bare_str()), p.get_ast_manager()) +#endif + { +#if DL_LEAK_HUNTING + leak_guard_check(m_leak_guard->get_name()); +#endif + } + +#if DL_LEAK_HUNTING + base_ancestor(const base_ancestor & o) + : m_plugin(o.m_plugin), + m_signature(o.m_signature), + m_kind(o.m_kind), + m_leak_guard(m_plugin.get_ast_manager().mk_fresh_sort(m_plugin.get_name().bare_str()), + m_plugin.get_ast_manager()) { + leak_guard_check(m_leak_guard->get_name()); + } +#endif + + virtual ~base_ancestor() {} + + void set_kind(family_id kind) { SASSERT(kind>=0); m_kind = kind; } + + /** + Since the destructor is protected, we cannot use the \c dealloc macro. + */ + void destroy() { + SASSERT(this); + this->~base_ancestor(); +#if _DEBUG + memory::deallocate(__FILE__, __LINE__, this); +#else + memory::deallocate(this); +#endif + } + public: + /** + Deallocate the current object. + + Pointers and references to the current object become invalid after a call to this function. + */ + virtual void deallocate() { + destroy(); + } + /** + It must hold that operations created for particular table/relation are able to operate on + tables/relations of the same signature and kind. In most cases it is sufficient to use the kind + of the plugin object. + + However, it there is some parameter that is not reflected in the signature, the plugin may need to + allocate different kind numbers to the tables is creates. + */ + family_id get_kind() const { return m_kind; } + const signature & get_signature() const { return m_signature; } + plugin & get_plugin() const { return m_plugin; } + relation_manager & get_manager() const { return get_plugin().get_manager(); } + + virtual bool empty() const = 0; + virtual void add_fact(const fact & f) = 0; + /** + \brief Like \c add_fact, only here the caller guarantees that the fact is not present in + the table yet. + */ + virtual void add_new_fact(const fact & f) { + add_fact(f); + } + virtual bool contains_fact(const fact & f) const = 0; + + virtual void reset() = 0; + + /** + \brief Return table/relation that contains the same data as the current one. + */ + virtual base_object * clone() const = 0; + + virtual bool can_swap(const base_object & o) const { return false; } + + virtual void swap(base_object & o) { + std::swap(m_kind, o.m_kind); +#if DL_LEAK_HUNTING + m_leak_guard = get_plugin().get_ast_manager().mk_fresh_sort(get_plugin().get_name().bare_str()); + o.m_leak_guard = get_plugin().get_ast_manager().mk_fresh_sort(get_plugin().get_name().bare_str()); + leak_guard_check(m_leak_guard->get_name()); + leak_guard_check(o.m_leak_guard->get_name()); +#endif + } + + virtual unsigned get_size_estimate_rows() const { return UINT_MAX; } + virtual unsigned get_size_estimate_bytes() const { return UINT_MAX; } + virtual bool knows_exact_size() const { return false; } + + virtual void display(std::ostream & out) const = 0; + }; + + + class convenient_join_fn : public join_fn { + signature m_result_sig; + protected: + unsigned_vector m_cols1; + unsigned_vector m_cols2; + + convenient_join_fn(const signature & o1_sig, const signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : m_cols1(col_cnt, cols1), + m_cols2(col_cnt, cols2) { + signature::from_join(o1_sig, o2_sig, col_cnt, cols1, cols2, m_result_sig); + } + + const signature & get_result_signature() const { return m_result_sig; } + }; + + class convenient_join_project_fn : public join_fn { + signature m_result_sig; + protected: + unsigned_vector m_cols1; + unsigned_vector m_cols2; + //it is non-const because it needs to be modified in sparse_table version of the join_project operator + unsigned_vector m_removed_cols; + + convenient_join_project_fn(const signature & o1_sig, const signature & o2_sig, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) + : m_cols1(joined_col_cnt, cols1), + m_cols2(joined_col_cnt, cols2), + m_removed_cols(removed_col_cnt, removed_cols) { + + signature::from_join_project(o1_sig, o2_sig, joined_col_cnt, cols1, cols2, + removed_col_cnt, removed_cols, m_result_sig); + } + + const signature & get_result_signature() const { return m_result_sig; } + }; + + class convenient_transformer_fn : public transformer_fn { + signature m_result_sig; + protected: + signature & get_result_signature() { return m_result_sig; } + const signature & get_result_signature() const { return m_result_sig; } + }; + + class convenient_project_fn : public convenient_transformer_fn { + protected: + unsigned_vector m_removed_cols; + + convenient_project_fn(const signature & orig_sig, unsigned col_cnt, const unsigned * removed_cols) + : m_removed_cols(col_cnt, removed_cols) { + signature::from_project(orig_sig, col_cnt, removed_cols, + convenient_transformer_fn::get_result_signature()); + } + }; + + class convenient_rename_fn : public convenient_transformer_fn { + protected: + const unsigned_vector m_cycle; + + convenient_rename_fn(const signature & orig_sig, unsigned cycle_len, + const unsigned * permutation_cycle) + : m_cycle(cycle_len, permutation_cycle) { + signature::from_rename(orig_sig, cycle_len, permutation_cycle, + convenient_transformer_fn::get_result_signature()); + } + }; + + class convenient_negation_filter_fn : public intersection_filter_fn { + protected: + unsigned m_joined_col_cnt; + const unsigned_vector m_cols1; + const unsigned_vector m_cols2; + bool m_all_neg_bound; //all columns are bound at least once + bool m_overlap; //one column in negated table is bound multiple times + svector m_bound; + + convenient_negation_filter_fn(const base_object & tgt, const base_object & neg_t, + unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) + : m_joined_col_cnt(joined_col_cnt), m_cols1(joined_col_cnt, t_cols), + m_cols2(joined_col_cnt, negated_cols) { + unsigned neg_sig_size = neg_t.get_signature().size(); + m_overlap = false; + m_bound.resize(neg_sig_size, false); + for(unsigned i=0; i + void make_neg_bindings(T & tgt_neg, const U & src) const { + SASSERT(m_all_neg_bound); + SASSERT(!m_overlap); + for(unsigned i=0; i + bool bindings_match(const T & tgt_neg, const U & src) const { + for(unsigned i=0; i renamer_vector; + + unsigned_vector m_permutation; //this is valid only before m_renamers_initialized becomes true + bool m_renamers_initialized; + renamer_vector m_renamers; + public: + default_permutation_rename_fn(const base_object & o, const unsigned * permutation) + : m_permutation(o.get_signature().size(), permutation), + m_renamers_initialized(false) {} + + ~default_permutation_rename_fn() { + dealloc_ptr_vector_content(m_renamers); + } + + base_object * operator()(const base_object & o) { + const base_object * res = &o; + scoped_rel res_scoped; + if(m_renamers_initialized) { + typename renamer_vector::iterator rit = m_renamers.begin(); + typename renamer_vector::iterator rend = m_renamers.end(); + for(; rit!=rend; ++rit) { + res_scoped = (**rit)(*res); + res = res_scoped.get(); + } + } + else { + SASSERT(m_renamers.empty()); + unsigned_vector cycle; + while(try_remove_cycle_from_permutation(m_permutation, cycle)) { + transformer_fn * renamer = o.get_manager().mk_rename_fn(*res, cycle); + SASSERT(renamer); + m_renamers.push_back(renamer); + cycle.reset(); + + res_scoped = (*renamer)(*res); + res = res_scoped.get(); + } + m_renamers_initialized = true; + } + if(res_scoped) { + SASSERT(res==res_scoped.get()); + //we don't want to delete the last one since we'll be returning it + return res_scoped.release(); + } + else { + SASSERT(res==&o); + return res->clone(); + } + } + + }; + + + }; + + + // ----------------------------------- + // + // relation_base + // + // ----------------------------------- + + class relation_signature; + class relation_plugin; + class relation_base; + + typedef ptr_vector relation_signature_base0; + typedef ptr_hash relation_sort_hash; + + + struct relation_traits { + typedef relation_plugin plugin; + typedef relation_base base_object; + typedef relation_element element; + typedef relation_fact fact; + typedef relation_sort sort; + typedef relation_signature_base0 signature_base_base; + typedef relation_signature signature; + }; + + typedef tr_infrastructure relation_infrastructure; + + typedef relation_infrastructure::base_fn base_relation_fn; + typedef relation_infrastructure::join_fn relation_join_fn; + typedef relation_infrastructure::transformer_fn relation_transformer_fn; + typedef relation_infrastructure::union_fn relation_union_fn; + typedef relation_infrastructure::mutator_fn relation_mutator_fn; + typedef relation_infrastructure::intersection_filter_fn relation_intersection_filter_fn; + + typedef relation_infrastructure::convenient_join_fn convenient_relation_join_fn; + typedef relation_infrastructure::convenient_join_project_fn convenient_relation_join_project_fn; + typedef relation_infrastructure::convenient_transformer_fn convenient_relation_transformer_fn; + typedef relation_infrastructure::convenient_project_fn convenient_relation_project_fn; + typedef relation_infrastructure::convenient_rename_fn convenient_relation_rename_fn; + typedef relation_infrastructure::convenient_negation_filter_fn convenient_relation_negation_filter_fn; + typedef relation_infrastructure::identity_transformer_fn identity_relation_transformer_fn; + typedef relation_infrastructure::identity_mutator_fn identity_relation_mutator_fn; + typedef relation_infrastructure::identity_intersection_filter_fn identity_relation_intersection_filter_fn; + typedef relation_infrastructure::default_permutation_rename_fn default_relation_permutation_rename_fn; + + class relation_signature : public relation_infrastructure::signature_base { + public: + bool operator!=(const relation_signature & o) const { + return !(*this==o); + } + + void output(ast_manager & m, std::ostream & out) const; + + struct hash { + unsigned operator()(relation_signature const& s) const { + return obj_vector_hash(s); + } + }; + + struct eq { + bool operator()(relation_signature const& s1, relation_signature const& s2) const { + return s1 == s2; + } + }; + }; + + class relation_plugin : public relation_infrastructure::plugin_object { + protected: + enum special_relation_type { + ST_ORDINARY, + ST_TABLE_RELATION, + ST_FINITE_PRODUCT_RELATION, + ST_PRODUCT_RELATION, + ST_SIEVE_RELATION + }; + private: + special_relation_type m_special_type; + protected: + relation_plugin(symbol const& name, relation_manager & manager, + special_relation_type special_type = ST_ORDINARY) + : plugin_object(name, manager), + m_special_type(special_type) {} + public: + bool from_table() const { return m_special_type==ST_TABLE_RELATION; } + bool is_finite_product_relation() const { return m_special_type==ST_FINITE_PRODUCT_RELATION; } + bool is_product_relation() const { return m_special_type==ST_PRODUCT_RELATION; } + bool is_sieve_relation() const { return m_special_type==ST_SIEVE_RELATION; } + + /** + \brief If true, the relation can contain only one or zero elements. + + Having this zero allows the finite_product_relation to perform some operations in a simpler way. + (KH: I started implementing finite_product_relation::inner_singleton_union_fn that takes advantage of + it, but it's not finished.) + */ + virtual bool is_singleton_relation() const { return false; } + }; + + class relation_base : public relation_infrastructure::base_ancestor { + protected: + relation_base(relation_plugin & plugin, const relation_signature & s) + : base_ancestor(plugin, s) {} + virtual ~relation_base() {} + public: + virtual relation_base * complement(func_decl* p) const = 0; + + virtual void reset(); + + virtual void display_tuples(func_decl & pred, std::ostream & out) const { + out << "Tuples in " << pred.get_name() << ": \n"; + display(out); + } + + virtual void to_formula(expr_ref& fml) const = 0; + + bool from_table() const { return get_plugin().from_table(); } + virtual bool is_precise() const { return true; } + }; + + typedef ptr_vector relation_vector; + + // ----------------------------------- + // + // table_base + // + // ----------------------------------- + + class table_signature; + class table_plugin; + class table_base; + + typedef uint64 table_sort; + typedef svector table_signature_base0; + typedef uint64_hash table_sort_hash; + + typedef uint64_hash table_element_hash; + + struct table_traits { + typedef table_plugin plugin; + typedef table_base base_object; + typedef table_element element; + typedef table_fact fact; + typedef table_sort sort; + typedef table_signature_base0 signature_base_base; + typedef table_signature signature; + }; + + typedef tr_infrastructure table_infrastructure; + + typedef table_infrastructure::base_fn base_table_fn; + typedef table_infrastructure::join_fn table_join_fn; + typedef table_infrastructure::transformer_fn table_transformer_fn; + typedef table_infrastructure::union_fn table_union_fn; + typedef table_infrastructure::mutator_fn table_mutator_fn; + typedef table_infrastructure::intersection_filter_fn table_intersection_filter_fn; + + typedef table_infrastructure::convenient_join_fn convenient_table_join_fn; + typedef table_infrastructure::convenient_join_project_fn convenient_table_join_project_fn; + typedef table_infrastructure::convenient_transformer_fn convenient_table_transformer_fn; + typedef table_infrastructure::convenient_project_fn convenient_table_project_fn; + typedef table_infrastructure::convenient_rename_fn convenient_table_rename_fn; + typedef table_infrastructure::convenient_negation_filter_fn convenient_table_negation_filter_fn; + typedef table_infrastructure::identity_transformer_fn identity_table_transformer_fn; + typedef table_infrastructure::identity_mutator_fn identity_table_mutator_fn; + typedef table_infrastructure::identity_intersection_filter_fn identity_table_intersection_filter_fn; + typedef table_infrastructure::default_permutation_rename_fn default_table_permutation_rename_fn; + + class table_row_mutator_fn { + public: + /** + \brief The function is called for a particular table row. The \c func_columns contains + a pointer to an array of functional column values that can be modified. If the function + returns true, the modification will appear in the table; otherwise the row will be deleted. + + It is possible that one call to the function stands for multiple table rows that share + the same functional column values. + */ + virtual bool operator()(table_element * func_columns) = 0; + }; + + class table_row_pair_reduce_fn { + public: + /** + \brief The function is called for pair of table rows that became duplicit due to projection. + The values that are in the first array after return from the function will be used for the + resulting row. + + It is assumed that the function is idempotent: when the two functional sub-tuples are equal, + the result is assumed to be equal to them as well, so the function may not be evaluated for them. + */ + virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) = 0; + }; + + + class table_signature : public table_infrastructure::signature_base { + public: + struct hash { + unsigned operator()(table_signature const& s) const { + return svector_hash()(s); + } + }; + + struct eq { + bool operator()(table_signature const& s1, table_signature const& s2) const { + return s1 == s2; + } + }; + private: + unsigned m_functional_columns; + public: + table_signature() : m_functional_columns(0) {} + + void swap(table_signature & s) { + signature_base::swap(s); + std::swap(m_functional_columns, s.m_functional_columns); + } + + /** + \brief The returned value is the number of last columns that are functional. + + The uniqueness is enforced on non-functional columns. When projection causes two + facts to have equal non-functional parts, it is not defined which one of them is retained. + */ + unsigned functional_columns() const { return m_functional_columns; } + void set_functional_columns(unsigned val) { SASSERT(size()>=val); m_functional_columns = val; } + + /** + \brief Return index of the first functional column, or the size of the signature if there + are no functional columns. + */ + unsigned first_functional() const { return size()-m_functional_columns; } + + bool operator==(const table_signature & o) const { + return signature_base::operator==(o) && m_functional_columns==o.m_functional_columns; + } + bool operator!=(const table_signature & o) const { + return !(*this==o); + } + + /** + \brief return true iof the two signatures are equal when we ignore which columns are functional. + */ + bool equal_up_to_fn_mark(const table_signature & o) const { + return signature_base::operator==(o); + } + + + /** + \brief Into \c result assign signature of result of join of relations with signatures \c s1 + and \c s2. The result is + + (non-functional of s1)(non-functional of s2)(functional of s1)(functional of s2) + */ + static void from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, table_signature & result); + + static void from_join_project(const table_signature & s1, const table_signature & s2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols, table_signature & result); + + + /** + \brief Into \c result assign signature projected from \c src. + + The array of removed columns must be sorted in ascending order. + + If we remove at least one non-functional column, all the columns in the result are non-functional. + */ + static void from_project(const table_signature & src, unsigned col_cnt, + const unsigned * removed_cols, table_signature & result); + + static void from_project_with_reduce(const table_signature & src, unsigned col_cnt, + const unsigned * removed_cols, table_signature & result); + + /** + \brief Into \c result assign signature \c src with reordered columns. + + Permutations between functional and nonfunctional columns are not allowed. + */ + static void from_rename(const table_signature & src, unsigned cycle_len, + const unsigned * permutation_cycle, table_signature & result) { + signature_base::from_rename(src, cycle_len, permutation_cycle, result); + result.set_functional_columns(src.functional_columns()); +#if Z3DEBUG + unsigned first_src_fun = src.size()-src.functional_columns(); + bool in_func = permutation_cycle[0]>=first_src_fun; + for(unsigned i=1;i=first_src_fun)); + } +#endif + } + + /** + \brief Into \c result assign signature \c src with reordered columns. + + Permutations mixing functional and nonfunctional columns are not allowed. + */ + static void from_permutation_rename(const table_signature & src, + const unsigned * permutation, table_signature & result) { + signature_base::from_permutation_rename(src, permutation, result); + result.set_functional_columns(src.functional_columns()); +#if Z3DEBUG + unsigned sz = src.size(); + unsigned first_src_fun = sz-src.functional_columns(); + for(unsigned i=first_src_fun;i=first_src_fun); + } +#endif + } + + }; + + class table_plugin : public table_infrastructure::plugin_object { + friend class relation_manager; + protected: + table_plugin(symbol const& n, relation_manager & manager) : plugin_object(n, manager) {} + public: + + virtual bool can_handle_signature(const table_signature & s) { return s.functional_columns()==0; } + + protected: + /** + If the returned value is non-zero, the returned object must take ownership of \c mapper. + Otherwise \c mapper must remain unmodified. + */ + virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { return 0; } + + /** + If the returned value is non-zero, the returned object must take ownership of \c reducer. + Otherwise \c reducer must remain unmodified. + */ + virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { return 0; } + + }; + + class table_base : public table_infrastructure::base_ancestor { + protected: + table_base(table_plugin & plugin, const table_signature & s) + : base_ancestor(plugin, s) {} + virtual ~table_base() {} + public: + virtual table_base * clone() const; + virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; + virtual bool empty() const; + + /** + \brief Return true if table contains fact that corresponds to \c f in all non-functional + columns. + */ + virtual bool contains_fact(const table_fact & f) const; + + /** + \brief If \c f (i.e. its non-functional part) is not present in the table, + add it and return true. Otherwise update \c f, so that the values of functional + columns correspond to the ones present in the table. + */ + virtual bool suggest_fact(table_fact & f); + + /** + \brief If \c f (i.e. its non-functional part) is not present in the table, + return false. Otherwise update \c f, so that the values of functional + columns correspond to the ones present in the table and return true. + */ + virtual bool fetch_fact(table_fact & f) const; + + /** + \brief Ensure fact \c f is present in the table (including the values of its functional columns). + */ + virtual void ensure_fact(const table_fact & f); + + virtual void remove_fact(const table_fact & fact) { + SASSERT(fact.size() == get_signature().size()); + remove_fact(fact.c_ptr()); } + + virtual void remove_fact(table_element const* fact) = 0; + virtual void remove_facts(unsigned fact_cnt, const table_fact * facts); + virtual void remove_facts(unsigned fact_cnt, const table_element * facts); + virtual void reset(); + + class row_interface; + + virtual void display(std::ostream & out) const; + + /** + \brief Convert table to a formula that encodes the table. + The columns correspond to bound variables indexed as + 0, .., sig.size()-1 + */ + virtual void to_formula(relation_signature const& sig, expr_ref& fml) const; + + protected: + + + class iterator_core { + unsigned m_ref_cnt; + public: + iterator_core() : m_ref_cnt(0) {} + virtual ~iterator_core() {} + + void inc_ref() { m_ref_cnt++; } + void dec_ref() { + SASSERT(m_ref_cnt>0); + m_ref_cnt--; + if(m_ref_cnt==0) { + dealloc(this); + } + } + + virtual bool is_finished() const = 0; + + virtual row_interface & operator*() = 0; + virtual void operator++() = 0; + virtual bool operator==(const iterator_core & it) { + //we worry about the equality operator only because of checking + //the equality with the end() iterator + if(is_finished() && it.is_finished()) { + return true; + } + return false; + } + private: + //private and undefined copy constructor and assignment operator + iterator_core(const iterator_core &); + iterator_core & operator=(const iterator_core &); + }; + + struct row_iterator_core { + unsigned m_ref_cnt; + public: + row_iterator_core() : m_ref_cnt(0) {} + virtual ~row_iterator_core() {} + + void inc_ref() { m_ref_cnt++; } + void dec_ref() { + SASSERT(m_ref_cnt>0); + m_ref_cnt--; + if(m_ref_cnt==0) { + dealloc(this); + } + } + + virtual bool is_finished() const = 0; + + virtual table_element operator*() = 0; + virtual void operator++() = 0; + virtual bool operator==(const row_iterator_core & it) { + //we worry about the equality operator only because of checking + //the equality with the end() iterator + if(is_finished() && it.is_finished()) { + return true; + } + return false; + } + private: + //private and undefined copy constructor and assignment operator + row_iterator_core(const row_iterator_core &); + row_iterator_core & operator=(const row_iterator_core &); + }; + + public: + class iterator { + friend class table_base; + + ref m_core; + + iterator(iterator_core * core) : m_core(core) {} + public: + /** + \brief Return reference to a row_interface object for the current row. + + The reference is valid only until the \c operator++() is called or + until the iterator is invalidated. + */ + row_interface & operator*() + { return *(*m_core); } + row_interface * operator->() + { return &(*(*m_core)); } + iterator & operator++() + { ++(*m_core); return *this; } + bool operator==(const iterator & it) + { return (*m_core)==(*it.m_core); } + bool operator!=(const iterator & it) + { return !operator==(it); } + }; + + class row_iterator { + friend class table_base; + friend class row_interface; + + ref m_core; + + row_iterator(row_iterator_core * core) : m_core(core) {} + public: + table_element operator*() + { return *(*m_core); } + row_iterator & operator++() + { ++(*m_core); return *this; } + bool operator==(const row_iterator & it) + { return (*m_core)==(*it.m_core); } + bool operator!=(const row_iterator & it) + { return !operator==(it); } + }; + + virtual iterator begin() const = 0; + virtual iterator end() const = 0; + + class row_interface { + class fact_row_iterator; + + const table_base & m_parent_table; + public: + typedef row_iterator iterator; + typedef row_iterator const_iterator; + + row_interface(const table_base & parent_table) : m_parent_table(parent_table) {} + virtual ~row_interface() {} + + virtual table_element operator[](unsigned col) const = 0; + + unsigned size() const { return m_parent_table.get_signature().size(); } + virtual void get_fact(table_fact & result) const; + virtual row_iterator begin() const; + virtual row_iterator end() const; + virtual void display(std::ostream & out) const; + }; + + protected: + + class caching_row_interface : public row_interface { + mutable table_fact m_current; + + bool populated() const { return !m_current.empty(); } + void ensure_populated() const { + if(!populated()) { + get_fact(m_current); + } + } + public: + caching_row_interface(const table_base & parent) : row_interface(parent) {} + + virtual void get_fact(table_fact & result) const = 0; + + virtual table_element operator[](unsigned col) const { + ensure_populated(); + return m_current[col]; + } + /** + \brief Resets the cache of the row object. + + Must be called when the row object begins to represent a different row in the table. + */ + void reset() { m_current.reset(); } + }; + + //This function is here to create iterator instances in classes that derive from table_base. + //We do not want to make the constructor of the iterator class public, and being private, the + //inheritor classes cannot see it directly. + static iterator mk_iterator(iterator_core * core) { + return iterator(core); + } + }; + + /** + \brief Populate vector \c renaming_args so that it can be used as an argument to \c var_subst. + The renaming we want is one that transforms variables with numbers of indexes of \c map into the + values of at those indexes. If a value if \c UINT_MAX, it means we do not transform the index + corresponding to it. + */ + void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig, + expr_ref_vector & renaming_arg); + + +}; + +#endif /* _DL_BASE_H_ */ + diff --git a/src/muz/rel/dl_bound_relation.cpp b/src/muz/rel/dl_bound_relation.cpp new file mode 100644 index 000000000..182046c1e --- /dev/null +++ b/src/muz/rel/dl_bound_relation.cpp @@ -0,0 +1,707 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_bound_relation.cpp + +Abstract: + + Basic (strict upper) bound relation. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-2-11 + +Revision History: + +--*/ + +#include "dl_bound_relation.h" +#include "debug.h" +#include "ast_pp.h" + +namespace datalog { + + bound_relation_plugin::bound_relation_plugin(relation_manager& m): + relation_plugin(bound_relation_plugin::get_name(), m), + m_arith(get_ast_manager()), + m_bsimp(get_ast_manager()) { + } + + bool bound_relation_plugin::can_handle_signature(const relation_signature & sig) { + for (unsigned i = 0; i < sig.size(); ++i) { + if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) { + return false; + } + } + return true; + } + + bound_relation& bound_relation_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + + bound_relation const & bound_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + bound_relation* bound_relation_plugin::get(relation_base* r) { + return dynamic_cast(r); + } + + bool bound_relation_plugin::is_interval_relation(relation_base const& r) { + return symbol("interval_relation") == r.get_plugin().get_name(); + } + + interval_relation& bound_relation_plugin::get_interval_relation(relation_base& r) { + SASSERT(is_interval_relation(r)); + return dynamic_cast(r); + } + + interval_relation const& bound_relation_plugin::get_interval_relation(relation_base const& r) { + SASSERT(is_interval_relation(r)); + return dynamic_cast(r); + } + + relation_base * bound_relation_plugin::mk_empty(const relation_signature & s) { + return alloc(bound_relation, *this, s, true); + } + + relation_base * bound_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + return alloc(bound_relation, *this, s, false); + } + + class bound_relation_plugin::join_fn : public convenient_relation_join_fn { + public: + join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2) { + } + + virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + bound_relation const& r1 = get(_r1); + bound_relation const& r2 = get(_r2); + bound_relation_plugin& p = r1.get_plugin(); + bound_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); + return result; + } + }; + + relation_join_fn * bound_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(r1) || !check_kind(r2)) { + return 0; + } + return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); + } + + + class bound_relation_plugin::project_fn : public convenient_relation_project_fn { + public: + project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { + } + + virtual relation_base * operator()(const relation_base & _r) { + bound_relation const& r = get(_r); + bound_relation_plugin& p = r.get_plugin(); + bound_relation* result = get(p.mk_full(0, get_result_signature())); + result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); + return result; + } + }; + + relation_transformer_fn * bound_relation_plugin::mk_project_fn(const relation_base & r, + unsigned col_cnt, const unsigned * removed_cols) { + return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); + } + + class bound_relation_plugin::rename_fn : public convenient_relation_rename_fn { + public: + rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { + } + + virtual relation_base * operator()(const relation_base & _r) { + bound_relation const& r = get(_r); + bound_relation_plugin& p = r.get_plugin(); + bound_relation* result = get(p.mk_full(0, get_result_signature())); + result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); + return result; + } + }; + + relation_transformer_fn * bound_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if(check_kind(r)) { + return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); + } + return 0; + } + + + class bound_relation_plugin::union_fn : public relation_union_fn { + bool m_is_widen; + public: + union_fn(bool is_widen) : + m_is_widen(is_widen) { + } + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + get(_r).mk_union(get(_src), get(_delta), m_is_widen); + } + }; + + class bound_relation_plugin::union_fn_i : public relation_union_fn { + bool m_is_widen; + public: + union_fn_i(bool is_widen) : + m_is_widen(is_widen) { + } + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + get(_r).mk_union_i(get_interval_relation(_src), get(_delta), m_is_widen); + TRACE("bound_relation", _r.display(tout << "dst':\n");); + } + }; + + + relation_union_fn * bound_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) { + return alloc(union_fn_i, false); + } + if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { + return alloc(union_fn, false); + } + return 0; + } + + relation_union_fn * bound_relation_plugin::mk_widen_fn( + const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) { + return alloc(union_fn_i, true); + } + if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { + return alloc(union_fn, true); + } + return 0; + } + + class bound_relation_plugin::filter_identical_fn : public relation_mutator_fn { + unsigned_vector m_cols; + public: + filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) + : m_cols(col_cnt, identical_cols) {} + + virtual void operator()(relation_base & r) { + for (unsigned i = 1; i < m_cols.size(); ++i) { + get(r).equate(m_cols[0], m_cols[i]); + } + } + }; + + relation_mutator_fn * bound_relation_plugin::mk_filter_identical_fn( + const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { + if(check_kind(t)) { + return alloc(filter_identical_fn, col_cnt, identical_cols); + } + return 0; + } + + class bound_relation_plugin::filter_equal_fn : public relation_mutator_fn { + public: + filter_equal_fn(relation_element const& value, unsigned col) {} + + virtual void operator()(relation_base & r) { } + }; + + relation_mutator_fn * bound_relation_plugin::mk_filter_equal_fn(const relation_base & r, + const relation_element & value, unsigned col) { + if (check_kind(r)) { + return alloc(filter_equal_fn, value, col); + } + return 0; + } + + class bound_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + enum kind_t { NOT_APPLICABLE, EQ_VAR, EQ_SUB, LT_VAR, LE_VAR, K_FALSE }; + app_ref m_cond; + app_ref m_lt; + arith_util m_arith; + interval_relation* m_interval; + unsigned_vector m_vars; + kind_t m_kind; + + unsigned get_var(expr* a) { + SASSERT(is_var(a)); + return to_var(a)->get_idx(); + } + + // x = z - y + void mk_sub_eq(expr* x, expr* z, expr* y) { + SASSERT(is_var(x)); + SASSERT(is_var(z)); + SASSERT(is_var(y)); + m_vars.push_back(get_var(x)); + m_vars.push_back(get_var(z)); + m_vars.push_back(get_var(y)); + m_kind = EQ_SUB; + } + + void mk_lt(expr* l, expr* r) { + SASSERT(is_var(l)); + SASSERT(is_var(r)); + m_vars.push_back(get_var(l)); + m_vars.push_back(get_var(r)); + m_lt = m_arith.mk_lt(l, r); + m_kind = LT_VAR; + } + + + void mk_le(expr* l, expr* r) { + SASSERT(is_var(l)); + SASSERT(is_var(r)); + m_vars.push_back(get_var(l)); + m_vars.push_back(get_var(r)); + m_kind = LE_VAR; + } + + void mk_eq(expr* l, expr* r) { + m_vars.push_back(get_var(l)); + m_vars.push_back(get_var(r)); + m_kind = EQ_VAR; + } + + public: + + filter_interpreted_fn(ast_manager& m, app* cond) : + m_cond(cond, m), + m_lt(m), m_arith(m), m_interval(0), m_kind(NOT_APPLICABLE) { + expr* l, *r, *r1, *r2, *c2; + rational n1; + if ((m_arith.is_lt(cond, l, r) || m_arith.is_gt(cond, r, l)) && + is_var(l) && is_var(r)) { + mk_lt(l, r); + } + else if (m.is_not(cond, c2) && + (m_arith.is_ge(c2, l, r) || m_arith.is_le(c2, r, l)) && + is_var(l) && is_var(r)) { + mk_lt(l, r); + } + else if ((m_arith.is_le(cond, l, r) || m_arith.is_ge(cond, r, l)) && + is_var(l) && is_var(r)) { + mk_le(l, r); + } + else if (m.is_not(cond, c2) && + (m_arith.is_gt(c2, l, r) || m_arith.is_lt(c2, r, l)) && + is_var(l) && is_var(r)) { + mk_le(l, r); + } + else if (m.is_false(cond)) { + m_kind = K_FALSE; + } + else if (m.is_eq(cond, l, r) && is_var(l) && is_var(r)) { + mk_eq(l, r); + } + else if (m.is_eq(cond, l, r) && + m_arith.is_sub(r, r1, r2) && + is_var(l) && is_var(r1) && is_var(r2)) { + mk_sub_eq(l, r1, r2); + } + else if (m.is_eq(cond, l, r) && + m_arith.is_sub(l, r1, r2) && + is_var(r) && is_var(r1) && is_var(r2)) { + mk_sub_eq(r, r1, r2); + } + else if (m.is_eq(cond, l, r) && + m_arith.is_add(r, r1, r2) && + m_arith.is_numeral(r1, n1) && + n1.is_pos() && is_var(l) && is_var(r2)) { + mk_lt(r2, l); + } + else if (m.is_eq(cond, l, r) && + m_arith.is_add(r, r1, r2) && + m_arith.is_numeral(r2, n1) && + n1.is_pos() && is_var(l) && is_var(r1)) { + mk_lt(r1, l); + } + else { + + } + } + + // + // x = z - y + // x = y + // x < y + // x <= y + // x < y + z + // + + void operator()(relation_base& t) { + TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); + bound_relation& r = get(t); + switch(m_kind) { + case K_FALSE: + r.set_empty(); + break; + case NOT_APPLICABLE: + break; + case EQ_VAR: + r.equate(m_vars[0], m_vars[1]); + break; + case EQ_SUB: + // TBD + break; + case LT_VAR: + r.mk_lt(m_vars[0], m_vars[1]); + break; + case LE_VAR: + r.mk_le(m_vars[0], m_vars[1]); + break; + default: + UNREACHABLE(); + break; + } + TRACE("dl", t.display(tout << "result\n");); + } + + bool supports_attachment(relation_base& t) { + return is_interval_relation(t); + } + + void attach(relation_base& t) { + SASSERT(is_interval_relation(t)); + interval_relation& r = get_interval_relation(t); + m_interval = &r; + } + }; + + relation_mutator_fn * bound_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + return alloc(filter_interpreted_fn, t.get_plugin().get_ast_manager(), condition); + } + + // ----------------------------- + // bound_relation + + void bound_relation_helper::mk_project_t(uint_set2& t, unsigned_vector const& renaming) { + if (t.lt.empty() && t.le.empty()) { + return; + } + uint_set::iterator it = t.lt.begin(), end = t.lt.end(); + unsigned_vector ltv, lev; + for (; it != end; ++it) { + ltv.push_back(renaming[*it]); + } + it = t.le.begin(), end = t.le.end(); + for (; it != end; ++it) { + lev.push_back(renaming[*it]); + } + TRACE("dl", + tout << "project: "; + for (unsigned i = 0; i < renaming.size(); ++i) + if (renaming[i] == UINT_MAX) tout << i << " "; + tout << ": "; + it = t.lt.begin(); end = t.lt.end(); + for (; it != end; ++it) tout << *it << " "; + tout << " le "; + it = t.le.begin(); end = t.le.end(); + for (; it != end; ++it) tout << *it << " "; + tout << " => "; + for (unsigned i = 0; i < ltv.size(); ++i) tout << ltv[i] << " "; + tout << " le "; + for (unsigned i = 0; i < lev.size(); ++i) tout << lev[i] << " "; + tout << "\n";); + t.lt.reset(); + for (unsigned i = 0; i < ltv.size(); ++i) { + t.lt.insert(ltv[i]); + } + t.le.reset(); + for (unsigned i = 0; i < lev.size(); ++i) { + t.le.insert(lev[i]); + } + } + + bound_relation::bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty): + vector_relation(p, s, is_empty, uint_set2()) + { + } + + + uint_set2 bound_relation::mk_intersect(uint_set2 const& t1, uint_set2 const& t2, bool& is_empty) const { + is_empty = false; + uint_set2 r(t1); + r.lt |= t2.lt; + r.le |= t2.le; + return r; + } + + uint_set2 bound_relation::mk_widen(uint_set2 const& t1, uint_set2 const& t2) const { + return mk_unite(t1, t2); + } + + uint_set2 bound_relation::mk_unite(uint_set2 const& t1, uint_set2 const& t2) const { + uint_set2 s1(t1); + s1.lt &= t2.lt; + s1.le &= t2.le; + return s1; + } + + uint_set2 bound_relation::mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, uint_set2 const& t) const { + unsigned sz = old_eqs.get_num_vars(); + SASSERT(sz == new_eqs.get_num_vars()); + uint_set2 result; + for (unsigned i = 0; i < sz; ++i) { + if (t.lt.contains(i)) { + unsigned j = i; + do { + result.lt.insert(new_eqs.find(j)); + j = old_eqs.next(j); + } + while (j != i); + } + if (t.le.contains(i)) { + unsigned j = i; + do { + result.le.insert(new_eqs.find(j)); + j = old_eqs.next(j); + } + while (j != i); + } + } + return result; + } + + bool bound_relation::is_subset_of(uint_set2 const& t1, uint_set2 const& t2) const { + uint_set2 s1, s2; + normalize(t1, s1); + normalize(t2, s2); + return s1.lt.subset_of(s2.lt) && s1.le.subset_of(s2.le); + } + + void bound_relation::mk_rename_elem(uint_set2& t, unsigned col_cnt, unsigned const* cycle) { + // [ 0 -> 2 -> 3 -> 0] + if (col_cnt == 0) return; + unsigned col1, col2; + col1 = find(cycle[0]); + col2 = find(cycle[col_cnt-1]); + bool has_col2_lt = t.lt.contains(col2); + t.lt.remove(col2); + bool has_col2_le = t.le.contains(col2); + t.le.remove(col2); + for (unsigned i = 0; i + 1 < col_cnt; ++i) { + col1 = find(cycle[i]); + col2 = find(cycle[i+1]); + if (t.lt.contains(col1)) { + t.lt.remove(col1); + t.lt.insert(col2); + } + if (t.le.contains(col1)) { + t.le.remove(col1); + t.le.insert(col2); + } + } + if (has_col2_lt) { + col1 = find(cycle[0]); + t.lt.insert(col1); + } + if (has_col2_le) { + col1 = find(cycle[0]); + t.le.insert(col1); + } + } + + + bool bound_relation::is_full(uint_set2 const& t) const { + return t.lt.empty() && t.le.empty(); + } + + bool bound_relation::is_empty(unsigned index, uint_set2 const& t) const { + return t.lt.contains(find(index)) || t.le.contains(find(index)); + } + + void bound_relation::normalize(uint_set const& src, uint_set& dst) const { + uint_set::iterator it = src.begin(), end = src.end(); + for (; it != end; ++it) { + dst.insert(find(*it)); + } + } + void bound_relation::normalize(uint_set2 const& src, uint_set2& dst) const { + normalize(src.lt, dst.lt); + normalize(src.le, dst.le); + } + + + void bound_relation::mk_lt(unsigned i) { + uint_set2& dst = (*this)[i]; + while (!m_todo.empty()) { + unsigned j = m_todo.back().first; + bool strict = m_todo.back().second; + if (i == j && strict) { + m_todo.reset(); + m_empty = true; + return; + } + m_todo.pop_back(); + if (i == j) { + continue; + } + uint_set2& src = (*m_elems)[j]; + uint_set::iterator it = src.lt.begin(), end = src.lt.end(); + for(; it != end; ++it) { + m_todo.push_back(std::make_pair(*it, true)); + } + it = src.le.begin(), end = src.le.end(); + for(; it != end; ++it) { + m_todo.push_back(std::make_pair(*it, strict)); + } + if (strict) { + dst.lt.insert(j); + } + else { + dst.le.insert(j); + } + } + } + + void bound_relation::mk_lt(unsigned i, unsigned j) { + m_todo.reset(); + i = find(i); + m_todo.push_back(std::make_pair(find(j), true)); + mk_lt(i); + } + + void bound_relation::mk_le(unsigned i, unsigned j) { + m_todo.reset(); + i = find(i); + m_todo.push_back(std::make_pair(find(j), false)); + mk_lt(i); + } + + bool bound_relation::is_lt(unsigned i, unsigned j) const { + return (*this)[i].lt.contains(find(j)); + } + + void bound_relation::add_fact(const relation_fact & f) { + bound_relation r(get_plugin(), get_signature(), false); + for (unsigned i = 0; i < f.size(); ++i) { + scoped_ptr fe = get_plugin().mk_filter_equal_fn(r, f[i], i); + (*fe)(r); + } + mk_union(r, 0, false); + } + + bool bound_relation::contains_fact(const relation_fact & f) const { + if (empty()) { + return false; + } + // this is a very rough approximation. + return true; + } + + bound_relation * bound_relation::clone() const { + bound_relation* result = 0; + if (empty()) { + result = bound_relation_plugin::get(get_plugin().mk_empty(get_signature())); + } + else { + result = bound_relation_plugin::get(get_plugin().mk_full(0, get_signature())); + result->copy(*this); + } + return result; + } + + void bound_relation::mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen) { + unsigned size = get_signature().size(); + for (unsigned i = 0; i < size; ++i) { + if (find(i) != i) { + continue; + } + uint_set2& s = (*this)[i]; + ext_numeral const& lo = src[i].sup(); + if (lo.is_infinite()) { + s.lt.reset(); + s.le.reset(); + continue; + } + uint_set::iterator it = s.lt.begin(), end = s.lt.end(); + for(; it != end; ++it) { + ext_numeral const& hi = src[*it].inf(); + if (hi.is_infinite() || lo.to_rational() >= hi.to_rational()) { + s.lt.remove(*it); + } + } + it = s.le.begin(), end = s.le.end(); + for(; it != end; ++it) { + ext_numeral const& hi = src[*it].inf(); + if (hi.is_infinite() || lo.to_rational() > hi.to_rational()) { + s.le.remove(*it); + } + } + } + } + + bound_relation * bound_relation::complement(func_decl* p) const { + UNREACHABLE(); + return 0; + } + + void bound_relation::to_formula(expr_ref& fml) const { + ast_manager& m = get_plugin().get_ast_manager(); + arith_util& arith = get_plugin().m_arith; + basic_simplifier_plugin& bsimp = get_plugin().m_bsimp; + expr_ref_vector conjs(m); + relation_signature const& sig = get_signature(); + for (unsigned i = 0; i < sig.size(); ++i) { + if (i != find(i)) { + conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), m.mk_var(find(i), sig[find(i)]))); + continue; + } + uint_set2 const& upper = (*this)[i]; + uint_set::iterator it = upper.lt.begin(), end = upper.lt.end(); + for (; it != end; ++it) { + conjs.push_back(arith.mk_lt(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it]))); + } + it = upper.le.begin(), end = upper.le.end(); + for (; it != end; ++it) { + conjs.push_back(arith.mk_le(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it]))); + } + } + bsimp.mk_and(conjs.size(), conjs.c_ptr(), fml); + } + + + void bound_relation::display_index(unsigned i, uint_set2 const& src, std::ostream & out) const { + uint_set::iterator it = src.lt.begin(), end = src.lt.end(); + out << "#" << i; + if (!src.lt.empty()) { + out << " < "; + for(; it != end; ++it) { + out << *it << " "; + } + } + if (!src.le.empty()) { + it = src.le.begin(), end = src.le.end(); + out << " <= "; + for(; it != end; ++it) { + out << *it << " "; + } + } + if (src.lt.empty() && src.le.empty()) { + out << " < oo"; + } + out << "\n"; + } + + bound_relation_plugin& bound_relation::get_plugin() const { + return dynamic_cast(relation_base::get_plugin()); + } + + +}; + + diff --git a/src/muz/rel/dl_bound_relation.h b/src/muz/rel/dl_bound_relation.h new file mode 100644 index 000000000..906ba571a --- /dev/null +++ b/src/muz/rel/dl_bound_relation.h @@ -0,0 +1,178 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_bound_relation.h + +Abstract: + + Basic (strict upper) bound relation. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-2-11 + +Revision History: + +--*/ +#ifndef _DL_BOUND_RELATION_H_ +#define _DL_BOUND_RELATION_H_ + +#include "dl_context.h" +#include "dl_relation_manager.h" +#include "dl_base.h" +#include "uint_set.h" +#include "dl_vector_relation.h" +#include "dl_interval_relation.h" +#include "arith_decl_plugin.h" +#include "basic_simplifier_plugin.h" + +namespace datalog { + + class bound_relation; + + class bound_relation_plugin : public relation_plugin { + friend class bound_relation; + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class union_fn_i; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + class filter_intersection_fn; + arith_util m_arith; + basic_simplifier_plugin m_bsimp; + public: + bound_relation_plugin(relation_manager& m); + virtual bool can_handle_signature(const relation_signature & s); + static symbol get_name() { return symbol("bound_relation"); } + virtual relation_base * mk_empty(const relation_signature & s); + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + + virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } + + +#if 0 + virtual intersection_filter_fn * mk_filter_by_intersection_fn( + const relation_base & t, + const relation_base & src, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * src_cols) { + return 0; + } +#endif + + static bound_relation* get(relation_base* r); + private: + static bound_relation& get(relation_base& r); + static bound_relation const & get(relation_base const& r); + + + static bool is_interval_relation(relation_base const& r); + static interval_relation& get_interval_relation(relation_base& r); + static interval_relation const& get_interval_relation(relation_base const& r); + }; + + struct uint_set2 { + uint_set lt; + uint_set le; + uint_set2(uint_set2 const& other):lt(other.lt), le(other.le) {} + uint_set2() {} + bool operator==(const uint_set2& other) const { + return other.lt == lt && other.le == le; + } + bool operator!=(const uint_set2& other) const { + return other.lt != lt || other.le != le; + } + }; + + inline std::ostream & operator<<(std::ostream & target, const uint_set2 & s) { + return target << s.lt << " " << s.le; + } + + + class bound_relation_helper { + public: + static void mk_project_t(uint_set2& t, unsigned_vector const& renaming); + }; + + class bound_relation : public vector_relation { + friend class bound_relation_plugin; + svector > m_todo; + + public: + bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty); + bound_relation& operator=(bound_relation const& other); + + virtual bool empty() const { return m_empty; } + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual bound_relation * clone() const; + virtual bound_relation * complement(func_decl* p) const; + virtual void to_formula(expr_ref& fml) const; + bound_relation_plugin& get_plugin() const; + + void mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen); + + void mk_lt(unsigned i, unsigned j); + + void mk_lt(unsigned i); + + void mk_le(unsigned i, unsigned j); + + bool is_lt(unsigned i, unsigned j) const; + + virtual bool is_precise() const { return false; } + + private: + typedef uint_set2 T; + virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const; + + virtual T mk_widen(T const& t1, T const& t2) const; + + virtual T mk_unite(T const& t1, T const& t2) const; + + virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, T const& t) const; + + virtual void mk_rename_elem(T& i, unsigned col_cnt, unsigned const* cycle); + + + virtual bool is_subset_of(T const& t1, T const& t2) const; + + virtual bool is_full(T const& t) const; + + virtual bool is_empty(unsigned idx, T const& t) const; + + virtual void display_index(unsigned idx, T const& t, std::ostream& out) const; + + void normalize(T const& src, T& dst) const; + + void normalize(uint_set const& src, uint_set& dst) const; + + + + }; + +}; + +#endif + diff --git a/src/muz/rel/dl_check_table.cpp b/src/muz/rel/dl_check_table.cpp new file mode 100644 index 000000000..ea4003e5f --- /dev/null +++ b/src/muz/rel/dl_check_table.cpp @@ -0,0 +1,439 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_check_table.cpp + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2010-11-15 + + +Revision History: + +--*/ + + +#include "dl_check_table.h" +#include "dl_table.h" + + +namespace datalog { + + bool check_table_plugin::can_handle_signature(table_signature const& s) { + return m_tocheck.can_handle_signature(s) && m_checker.can_handle_signature(s); + } + + + check_table & check_table_plugin::get(table_base& r) { + return static_cast(r); + } + + check_table const & check_table_plugin::get(table_base const& r) { + return static_cast(r); + } + + table_base& check_table_plugin::checker(table_base& r) { return *get(r).m_checker; } + table_base const& check_table_plugin::checker(table_base const& r) { return *get(r).m_checker; } + table_base* check_table_plugin::checker(table_base* r) { return r?(get(*r).m_checker):0; } + table_base const* check_table_plugin::checker(table_base const* r) { return r?(get(*r).m_checker):0; } + table_base& check_table_plugin::tocheck(table_base& r) { return *get(r).m_tocheck; } + table_base const& check_table_plugin::tocheck(table_base const& r) { return *get(r).m_tocheck; } + table_base* check_table_plugin::tocheck(table_base* r) { return r?(get(*r).m_tocheck):0; } + table_base const* check_table_plugin::tocheck(table_base const* r) { return r?(get(*r).m_tocheck):0; } + + table_base * check_table_plugin::mk_empty(const table_signature & s) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + table_base* checker = m_checker.mk_empty(s); + table_base* tocheck = m_tocheck.mk_empty(s); + return alloc(check_table, *this, s, tocheck, checker); + } + + class check_table_plugin::join_fn : public table_join_fn { + scoped_ptr m_tocheck; + scoped_ptr m_checker; + public: + join_fn(check_table_plugin& p, + const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + m_tocheck = p.get_manager().mk_join_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2); + m_checker = p.get_manager().mk_join_fn(checker(t1), checker(t2), col_cnt, cols1, cols2); + } + + virtual table_base* operator()(const table_base & t1, const table_base & t2) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); + table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); + check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_join_fn * check_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(t1) || !check_kind(t2)) { + return 0; + } + return alloc(join_fn, *this, t1, t2, col_cnt, cols1, cols2); + } + + class check_table_plugin::join_project_fn : public table_join_fn { + scoped_ptr m_tocheck; + scoped_ptr m_checker; + public: + join_project_fn(check_table_plugin& p, const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { + m_tocheck = p.get_manager().mk_join_project_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + m_checker = p.get_manager().mk_join_project_fn(checker(t1), checker(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + } + + virtual table_base* operator()(const table_base & t1, const table_base & t2) { + table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); + table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); + check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_join_fn * check_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) { + if (!check_kind(t1) || !check_kind(t2)) { + return 0; + } + return alloc(join_project_fn, *this, t1, t2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + } + + class check_table_plugin::union_fn : public table_union_fn { + scoped_ptr m_tocheck; + scoped_ptr m_checker; + public: + union_fn(check_table_plugin& p, table_base const& tgt, const table_base& src, table_base const* delta) { + m_tocheck = p.get_manager().mk_union_fn(tocheck(tgt), tocheck(src), tocheck(delta)); + m_checker = p.get_manager().mk_union_fn(checker(tgt), checker(src), checker(delta)); + } + + virtual void operator()(table_base& tgt, const table_base& src, table_base* delta) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + (*m_tocheck)(tocheck(tgt), tocheck(src), tocheck(delta)); + (*m_checker)(checker(tgt), checker(src), checker(delta)); + get(tgt).well_formed(); + if (delta) { + get(*delta).well_formed(); + } + } + }; + + table_union_fn * check_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, *this, tgt, src, delta); + + } + + class check_table_plugin::project_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + project_fn(check_table_plugin& p, const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { + m_checker = p.get_manager().mk_project_fn(checker(t), col_cnt, removed_cols); + m_tocheck = p.get_manager().mk_project_fn(tocheck(t), col_cnt, removed_cols); + } + + table_base* operator()(table_base const& src) { + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { + if (!check_kind(t)) { + return 0; + } + return alloc(project_fn, *this, t, col_cnt, removed_cols); + } + + class check_table_plugin::select_equal_and_project_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + select_equal_and_project_fn(check_table_plugin& p, const table_base & t, const table_element & value, unsigned col) { + m_checker = p.get_manager().mk_select_equal_and_project_fn(checker(t), value, col); + m_tocheck = p.get_manager().mk_select_equal_and_project_fn(tocheck(t), value, col); + } + + table_base* operator()(table_base const& src) { + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col) { + if (!check_kind(t)) { + return 0; + } + return alloc(select_equal_and_project_fn, *this, t, value, col); + } + + class check_table_plugin::rename_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + rename_fn(check_table_plugin& p, const table_base & t, unsigned cycle_len, unsigned const* cycle) { + m_checker = p.get_manager().mk_rename_fn(checker(t), cycle_len, cycle); + m_tocheck = p.get_manager().mk_rename_fn(tocheck(t), cycle_len, cycle); + } + + table_base* operator()(table_base const& src) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) { + if (!check_kind(t)) { + return 0; + } + return alloc(rename_fn, *this, t, len, cycle); + } + + class check_table_plugin::filter_identical_fn : public table_mutator_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_identical_fn(check_table_plugin& p, const table_base & t,unsigned cnt, unsigned const* cols) + { + m_checker = p.get_manager().mk_filter_identical_fn(checker(t), cnt, cols); + m_tocheck = p.get_manager().mk_filter_identical_fn(tocheck(t), cnt, cols); + } + + void operator()(table_base & t) { + (*m_checker)(checker(t)); + (*m_tocheck)(tocheck(t)); + get(t).well_formed(); + } + }; + + table_mutator_fn * check_table_plugin::mk_filter_identical_fn(const table_base & t, unsigned col_cnt, + const unsigned * identical_cols) { + if (check_kind(t)) { + return alloc(filter_identical_fn, *this, t, col_cnt, identical_cols); + } + return 0; + } + + class check_table_plugin::filter_equal_fn : public table_mutator_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_equal_fn(check_table_plugin& p, const table_base & t, const table_element & v, unsigned col) + { + m_checker = p.get_manager().mk_filter_equal_fn(checker(t), v, col); + m_tocheck = p.get_manager().mk_filter_equal_fn(tocheck(t), v, col); + } + + virtual void operator()(table_base& src) { + (*m_checker)(checker(src)); + (*m_tocheck)(tocheck(src)); + get(src).well_formed(); + } + }; + + table_mutator_fn * check_table_plugin::mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col) { + if (check_kind(t)) { + return alloc(filter_equal_fn, *this, t, value, col); + } + return 0; + } + + class check_table_plugin::filter_interpreted_fn : public table_mutator_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_interpreted_fn(check_table_plugin& p, const table_base & t, app * condition) + { + m_checker = p.get_manager().mk_filter_interpreted_fn(checker(t), condition); + m_tocheck = p.get_manager().mk_filter_interpreted_fn(tocheck(t), condition); + } + + virtual void operator()(table_base& src) { + (*m_checker)(checker(src)); + (*m_tocheck)(tocheck(src)); + get(src).well_formed(); + } + }; + + table_mutator_fn * check_table_plugin::mk_filter_interpreted_fn(const table_base & t, app * condition) { + if (check_kind(t)) { + return alloc(filter_interpreted_fn, *this, t, condition); + } + return 0; + } + + class check_table_plugin::filter_interpreted_and_project_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_interpreted_and_project_fn(check_table_plugin& p, const table_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols) + { + m_checker = p.get_manager().mk_filter_interpreted_and_project_fn(checker(t), condition, removed_col_cnt, removed_cols); + m_tocheck = p.get_manager().mk_filter_interpreted_and_project_fn(tocheck(t), condition, removed_col_cnt, removed_cols); + } + + table_base* operator()(table_base const& src) { + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + if (check_kind(t)) { + return alloc(filter_interpreted_and_project_fn, *this, t, condition, removed_col_cnt, removed_cols); + } + return 0; + } + + class check_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_by_negation_fn( + check_table_plugin& p, + const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + m_checker = p.get_manager().mk_filter_by_negation_fn(checker(t), checker(negated_obj), joined_col_cnt, t_cols, negated_cols); + m_tocheck = p.get_manager().mk_filter_by_negation_fn(tocheck(t), tocheck(negated_obj), joined_col_cnt, t_cols, negated_cols); + } + + virtual void operator()(table_base& src, table_base const& negated_obj) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + (*m_checker)(checker(src), checker(negated_obj)); + (*m_tocheck)(tocheck(src), tocheck(negated_obj)); + get(src).well_formed(); + } + + }; + + table_intersection_filter_fn * check_table_plugin::mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + if (check_kind(t) && check_kind(negated_obj)) { + return alloc(filter_by_negation_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); + } + return 0; + } + + // ------------------ + // check_table + + + check_table::check_table(check_table_plugin & p, const table_signature & sig): + table_base(p, sig) { + (well_formed()); + } + + check_table::check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker): + table_base(p, sig), + m_checker(checker), + m_tocheck(tocheck) { + well_formed(); + } + + check_table::~check_table() { + m_tocheck->deallocate(); + m_checker->deallocate(); + } + + bool check_table::well_formed() const { + get_plugin().m_count++; + iterator it = m_tocheck->begin(), end = m_tocheck->end(); + for (; it != end; ++it) { + table_fact fact; + it->get_fact(fact); + if (!m_checker->contains_fact(fact)) { + m_tocheck->display(verbose_stream()); + m_checker->display(verbose_stream()); + verbose_stream() << get_plugin().m_count << "\n"; + UNREACHABLE(); + fatal_error(0); + return false; + } + } + iterator it2 = m_checker->begin(), end2 = m_checker->end(); + for (; it2 != end2; ++it2) { + table_fact fact; + it2->get_fact(fact); + if (!m_tocheck->contains_fact(fact)) { + m_tocheck->display(verbose_stream()); + m_checker->display(verbose_stream()); + verbose_stream() << get_plugin().m_count << "\n"; + UNREACHABLE(); + fatal_error(0); + return false; + } + } + return true; + } + + bool check_table::empty() const { + if (m_tocheck->empty() != m_checker->empty()) { + m_tocheck->display(verbose_stream()); + m_checker->display(verbose_stream()); + verbose_stream() << get_plugin().m_count << "\n"; + fatal_error(0); + } + return m_tocheck->empty(); + } + + + void check_table::add_fact(const table_fact & f) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + m_tocheck->add_fact(f); + m_checker->add_fact(f); + well_formed(); + } + + void check_table::remove_fact(const table_element* f) { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + m_tocheck->remove_fact(f); + m_checker->remove_fact(f); + well_formed(); + } + + bool check_table::contains_fact(const table_fact & f) const { + return m_checker->contains_fact(f); + } + + table_base * check_table::clone() const { + IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); + check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->clone(), m_checker->clone()); + return result; + } + + table_base * check_table::complement(func_decl* p, const table_element * func_columns) const { + check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->complement(p, func_columns), m_checker->complement(p, func_columns)); + return result; + } + +}; + diff --git a/src/muz/rel/dl_check_table.h b/src/muz/rel/dl_check_table.h new file mode 100644 index 000000000..e4f439590 --- /dev/null +++ b/src/muz/rel/dl_check_table.h @@ -0,0 +1,135 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_check_table.h + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2010-11-15 + + +Revision History: + +--*/ + +#ifndef _DL_CHECK_TABLE_H_ +#define _DL_CHECK_TABLE_H_ + +#include "dl_base.h" +#include "dl_decl_plugin.h" +#include "dl_relation_manager.h" + +namespace datalog { + class check_table; + + class check_table_plugin : public table_plugin { + friend class check_table; + table_plugin& m_checker; + table_plugin& m_tocheck; + unsigned m_count; + protected: + class join_fn; + class join_project_fn; + class union_fn; + class transformer_fn; + class rename_fn; + class project_fn; + class select_equal_and_project_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + class filter_interpreted_and_project_fn; + class filter_by_negation_fn; + + public: + check_table_plugin(relation_manager & manager, symbol const& checker, symbol const& tocheck) + : table_plugin(symbol("check"), manager), + m_checker(*manager.get_table_plugin(checker)), + m_tocheck(*manager.get_table_plugin(tocheck)), m_count(0) {} + + virtual table_base * mk_empty(const table_signature & s); + + virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols); + virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta); + virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col); + virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, + unsigned col); + virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); + virtual table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); + virtual table_intersection_filter_fn * mk_filter_by_negation_fn( + const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + + virtual bool can_handle_signature(table_signature const& s); + + private: + static check_table& get(table_base& r); + + static check_table const & get(table_base const& r); + + static table_base& checker(table_base& r); + static table_base const& checker(table_base const& r); + static table_base* checker(table_base* r); + static table_base const* checker(table_base const* r); + static table_base& tocheck(table_base& r); + static table_base const& tocheck(table_base const& r); + static table_base* tocheck(table_base* r); + static table_base const* tocheck(table_base const* r); + }; + + class check_table : public table_base { + friend class check_table_plugin; + + table_base* m_checker; + table_base* m_tocheck; + + check_table(check_table_plugin & p, const table_signature & sig); + check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker); + + virtual ~check_table(); + + bool well_formed() const; + + public: + + check_table_plugin & get_plugin() const { + return static_cast(table_base::get_plugin()); + } + + virtual bool empty() const; + virtual void add_fact(const table_fact & f); + virtual void remove_fact(const table_element* fact); + virtual bool contains_fact(const table_fact & f) const; + virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; + virtual table_base * clone() const; + + virtual iterator begin() const { SASSERT(well_formed()); return m_tocheck->begin(); } + virtual iterator end() const { return m_tocheck->end(); } + + virtual unsigned get_size_estimate_rows() const { return m_tocheck->get_size_estimate_rows(); } + virtual unsigned get_size_estimate_bytes() const { return m_tocheck->get_size_estimate_bytes(); } + }; + + }; + + #endif /* _DL_CHECK_TABLE_H_ */ diff --git a/src/muz/rel/dl_external_relation.cpp b/src/muz/rel/dl_external_relation.cpp new file mode 100644 index 000000000..f32509473 --- /dev/null +++ b/src/muz/rel/dl_external_relation.cpp @@ -0,0 +1,456 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_external_relation.cpp + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2010-05-10 + +Revision History: + +--*/ + +#include "debug.h" +#include "ast_pp.h" +#include "dl_context.h" +#include "dl_external_relation.h" +#include "dl_decl_plugin.h" + +namespace datalog { + + external_relation::external_relation(external_relation_plugin & p, const relation_signature & s, expr* r) + : relation_base(p, s), + m_rel(r, p.get_ast_manager()), + m_select_fn(p.get_ast_manager()), + m_store_fn(p.get_ast_manager()), + m_is_empty_fn(p.get_ast_manager()) + { + } + + external_relation::~external_relation() { + } + + void external_relation::mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const { + ast_manager& m = m_rel.get_manager(); + family_id fid = get_plugin().get_family_id(); + ptr_vector args; + args.push_back(m_rel); + for (unsigned i = 0; i < f.size(); ++i) { + args.push_back(f[i]); + } + if (!fn.get()) { + fn = m.mk_func_decl(fid, k, 0, 0, args.size(), args.c_ptr()); + } + if (destructive) { + get_plugin().reduce_assign(fn, args.size(), args.c_ptr(), 1, args.c_ptr()); + res = m_rel; + } + else { + get_plugin().reduce(fn, args.size(), args.c_ptr(), res); + } + } + + bool external_relation::empty() const { + ast_manager& m = m_rel.get_manager(); + expr* r = m_rel.get(); + expr_ref res(m); + if (!m_is_empty_fn.get()) { + family_id fid = get_plugin().get_family_id(); + const_cast(m_is_empty_fn) = m.mk_func_decl(fid, OP_RA_IS_EMPTY, 0, 0, 1, &r); + } + get_plugin().reduce(m_is_empty_fn, 1, &r, res); + return m.is_true(res); + } + + void external_relation::add_fact(const relation_fact & f) { + mk_accessor(OP_RA_STORE, m_store_fn, f, true, m_rel); + } + + bool external_relation::contains_fact(const relation_fact & f) const { + ast_manager& m = get_plugin().get_ast_manager(); + expr_ref res(m); + mk_accessor(OP_RA_SELECT, const_cast(m_select_fn), f, false, res); + return !m.is_false(res); + } + + external_relation * external_relation::clone() const { + ast_manager& m = m_rel.get_manager(); + family_id fid = get_plugin().get_family_id(); + expr* rel = m_rel.get(); + expr_ref res(m.mk_fresh_const("T", m.get_sort(rel)), m); + expr* rel_out = res.get(); + func_decl_ref fn(m.mk_func_decl(fid, OP_RA_CLONE,0,0, 1, &rel), m); + get_plugin().reduce_assign(fn, 1, &rel, 1, &rel_out); + return alloc(external_relation, get_plugin(), get_signature(), res); + } + + external_relation * external_relation::complement(func_decl* p) const { + ast_manager& m = m_rel.get_manager(); + family_id fid = get_plugin().get_family_id(); + expr_ref res(m); + expr* rel = m_rel; + func_decl_ref fn(m.mk_func_decl(fid, OP_RA_COMPLEMENT,0,0, 1, &rel), m); + get_plugin().reduce(fn, 1, &rel, res); + return alloc(external_relation, get_plugin(), get_signature(), res); + } + + void external_relation::display(std::ostream & out) const { + out << mk_pp(m_rel, m_rel.get_manager()) << "\n"; + } + + void external_relation::display_tuples(func_decl & pred, std::ostream & out) const { + display(out); + } + + + external_relation_plugin & external_relation::get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + + // ----------------------------------- + // + // external_relation_plugin + // + // ----------------------------------- + + + external_relation_plugin::external_relation_plugin(external_relation_context& ctx, relation_manager & m) + : relation_plugin(external_relation_plugin::get_name(), m), m_ext(ctx) {} + + external_relation const & external_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + external_relation & external_relation_plugin::get(relation_base & r) { + return dynamic_cast(r); + } + + relation_base * external_relation_plugin::mk_empty(const relation_signature & s) { + ast_manager& m = get_ast_manager(); + sort* r_sort = get_relation_sort(s); + parameter param(r_sort); + family_id fid = get_family_id(); + expr_ref e(m.mk_fresh_const("T", r_sort), m); + expr* args[1] = { e.get() }; + func_decl_ref empty_decl(m.mk_func_decl(fid, OP_RA_EMPTY, 1, ¶m, 0, (sort*const*)0), m); + reduce_assign(empty_decl, 0, 0, 1, args); + return alloc(external_relation, *this, s, e); + } + + sort* external_relation_plugin::get_relation_sort(relation_signature const& sig) { + vector sorts; + ast_manager& m = get_ast_manager(); + family_id fid = get_family_id(); + for (unsigned i = 0; i < sig.size(); ++i) { + sorts.push_back(parameter(sig[i])); + } + return m.mk_sort(fid, DL_RELATION_SORT, sorts.size(), sorts.c_ptr()); + } + + sort* external_relation_plugin::get_column_sort(unsigned col, sort* s) { + SASSERT(s->get_num_parameters() > col); + SASSERT(s->get_parameter(col).is_ast()); + SASSERT(is_sort(s->get_parameter(col).get_ast())); + return to_sort(s->get_parameter(col).get_ast()); + } + + family_id external_relation_plugin::get_family_id() { + return m_ext.get_family_id(); + } + + + void external_relation_plugin::mk_filter_fn(sort* s, app* condition, func_decl_ref& f) { + ast_manager& m = get_ast_manager(); + family_id fid = get_family_id(); + parameter param(condition); + f = m.mk_func_decl(fid, OP_RA_FILTER, 1, ¶m, 1, &s); + } + + class external_relation_plugin::join_fn : public convenient_relation_join_fn { + external_relation_plugin& m_plugin; + func_decl_ref m_join_fn; + expr* m_args[2]; + public: + join_fn(external_relation_plugin& p, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2), + m_plugin(p), + m_join_fn(p.get_ast_manager()) { + ast_manager& m = p.get_ast_manager(); + family_id fid = p.get_family_id(); + vector params; + for (unsigned i = 0; i < col_cnt; ++i) { + params.push_back(parameter(cols1[i])); + params.push_back(parameter(cols2[i])); + } + sort* domain[2] = { p.get_relation_sort(o1_sig), p.get_relation_sort(o2_sig) }; + m_join_fn = m.mk_func_decl(fid, OP_RA_JOIN, params.size(), params.c_ptr(), 2, domain); + } + + virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + expr_ref res(m_plugin.get_ast_manager()); + m_args[0] = get(r1).get_relation(); + m_args[1] = get(r2).get_relation(); + m_plugin.reduce(m_join_fn, 2, m_args, res); + return alloc(external_relation, m_plugin, get_result_signature(), res); + } + }; + + relation_join_fn * external_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(r1) || !check_kind(r2)) { + return 0; + } + return alloc(join_fn, *this, r1.get_signature(), r2.get_signature() , col_cnt, cols1, cols2); + } + + + class external_relation_plugin::project_fn : public convenient_relation_project_fn { + external_relation_plugin& m_plugin; + func_decl_ref m_project_fn; + public: + project_fn(external_relation_plugin& p, sort* relation_sort, + const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols), + m_plugin(p), + m_project_fn(p.get_ast_manager()) { + vector params; + ast_manager& m = p.get_ast_manager(); + family_id fid = p.get_family_id(); + for (unsigned i = 0; i < removed_col_cnt; ++i) { + params.push_back(parameter(removed_cols[i])); + } + m_project_fn = m.mk_func_decl(fid, OP_RA_PROJECT, params.size(), params.c_ptr(), 1, &relation_sort); + } + + virtual relation_base * operator()(const relation_base & r) { + expr_ref res(m_plugin.get_ast_manager()); + expr* rel = get(r).get_relation(); + m_plugin.reduce(m_project_fn, 1, &rel, res); + return alloc(external_relation, m_plugin, get_result_signature(), to_app(res)); + } + }; + + relation_transformer_fn * external_relation_plugin::mk_project_fn(const relation_base & r, + unsigned col_cnt, const unsigned * removed_cols) { + return alloc(project_fn, *this, get(r).get_sort(), r.get_signature(), col_cnt, removed_cols); + } + + + class external_relation_plugin::rename_fn : public convenient_relation_rename_fn { + external_relation_plugin& m_plugin; + func_decl_ref m_rename_fn; + expr* m_args[2]; + public: + rename_fn(external_relation_plugin& p, sort* relation_sort, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle), + m_plugin(p), + m_rename_fn(p.get_ast_manager()) { + + ast_manager& m = p.get_ast_manager(); + family_id fid = p.get_family_id(); + vector params; + for (unsigned i = 0; i < cycle_len; ++i) { + SASSERT(cycle[i] < orig_sig.size()); + params.push_back(parameter(cycle[i])); + } + m_rename_fn = m.mk_func_decl(fid, OP_RA_RENAME, params.size(), params.c_ptr(), 1, &relation_sort); + } + + virtual relation_base * operator()(const relation_base & r) { + expr* rel = get(r).get_relation(); + expr_ref res(m_plugin.get_ast_manager()); + m_args[0] = rel; + m_plugin.reduce(m_rename_fn, 1, &rel, res); + return alloc(external_relation, m_plugin, get_result_signature(), res); + } + }; + + relation_transformer_fn * external_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if(!check_kind(r)) { + return 0; + } + return alloc(rename_fn, *this, get(r).get_sort(), r.get_signature(), cycle_len, permutation_cycle); + } + + + class external_relation_plugin::union_fn : public relation_union_fn { + external_relation_plugin& m_plugin; + func_decl_ref m_union_fn; + expr* m_args[2]; + expr* m_outs[2]; + + public: + union_fn(external_relation_plugin& p, decl_kind k, sort* relation_sort): + m_plugin(p), + m_union_fn(p.get_ast_manager()) { + ast_manager& m = p.get_ast_manager(); + sort* domain[2] = { relation_sort, relation_sort }; + m_union_fn = m.mk_func_decl(p.get_family_id(), k, 0, 0, 2, domain); + } + + virtual void operator()(relation_base & r, const relation_base & src, relation_base * delta) { + ast_manager& m = m_plugin.get_ast_manager(); + expr_ref_vector res(m); + m_args[0] = get(r).get_relation(); + m_args[1] = get(src).get_relation(); + m_outs[0] = m_args[0]; + unsigned num_out = 1; + if (delta) { + m_outs[1] = get(*delta).get_relation(); + ++num_out; + } + m_plugin.reduce_assign(m_union_fn, 2, m_args, num_out, m_outs); + } + }; + + relation_union_fn * external_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, *this, OP_RA_UNION, get(src).get_sort()); + } + + relation_union_fn * external_relation_plugin::mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, *this, OP_RA_WIDEN, get(src).get_sort()); + } + + class external_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + external_relation_plugin& m_plugin; + app_ref m_condition; + func_decl_ref m_filter_fn; + public: + filter_interpreted_fn(external_relation_plugin& p, sort* relation_sort, app * condition) + : m_plugin(p), + m_condition(condition, p.get_ast_manager()), + m_filter_fn(p.get_ast_manager()) { + ast_manager& m = p.get_ast_manager(); + p.mk_filter_fn(relation_sort, condition, m_filter_fn); + SASSERT(m.is_bool(condition)); + } + + virtual void operator()(relation_base & r) { + SASSERT(m_plugin.check_kind(r)); + expr* arg = get(r).get_relation(); + m_plugin.reduce_assign(m_filter_fn, 1, &arg, 1, &arg); + } + }; + + relation_mutator_fn * external_relation_plugin::mk_filter_interpreted_fn(const relation_base & r, app * condition) { + if(!check_kind(r)) { + return 0; + } + return alloc(filter_interpreted_fn, *this, get(r).get_sort(), condition); + } + + relation_mutator_fn * external_relation_plugin::mk_filter_equal_fn(const relation_base & r, + const relation_element & value, unsigned col) { + if(!check_kind(r)) { + return 0; + } + ast_manager& m = get_ast_manager(); + app_ref condition(m); + expr_ref var(m); + sort* relation_sort = get(r).get_sort(); + sort* column_sort = get_column_sort(col, relation_sort); + var = m.mk_var(col, column_sort); + condition = m.mk_eq(var, value); + return mk_filter_interpreted_fn(r, condition); + } + + class external_relation_plugin::filter_identical_fn : public relation_mutator_fn { + external_relation_plugin& m_plugin; + func_decl_ref_vector m_filter_fn; + public: + filter_identical_fn(external_relation_plugin& p, sort* relation_sort, + unsigned col_cnt, const unsigned * identical_cols) + : m_plugin(p), m_filter_fn(p.get_ast_manager()) { + ast_manager& m = p.get_ast_manager(); + func_decl_ref fn(m); + app_ref eq(m); + if (col_cnt <= 1) { + return; + } + unsigned col = identical_cols[0]; + sort* s = p.get_column_sort(col, relation_sort); + var* v0 = m.mk_var(col, s); + for (unsigned i = 1; i < col_cnt; ++i) { + col = identical_cols[i]; + s = p.get_column_sort(col, relation_sort); + eq = m.mk_eq(v0, m.mk_var(col, s)); + p.mk_filter_fn(relation_sort, eq.get(), fn); + m_filter_fn.push_back(fn); + } + } + + virtual void operator()(relation_base & r) { + expr* r0 = get(r).get_relation(); + for (unsigned i = 0; i < m_filter_fn.size(); ++i) { + m_plugin.reduce_assign(m_filter_fn[i].get(), 1, &r0, 1, &r0); + } + } + }; + + relation_mutator_fn * external_relation_plugin::mk_filter_identical_fn(const relation_base & r, + unsigned col_cnt, const unsigned * identical_cols) { + if (!check_kind(r)) { + return 0; + } + return alloc(filter_identical_fn, *this, get(r).get_sort(), col_cnt, identical_cols); + } + + + class external_relation_plugin::negation_filter_fn : public convenient_relation_negation_filter_fn { + external_relation_plugin& m_plugin; + func_decl_ref m_negated_filter_fn; + expr* m_args[2]; + public: + negation_filter_fn(external_relation_plugin& p, + const relation_base & tgt, const relation_base & neg_t, + unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : + convenient_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), + m_plugin(p), + m_negated_filter_fn(p.get_ast_manager()) + { + ast_manager& m = p.get_ast_manager(); + family_id fid = p.get_family_id(); + vector params; + for (unsigned i = 0; i < joined_col_cnt; ++i) { + params.push_back(parameter(t_cols[i])); + params.push_back(parameter(negated_cols[i])); + } + sort* domain[2] = { get(tgt).get_sort(), get(neg_t).get_sort() }; + m_negated_filter_fn = m.mk_func_decl(fid, OP_RA_NEGATION_FILTER, params.size(), params.c_ptr(), 2, domain); + } + + void operator()(relation_base & t, const relation_base & negated_obj) { + m_args[0] = get(t).get_relation(); + m_args[1] = get(negated_obj).get_relation(); + m_plugin.reduce_assign(m_negated_filter_fn.get(), 2, m_args, 1, m_args); + } + }; + + relation_intersection_filter_fn * external_relation_plugin::mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + if (!check_kind(t) || !check_kind(negated_obj)) { + return 0; + } + return alloc(negation_filter_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); + } + +}; diff --git a/src/muz/rel/dl_external_relation.h b/src/muz/rel/dl_external_relation.h new file mode 100644 index 000000000..03838ecc4 --- /dev/null +++ b/src/muz/rel/dl_external_relation.h @@ -0,0 +1,154 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_external_relation.h + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2010-05-10 + +Revision History: + +--*/ +#ifndef _DL_EXTERNAL_RELATION_H_ +#define _DL_EXTERNAL_RELATION_H_ + +#include "dl_base.h" + +namespace datalog { + + class external_relation; + + class external_relation_context { + public: + virtual ~external_relation_context() {} + + virtual family_id get_family_id() const = 0; + + // reduce arguments. + virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) = 0; + + // overwrite terms passed in outs vector with values computed by function. + virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) = 0; + }; + + class external_relation_plugin : public relation_plugin { + + friend class external_relation; + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class filter_identical_fn; + class filter_interpreted_fn; + class negation_filter_fn; + + external_relation_context& m_ext; + + public: + external_relation_plugin(external_relation_context& ctx, relation_manager & m); + + virtual bool can_handle_signature(const relation_signature & s) { return true; } + + static symbol get_name() { return symbol("external_relation"); } + + virtual relation_base * mk_empty(const relation_signature & s); + + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + + private: + + static external_relation& get(relation_base& r); + static external_relation const & get(relation_base const& r); + + void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { + m_ext.reduce(f, num_args, args, result); + } + + void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { + m_ext.reduce_assign(f, num_args, args, num_out, outs); + } + + sort* get_relation_sort(relation_signature const& sig); + + sort* get_column_sort(unsigned col, sort* relation_sort); + + void mk_filter_fn(sort* s, app* condition, func_decl_ref& f); + + family_id get_family_id(); + }; + + class external_relation : public relation_base { + friend class external_relation_plugin; + friend class external_relation_plugin::join_fn; + friend class external_relation_plugin::project_fn; + friend class external_relation_plugin::rename_fn; + friend class external_relation_plugin::union_fn; + friend class external_relation_plugin::filter_identical_fn; + friend class external_relation_plugin::filter_interpreted_fn; + friend class external_relation_plugin::negation_filter_fn; + + expr_ref m_rel; + func_decl_ref m_select_fn; + func_decl_ref m_store_fn; + func_decl_ref m_is_empty_fn; + + unsigned size() const { return get_signature().size(); } + + sort* get_sort() const { return m_rel.get_manager().get_sort(m_rel); } + + void mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const; + + external_relation(external_relation_plugin & p, const relation_signature & s, expr* r); + virtual ~external_relation(); + + public: + external_relation_plugin & get_plugin() const; + + virtual bool empty() const; + + virtual void add_fact(const relation_fact & f); + + virtual bool contains_fact(const relation_fact & f) const; + + virtual external_relation * clone() const; + + virtual external_relation * complement(func_decl*) const; + + virtual void display(std::ostream & out) const; + + virtual void display_tuples(func_decl & pred, std::ostream & out) const; + + expr* get_relation() const { return m_rel.get(); } + + virtual void to_formula(expr_ref& fml) const { fml = get_relation(); } + + }; + + +}; + +#endif diff --git a/src/muz/rel/dl_finite_product_relation.cpp b/src/muz/rel/dl_finite_product_relation.cpp new file mode 100644 index 000000000..86fef433b --- /dev/null +++ b/src/muz/rel/dl_finite_product_relation.cpp @@ -0,0 +1,2377 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_finite_product_relation.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-14. + +Revision History: + +--*/ + + +#include +#include"dl_context.h" +#include"dl_relation_manager.h" +#include"dl_table_relation.h" +#include"dl_finite_product_relation.h" +#include"bool_rewriter.h" + +namespace datalog { + + //static variables + + const table_sort finite_product_relation::s_rel_idx_sort = INT_MAX; + + void universal_delete(finite_product_relation* ptr) { + ptr->deallocate(); + } + + + // ----------------------------------- + // + // finite_product_relation_plugin + // + // ----------------------------------- + + finite_product_relation & finite_product_relation_plugin::get(relation_base & r) { + SASSERT(r.get_plugin().is_finite_product_relation()); + return static_cast(r); + } + + const finite_product_relation & finite_product_relation_plugin::get(const relation_base & r) { + SASSERT(r.get_plugin().is_finite_product_relation()); + return static_cast(r); + } + + finite_product_relation * finite_product_relation_plugin::get(relation_base * r) { + SASSERT(!r || r->get_plugin().is_finite_product_relation()); + return static_cast(r); + } + + const finite_product_relation * finite_product_relation_plugin::get(const relation_base * r) { + SASSERT(!r || r->get_plugin().is_finite_product_relation()); + return static_cast(r); + } + + symbol finite_product_relation_plugin::get_name(relation_plugin & inner_plugin) { + std::string str = std::string("fpr_")+inner_plugin.get_name().bare_str(); + return symbol(str.c_str()); + } + + finite_product_relation_plugin & finite_product_relation_plugin::get_plugin(relation_manager & rmgr, + relation_plugin & inner) { + finite_product_relation_plugin * res; + if(!rmgr.try_get_finite_product_relation_plugin(inner, res)) { + res = alloc(finite_product_relation_plugin, inner, rmgr); + rmgr.register_plugin(res); + } + return *res; + } + + finite_product_relation_plugin::finite_product_relation_plugin(relation_plugin & inner_plugin, + relation_manager & manager) + : relation_plugin(get_name(inner_plugin), manager, ST_FINITE_PRODUCT_RELATION), + m_inner_plugin(inner_plugin), m_spec_store(*this) { + } + + void finite_product_relation_plugin::initialize(family_id fid) { + relation_plugin::initialize(fid); + m_spec_store.add_available_kind(get_kind()); + } + + family_id finite_product_relation_plugin::get_relation_kind(finite_product_relation & r, + const bool * table_columns) { + const relation_signature & sig = r.get_signature(); + svector table_cols_vect(sig.size(), table_columns); + return m_spec_store.get_relation_kind(sig, rel_spec(table_cols_vect)); + } + + void finite_product_relation_plugin::get_all_possible_table_columns(relation_manager & rmgr, + const relation_signature & s, svector & table_columns) { + SASSERT(table_columns.empty()); + unsigned s_sz = s.size(); + for(unsigned i=0; i table_columns; + get_all_possible_table_columns(s, table_columns); +#ifndef _EXTERNAL_RELEASE + unsigned s_sz = s.size(); + unsigned rel_col_cnt = 0; + for(unsigned i=0; icomplement_self(p); + return res; + } + + bool finite_product_relation_plugin::can_convert_to_table_relation(const finite_product_relation & r) { + return r.m_other_sig.empty(); + } + + table_relation * finite_product_relation_plugin::to_table_relation(const finite_product_relation & r) { + SASSERT(can_convert_to_table_relation(r)); + r.garbage_collect(true); + //now all rows in the table will correspond to rows in the resulting table_relation + + const table_base & t = r.get_table(); + + unsigned removed_col = t.get_signature().size()-1; + scoped_ptr project_fun = + get_manager().mk_project_fn(r.get_table(), 1, &removed_col); + + table_base * res_table = (*project_fun)(t); + SASSERT(res_table->get_signature().functional_columns()==0); + return static_cast(get_manager().mk_table_relation(r.get_signature(), res_table)); + } + + + bool finite_product_relation_plugin::can_be_converted(const relation_base & r) { + if(&r.get_plugin()==&get_inner_plugin()) { + //can be converted by mk_from_inner_relation + return true; + } + if(r.from_table()) { + //We can convert directly from table plugin only if the inner plugin can handle empty signatures. + + //TODO: If the inner plugin cannot handle empty signatures, we may try to move some of the + //table columns into the inner relation signature. + return get_inner_plugin().can_handle_signature(relation_signature()); + } + return false; + } + + finite_product_relation * finite_product_relation_plugin::mk_from_table_relation(const table_relation & r) { + func_decl* pred = 0; + const relation_signature & sig = r.get_signature(); + const table_base & t = r.get_table(); + table_plugin & tplugin = r.get_table().get_plugin(); + + relation_signature inner_sig; //empty signature for the inner relation + if(!get_inner_plugin().can_handle_signature(inner_sig)) { + return 0; + } + + table_signature idx_singleton_sig; + idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort); + idx_singleton_sig.set_functional_columns(1); + + scoped_rel idx_singleton; + if(tplugin.can_handle_signature(idx_singleton_sig)) { + idx_singleton = tplugin.mk_empty(idx_singleton_sig); + } + else { + idx_singleton = get_manager().mk_empty_table(idx_singleton_sig); + } + table_fact idx_singleton_fact; + idx_singleton_fact.push_back(0); + idx_singleton->add_fact(idx_singleton_fact); + + scoped_ptr join_fun = get_manager().mk_join_fn(t, *idx_singleton, 0, 0, 0); + SASSERT(join_fun); + scoped_rel res_table = (*join_fun)(t, *idx_singleton); + + svector table_cols(sig.size(), true); + finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); + + //this one does not need to be deleted -- it will be taken over by \c res in the \c init function + relation_base * inner_rel = get_inner_plugin().mk_full(pred, inner_sig, get_inner_plugin().get_kind()); + + relation_vector rels; + rels.push_back(inner_rel); + + res->init(*res_table, rels, true); + return res; + } + + finite_product_relation * finite_product_relation_plugin::mk_from_inner_relation(const relation_base & r) { + SASSERT(&r.get_plugin()==&get_inner_plugin()); + const relation_signature & sig = r.get_signature(); + + table_signature idx_singleton_sig; + idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort); + idx_singleton_sig.set_functional_columns(1); + + scoped_rel idx_singleton = get_manager().mk_empty_table(idx_singleton_sig); + table_fact idx_singleton_fact; + idx_singleton_fact.push_back(0); + idx_singleton->add_fact(idx_singleton_fact); + + svector table_cols(sig.size(), false); + finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); + + relation_vector rels; + rels.push_back(r.clone()); + + res->init(*idx_singleton, rels, true); + return res; + } + + class finite_product_relation_plugin::converting_join_fn : public convenient_relation_join_fn { + finite_product_relation_plugin & m_plugin; + scoped_ptr m_native_join; + + finite_product_relation * convert(const relation_base & r) { + SASSERT(&r.get_plugin()!=&m_plugin); + if(&r.get_plugin()==&m_plugin.get_inner_plugin()) { + return m_plugin.mk_from_inner_relation(r); + } + SASSERT(r.from_table()); + const table_relation & tr = static_cast(r); + finite_product_relation * res = m_plugin.mk_from_table_relation(tr); + SASSERT(res); + return res; + } + + public: + converting_join_fn(finite_product_relation_plugin & plugin, const relation_signature & sig1, + const relation_signature & sig2, unsigned col_cnt, const unsigned * cols1, + const unsigned * cols2) + : convenient_relation_join_fn(sig1, sig2, col_cnt, cols1, cols2), + m_plugin(plugin) {} + + virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + scoped_rel r1_conv; + if(&r1.get_plugin()!=&m_plugin) { + r1_conv = convert(r1); + } + scoped_rel r2_conv; + if(&r2.get_plugin()!=&m_plugin) { + r2_conv = convert(r2); + } + + const finite_product_relation & fpr1 = r1_conv ? *r1_conv : get(r1); + const finite_product_relation & fpr2 = r2_conv ? *r2_conv : get(r2); + + SASSERT(&fpr1.get_plugin()==&m_plugin); + SASSERT(&fpr2.get_plugin()==&m_plugin); + + if(!m_native_join) { + m_native_join = m_plugin.get_manager().mk_join_fn(fpr1, fpr2, m_cols1, m_cols2, false); + } + return (*m_native_join)(fpr1, fpr2); + } + }; + + + class finite_product_relation_plugin::join_fn : public convenient_relation_join_fn { + scoped_ptr m_tjoin_fn; + scoped_ptr m_rjoin_fn; + + unsigned_vector m_t_joined_cols1; + unsigned_vector m_t_joined_cols2; + unsigned_vector m_r_joined_cols1; + unsigned_vector m_r_joined_cols2; + + //Column equalities betweet table and inner relations. + //The columns numbers correspont to the columns of the table/inner relation + //in the result of the join operation + unsigned_vector m_tr_table_joined_cols; + unsigned_vector m_tr_rel_joined_cols; + + scoped_ptr m_filter_tr_identities; + + scoped_ptr m_tjoined_second_rel_remover; + + //determines which columns of the result are table columns and which are in the inner relation + svector m_res_table_columns; + + public: + class join_maker : public table_row_mutator_fn { + join_fn & m_parent; + const finite_product_relation & m_r1; + const finite_product_relation & m_r2; + relation_vector & m_rjoins; + public: + join_maker(join_fn & parent, const finite_product_relation & r1, const finite_product_relation & r2, + relation_vector & rjoins) + : m_parent(parent), m_r1(r1), m_r2(r2), m_rjoins(rjoins) {} + + virtual bool operator()(table_element * func_columns) { + const relation_base & or1 = m_r1.get_inner_rel(func_columns[0]); + const relation_base & or2 = m_r2.get_inner_rel(func_columns[1]); + SASSERT(&or1); + SASSERT(&or2); + unsigned new_rel_num = m_rjoins.size(); + m_rjoins.push_back(m_parent.do_rjoin(or1, or2)); + func_columns[0]=new_rel_num; + return true; + } + }; + + join_fn(const finite_product_relation & r1, const finite_product_relation & r2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2) { + unsigned second_table_after_join_ofs = r1.m_table2sig.size(); + unsigned second_inner_rel_after_join_ofs = r1.m_other2sig.size(); + for(unsigned i=0;i tjoined = (*m_tjoin_fn)(r1.get_table(), r2.get_table()); + + relation_vector joined_orelations; + + { + join_maker * mutator = alloc(join_maker, *this, r1, r2, joined_orelations); //dealocated in inner_join_mapper + scoped_ptr inner_join_mapper = rmgr.mk_map_fn(*tjoined, mutator); + (*inner_join_mapper)(*tjoined); + } + + + if(!m_tjoined_second_rel_remover) { + unsigned removed_col = tjoined->get_signature().size()-1; + m_tjoined_second_rel_remover = rmgr.mk_project_fn(*tjoined, 1, &removed_col); + } + //remove the second functional column from tjoined to get a table that corresponds + //to the table signature of the resulting relation + scoped_rel res_table = (*m_tjoined_second_rel_remover)(*tjoined); + + relation_plugin & res_oplugin = + joined_orelations.empty() ? r1.m_other_plugin : joined_orelations.back()->get_plugin(); + + //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. + //It would however need to be somehow inferred for the new signature. + + finite_product_relation * res = alloc(finite_product_relation, r1.get_plugin(), get_result_signature(), + m_res_table_columns.c_ptr(), res_table->get_plugin(), res_oplugin, null_family_id); + + res->init(*res_table, joined_orelations, true); + + if(m_tr_table_joined_cols.size()) { + //There were some shared variables between the table and the relation part. + //We enforce those equalities here. + if(!m_filter_tr_identities) { + m_filter_tr_identities = plugin.mk_filter_identical_pairs(*res, m_tr_table_joined_cols.size(), + m_tr_table_joined_cols.c_ptr(), m_tr_rel_joined_cols.c_ptr()); + SASSERT(m_filter_tr_identities); + } + (*m_filter_tr_identities)(*res); + } + return res; + } + }; + + + + + relation_join_fn * finite_product_relation_plugin::mk_join_fn(const relation_base & rb1, const relation_base & rb2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if(!check_kind(rb1) || !check_kind(rb2)) { + bool r1foreign = &rb1.get_plugin()!=this; + bool r2foreign = &rb2.get_plugin()!=this; + if( (!r1foreign || can_be_converted(rb1)) && (!r2foreign || can_be_converted(rb2))) { + return alloc(converting_join_fn, *this, rb1.get_signature(), rb2.get_signature(), col_cnt, cols1, + cols2); + } + return 0; + } + const finite_product_relation & r1 = get(rb1); + const finite_product_relation & r2 = get(rb2); + + return alloc(join_fn, r1, r2, col_cnt, cols1, cols2); + } + + + class finite_product_relation_plugin::project_fn : public convenient_relation_project_fn { + unsigned_vector m_removed_table_cols; + unsigned_vector m_removed_rel_cols; + + scoped_ptr m_rel_projector; + scoped_ptr m_inner_rel_union; + + //determines which columns of the result are table columns and which are in the inner relation + svector m_res_table_columns; + public: + project_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(r.get_signature(), col_cnt, removed_cols) { + SASSERT(col_cnt>0); + for(unsigned i=0; ii); + m_res_table_columns.push_back(r.is_table_column(i)); + } + } + + class project_reducer : public table_row_pair_reduce_fn { + project_fn & m_parent; + relation_vector & m_relations; + public: + + project_reducer(project_fn & parent, relation_vector & relations) + : m_parent(parent), m_relations(relations) {} + + virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { + relation_base * tgt = m_relations[static_cast(func_columns[0])]->clone(); + relation_base & src = *m_relations[static_cast(merged_func_columns[0])]; + if(!m_parent.m_inner_rel_union) { + m_parent.m_inner_rel_union = tgt->get_manager().mk_union_fn(*tgt, src); + } + (*m_parent.m_inner_rel_union)(*tgt, src); + + unsigned new_idx = m_relations.size(); + m_relations.push_back(tgt); + func_columns[0] = new_idx; + } + }; + + virtual relation_base * operator()(const relation_base & rb) { + const finite_product_relation & r = get(rb); + finite_product_relation_plugin & plugin = r.get_plugin(); + const table_base & rtable = r.get_table(); + relation_manager & rmgr = plugin.get_manager(); + + r.garbage_collect(false); + relation_vector res_relations; + unsigned orig_rel_cnt = r.m_others.size(); + for(unsigned i=0; iclone() : 0); + } + SASSERT(res_relations.size()==orig_rel_cnt); + + bool shared_res_table = false; + const table_base * res_table; + + if(m_removed_table_cols.empty()) { + shared_res_table = true; + res_table = &rtable; + } + else { + project_reducer * preducer = alloc(project_reducer, *this, res_relations); + scoped_ptr tproject = + rmgr.mk_project_with_reduce_fn(rtable, m_removed_table_cols.size(), m_removed_table_cols.c_ptr(), preducer); + res_table = (*tproject)(rtable); + } + + relation_plugin * res_oplugin = 0; + + if(!m_removed_rel_cols.empty()) { + unsigned res_rel_cnt = res_relations.size(); + for(unsigned i=0; ideallocate(); + if(!res_oplugin) { + res_oplugin = &res_relations[i]->get_plugin(); + } + } + } + + if(!res_oplugin) { + res_oplugin = &r.m_other_plugin; + } + + //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. + //It would however need to be somehow inferred for the new signature. + + finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), + m_res_table_columns.c_ptr(), res_table->get_plugin(), *res_oplugin, null_family_id); + + res->init(*res_table, res_relations, false); + + if(!shared_res_table) { + const_cast(res_table)->deallocate(); + } + + return res; + } + }; + + relation_transformer_fn * finite_product_relation_plugin::mk_project_fn(const relation_base & rb, unsigned col_cnt, + const unsigned * removed_cols) { + if(&rb.get_plugin()!=this) { + return 0; + } + return alloc(project_fn, get(rb), col_cnt, removed_cols); + } + + + + class finite_product_relation_plugin::rename_fn : public convenient_relation_rename_fn { + scoped_ptr m_table_renamer; + scoped_ptr m_rel_renamer; + bool m_rel_identity; + + unsigned_vector m_rel_permutation; + + //determines which columns of the result are table columns and which are in the inner relation + svector m_res_table_columns; + public: + rename_fn(const finite_product_relation & r, unsigned cycle_len, const unsigned * permutation_cycle) + : convenient_relation_rename_fn(r.get_signature(), cycle_len, permutation_cycle) { + SASSERT(cycle_len>1); + + unsigned sig_sz = r.get_signature().size(); + unsigned_vector permutation; + add_sequence(0, sig_sz, permutation); + permutate_by_cycle(permutation, cycle_len, permutation_cycle); + + unsigned_vector table_permutation; + + bool table_identity = true; + m_rel_identity = true; + for(unsigned new_i=0; new_iclone() : 0); + } + + if(!m_rel_identity) { + unsigned res_rel_cnt = res_relations.size(); + for(unsigned i=0; i inner_rel = res_relations[i]; + if(!m_rel_renamer) { + m_rel_renamer = r.get_manager().mk_permutation_rename_fn(*inner_rel, m_rel_permutation); + } + + res_relations[i] = (*m_rel_renamer)(*inner_rel); + } + } + scoped_rel res_table_scoped; + const table_base * res_table = &rtable; + + if(m_table_renamer) { + res_table_scoped = (*m_table_renamer)(*res_table); + res_table = res_table_scoped.get(); + } + + //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. + //It would however need to be somehow inferred for the new signature. + + finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), + m_res_table_columns.c_ptr(), res_table->get_plugin(), r.m_other_plugin, null_family_id); + + res->init(*res_table, res_relations, false); + + return res; + } + }; + + relation_transformer_fn * finite_product_relation_plugin::mk_rename_fn(const relation_base & rb, + unsigned permutation_cycle_len, const unsigned * permutation_cycle) { + + if(&rb.get_plugin()!=this) { + return 0; + } + const finite_product_relation & r = get(rb); + return alloc(rename_fn, r, permutation_cycle_len, permutation_cycle); + } + + + class finite_product_relation_plugin::union_fn : public relation_union_fn { + bool m_use_delta; + unsigned_vector m_data_cols;//non-functional columns in the product-relation table (useful for creating operations) + scoped_ptr m_common_join; //result of the join contains (data columns), tgt_rel_idx, src_rel_idx + scoped_ptr m_rel_union; + scoped_ptr m_table_union; + scoped_ptr m_remove_overlaps; + scoped_ptr m_remove_src_column_from_overlap; + + //this one is populated only if we're doing union with delta + scoped_ptr m_delta_merging_union; + + scoped_ptr m_overlap_delta_table_builder; + public: + union_fn(const finite_product_relation & tgt, bool use_delta) : m_use_delta(use_delta) {} + + relation_union_fn & get_inner_rel_union_op(relation_base & r) { + if(!m_rel_union) { + m_rel_union = r.get_manager().mk_union_fn(r, r, m_use_delta ? &r : 0); + } + return *m_rel_union; + } + + class union_mapper : public table_row_mutator_fn { + union_fn & m_parent; + finite_product_relation & m_tgt; + const finite_product_relation & m_src; + table_base * m_delta_indexes; //table with signature (updated tgt rel index, delta_index in m_delta_rels) + relation_vector * m_delta_rels; + table_fact m_di_fact; //auxiliary fact for inserting into \c m_delta_indexes + public: + /** + If \c delta_indexes is 0, it means we are not collecting indexes. + */ + union_mapper(union_fn & parent, finite_product_relation & tgt, const finite_product_relation & src, + table_base * delta_indexes, relation_vector * delta_rels) + : m_parent(parent), + m_tgt(tgt), + m_src(src), + m_delta_indexes(delta_indexes), + m_delta_rels(delta_rels) {} + + virtual ~union_mapper() {} + + virtual bool operator()(table_element * func_columns) { + relation_base & otgt_orig = m_tgt.get_inner_rel(func_columns[0]); + const relation_base & osrc = m_src.get_inner_rel(func_columns[1]); + + relation_base * otgt = otgt_orig.clone(); + unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); + m_tgt.set_inner_rel(new_tgt_idx, otgt); + if(m_delta_indexes) { + relation_base * odelta = otgt->get_plugin().mk_empty(otgt->get_signature()); + m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc, odelta); + + unsigned delta_idx = m_delta_rels->size(); + m_delta_rels->push_back(odelta); + m_di_fact.reset(); + m_di_fact.push_back(new_tgt_idx); + m_di_fact.push_back(delta_idx); + m_delta_indexes->add_fact(m_di_fact); + } + else { + m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc); + } + + func_columns[0]=new_tgt_idx; + return true; + } + }; + + /** + Makes a table whose last column has indexes to relations in \c src into a table + with indexes to relation \c tgt. + */ + class src_copying_mapper : public table_row_mutator_fn { + finite_product_relation & m_tgt; + const finite_product_relation & m_src; + public: + /** + If \c delta_indexes is 0, it means we are not collecting indexes. + */ + src_copying_mapper(finite_product_relation & tgt, const finite_product_relation & src) + : m_tgt(tgt), m_src(src) {} + + virtual bool operator()(table_element * func_columns) { + const relation_base & osrc = m_src.get_inner_rel(func_columns[0]); + unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); + m_tgt.set_inner_rel(new_tgt_idx, osrc.clone()); + func_columns[0]=new_tgt_idx; + return true; + } + }; + + virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { + finite_product_relation & tgt = get(tgtb); + const finite_product_relation & src0 = get(srcb); + finite_product_relation * delta = get(deltab); + + relation_manager & rmgr = tgt.get_manager(); + + scoped_rel src_aux_copy; //copy of src in case its specification needs to be modified + + if(!vectors_equal(tgt.m_table2sig, src0.m_table2sig) + || (delta && !vectors_equal(tgt.m_table2sig, delta->m_table2sig)) ) { + src_aux_copy = src0.clone(); + ptr_vector orig_rels; + orig_rels.push_back(src_aux_copy.get()); + orig_rels.push_back(&tgt); + if(delta) { + orig_rels.push_back(delta); + } + if(!finite_product_relation::try_unify_specifications(orig_rels)) { + throw default_exception("finite_product_relation union: cannot convert relations to common specification"); + } + } + + const finite_product_relation & src = src_aux_copy ? *src_aux_copy : src0; + + table_plugin & tplugin = tgt.get_table_plugin(); + + if(!m_common_join) { + unsigned data_cols_cnt = tgt.m_table_sig.size()-1; + for(unsigned i=0; i table_overlap = (*m_common_join)(tgt.get_table(), src.get_table()); + + scoped_rel delta_indexes; + relation_vector delta_rels; + if(m_use_delta) { + table_signature di_sig; + di_sig.push_back(finite_product_relation::s_rel_idx_sort); + di_sig.push_back(finite_product_relation::s_rel_idx_sort); + di_sig.set_functional_columns(1); + delta_indexes = tplugin.mk_empty(di_sig); + } + + { + union_mapper * umapper = alloc(union_mapper, *this, tgt, src, delta_indexes.get(), &delta_rels); + scoped_ptr mapping_fn = rmgr.mk_map_fn(*table_overlap, umapper); + (*mapping_fn)(*table_overlap); + } + + if(!m_remove_src_column_from_overlap) { + unsigned removed_cols[] = { table_overlap->get_signature().size()-1 }; + m_remove_src_column_from_overlap = rmgr.mk_project_fn(*table_overlap, 1, removed_cols); + } + //transform table_overlap into the signature of tgt.get_table(), so that the functional + //column contains indexes of the united relations + scoped_rel regular_overlap = (*m_remove_src_column_from_overlap)(*table_overlap); + + + if(!m_remove_overlaps) { + m_remove_overlaps = rmgr.mk_filter_by_negation_fn(tgt.get_table(), *regular_overlap, m_data_cols, + m_data_cols); + } + + //in tgt keep only the rows that are in tgt only + (*m_remove_overlaps)(tgt.get_table(), *regular_overlap); + + //add rows in which tgt and src overlapped + if(!m_table_union) { + m_table_union = rmgr.mk_union_fn(tgt.get_table(), tgt.get_table()); + } + (*m_table_union)(tgt.get_table(), *regular_overlap); + + scoped_rel src_only = src.get_table().clone(); + (*m_remove_overlaps)(*src_only, *regular_overlap); + + scoped_rel src_only2; //a copy of src_only for use in building the delta + if(m_use_delta) { + src_only2 = src_only->clone(); + } + + { + src_copying_mapper * cpmapper = alloc(src_copying_mapper, tgt, src); + scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only, cpmapper); + (*mapping_fn)(*src_only); + } + + //add rows that were only in src + (*m_table_union)(tgt.get_table(), *src_only); + + if(m_use_delta) { + bool extending_delta = !delta->empty(); + //current delta, we will add it to the deltab argument later if it was not given to us empty + finite_product_relation * cdelta; + if(extending_delta) { + cdelta = delta->get_plugin().mk_empty(*delta); + } + else { + cdelta = delta; + } + + if(!m_overlap_delta_table_builder) { + unsigned table_fn_col = regular_overlap->get_signature().size()-1; + unsigned first_col = 0; + unsigned removed_cols[] = { table_fn_col, table_fn_col+1 }; + m_overlap_delta_table_builder = rmgr.mk_join_project_fn(*regular_overlap, *delta_indexes, 1, + &table_fn_col, &first_col, 2, removed_cols); + } + + scoped_rel overlap_delta_table = + (*m_overlap_delta_table_builder)(*regular_overlap, *delta_indexes); + + cdelta->init(*overlap_delta_table, delta_rels, true); + + { + src_copying_mapper * cpmapper = alloc(src_copying_mapper, *cdelta, src); + scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only2, cpmapper); + (*mapping_fn)(*src_only2); + } + + //add rows that were only in src + (*m_table_union)(cdelta->get_table(), *src_only2); + + if(extending_delta) { + if(!m_delta_merging_union) { + m_delta_merging_union = rmgr.mk_union_fn(*delta, *cdelta); + } + (*m_delta_merging_union)(*delta, *cdelta); + cdelta->deallocate(); + } + } + } + }; + +#if 0 + /** + Union operation taking advantage of the fact that the inner relation of all the arguments + is a singleton relation. + */ + class finite_product_relation_plugin::inner_singleton_union_fn : public relation_union_fn { + + class offset_row_mapper : public table_row_mutator_fn { + public: + unsigned m_ofs; + virtual bool operator()(table_element * func_columns) { + func_columns[0] += m_ofs; + return true; + } + }; + + // [Leo]: gcc complained about the following class. + // It does not have a constructor and uses a reference + + class inner_relation_copier : public table_row_mutator_fn { + finite_product_relation & m_tgt; + const finite_product_relation & m_src; + finite_product_relation * m_delta; + unsigned m_tgt_ofs; + unsigned m_delta_ofs; + public: + virtual bool operator()(table_element * func_columns) { + unsigned src_idx = static_cast(func_columns[0]); + unsigned tgt_idx = src_idx + m_tgt_ofs; + unsigned delta_idx = m_delta ? (src_idx + m_delta_ofs) : 0; + SASSERT(!m_delta || m_tgt.m_others[tgt_idx]==m_delta->m_others[delta_idx]); + SASSERT(m_tgt.m_others[tgt_idx]==0 || m_tgt.m_others[tgt_idx]==m_src.m_others[src_idx]); + if(m_tgt.m_others[tgt_idx]==0) { + m_tgt.m_others[tgt_idx] = m_src.m_others[src_idx]->clone(); + if(m_delta) { + m_delta->m_others[delta_idx] = m_src.m_others[src_idx]->clone(); + } + } + return true; + } + }; + + scoped_ptr m_t_union_fun; + offset_row_mapper * m_offset_mapper_obj; //initialized together with m_offset_mapper_fun, and deallocated by it + scoped_ptr m_offset_mapper_fun; + + + + public: + virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { + finite_product_relation & tgt = get(tgtb); + const finite_product_relation & src = get(srcb); + finite_product_relation * delta = get(deltab); + + finite_product_relation_plugin & plugin = tgt.get_plugin(); + relation_manager & rmgr = plugin.get_manager(); + + //we want only non-empty inner relations to remain + tgt.garbage_collect(true); + src.garbage_collect(true); + + table_base & tgt_table = tgt.get_table(); + const table_base & src_table = src.get_table(); + + scoped_rel offsetted_src = src_table.clone(); + + if(!m_offset_mapper_fun) { + m_offset_mapper_obj = alloc(offset_row_mapper); + m_offset_mapper_fun = rmgr.mk_map_fn(*offsetted_src, m_offset_mapper_obj); + } + unsigned src_rel_offset = tgt.m_others.size(); + m_offset_mapper_obj->m_ofs = src_rel_offset; + + (*m_offset_mapper_fun)(*offsetted_src); + + //if we need to generate a delta, we get collect it into an empty relation and then union + //it with the delta passed in as argument. + scoped_rel loc_delta = delta ? get(plugin.mk_empty(*delta)) : 0; + //even if we don't need to generate the delta for the caller, we still want to have a delta + //table to know which relations to copy. + scoped_rel loc_delta_table_scoped; + if(!loc_delta) { + loc_delta_table_scoped = tgt_table.get_plugin().mk_empty(tgt_table); + } + table_base * loc_delta_table = loc_delta ? &loc_delta->get_table() : loc_delta_table_scoped.get(); + + if(!m_t_union_fun) { + m_t_union_fun = rmgr.mk_union_fn(tgt_table, *offsetted_src, loc_delta_table); + } + (*m_t_union_fun)(tgt_table, *offsetted_src, loc_delta_table); + + + //TODO: copy the relations into tgt and (possibly) delta using inner_relation_copier + //TODO: unite the local delta with the delta passed in as an argument + NOT_IMPLEMENTED_YET(); + } + }; +#endif + + class finite_product_relation_plugin::converting_union_fn : public relation_union_fn { + scoped_ptr m_tr_union_fun; + public: + virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { + SASSERT(srcb.get_plugin().is_finite_product_relation()); + const finite_product_relation & src = get(srcb); + finite_product_relation_plugin & plugin = src.get_plugin(); + scoped_rel tr_src = plugin.to_table_relation(src); + if(!m_tr_union_fun) { + m_tr_union_fun = plugin.get_manager().mk_union_fn(tgtb, *tr_src, deltab); + SASSERT(m_tr_union_fun); + } + (*m_tr_union_fun)(tgtb, *tr_src, deltab); + } + }; + + relation_union_fn * finite_product_relation_plugin::mk_union_fn(const relation_base & tgtb, const relation_base & srcb, + const relation_base * deltab) { + if(&srcb.get_plugin()!=this) { + return 0; + } + const finite_product_relation & src = get(srcb); + if(&tgtb.get_plugin()!=this || (deltab && &deltab->get_plugin()!=this) ) { + if(can_convert_to_table_relation(src)) { + return alloc(converting_union_fn); + } + return 0; + } + + const finite_product_relation * delta = get(deltab); + + return alloc(union_fn, get(tgtb), delta!=0); + } + + + class finite_product_relation_plugin::filter_identical_fn : public relation_mutator_fn { + //the table and relation columns that should be identical + //the column numbering is local to the table or inner relation + unsigned_vector m_table_cols; + unsigned_vector m_rel_cols; + + scoped_ptr m_table_filter; + scoped_ptr m_rel_filter; + scoped_ptr m_tr_filter; + public: + filter_identical_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * identical_cols) + : m_table_filter(0), m_rel_filter(0), m_tr_filter(0) { + finite_product_relation_plugin & plugin = r.get_plugin(); + for(unsigned i=0; i1) { + m_table_filter = r.get_manager().mk_filter_identical_fn(r.get_table(), m_table_cols.size(), + m_table_cols.c_ptr()); + SASSERT(m_table_filter); + } + if(!m_table_cols.empty() && !m_rel_cols.empty()) { + unsigned tr_filter_table_cols[] = { m_table_cols[0] }; + unsigned tr_filter_rel_cols[] = { m_rel_cols[0] }; + m_tr_filter = plugin.mk_filter_identical_pairs(r, 1, tr_filter_table_cols, tr_filter_rel_cols); + SASSERT(m_tr_filter); + } + } + + void ensure_rel_filter(const relation_base & orel) { + SASSERT(m_rel_cols.size()>1); + if(m_rel_filter) { + return; + } + m_rel_filter = orel.get_manager().mk_filter_identical_fn(orel, m_rel_cols.size(), m_rel_cols.c_ptr()); + SASSERT(m_rel_filter); + } + + virtual void operator()(relation_base & rb) { + finite_product_relation & r = get(rb); + + if(m_table_cols.size()>1) { + (*m_table_filter)(r.get_table()); + } + + if(m_rel_cols.size()>1) { + r.garbage_collect(true); + unsigned rel_cnt = r.m_others.size(); + for(unsigned rel_idx=0; rel_idx m_table_filter; + scoped_ptr m_rel_filter; + unsigned m_col; + app_ref m_value; + public: + filter_equal_fn(const finite_product_relation & r, const relation_element & value, unsigned col) + : m_col(col), m_value(value, r.get_context().get_manager()) { + if(r.is_table_column(col)) { + table_element tval; + r.get_manager().relation_to_table(r.get_signature()[col], value, tval); + m_table_filter = r.get_manager().mk_filter_equal_fn(r.get_table(), tval, r.m_sig2table[col]); + } + } + + virtual void operator()(relation_base & rb) { + finite_product_relation & r = get(rb); + + if(m_table_filter) { + (*m_table_filter)(r.get_table()); + return; + } + r.garbage_collect(false); + relation_vector & inner_rels = r.m_others; + unsigned rel_cnt = inner_rels.size(); + for(unsigned i=0; i m_table_filter; + scoped_ptr m_rel_filter; + app_ref m_cond; + + idx_set m_table_cond_columns; + idx_set m_rel_cond_columns; + + //like m_table_cond_columns and m_rel_cond_columns, only the indexes are local to the + //table/relation, not to the signature of the whole relation + idx_set m_table_local_cond_columns; + idx_set m_rel_local_cond_columns; + + /** + If equal to 0, it means the interpreted condition uses all table columns. Then the original + table is used instead of the result of the projection. + */ + scoped_ptr m_table_cond_columns_project; + /** + \brief Column indexes of the global relations to which correspond the data columns in the table + that is result of applying the \c m_table_cond_columns_project functor. + */ + unsigned_vector m_global_origins_of_projected_columns; + + scoped_ptr m_assembling_join_project; + + + /** + \brief Renaming that transforms the variable numbers pointing to the global relation into + variables that point to the inner relation variables. + + The elements that do not correspond to columns of the inner relation (but rather to the table + columns) is modified in \c operator() when evaluating the condition for all the relevant + combinations of table values. + */ + expr_ref_vector m_renaming_for_inner_rel; + public: + filter_interpreted_fn(const finite_product_relation & r, app * condition) + : m_manager(r.get_context().get_manager()), + m_subst(r.get_context().get_var_subst()), + m_cond(condition, m_manager), + m_renaming_for_inner_rel(m_manager) { + relation_manager & rmgr = r.get_manager(); + + rule_manager& rm = r.get_context().get_rule_manager(); + idx_set& cond_columns = rm.collect_vars(m_cond); + + unsigned sig_sz = r.get_signature().size(); + for(unsigned i=0; i tproj_scope; + const table_base * tproj; + if(m_table_cond_columns_project) { + tproj_scope = (*m_table_cond_columns_project)(rtable); + tproj = tproj_scope.get(); + } + else { + tproj = &rtable; + } + unsigned projected_data_cols = tproj->get_signature().size()-1; + SASSERT(m_table_cond_columns.num_elems()==projected_data_cols); + + table_signature filtered_sig = tproj->get_signature(); + filtered_sig.push_back(finite_product_relation::s_rel_idx_sort); + filtered_sig.set_functional_columns(1); + + relation_vector new_rels; + + scoped_rel filtered_table = tplugin.mk_empty(filtered_sig); + table_fact f; + unsigned renaming_ofs = m_renaming_for_inner_rel.size()-1; + table_base::iterator pit = tproj->begin(); + table_base::iterator pend = tproj->end(); + for(; pit!=pend; ++pit) { + pit->get_fact(f); + unsigned old_rel_idx = static_cast(f.back()); + const relation_base & old_rel = r.get_inner_rel(old_rel_idx); + + //put the table values into the substitution + for(unsigned i=0; i filter = rmgr.mk_filter_interpreted_fn(*new_rel, to_app(inner_cond)); + (*filter)(*new_rel); + + if(new_rel->empty()) { + new_rel->deallocate(); + continue; + } + + unsigned new_rel_idx = new_rels.size(); + new_rels.push_back(new_rel); + f.push_back(new_rel_idx); + filtered_table->add_fact(f); + } + + if(!m_assembling_join_project) { + unsigned_vector table_cond_columns_vect; + for(unsigned i=0; i new_table = (*m_assembling_join_project)(rtable, *filtered_table); + r.reset(); + r.init(*new_table, new_rels, true); + } + }; + + relation_mutator_fn * finite_product_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, + app * condition) { + if(&rb.get_plugin()!=this) { + return 0; + } + return alloc(filter_interpreted_fn, get(rb), condition); + } + + class finite_product_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { + + unsigned_vector m_r_cols; + unsigned_vector m_neg_cols; + + scoped_ptr m_table_neg_filter; + scoped_ptr m_table_neg_complement_selector; + scoped_ptr m_neg_intersection_join; + scoped_ptr m_table_intersection_join; + scoped_ptr m_table_overlap_union; + scoped_ptr m_table_subtract; + scoped_ptr m_inner_subtract; + scoped_ptr m_overlap_table_last_column_remover; + scoped_ptr m_r_table_union; + + bool m_table_overlaps_only; + + unsigned_vector m_r_shared_table_cols; + unsigned_vector m_neg_shared_table_cols; + + unsigned_vector m_r_shared_rel_cols; + unsigned_vector m_neg_shared_rel_cols; + public: + negation_filter_fn(const finite_product_relation & r, const finite_product_relation & neg, + unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * neg_cols) + : m_r_cols(joined_col_cnt, r_cols), + m_neg_cols(joined_col_cnt, neg_cols), + m_table_overlaps_only(true) { + const table_signature & tsig = r.m_table_sig; + const table_base & rtable = r.get_table(); + relation_manager & rmgr = r.get_manager(); + + for(unsigned i=0; iget_signature().size() , all_rel_cols); + m_parent.m_inner_subtract = m_r.get_manager().mk_filter_by_negation_fn(*r_inner, + inters_inner, all_rel_cols, all_rel_cols); + } + (*m_parent.m_inner_subtract)(*r_inner, inters_inner); + + unsigned new_rel_num = m_r.get_next_rel_idx(); + m_r.set_inner_rel(new_rel_num, r_inner); + func_columns[0]=new_rel_num; + return true; + } + }; + + + virtual void operator()(relation_base & rb, const relation_base & negb) { + finite_product_relation & r = get(rb); + const finite_product_relation & neg = get(negb); + + if(m_table_overlaps_only) { + handle_only_tables_overlap_case(r, neg); + return; + } + + finite_product_relation_plugin & plugin = r.get_plugin(); + if(!m_neg_intersection_join) { + } + scoped_rel intersection = get((*m_neg_intersection_join)(r, neg)); + SASSERT(&intersection->get_plugin()==&plugin); //the result of join is also finite product + SASSERT(r.get_signature()==intersection->get_signature()); + + table_base & r_table = r.get_table(); + table_plugin & tplugin = r_table.get_plugin(); + relation_manager & rmgr = r.get_manager(); + + //we need to do this before we perform the \c m_table_subtract + //the sigrature of the \c table_overlap0 relation is + //(data_columns)(original rel idx)(subtracted rel idx) + scoped_rel table_overlap0 = (*m_table_intersection_join)(r_table, + intersection->get_table()); + + //the rows that don't appear in the table of the intersection are safe to stay + (*m_table_subtract)(r_table, intersection->get_table()); + + //now we will examine the rows that do appear in the intersection + + //There are no functional columns in the \c table_overlap0 relation (because of + //the project we did). We need to make both rel idx columns functional. + //We do not lose any rows, since the data columns by themselves are unique. + + table_signature table_overlap_sig(table_overlap0->get_signature()); + table_overlap_sig.set_functional_columns(2); + scoped_rel table_overlap = tplugin.mk_empty(table_overlap_sig); + + if(!m_table_overlap_union) { + m_table_overlap_union = rmgr.mk_union_fn(*table_overlap, *table_overlap0); + SASSERT(m_table_overlap_union); + } + (*m_table_overlap_union)(*table_overlap, *table_overlap0); + + { + rel_subtractor * mutator = alloc(rel_subtractor, *this, r, *intersection); + scoped_ptr mapper = rmgr.mk_map_fn(*table_overlap, mutator); + (*mapper)(*table_overlap); + } + + if(!m_overlap_table_last_column_remover) { + unsigned removed_col = table_overlap->get_signature().size()-1; + m_overlap_table_last_column_remover = rmgr.mk_project_fn(*table_overlap, 1, &removed_col); + } + scoped_rel final_overlapping_rows_table = + (*m_overlap_table_last_column_remover)(*table_overlap); + + if(!m_r_table_union) { + m_r_table_union = rmgr.mk_union_fn(r_table, *final_overlapping_rows_table); + } + + (*m_r_table_union)(r_table, *final_overlapping_rows_table); + } + }; + + relation_intersection_filter_fn * finite_product_relation_plugin::mk_filter_by_negation_fn(const relation_base & rb, + const relation_base & negb, unsigned joined_col_cnt, + const unsigned * r_cols, const unsigned * negated_cols) { + if(&rb.get_plugin()!=this || &negb.get_plugin()!=this) { + return 0; + } + + return alloc(negation_filter_fn, get(rb), get(negb), joined_col_cnt, r_cols, negated_cols); + } + + + class finite_product_relation_plugin::filter_identical_pairs_fn : public relation_mutator_fn { + scoped_ptr m_tproject_fn; //if zero, no columns need to be projected away + unsigned m_col_cnt; + unsigned_vector m_table_cols; + unsigned_vector m_rel_cols; + + scoped_ptr m_assembling_join_project; + scoped_ptr m_updating_union; + public: + filter_identical_pairs_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * table_cols, + const unsigned * rel_cols) : + m_col_cnt(col_cnt), + m_table_cols(col_cnt, table_cols), + m_rel_cols(col_cnt, rel_cols) { + SASSERT(col_cnt>0); + const table_signature & tsig = r.m_table_sig; + unsigned t_sz = tsig.size(); + + sort_two_arrays(col_cnt, m_table_cols.begin(), m_rel_cols.begin()); + SASSERT(m_table_cols.back() tproj; + if(m_tproject_fn) { + tproj = (*m_tproject_fn)(r.get_table()); + } + else { + tproj = r.get_table().clone(); + } + SASSERT(m_col_cnt+1==tproj->get_signature().size()); + + table_signature filtered_sig = tproj->get_signature(); + filtered_sig.push_back(finite_product_relation::s_rel_idx_sort); + filtered_sig.set_functional_columns(1); + + relation_vector new_rels; + scoped_rel filtered_table = tplugin.mk_empty(filtered_sig); + table_fact f; + table_base::iterator pit = tproj->begin(); + table_base::iterator pend = tproj->end(); + for(; pit!=pend; ++pit) { + pit->get_fact(f); + unsigned old_rel_idx = static_cast(f.back()); + const relation_base & old_rel = r.get_inner_rel(old_rel_idx); + relation_base * new_rel = old_rel.clone(); + for(unsigned i=0; i filter = rmgr.mk_filter_equal_fn(*new_rel, r_el, m_rel_cols[i]); + (*filter)(*new_rel); + } + + if(new_rel->empty()) { + new_rel->deallocate(); + continue; + } + + unsigned new_rel_idx = new_rels.size(); + new_rels.push_back(new_rel); + f.push_back(new_rel_idx); + filtered_table->add_fact(f); + } + + if(!m_assembling_join_project) { + m_assembling_join_project = mk_assembler_of_filter_result(rtable, *filtered_table, m_table_cols); + } + + scoped_rel new_table = (*m_assembling_join_project)(r.get_table(), *filtered_table); + + r.reset(); + r.init(*new_table, new_rels, true); + } + }; + + relation_mutator_fn * finite_product_relation_plugin::mk_filter_identical_pairs(const finite_product_relation & r, + unsigned col_cnt, const unsigned * table_cols, const unsigned * rel_cols) { + return alloc(filter_identical_pairs_fn, r, col_cnt, table_cols, rel_cols); + } + + table_join_fn * finite_product_relation_plugin::mk_assembler_of_filter_result(const table_base & relation_table, + const table_base & filtered_table, const unsigned_vector & selected_columns) { + + table_plugin & tplugin = relation_table.get_plugin(); + const table_signature & rtable_sig = relation_table.get_signature(); + unsigned rtable_sig_sz = rtable_sig.size(); + unsigned selected_col_cnt = selected_columns.size(); + SASSERT(selected_col_cnt+2==filtered_table.get_signature().size()); + SASSERT(rtable_sig.functional_columns()==1); + SASSERT(filtered_table.get_signature().functional_columns()==1); + + unsigned_vector rtable_joined_cols; + rtable_joined_cols.append(selected_col_cnt, selected_columns.c_ptr()); //filtered table cols + rtable_joined_cols.push_back(rtable_sig_sz-1); //unfiltered relation indexes + + unsigned_vector filtered_joined_cols; + add_sequence(0, selected_col_cnt, filtered_joined_cols); //filtered table cols + filtered_joined_cols.push_back(selected_col_cnt); //unfiltered relation indexes + SASSERT(rtable_joined_cols.size()==filtered_joined_cols.size()); + + //the signature after join: + //(all relation table columns)(all filtered relation table columns)(unfiltered rel idx non-func from 'filtered') + // (unfiltered rel idx func from 'rtable')(filtered rel idx) + unsigned_vector removed_cols; + unsigned filtered_nonfunc_ofs = rtable_sig_sz-1; + add_sequence(filtered_nonfunc_ofs, selected_col_cnt, removed_cols); //data columns from 'filtered' + unsigned idx_ofs = filtered_nonfunc_ofs+selected_col_cnt; + removed_cols.push_back(idx_ofs); //unfiltered relation indexes from 'filtered' + removed_cols.push_back(idx_ofs+1); //unfiltered relation indexes from rtable + + table_join_fn * res = tplugin.get_manager().mk_join_project_fn(relation_table, filtered_table, + rtable_joined_cols, filtered_joined_cols, removed_cols); + SASSERT(res); + return res; + } + + // ----------------------------------- + // + // finite_product_relation + // + // ----------------------------------- + + finite_product_relation::finite_product_relation(finite_product_relation_plugin & p, + const relation_signature & s, const bool * table_columns, table_plugin & tplugin, + relation_plugin & oplugin, family_id other_kind) + : relation_base(p, s), + m_other_plugin(oplugin), + m_other_kind(other_kind), + m_full_rel_idx(UINT_MAX) { + const relation_signature & rel_sig = get_signature(); + unsigned sz = rel_sig.size(); + m_sig2table.resize(sz, UINT_MAX); + m_sig2other.resize(sz, UINT_MAX); + for(unsigned i=0; iclone()), + m_others(r.m_others), + m_available_rel_indexes(r.m_available_rel_indexes), + m_full_rel_idx(r.m_full_rel_idx), + m_live_rel_collection_project(), + m_empty_rel_removal_filter() { + //m_others is now just a shallow copy, we need use clone of the relations that in it now + unsigned other_sz = m_others.size(); + for(unsigned i=0; ideallocate(); + relation_vector::iterator it = m_others.begin(); + relation_vector::iterator end = m_others.end(); + for(; it!=end; ++it) { + relation_base * rel= *it; + if(rel) { + rel->deallocate(); + } + } + } + + context & finite_product_relation::get_context() const { + return get_manager().get_context(); + } + + unsigned finite_product_relation::get_next_rel_idx() const { + unsigned res; + if(!m_available_rel_indexes.empty()) { + res = m_available_rel_indexes.back(); + m_available_rel_indexes.pop_back(); + } + else { + res = m_others.size(); + m_others.push_back(0); + } + SASSERT(m_others[res]==0); + return res; + } + + void finite_product_relation::recycle_rel_idx(unsigned idx) const { + SASSERT(m_others[idx]==0); + m_available_rel_indexes.push_back(idx); + } + + unsigned finite_product_relation::get_full_rel_idx() { + if(m_full_rel_idx==UINT_MAX) { + m_full_rel_idx = get_next_rel_idx(); + relation_base * full_other = mk_full_inner(0); + m_others[m_full_rel_idx] = full_other; + } + return m_full_rel_idx; + } + + void finite_product_relation::init(const table_base & table_vals, const relation_vector & others, bool contiguous) { + SASSERT(m_table_sig.equal_up_to_fn_mark(table_vals.get_signature())); + SASSERT(empty()); + if(!m_others.empty()) { + garbage_collect(false); + } + SASSERT(m_others.empty()); + + m_others = others; + scoped_ptr table_union = get_manager().mk_union_fn(get_table(), table_vals); + (*table_union)(get_table(), table_vals); + + if(!contiguous) { + unsigned rel_cnt = m_others.size(); + for(unsigned i=0; isuggest_fact(t_f)) { + SASSERT(t_f.back()==new_rel_idx); + new_rel = mk_empty_inner(); + } else { + new_rel = get_inner_rel(t_f.back()).clone(); + + t_f[t_f.size()-1]=new_rel_idx; + m_table->ensure_fact(t_f); + } + new_rel->add_fact(o_f); + set_inner_rel(new_rel_idx, new_rel); + } + + bool finite_product_relation::contains_fact(const relation_fact & f) const { + table_fact t_f; + extract_table_fact(f, t_f); + + if(!m_table->fetch_fact(t_f)) { + return false; + } + + relation_fact o_f(get_context()); + extract_other_fact(f, o_f); + + const relation_base & other = get_inner_rel(t_f.back()); + + return other.contains_fact(o_f); + } + + bool finite_product_relation::empty() const { + garbage_collect(true); + return m_table->empty(); + } + + finite_product_relation * finite_product_relation::clone() const { + return alloc(finite_product_relation, *this); + } + + void finite_product_relation::complement_self(func_decl* p) { + unsigned other_sz = m_others.size(); + for(unsigned i=0; icomplement(p); + std::swap(m_others[i],r); + r->deallocate(); + } + table_element full_rel_idx = get_full_rel_idx(); + scoped_rel complement_table = m_table->complement(p, &full_rel_idx); + + scoped_ptr u_fn = get_manager().mk_union_fn(*m_table, *complement_table, 0); + SASSERT(u_fn); + (*u_fn)(*m_table, *complement_table, 0); + } + + finite_product_relation * finite_product_relation::complement(func_decl* p) const { + finite_product_relation * res = clone(); + res->complement_self(p); + return res; + } + + class finite_product_relation::live_rel_collection_reducer : public table_row_pair_reduce_fn { + idx_set & m_accumulator; + public: + live_rel_collection_reducer(idx_set & accumulator) : m_accumulator(accumulator) {} + + virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { + m_accumulator.insert(static_cast(merged_func_columns[0])); + } + }; + + void finite_product_relation::collect_live_relation_indexes(idx_set & res) const { + SASSERT(res.empty()); + unsigned table_data_col_cnt = m_table_sig.size()-1; + + if(table_data_col_cnt==0) { + if(!get_table().empty()) { + table_base::iterator iit = get_table().begin(); + table_base::iterator iend = get_table().end(); + + SASSERT(iit!=iend); + res.insert(static_cast((*iit)[0])); + SASSERT((++iit)==iend); + } + return; + } + + if(!m_live_rel_collection_project) { + buffer removed_cols; + removed_cols.resize(table_data_col_cnt); + for(unsigned i=0; i live_indexes_table = (*m_live_rel_collection_project)(get_table()); + res.swap(m_live_rel_collection_acc); + + SASSERT(live_indexes_table->get_signature().size()==1); + SASSERT(live_indexes_table->get_signature().functional_columns()==1); + if(!live_indexes_table->empty()) { + table_base::iterator iit = live_indexes_table->begin(); + table_base::iterator iend = live_indexes_table->end(); + + SASSERT(iit!=iend); + res.insert(static_cast((*iit)[0])); + SASSERT((++iit)==iend); + } + } + + void finite_product_relation::garbage_collect(bool remove_empty) const { + idx_set live_indexes; + collect_live_relation_indexes(live_indexes); + + scoped_rel empty_rel_indexes; //populated only if \c remove_empty==true + table_fact empty_rel_fact; + + unsigned rel_cnt = m_others.size(); +#if Z3DEBUG + unsigned encountered_live_indexes = 0; +#endif + for(unsigned rel_idx=0; rel_idxempty()) { + continue; + } + if(empty_rel_indexes==0) { + table_signature empty_rel_indexes_sig; + empty_rel_indexes_sig.push_back(s_rel_idx_sort); + empty_rel_indexes = get_table_plugin().mk_empty(empty_rel_indexes_sig); + } + empty_rel_fact.reset(); + empty_rel_fact.push_back(rel_idx); + empty_rel_indexes->add_fact(empty_rel_fact); + } + m_others[rel_idx]->deallocate(); + m_others[rel_idx] = 0; + if(rel_idx==m_full_rel_idx) { + m_full_rel_idx = UINT_MAX; + } + recycle_rel_idx(rel_idx); + } + SASSERT(encountered_live_indexes==live_indexes.num_elems()); + + if(m_available_rel_indexes.size()==m_others.size()) { + m_available_rel_indexes.reset(); + m_others.reset(); + } + + if(empty_rel_indexes) { + SASSERT(remove_empty); + + if(!m_empty_rel_removal_filter) { + unsigned t_joined_cols[] = { m_table_sig.size()-1 }; + unsigned ei_joined_cols[] = { 0 }; + m_empty_rel_removal_filter = get_manager().mk_filter_by_negation_fn(get_table(), *empty_rel_indexes, + 1, t_joined_cols, ei_joined_cols); + } + + (*m_empty_rel_removal_filter)(*m_table, *empty_rel_indexes); + } + } + + bool finite_product_relation::try_unify_specifications(ptr_vector & rels) { + if(rels.empty()) { + return true; + } + unsigned sig_sz = rels.back()->get_signature().size(); + svector table_cols(sig_sz, true); + + ptr_vector::iterator it = rels.begin(); + ptr_vector::iterator end = rels.end(); + for(; it!=end; ++it) { + finite_product_relation & rel = **it; + for(unsigned i=0; i pr_fun = get_manager().mk_project_fn(get_table(), to_project_away); + table_base * moved_cols_table = (*pr_fun)(get_table()); //gets destroyed inside moved_cols_trel + scoped_rel moved_cols_trel = + rmgr.get_table_relation_plugin(moved_cols_table->get_plugin()).mk_from_table(moved_cols_sig, moved_cols_table); + + svector moved_cols_table_flags(moved_cols_sig.size(), false); + + scoped_rel moved_cols_rel = get_plugin().mk_empty(moved_cols_sig, + moved_cols_table_flags.c_ptr()); + + scoped_ptr union_fun = + get_manager().mk_union_fn(*moved_cols_rel, *moved_cols_trel); + SASSERT(union_fun); //the table_relation should be able to be 'unioned into' any relation + + (*union_fun)(*moved_cols_rel, *moved_cols_trel); + + unsigned_vector all_moved_cols_indexes; + add_sequence(0, moved_cols_sig.size(), all_moved_cols_indexes); + + scoped_ptr join_fun = get_manager().mk_join_project_fn(*this, *moved_cols_rel, new_rel_columns, + all_moved_cols_indexes, new_rel_columns, false); + + scoped_rel unordered_rel = (*join_fun)(*this, *moved_cols_rel); + SASSERT(unordered_rel->get_signature().size()==sig_sz); //the signature size should be the same as original + + //now we need to reorder the columns in the \c new_rel to match the original table + unsigned_vector permutation; + unsigned moved_cols_cnt = new_rel_columns.size(); + unsigned next_replaced_idx = 0; + unsigned next_orig_idx = 0; + for(unsigned i=0; i perm_fun = get_manager().mk_rename_fn(*unordered_rel, cycle); + //the scoped_rel wrapper does the destruction of the old object + unordered_rel = (*perm_fun)(*unordered_rel); + cycle.reset(); + } + + finite_product_relation & new_rel = finite_product_relation_plugin::get(*unordered_rel); + + //Swap the content of the current object and new_rel. On exitting the function new_rel will be destroyed + //since it points to the content of scoped_rel unordered_rel. + swap(new_rel); + + return true; + } + + void finite_product_relation::display(std::ostream & out) const { + + garbage_collect(true); + + out << "finite_product_relation:\n"; + + out << " table:\n"; + get_table().display(out); + + unsigned other_sz = m_others.size(); + for(unsigned i=0; iget_fact(tfact); + + const table_relation & orel = static_cast(get_inner_rel(tfact[rel_idx_col])); + const table_base & otable = orel.get_table(); + table_base::iterator oit = otable.begin(); + table_base::iterator oend = otable.end(); + for(; oit!=oend; ++oit) { + oit->get_fact(ofact); + + out << "\t("; + for(unsigned i=0; iget_fact(fact); + conjs.reset(); + SASSERT(fact.size() == fact_sz); + unsigned rel_idx = static_cast(fact[fact_sz-1]); + m_others[rel_idx]->to_formula(tmp); + for (unsigned i = 0; i + 1 < fact_sz; ++i) { + conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), util.mk_numeral(fact[i], sig[i]))); + } + sh(tmp, fact_sz-1, tmp); + conjs.push_back(tmp); + disjs.push_back(m.mk_and(conjs.size(), conjs.c_ptr())); + } + bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), fml); + } + +}; + diff --git a/src/muz/rel/dl_finite_product_relation.h b/src/muz/rel/dl_finite_product_relation.h new file mode 100644 index 000000000..d5181d122 --- /dev/null +++ b/src/muz/rel/dl_finite_product_relation.h @@ -0,0 +1,366 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_finite_product_relation.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-24. + +Revision History: + +--*/ +#ifndef _DL_FINITE_PRODUCT_RELATION_H_ +#define _DL_FINITE_PRODUCT_RELATION_H_ + + +#include "dl_base.h" +#include "dl_relation_manager.h" +#include "dl_table_relation.h" + +namespace datalog { + + class finite_product_relation; + + void universal_delete(finite_product_relation* ptr); + + + class finite_product_relation_plugin : public relation_plugin { + friend class finite_product_relation; + public: + struct rel_spec { + family_id m_inner_kind; //null_family_id means we don't care about the kind + svector m_table_cols; + + rel_spec() : m_inner_kind(null_family_id) {} + rel_spec(const svector& table_cols) + : m_inner_kind(null_family_id), m_table_cols(table_cols) {} + + bool operator==(const rel_spec & o) const { + return m_inner_kind==o.m_inner_kind && vectors_equal(m_table_cols, o.m_table_cols); + } + struct hash { + unsigned operator()(const rel_spec & o) const { + return o.m_inner_kind^svector_hash()(o.m_table_cols); + } + }; + }; + private: + + class join_fn; + class converting_join_fn; + class project_fn; + class rename_fn; + class union_fn; + class inner_singleton_union_fn; + class converting_union_fn; + class filter_identical_fn; + class filter_equal_fn; + class filter_interpreted_fn; + class negation_filter_fn; + class filter_identical_pairs_fn; + + relation_plugin & m_inner_plugin; + + rel_spec_store > m_spec_store; + + static symbol get_name(relation_plugin & inner_plugin); + family_id get_relation_kind(finite_product_relation & r, const bool * table_columns); + + static void get_all_possible_table_columns(relation_manager & rmgr, const relation_signature & s, + svector & table_columns); + void get_all_possible_table_columns(const relation_signature & s, svector & table_columns) { + get_all_possible_table_columns(get_manager(), s, table_columns); + } + + void split_signatures(const relation_signature & s, table_signature & table_sig, + relation_signature & remaining_sig); + void split_signatures(const relation_signature & s, const bool * table_columns, + table_signature & table_sig, relation_signature & remaining_sig); + public: + static finite_product_relation & get(relation_base & r); + static const finite_product_relation & get(const relation_base & r); + static finite_product_relation * get(relation_base * r); + static const finite_product_relation * get(const relation_base * r); + + static finite_product_relation_plugin & get_plugin(relation_manager & rmgr, relation_plugin & inner); + + finite_product_relation_plugin(relation_plugin & inner_plugin, relation_manager & manager); + + virtual void initialize(family_id fid); + + relation_plugin & get_inner_plugin() const { return m_inner_plugin; } + + virtual bool can_handle_signature(const relation_signature & s); + + virtual relation_base * mk_empty(const relation_signature & s); + /** + \c inner_kind==null_family_id means we don't care about the kind of the inner relation + */ + finite_product_relation * mk_empty(const relation_signature & s, const bool * table_columns, + family_id inner_kind=null_family_id); + finite_product_relation * mk_empty(const finite_product_relation & original); + virtual relation_base * mk_empty(const relation_base & original); + virtual relation_base * mk_empty(const relation_signature & s, family_id kind); + + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + + /** + \brief Return true if \c r can be converted to \c finite_product_relation_plugin either + by \c mk_from_table_relation or by \c mk_from_inner_relation. + */ + bool can_be_converted(const relation_base & r); + + /** + If the conversion cannot be performed, 0 is returned. + */ + finite_product_relation * mk_from_table_relation(const table_relation & r); + finite_product_relation * mk_from_inner_relation(const relation_base & r); + + bool can_convert_to_table_relation(const finite_product_relation & r); + table_relation * to_table_relation(const finite_product_relation & r); + + protected: + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + + private: + /** + \brief Create a filter that enforces equality between pairs of table and relation columns + + The column numbers in arrays \c table_cols and \c rel_cols must be local to the table/inner relation. + */ + relation_mutator_fn * mk_filter_identical_pairs(const finite_product_relation & r, unsigned col_cnt, + const unsigned * table_cols, const unsigned * rel_cols); + + /** + \brief Create a join-project operation that creates a table according to \c relation_table + but with references to relations updated and removed according to the content of \c filtered_table. + \c selected_columns contains sorted indexes of data columns in \c relation_table that are also in + the \c filtered_table (so that the first column in \c filtered_table corresponds to + \c selected_columns[0] -th column in \c relation_table etc...) + + Signature of \c relation_table: + (data columns)(functional column with indexes of relation objects) + + Signature of \c filtered_table: + (selected data columns)(non-functional column with original relation object indexes) + (functional column with indexes of filtered relation objects) + + */ + static table_join_fn * mk_assembler_of_filter_result(const table_base & relation_table, + const table_base & filtered_table, const unsigned_vector & selected_columns); + + }; + + class finite_product_relation : public relation_base { + friend class finite_product_relation_plugin; + friend class finite_product_relation_plugin::join_fn; + friend class finite_product_relation_plugin::project_fn; + friend class finite_product_relation_plugin::union_fn; + friend class finite_product_relation_plugin::rename_fn; + friend class finite_product_relation_plugin::inner_singleton_union_fn; + friend class finite_product_relation_plugin::filter_equal_fn; + friend class finite_product_relation_plugin::filter_identical_pairs_fn; + + class live_rel_collection_reducer; + + + public: + /** + Size of this sort determines how many different relation objects can we refer to. + */ + static const table_sort s_rel_idx_sort; + + + /** + \brief The last column in the signature is a functional column with index of the + associated inner relation. The other columns correspond to the relation signature + according to \c m_table2sig. + + It holds that \c m_table_sig.size()-1==m_table2sig.size() + */ + + table_signature m_table_sig; + unsigned_vector m_table2sig; // (ordered list) + unsigned_vector m_sig2table; //index of corresponding table column or UINT_MAX + private: + relation_signature m_other_sig; + unsigned_vector m_other2sig; // (ordered list) + public: + unsigned_vector m_sig2other; //index of corresponding other relation column or UINT_MAX + private: + relation_plugin & m_other_plugin; + family_id m_other_kind; + + mutable table_base * m_table; + public: + mutable relation_vector m_others; + private: + mutable unsigned_vector m_available_rel_indexes; + + /** + \c UINT_MAX means uninitialized. + If we can get away with it, we want to have a single full relation to refer to. + */ + mutable unsigned m_full_rel_idx; + + mutable idx_set m_live_rel_collection_acc; + mutable scoped_ptr m_live_rel_collection_project; + + mutable scoped_ptr m_empty_rel_removal_filter; + + void recycle_rel_idx(unsigned idx) const; + + // creates a full relation if it does not exist. + unsigned get_full_rel_idx(); + + + + public: + relation_base & get_inner_rel(table_element idx) + { SASSERT(idx(idx)); } + relation_base & get_inner_rel(unsigned idx) { SASSERT(m_others[idx]); return *m_others[idx]; } + const relation_base & get_inner_rel(unsigned idx) const + { return const_cast(*this).get_inner_rel(idx); } + + unsigned get_next_rel_idx() const; + + /** + The relation takes ownership of the \c inner object. + */ + void set_inner_rel(table_element idx, relation_base * inner) + { SASSERT(idx(idx), inner); } + /** + The relation takes ownership of the \c inner object. + */ + void set_inner_rel(unsigned idx, relation_base * inner) { + SASSERT(!m_others[idx]); + SASSERT(inner); + m_others[idx] = inner; + } + table_base & get_table() { return *m_table; } + + table_plugin & get_table_plugin() const { return get_table().get_plugin(); } + + void garbage_collect(bool remove_empty) const; + + /** + \brief Initialize an empty relation with table \c table_vals and relations in \c others. + + The relation object takes ownership of relations inside the \c others vector. + + If \c contiguous is true, it can be assumed that there are no zero elements in the \c others array. + */ + void init(const table_base & table_vals, const relation_vector & others, bool contiguous); + + private: + + + /** + \brief Extract the values of table non-functional columns from the relation fact. + The value of the functional column which determines index of the inner relation is undefined. + */ + void extract_table_fact(const relation_fact rf, table_fact & tf) const; + /** + \brief Extract the values of the inner relation columns from the relation fact. + */ + void extract_other_fact(const relation_fact rf, relation_fact & of) const; + + relation_base * mk_empty_inner(); + relation_base * mk_full_inner(func_decl* pred); + + + void complement_self(func_decl* pred); + + void collect_live_relation_indexes(idx_set & res) const; + + + /** + \brief Try to modify relations in \c rels so that they have the same columns corresponding to the table + and the inner relation (so that the union can be perofrmed on theim in a straightforward way). + + Relations in \c rels must all have equal signature. + + Even if the function fails and false is returned, some relations may already be modified. They are + in a valid state, but with different specification. + */ + static bool try_unify_specifications(ptr_vector & rels); + + bool try_modify_specification(const bool * table_cols); + + virtual bool can_swap(const relation_base & r) const + { return &get_plugin()==&r.get_plugin(); } + + /** + \brief Swap content of the current relation with the content of \c r. + + Both relations must come from the same plugin and be of the same signature. + */ + virtual void swap(relation_base & r); + + /** + \brief Create a \c finite_product_relation object. + */ + finite_product_relation(finite_product_relation_plugin & p, const relation_signature & s, + const bool * table_columns, table_plugin & tplugin, relation_plugin & oplugin, family_id other_kind); + finite_product_relation(const finite_product_relation & r); + virtual ~finite_product_relation(); + public: + context & get_context() const; + finite_product_relation_plugin & get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + bool is_table_column(unsigned col_idx) const { return m_sig2table[col_idx]!=UINT_MAX; } + + const table_base & get_table() const { return *m_table; } + + const relation_base & get_inner_rel(table_element idx) const + { SASSERT(idx(idx)); } + + /** + The function calls garbage_collect, so the internal state may change when it is called. + */ + virtual bool empty() const; + void reset() { m_table->reset(); garbage_collect(false); } + + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + + virtual finite_product_relation * clone() const; + virtual finite_product_relation * complement(func_decl* p) const; + + virtual void display(std::ostream & out) const; + virtual void display_tuples(func_decl & pred, std::ostream & out) const; + + virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); } + virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); } + + virtual void to_formula(expr_ref& fml) const; + }; + +}; + +#endif /* _DL_FINITE_PRODUCT_RELATION_H_ */ + diff --git a/src/muz/rel/dl_interval_relation.cpp b/src/muz/rel/dl_interval_relation.cpp new file mode 100644 index 000000000..c04269b02 --- /dev/null +++ b/src/muz/rel/dl_interval_relation.cpp @@ -0,0 +1,653 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_interval_relation.cpp + +Abstract: + + Basic interval reatlion. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-2-11 + +Revision History: + +--*/ + +#include "debug.h" +#include "optional.h" +#include "ast_pp.h" +#include "dl_interval_relation.h" +#include "dl_relation_manager.h" +#include "bool_rewriter.h" + + +namespace datalog { + // ------------------------- + // interval_relation_plugin + + interval_relation_plugin::interval_relation_plugin(relation_manager& m): + relation_plugin(interval_relation_plugin::get_name(), m), + m_empty(m_dep), + m_arith(get_ast_manager()) { + } + + bool interval_relation_plugin::can_handle_signature(const relation_signature & sig) { + for (unsigned i = 0; i < sig.size(); ++i) { + if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) { + return false; + } + } + return true; + } + + + relation_base * interval_relation_plugin::mk_empty(const relation_signature & s) { + return alloc(interval_relation, *this, s, true); + } + + relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + return alloc(interval_relation, *this, s, false); + } + + class interval_relation_plugin::join_fn : public convenient_relation_join_fn { + public: + join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ + } + + virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + interval_relation const& r1 = get(_r1); + interval_relation const& r2 = get(_r2); + interval_relation_plugin& p = r1.get_plugin(); + interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); + return result; + } + }; + + relation_join_fn * interval_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(r1) || !check_kind(r2)) { + return 0; + } + return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); + } + + + class interval_relation_plugin::project_fn : public convenient_relation_project_fn { + public: + project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { + } + + virtual relation_base * operator()(const relation_base & _r) { + interval_relation const& r = get(_r); + interval_relation_plugin& p = r.get_plugin(); + interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); + return result; + } + }; + + relation_transformer_fn * interval_relation_plugin::mk_project_fn(const relation_base & r, + unsigned col_cnt, const unsigned * removed_cols) { + return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); + } + + class interval_relation_plugin::rename_fn : public convenient_relation_rename_fn { + public: + rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { + } + + virtual relation_base * operator()(const relation_base & _r) { + interval_relation const& r = get(_r); + interval_relation_plugin& p = r.get_plugin(); + interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); + return result; + } + }; + + relation_transformer_fn * interval_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if(!check_kind(r)) { + return 0; + } + return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); + } + + interval interval_relation_plugin::unite(interval const& src1, interval const& src2) { + bool l_open = src1.is_lower_open(); + bool r_open = src1.is_upper_open(); + ext_numeral low = src1.inf(); + ext_numeral high = src1.sup(); + if (src2.inf() < low || (src2.inf() == low && l_open)) { + low = src2.inf(); + l_open = src2.is_lower_open(); + } + if (src2.sup() > high || (src2.sup() == high && r_open)) { + high = src2.sup(); + r_open = src2.is_upper_open(); + } + return interval(dep(), low, l_open, 0, high, r_open, 0); + } + + interval interval_relation_plugin::widen(interval const& src1, interval const& src2) { + bool l_open = src1.is_lower_open(); + bool r_open = src1.is_upper_open(); + ext_numeral low = src1.inf(); + ext_numeral high = src1.sup(); + + if (src2.inf() < low || (low == src2.inf() && l_open && !src2.is_lower_open())) { + low = ext_numeral(false); + l_open = true; + } + if (high < src2.sup() || (src2.sup() == high && !r_open && src2.is_upper_open())) { + high = ext_numeral(true); + r_open = true; + } + return interval(dep(), low, l_open, 0, high, r_open, 0); + } + + interval interval_relation_plugin::meet(interval const& src1, interval const& src2, bool& isempty) { + isempty = false; + if (is_empty(0, src1) || is_infinite(src2)) { + return src1; + } + if (is_empty(0, src2) || is_infinite(src1)) { + return src2; + } + bool l_open = src1.is_lower_open(); + bool r_open = src1.is_upper_open(); + ext_numeral low = src1.inf(); + ext_numeral high = src1.sup(); + if (src2.inf() > low || (src2.inf() == low && !l_open)) { + low = src2.inf(); + l_open = src2.is_lower_open(); + } + if (src2.sup() < high || (src2.sup() == high && !r_open)) { + high = src2.sup(); + r_open = src2.is_upper_open(); + } + if (low > high || (low == high && (l_open || r_open))) { + isempty = true; + return interval(dep()); + } + else { + return interval(dep(), low, l_open, 0, high, r_open, 0); + } + } + + bool interval_relation_plugin::is_infinite(interval const& i) { + return i.plus_infinity() && i.minus_infinity(); + } + + bool interval_relation_plugin::is_empty(unsigned, interval const& i) { + return i.sup() < i.inf(); + } + + class interval_relation_plugin::union_fn : public relation_union_fn { + bool m_is_widen; + public: + union_fn(bool is_widen) : + m_is_widen(is_widen) { + } + + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + + TRACE("interval_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + + interval_relation& r = get(_r); + interval_relation const& src = get(_src); + if (_delta) { + interval_relation& d = get(*_delta); + r.mk_union(src, &d, m_is_widen); + } + else { + r.mk_union(src, 0, m_is_widen); + } + } + }; + + relation_union_fn * interval_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, false); + } + + relation_union_fn * interval_relation_plugin::mk_widen_fn( + const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, true); + } + + class interval_relation_plugin::filter_identical_fn : public relation_mutator_fn { + unsigned_vector m_identical_cols; + public: + filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) + : m_identical_cols(col_cnt, identical_cols) {} + + virtual void operator()(relation_base & r) { + interval_relation & pr = get(r); + for (unsigned i = 1; i < m_identical_cols.size(); ++i) { + unsigned c1 = m_identical_cols[0]; + unsigned c2 = m_identical_cols[i]; + pr.equate(c1, c2); + } + } + }; + + relation_mutator_fn * interval_relation_plugin::mk_filter_identical_fn( + const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { + if(!check_kind(t)) { + return 0; + } + return alloc(filter_identical_fn, col_cnt, identical_cols); + } + + + class interval_relation_plugin::filter_equal_fn : public relation_mutator_fn { + unsigned m_col; + rational m_value; + public: + filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) + : m_col(col) { + arith_util arith(m.get_context().get_manager()); + VERIFY(arith.is_numeral(value, m_value)); + } + + virtual void operator()(relation_base & _r) { + interval_relation & r = get(_r); + interval_relation_plugin & p = r.get_plugin(); + r.mk_intersect(m_col, interval(p.dep(), m_value)); + TRACE("interval_relation", tout << m_value << "\n"; r.display(tout);); + } + }; + + relation_mutator_fn * interval_relation_plugin::mk_filter_equal_fn(const relation_base & r, + const relation_element & value, unsigned col) { + if(check_kind(r)) { + return alloc(filter_equal_fn, get_manager(), value, col); + } + return 0; + } + + + class interval_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + app_ref m_cond; + public: + filter_interpreted_fn(interval_relation const& t, app* cond): + m_cond(cond, t.get_plugin().get_ast_manager()) { + } + + void operator()(relation_base& t) { + get(t).filter_interpreted(m_cond); + TRACE("interval_relation", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); + } + }; + + relation_mutator_fn * interval_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + if (check_kind(t)) { + return alloc(filter_interpreted_fn, get(t), condition); + } + return 0; + } + + interval_relation& interval_relation_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + + interval_relation const & interval_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + // ----------------------- + // interval_relation + + interval_relation::interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty): + vector_relation(p, s, is_empty, interval(p.dep())) + { + TRACE("interval_relation", tout << s.size() << "\n";); + } + + void interval_relation::add_fact(const relation_fact & f) { + interval_relation r(get_plugin(), get_signature(), false); + ast_manager& m = get_plugin().get_ast_manager(); + for (unsigned i = 0; i < f.size(); ++i) { + app_ref eq(m); + expr* e = f[i]; + eq = m.mk_eq(m.mk_var(i, m.get_sort(e)), e); + r.filter_interpreted(eq.get()); + } + mk_union(r, 0, false); + } + + bool interval_relation::contains_fact(const relation_fact & f) const { + SASSERT(f.size() == get_signature().size()); + interval_relation_plugin& p = get_plugin(); + + for (unsigned i = 0; i < f.size(); ++i) { + if (f[i] != f[find(i)]) { + return false; + } + interval const& iv = (*this)[i]; + if (p.is_infinite(iv)) { + continue; + } + rational v; + if (p.m_arith.is_numeral(f[i], v)) { + if (!iv.contains(v)) { + return false; + } + } + else { + // TBD: may or must? + } + } + return true; + } + + interval_relation * interval_relation::clone() const { + interval_relation* result = alloc(interval_relation, get_plugin(), get_signature(), empty()); + result->copy(*this); + return result; + } + + interval_relation * interval_relation::complement(func_decl*) const { + UNREACHABLE(); + return 0; + } + + void interval_relation::to_formula(expr_ref& fml) const { + ast_manager& m = get_plugin().get_ast_manager(); + arith_util& arith = get_plugin().m_arith; + expr_ref_vector conjs(m); + relation_signature const& sig = get_signature(); + for (unsigned i = 0; i < sig.size(); ++i) { + if (i != find(i)) { + conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), + m.mk_var(find(i), sig[find(i)]))); + continue; + } + interval const& iv = (*this)[i]; + sort* ty = sig[i]; + expr_ref var(m.mk_var(i, ty), m); + if (!iv.minus_infinity()) { + expr* lo = arith.mk_numeral(iv.get_lower_value(), ty); + if (iv.is_lower_open()) { + conjs.push_back(arith.mk_lt(lo, var)); + } + else { + conjs.push_back(arith.mk_le(lo, var)); + } + } + if (!iv.plus_infinity()) { + expr* hi = arith.mk_numeral(iv.get_upper_value(), ty); + if (iv.is_upper_open()) { + conjs.push_back(arith.mk_lt(var, hi)); + } + else { + conjs.push_back(arith.mk_le(var, hi)); + } + } + } + bool_rewriter br(m); + br.mk_and(conjs.size(), conjs.c_ptr(), fml); + } + + + void interval_relation::display_index(unsigned i, interval const& j, std::ostream & out) const { + out << i << " in " << j << "\n"; + } + + interval_relation_plugin& interval_relation::get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + void interval_relation::mk_intersect(unsigned idx, interval const& i) { + bool isempty; + (*this)[idx] = mk_intersect((*this)[idx], i, isempty); + if (isempty || is_empty(idx, (*this)[idx])) { + set_empty(); + } + } + + void interval_relation::mk_rename_elem(interval& i, unsigned, unsigned const* ) { + + } + + void interval_relation::filter_interpreted(app* cond) { + interval_relation_plugin& p = get_plugin(); + rational k; + unsigned x, y; + if (p.is_lt(cond, x, k, y)) { + // 0 < x - y + k + if (x == UINT_MAX) { + // y < k + mk_intersect(y, interval(p.dep(), k, true, false, 0)); + return; + } + if (y == UINT_MAX) { + // -k < x + mk_intersect(x, interval(p.dep(), -k, true, true, 0)); + return; + } + // y < x + k + ext_numeral x_hi = (*this)[x].sup(); + ext_numeral y_lo = (*this)[y].inf(); + if (!x_hi.is_infinite()) { + mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), true, false, 0)); + } + if (!y_lo.is_infinite()) { + mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, true, true, 0)); + } + return; + } + bool is_int = false; + if (p.is_le(cond, x, k, y, is_int)) { + // 0 <= x - y + k + if (x == UINT_MAX) { + // y <= k + mk_intersect(y, interval(p.dep(), k, false, false, 0)); + return; + } + if (y == UINT_MAX) { + // -k <= x + mk_intersect(x, interval(p.dep(), -k, false, true, 0)); + return; + } + ext_numeral x_hi = (*this)[x].sup(); + ext_numeral y_lo = (*this)[y].inf(); + if (!x_hi.is_infinite()) { + mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), false, false, 0)); + } + if (!y_lo.is_infinite()) { + mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, false, true, 0)); + } + return; + } + if (p.is_eq(cond, x, k, y)) { + // y = x + k + if (x == UINT_MAX) { + SASSERT(y != UINT_MAX); + mk_intersect(y, interval(p.dep(), k)); + return; + } + if (y == UINT_MAX) { + // x = - k + SASSERT(x != UINT_MAX); + mk_intersect(x, interval(p.dep(), -k)); + return; + } + interval x_i = (*this)[x]; + interval y_i = (*this)[y]; + x_i += interval(p.dep(), k); + y_i -= interval(p.dep(), k); + mk_intersect(x, y_i); + mk_intersect(y, x_i); + } + if (get_plugin().get_ast_manager().is_false(cond)) { + set_empty(); + } + } + + bool interval_relation_plugin::is_linear(expr* e, unsigned& neg, unsigned& pos, rational& k, bool is_pos) const { +#define SET_VAR(_idx_) \ + if (is_pos &&pos == UINT_MAX) { \ + pos = _idx_; \ + return true; \ + } \ + if (!is_pos && neg == UINT_MAX) { \ + neg = _idx_; \ + return true; \ + } \ + else { \ + return false; \ + } + + if (is_var(e)) { + SET_VAR(to_var(e)->get_idx()); + } + if (!is_app(e)) { + return false; + } + app* a = to_app(e); + + if (m_arith.is_add(e)) { + for (unsigned i = 0; i < a->get_num_args(); ++i) { + if (!is_linear(a->get_arg(i), neg, pos, k, is_pos)) return false; + } + return true; + } + if (m_arith.is_sub(e)) { + SASSERT(a->get_num_args() == 2); + return + is_linear(a->get_arg(0), neg, pos, k, is_pos) && + is_linear(a->get_arg(1), neg, pos, k, !is_pos); + } + rational k1; + SASSERT(!m_arith.is_mul(e) || a->get_num_args() == 2); + if (m_arith.is_mul(e) && + m_arith.is_numeral(a->get_arg(0), k1) && + k1.is_minus_one() && + is_var(a->get_arg(1))) { + SET_VAR(to_var(a->get_arg(1))->get_idx()); + } + + if (m_arith.is_numeral(e, k1)) { + if (is_pos) { + k += k1; + } + else { + k -= k1; + } + return true; + } + return false; + } + + // 0 <= x - y + k + bool interval_relation_plugin::is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const { + ast_manager& m = get_ast_manager(); + k.reset(); + x = UINT_MAX; + y = UINT_MAX; + + if (m_arith.is_le(cond)) { + is_int = m_arith.is_int(cond->get_arg(0)); + if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; + return (x != UINT_MAX || y != UINT_MAX); + } + if (m_arith.is_ge(cond)) { + is_int = m_arith.is_int(cond->get_arg(0)); + if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; + return (x != UINT_MAX || y != UINT_MAX); + } + if (m_arith.is_lt(cond) && m_arith.is_int(cond->get_arg(0))) { + is_int = true; + if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; + k -= rational::one(); + return (x != UINT_MAX || y != UINT_MAX); + } + if (m_arith.is_gt(cond) && m_arith.is_int(cond->get_arg(0))) { + is_int = true; + if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; + k += rational::one(); + return (x != UINT_MAX || y != UINT_MAX); + } + if (m.is_not(cond) && is_app(cond->get_arg(0))) { + // not (0 <= x - y + k) + // <=> + // 0 > x - y + k + // <=> + // 0 <= y - x - k - 1 + if (is_le(to_app(cond->get_arg(0)), x, k, y, is_int) && is_int) { + k.neg(); + k -= rational::one(); + std::swap(x, y); + return true; + } + // not (0 < x - y + k) + // <=> + // 0 >= x - y + k + // <=> + // 0 <= y - x - k + if (is_lt(to_app(cond->get_arg(0)), x, k, y)) { + is_int = false; + k.neg(); + std::swap(x, y); + return true; + } + } + return false; + } + + // 0 < x - y + k + bool interval_relation_plugin::is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const { + k.reset(); + x = UINT_MAX; + y = UINT_MAX; + if (m_arith.is_lt(cond) && m_arith.is_real(cond->get_arg(0))) { + if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; + return (x != UINT_MAX || y != UINT_MAX); + } + if (m_arith.is_gt(cond) && m_arith.is_real(cond->get_arg(0))) { + if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; + return (x != UINT_MAX || y != UINT_MAX); + } + return false; + } + + // 0 = x - y + k + bool interval_relation_plugin::is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const { + ast_manager& m = get_ast_manager(); + k.reset(); + x = UINT_MAX; + y = UINT_MAX; + if (m.is_eq(cond)) { + if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; + if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; + return (x != UINT_MAX || y != UINT_MAX); + } + return false; + } + +}; + diff --git a/src/muz/rel/dl_interval_relation.h b/src/muz/rel/dl_interval_relation.h new file mode 100644 index 000000000..703cb43c5 --- /dev/null +++ b/src/muz/rel/dl_interval_relation.h @@ -0,0 +1,142 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_interval_relation.h + +Abstract: + + Basic interval reatlion. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-2-11 + +Revision History: + +--*/ +#ifndef _DL_INTERVAL_RELATION_H_ +#define _DL_INTERVAL_RELATION_H_ + + +#include "dl_context.h" +#include "dl_relation_manager.h" +#include "dl_base.h" +#include "old_interval.h" +#include "dl_vector_relation.h" +#include "arith_decl_plugin.h" +#include "basic_simplifier_plugin.h" + +namespace datalog { + + class interval_relation; + + class interval_relation_plugin : public relation_plugin { + v_dependency_manager m_dep; + interval m_empty; + arith_util m_arith; + + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + friend class interval_relation; + + interval unite(interval const& src1, interval const& src2); + interval widen(interval const& src1, interval const& src2); + interval meet(interval const& src1, interval const& src2, bool& is_empty); + + v_dependency_manager & dep() const { return const_cast(m_dep); } + + public: + interval_relation_plugin(relation_manager& m); + virtual bool can_handle_signature(const relation_signature & s); + static symbol get_name() { return symbol("interval_relation"); } + virtual relation_base * mk_empty(const relation_signature & s); + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + + static bool is_empty(unsigned idx, interval const& i); + static bool is_infinite(interval const& i); + + private: + static interval_relation& get(relation_base& r); + static interval_relation const & get(relation_base const& r); + + bool is_linear(expr* e, unsigned& pos, unsigned& neg, rational& k, bool is_pos) const; + + // x + k <= y + bool is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const; + // x + k < y + bool is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const; + // x + k = y + bool is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const; + }; + + + class interval_relation : public vector_relation { + friend class interval_relation_plugin; + friend class interval_relation_plugin::filter_equal_fn; + public: + interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty); + + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual interval_relation * clone() const; + virtual interval_relation * complement(func_decl*) const; + virtual void to_formula(expr_ref& fml) const; + interval_relation_plugin& get_plugin() const; + + void filter_interpreted(app* cond); + virtual bool is_precise() const { return false; } + + private: + + virtual interval mk_intersect(interval const& t1, interval const& t2, bool& is_empty) const { + return get_plugin().meet(t1, t2, is_empty); + } + + virtual interval mk_unite(interval const& t1, interval const& t2) const { return get_plugin().unite(t1,t2); } + + virtual interval mk_widen(interval const& t1, interval const& t2) const { return get_plugin().widen(t1,t2); } + + virtual bool is_subset_of(interval const& t1, interval const& t2) const { NOT_IMPLEMENTED_YET(); return false; } + + virtual bool is_full(interval const& t) const { + return interval_relation_plugin::is_infinite(t); + } + + virtual bool is_empty(unsigned idx, interval const& t) const { + return interval_relation_plugin::is_empty(idx, t); + } + + virtual void mk_rename_elem(interval& i, unsigned col_cnt, unsigned const* cycle); + + virtual void display_index(unsigned idx, interval const & i, std::ostream& out) const; + + void mk_intersect(unsigned idx, interval const& i); + + }; + +}; + +#endif + diff --git a/src/muz/rel/dl_mk_explanations.cpp b/src/muz/rel/dl_mk_explanations.cpp new file mode 100644 index 000000000..60a10190a --- /dev/null +++ b/src/muz/rel/dl_mk_explanations.cpp @@ -0,0 +1,879 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_explanations.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-11-08. + +Revision History: + +--*/ + + +#include +#include"ast_pp.h" +#include"ast_smt_pp.h" +#include"dl_finite_product_relation.h" +#include"dl_product_relation.h" +#include"dl_sieve_relation.h" +#include"dl_mk_explanations.h" + +namespace datalog { + + // ----------------------------------- + // + // explanation_relation_plugin declaration + // + // ----------------------------------- + + class explanation_relation; + + class explanation_relation_plugin : public relation_plugin { + friend class explanation_relation; + + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class foreign_union_fn; + class assignment_filter_fn; + class negation_filter_fn; + class intersection_filter_fn; + + bool m_relation_level_explanations; + + func_decl_ref m_union_decl; + + vector > m_pool; + + + app * mk_union(app * a1, app * a2) { + return get_ast_manager().mk_app(m_union_decl, a1, a2); + } + + public: + static symbol get_name(bool relation_level) { + return symbol(relation_level ? "relation_explanation" : "fact_explanation"); + } + + explanation_relation_plugin(bool relation_level, relation_manager & manager) + : relation_plugin(get_name(relation_level), manager), + m_relation_level_explanations(relation_level), + m_union_decl(mk_explanations::get_union_decl(get_context()), get_ast_manager()) {} + + ~explanation_relation_plugin() { + for (unsigned i = 0; i < m_pool.size(); ++i) { + for (unsigned j = 0; j < m_pool[i].size(); ++j) { + dealloc(m_pool[i][j]); + } + } + } + + virtual bool can_handle_signature(const relation_signature & s) { + unsigned n=s.size(); + for (unsigned i=0; i(relation_base::get_plugin()); + } + + virtual void to_formula(expr_ref& fml) const { + ast_manager& m = fml.get_manager(); + fml = m.mk_eq(m.mk_var(0, m.get_sort(m_data[0])), m_data[0]); + } + + bool is_undefined(unsigned col_idx) const { + return m_data[col_idx]==0; + } + bool no_undefined() const { + if (empty()) { + return true; + } + unsigned n = get_signature().size(); + for (unsigned i=0; i(get_plugin().mk_empty(get_signature())); + res->m_empty = m_empty; + SASSERT(res->m_data.empty()); + res->m_data.append(m_data); + return res; + } + + virtual relation_base * complement(func_decl* pred) const { + explanation_relation * res = static_cast(get_plugin().mk_empty(get_signature())); + if (empty()) { + res->set_undefined(); + } + return res; + } + + void display_explanation(app * expl, std::ostream & out) const { + if (expl) { + //TODO: some nice explanation output + ast_smt_pp pp(get_plugin().get_ast_manager()); + pp.display_expr_smt2(out, expl); + } + else { + out << ""; + } + } + + virtual void display(std::ostream & out) const { + if (empty()) { + out << "\n"; + return; + } + unsigned sz = get_signature().size(); + for (unsigned i=0; i s.size() && !m_pool[s.size()].empty()) { + explanation_relation* r = m_pool[s.size()].back(); + m_pool[s.size()].pop_back(); + r->m_empty = true; + r->m_data.reset(); + return r; + } + return alloc(explanation_relation, *this, s); + } + + void explanation_relation_plugin::recycle(explanation_relation* r) { + relation_signature const& sig = r->get_signature(); + if (m_pool.size() <= sig.size()) { + m_pool.resize(sig.size()+1); + } + m_pool[sig.size()].push_back(r); + } + + + class explanation_relation_plugin::join_fn : public convenient_relation_join_fn { + public: + join_fn(const relation_signature & sig1, const relation_signature & sig2) + : convenient_relation_join_fn(sig1, sig2, 0, 0, 0) {} + + virtual relation_base * operator()(const relation_base & r1_0, const relation_base & r2_0) { + const explanation_relation & r1 = static_cast(r1_0); + const explanation_relation & r2 = static_cast(r2_0); + explanation_relation_plugin & plugin = r1.get_plugin(); + + explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); + if (!r1.empty() && !r2.empty()) { + res->m_empty = false; + SASSERT(res->m_data.empty()); + res->m_data.append(r1.m_data); + res->m_data.append(r2.m_data); + } + return res; + } + }; + + relation_join_fn * explanation_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (&r1.get_plugin()!=this || &r2.get_plugin()!=this) { + return 0; + } + if (col_cnt!=0) { + return 0; + } + return alloc(join_fn, r1.get_signature(), r2.get_signature()); + } + + + class explanation_relation_plugin::project_fn : public convenient_relation_project_fn { + public: + project_fn(const relation_signature & sig, unsigned col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(sig, col_cnt, removed_cols) {} + + virtual relation_base * operator()(const relation_base & r0) { + const explanation_relation & r = static_cast(r0); + explanation_relation_plugin & plugin = r.get_plugin(); + + explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); + if (!r.empty()) { + relation_fact proj_data = r.m_data; + project_out_vector_columns(proj_data, m_removed_cols); + res->assign_data(proj_data); + } + return res; + } + }; + + relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, + const unsigned * removed_cols) { + if (&r.get_plugin()!=this) { + return 0; + } + return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); + } + + + class explanation_relation_plugin::rename_fn : public convenient_relation_rename_fn { + public: + rename_fn(const relation_signature & sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) + : convenient_relation_rename_fn(sig, permutation_cycle_len, permutation_cycle) {} + + virtual relation_base * operator()(const relation_base & r0) { + const explanation_relation & r = static_cast(r0); + explanation_relation_plugin & plugin = r.get_plugin(); + + explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); + if (!r.empty()) { + relation_fact permutated_data = r.m_data; + permutate_by_cycle(permutated_data, m_cycle); + res->assign_data(permutated_data); + } + return res; + } + }; + + relation_transformer_fn * explanation_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned permutation_cycle_len, const unsigned * permutation_cycle) { + return alloc(rename_fn, r.get_signature(), permutation_cycle_len, permutation_cycle); + } + + + class explanation_relation_plugin::union_fn : public relation_union_fn { + scoped_ptr m_delta_union_fun; + public: + virtual void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) { + explanation_relation & tgt = static_cast(tgt0); + const explanation_relation & src = static_cast(src0); + explanation_relation * delta = delta0 ? static_cast(delta0) : 0; + explanation_relation_plugin & plugin = tgt.get_plugin(); + + if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { + UNREACHABLE(); + } + if (src.empty()) { + return; + } + if (plugin.m_relation_level_explanations) { + tgt.unite_with_data(src.m_data); + if (delta) { + if (!m_delta_union_fun) { + m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src); + SASSERT(m_delta_union_fun); + } + (*m_delta_union_fun)(*delta, src); + } + } + else { + if (tgt.empty()) { + tgt.assign_data(src.m_data); + if (delta && delta->empty()) { + delta->assign_data(src.m_data); + } + } + } + } + }; + + class explanation_relation_plugin::foreign_union_fn : public relation_union_fn { + scoped_ptr m_delta_union_fun; + public: + virtual void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) { + explanation_relation & tgt = static_cast(tgt0); + explanation_relation * delta = delta0 ? static_cast(delta0) : 0; + + if (src.empty()) { + return; + } + tgt.set_undefined(); + if (delta) { + delta->set_undefined(); + } + } + }; + + relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || (delta && !check_kind(*delta))) { + return 0; + } + if (!check_kind(src)) { + //this is to handle the product relation + return alloc(foreign_union_fn); + } + return alloc(union_fn); + } + + class explanation_relation_plugin::assignment_filter_fn : public relation_mutator_fn { + ast_manager & m_manager; + var_subst & m_subst; + unsigned m_col_idx; + app_ref m_new_rule; + public: + assignment_filter_fn(context & ctx, unsigned col_idx, app_ref new_rule) + : m_manager(ctx.get_manager()), + m_subst(ctx.get_var_subst()), + m_col_idx(col_idx), + m_new_rule(new_rule) {} + + virtual void operator()(relation_base & r0) { + explanation_relation & r = static_cast(r0); + + if (!r.is_undefined(m_col_idx)) { + UNREACHABLE(); + } + + unsigned sz = r.get_signature().size(); + ptr_vector subst_arg; + subst_arg.resize(sz, 0); + unsigned ofs = sz-1; + for (unsigned i=0; iget_arg(0); + expr * arg2 = cond->get_arg(1); + + if (is_var(arg2)) { + std::swap(arg1, arg2); + } + + if (!is_var(arg1) || !is_app(arg2)) { + return 0; + } + var * col_var = to_var(arg1); + app * new_rule = to_app(arg2); + if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { + return 0; + } + unsigned col_idx = col_var->get_idx(); + + return alloc(assignment_filter_fn, get_context(), col_idx, app_ref(new_rule, get_ast_manager())); + } + + + class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { + public: + virtual void operator()(relation_base & r, const relation_base & neg) { + if (!neg.empty()) { + r.reset(); + } + } + }; + + relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, + const relation_base & neg, unsigned joined_col_cnt, const unsigned * t_cols, + const unsigned * negated_cols) { + if (&r.get_plugin()!=this || &neg.get_plugin()!=this) { + return 0; + } + return alloc(negation_filter_fn); + } + + class explanation_relation_plugin::intersection_filter_fn : public relation_intersection_filter_fn { + func_decl_ref m_union_decl; + public: + intersection_filter_fn(explanation_relation_plugin & plugin) + : m_union_decl(plugin.m_union_decl) {} + + virtual void operator()(relation_base & tgt0, const relation_base & src0) { + explanation_relation & tgt = static_cast(tgt0); + const explanation_relation & src = static_cast(src0); + + if (src.empty()) { + tgt.reset(); + return; + } + if (tgt.empty()) { + return; + } + unsigned sz = tgt.get_signature().size(); + for (unsigned i=0; iget_decl()==m_union_decl.get()) { + if (curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) { + tgt.m_data.set(i, curr_src); + continue; + } + } + //the intersection is imprecise because we do nothing here, but it is good enough for + //the purpose of explanations + } + } + }; + + relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_intersection_fn( + const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, + const unsigned * tgt_cols, const unsigned * src_cols) { + if (&tgt.get_plugin()!=this || &src.get_plugin()!=this) { + return 0; + } + //this checks the join is one to one on all columns + if (tgt.get_signature()!=src.get_signature() + || joined_col_cnt!=tgt.get_signature().size() + || !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) { + return 0; + } + counter ctr; + ctr.count(joined_col_cnt, tgt_cols); + if (ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) { + return 0; + } + return alloc(intersection_filter_fn, *this); + } + + + // ----------------------------------- + // + // mk_explanations + // + // ----------------------------------- + + + mk_explanations::mk_explanations(context & ctx) + : plugin(50000), + m_manager(ctx.get_manager()), + m_context(ctx), + m_decl_util(ctx.get_decl_util()), + m_relation_level(ctx.explanations_on_relation_level()), + m_pinned(m_manager) { + m_e_sort = m_decl_util.mk_rule_sort(); + m_pinned.push_back(m_e_sort); + + relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); + symbol er_symbol = explanation_relation_plugin::get_name(m_relation_level); + m_er_plugin = static_cast(rmgr.get_relation_plugin(er_symbol)); + if (!m_er_plugin) { + m_er_plugin = alloc(explanation_relation_plugin, m_relation_level, rmgr); + rmgr.register_plugin(m_er_plugin); + if (!m_relation_level) { + DEBUG_CODE( + finite_product_relation_plugin * dummy; + SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); + ); + rmgr.register_plugin(alloc(finite_product_relation_plugin, *m_er_plugin, rmgr)); + } + } + DEBUG_CODE( + if (!m_relation_level) { + finite_product_relation_plugin * dummy; + SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); + } + ); + } + + func_decl * mk_explanations::get_union_decl(context & ctx) { + ast_manager & m = ctx.get_manager(); + sort_ref s(ctx.get_decl_util().mk_rule_sort(), m); + //can it happen that the function name would collide with some other symbol? + //if functions can be overloaded by their ranges, it should be fine. + return m.mk_func_decl(symbol("e_union"), s, s, s); + } + + void mk_explanations::assign_rel_level_kind(func_decl * e_decl, func_decl * orig) { + SASSERT(m_relation_level); + + relation_manager & rmgr = m_context.get_rel_context()->get_rmanager(); + unsigned sz = e_decl->get_arity(); + relation_signature sig; + rmgr.from_predicate(e_decl, sig); + + svector inner_sieve(sz-1, true); + inner_sieve.push_back(false); + + svector expl_sieve(sz-1, false); + expl_sieve.push_back(true); + + sieve_relation_plugin & sieve_plugin = sieve_relation_plugin::get_plugin(rmgr); + + family_id inner_kind = rmgr.get_requested_predicate_kind(orig); //may be null_family_id + family_id inner_sieve_kind = sieve_plugin.get_relation_kind(sig, inner_sieve, inner_kind); + family_id expl_kind = m_er_plugin->get_kind(); + family_id expl_sieve_kind = sieve_plugin.get_relation_kind(sig, expl_sieve, expl_kind); + + product_relation_plugin::rel_spec product_spec; + product_spec.push_back(inner_sieve_kind); + product_spec.push_back(expl_sieve_kind); + + family_id pred_kind = + product_relation_plugin::get_plugin(rmgr).get_relation_kind(sig, product_spec); + + rmgr.set_predicate_kind(e_decl, pred_kind); + } + + func_decl * mk_explanations::get_e_decl(func_decl * orig_decl) { + decl_map::obj_map_entry * e = m_e_decl_map.insert_if_not_there2(orig_decl, 0); + if (e->get_data().m_value==0) { + relation_signature e_domain; + e_domain.append(orig_decl->get_arity(), orig_decl->get_domain()); + e_domain.push_back(m_e_sort); + func_decl * new_decl = m_context.mk_fresh_head_predicate(orig_decl->get_name(), symbol("expl"), + e_domain.size(), e_domain.c_ptr(), orig_decl); + m_pinned.push_back(new_decl); + e->get_data().m_value = new_decl; + + if (m_relation_level) { + assign_rel_level_kind(new_decl, orig_decl); + } + } + return e->get_data().m_value; + } + + app * mk_explanations::get_e_lit(app * lit, unsigned e_var_idx) { + expr_ref_vector args(m_manager); + func_decl * e_decl = get_e_decl(lit->get_decl()); + args.append(lit->get_num_args(), lit->get_args()); + args.push_back(m_manager.mk_var(e_var_idx, m_e_sort)); + return m_manager.mk_app(e_decl, args.c_ptr()); + } + + symbol mk_explanations::get_rule_symbol(rule * r) { + if (r->name() == symbol::null) { + std::stringstream sstm; + r->display(m_context, sstm); + std::string res = sstm.str(); + res = res.substr(0, res.find_last_not_of('\n')+1); + return symbol(res.c_str()); + } + else { + return r->name(); + } + } + + rule * mk_explanations::get_e_rule(rule * r) { + rule_counter ctr; + ctr.count_rule_vars(m_manager, r); + unsigned max_var; + unsigned next_var = ctr.get_max_positive(max_var) ? (max_var+1) : 0; + unsigned head_var = next_var++; + app_ref e_head(get_e_lit(r->get_head(), head_var), m_manager); + + app_ref_vector e_tail(m_manager); + svector neg_flags; + unsigned pos_tail_sz = r->get_positive_tail_size(); + for (unsigned i=0; iget_tail(i), e_var)); + neg_flags.push_back(false); + } + unsigned tail_sz = r->get_tail_size(); + for (unsigned i=pos_tail_sz; iget_tail(i)); + neg_flags.push_back(r->is_neg_tail(i)); + } + + symbol rule_repr = get_rule_symbol(r); + + expr_ref_vector rule_expr_args(m_manager); + for (unsigned tail_idx=0; tail_idxget_arg(tail->get_num_args()-1)); + } + else { + //this adds argument values and the explanation term + //(values will be substituted for variables at runtime by the finite_product_relation) + rule_expr_args.append(tail->get_num_args(), tail->get_args()); + } + } + //rule_expr contains rule function with string representation of the rule as symbol and + //for each positive uninterpreted tail it contains its argument values and its explanation term + expr * rule_expr = m_decl_util.mk_rule(rule_repr, rule_expr_args.size(), rule_expr_args.c_ptr()); + + app_ref e_record(m_manager.mk_eq(m_manager.mk_var(head_var, m_e_sort), rule_expr), m_manager); + e_tail.push_back(e_record); + neg_flags.push_back(false); + SASSERT(e_tail.size()==neg_flags.size()); + + return m_context.get_rule_manager().mk(e_head, e_tail.size(), e_tail.c_ptr(), neg_flags.c_ptr()); + } + + void mk_explanations::transform_rules(const rule_set & src, rule_set & dst) { + rule_set::iterator rit = src.begin(); + rule_set::iterator rend = src.end(); + for (; rit!=rend; ++rit) { + rule * e_rule = get_e_rule(*rit); + dst.add_rule(e_rule); + } + + //add rules that will (for output predicates) copy facts from explained relations back to + //the original ones + expr_ref_vector lit_args(m_manager); + decl_set::iterator pit = src.get_output_predicates().begin(); + decl_set::iterator pend = src.get_output_predicates().end(); + for (; pit != pend; ++pit) { + func_decl * orig_decl = *pit; + + lit_args.reset(); + unsigned arity = orig_decl->get_arity(); + for (unsigned i=0; iget_domain(i))); + } + app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager); + app_ref e_lit(get_e_lit(orig_lit, arity), m_manager); + app * tail[] = { e_lit.get() }; + dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0)); + } + } + + void mk_explanations::translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, + relation_base & e_rel) { + SASSERT(m_e_fact_relation); + SASSERT(e_rel.get_plugin().is_product_relation()); + + product_relation & prod_rel = static_cast(e_rel); + SASSERT(prod_rel.size()==2); + SASSERT(prod_rel[0].get_plugin().is_sieve_relation()); + SASSERT(prod_rel[1].get_plugin().is_sieve_relation()); + sieve_relation * srels[] = { + static_cast(&prod_rel[0]), + static_cast(&prod_rel[1]) }; + if (&srels[0]->get_inner().get_plugin()==m_er_plugin) { + std::swap(srels[0], srels[1]); + } + SASSERT(&srels[0]->get_inner().get_plugin()==&orig.get_plugin()); + SASSERT(&srels[1]->get_inner().get_plugin()==m_er_plugin); + + relation_base & new_orig = srels[0]->get_inner(); + explanation_relation & expl_rel = static_cast(srels[1]->get_inner()); + + { + scoped_ptr orig_union_fun = rmgr.mk_union_fn(new_orig, orig); + SASSERT(orig_union_fun); + (*orig_union_fun)(new_orig, orig); + } + + { + scoped_ptr expl_union_fun = rmgr.mk_union_fn(expl_rel, *m_e_fact_relation); + SASSERT(expl_union_fun); + (*expl_union_fun)(expl_rel, *m_e_fact_relation); + } + } + + void mk_explanations::transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst) { + + if (!m_e_fact_relation) { + relation_signature expl_singleton_sig; + expl_singleton_sig.push_back(m_e_sort); + + relation_base * expl_singleton = rmgr.mk_empty_relation(expl_singleton_sig, m_er_plugin->get_kind()); + relation_fact es_fact(m_manager); + es_fact.push_back(m_decl_util.mk_fact(symbol("fact"))); + expl_singleton->add_fact(es_fact); + + SASSERT(&expl_singleton->get_plugin()==m_er_plugin); + m_e_fact_relation = static_cast(expl_singleton); + } + func_decl_set const& predicates = m_context.get_predicates(); + decl_set::iterator it = predicates.begin(); + decl_set::iterator end = predicates.end(); + for (; it!=end; ++it) { + func_decl * orig_decl = *it; + func_decl * e_decl = get_e_decl(orig_decl); + + if (!rmgr.try_get_relation(orig_decl) && + !src.contains(orig_decl)) { + // there are no facts or rules for this predicate + continue; + } + + dst.inherit_predicate(src, orig_decl, e_decl); + + relation_base & orig_rel = rmgr.get_relation(orig_decl); + relation_base & e_rel = rmgr.get_relation(e_decl); + SASSERT(e_rel.empty()); //the e_rel should be a new relation + + if (m_relation_level) { + translate_rel_level_relation(rmgr, orig_rel, e_rel); + } + else { + scoped_ptr product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, 0, 0); + SASSERT(product_fun); + scoped_rel aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation); + scoped_ptr union_fun = rmgr.mk_union_fn(e_rel, *aux_extended_rel); + SASSERT(union_fun); + (*union_fun)(e_rel, *aux_extended_rel); + } + } + } + + rule_set * mk_explanations::operator()(rule_set const & source) { + + if (source.empty()) { + return 0; + } + if (!m_context.generate_explanations()) { + return 0; + } + rule_set * res = alloc(rule_set, m_context); + transform_facts(m_context.get_rel_context()->get_rmanager(), source, *res); + transform_rules(source, *res); + return res; + } + +}; + diff --git a/src/muz/rel/dl_mk_explanations.h b/src/muz/rel/dl_mk_explanations.h new file mode 100644 index 000000000..9e4d705c3 --- /dev/null +++ b/src/muz/rel/dl_mk_explanations.h @@ -0,0 +1,86 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_explanations.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-11-08. + +Revision History: + +--*/ + +#ifndef _DL_MK_EXPLANATIONS_H_ +#define _DL_MK_EXPLANATIONS_H_ + +#include "dl_context.h" +#include "dl_rule_transformer.h" + +namespace datalog { + + class explanation_relation; + class explanation_relation_plugin; + + class mk_explanations : public rule_transformer::plugin { + + typedef obj_map decl_map; + + ast_manager & m_manager; + context & m_context; + dl_decl_util & m_decl_util; + bool m_relation_level; + ast_ref_vector m_pinned; + explanation_relation_plugin * m_er_plugin; + sort * m_e_sort; + scoped_rel m_e_fact_relation; + + decl_map m_e_decl_map; + + symbol get_rule_symbol(rule * r); + + app * get_e_lit(app * lit, unsigned e_var_idx); + rule * get_e_rule(rule * r); + + /** + If \c m_relation_level is true, ensure \c e_decl predicate will be represented by + the right relation object. \c orig is the predicate corresponding to \c e_decl without + the explanation column. + */ + void assign_rel_level_kind(func_decl * e_decl, func_decl * orig); + void translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel); + + void transform_rules(const rule_set & src, rule_set & dst); + + void transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst); + public: + /** + If relation_level is true, the explanation will not be stored for each fact, + but we will rather store history of the whole relation. + */ + mk_explanations(context & ctx); + + /** + \brief Return explanation predicate that corresponds to \c orig_decl. + */ + func_decl * get_e_decl(func_decl * orig_decl); + + static func_decl * get_union_decl(context & ctx); + func_decl * get_union_decl() const { + return get_union_decl(m_context); + } + + rule_set * operator()(rule_set const & source); + + static expr* get_explanation(relation_base const& r); + }; +}; + +#endif /* _DL_MK_EXPLANATIONS_H_ */ + diff --git a/src/muz/rel/dl_mk_partial_equiv.cpp b/src/muz/rel/dl_mk_partial_equiv.cpp new file mode 100644 index 000000000..d79a46720 --- /dev/null +++ b/src/muz/rel/dl_mk_partial_equiv.cpp @@ -0,0 +1,155 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + dl_mk_partial_equiv.cpp + +Abstract: + + Rule transformer which identifies predicates that are partial equivalence relations. + +Author: + + Nikolaj Bjorner (nbjorner) 2012-05-14 + +Revision History: + +--*/ + +#include "dl_mk_partial_equiv.h" +#include "dl_relation_manager.h" +#include "ast_pp.h" + +namespace datalog { + + bool mk_partial_equivalence_transformer::is_symmetry(rule const* r) { + func_decl* p = r->get_decl(); + return + p->get_arity() == 2 && + p->get_domain(0) == p->get_domain(1) && + r->get_tail_size() == 1 && + r->get_tail(0)->get_decl() == p && + r->get_head()->get_arg(0) == r->get_tail(0)->get_arg(1) && + r->get_head()->get_arg(1) == r->get_tail(0)->get_arg(0) && + is_var(r->get_head()->get_arg(0)) && + is_var(r->get_head()->get_arg(1)) && + r->get_head()->get_arg(0) != r->get_head()->get_arg(1); + } + + + bool mk_partial_equivalence_transformer::is_transitivity(rule const* r) { + func_decl* p = r->get_decl(); + if (p->get_arity() != 2 || + p->get_domain(0) != p->get_domain(1) || + r->get_tail_size() != 2 || + r->get_tail(0)->get_decl() != p || + r->get_tail(1)->get_decl() != p) { + return false; + } + app* h = r->get_head(); + app* a = r->get_tail(0); + app* b = r->get_tail(1); + expr* x1 = h->get_arg(0); + expr* x2 = h->get_arg(1); + expr* a1 = a->get_arg(0); + expr* a2 = a->get_arg(1); + expr* b1 = b->get_arg(0); + expr* b2 = b->get_arg(1); + + if (!(is_var(x1) && is_var(x2) && is_var(a1) && is_var(a2) && is_var(b1) && is_var(b2))) { + return false; + } + if (x1 == x2 || a1 == a2 || b1 == b2) { + return false; + } + if (a2 == b1) { + if (x1 == b2 && x2 == a1) { + return true; + } + if (x1 == a1 && x2 == b2) { + return true; + } + return false; + } + if (a1 == b2) { + if (x1 == b1 && x2 == a2) { + return true; + } + if (x1 == a2 && x2 == b1) { + return true; + } + return false; + } + + return false; +; + } + + + rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source) { + // TODO mc + + if (source.get_num_rules() == 0) { + return 0; + } + + if (m_context.get_engine() != DATALOG_ENGINE) { + return 0; + } + + relation_manager & rm = m_context.get_rel_context()->get_rmanager(); + rule_set::decl2rules::iterator it = source.begin_grouped_rules(); + rule_set::decl2rules::iterator end = source.end_grouped_rules(); + + rule_set* res = alloc(rule_set, m_context); + + for (; it != end; ++it) { + func_decl* p = it->m_key; + rule_vector const& rv = *(it->m_value); + bool has_symmetry = false; + bool has_transitivity = false; + unsigned i_symmetry, i_transitivity; + family_id kind = rm.get_requested_predicate_kind(p); + for (unsigned i = 0; i < rv.size(); ++i) { + + if (kind != null_family_id) { + res->add_rule(rv[i]); + } + else if (is_symmetry(rv[i])) { + i_symmetry = i; + has_symmetry = true; + } + else if (is_transitivity(rv[i])) { + i_transitivity = i; + has_transitivity = true; + } + else { + res->add_rule(rv[i]); + } + } + if (has_symmetry && !has_transitivity) { + res->add_rule(rv[i_symmetry]); + } + else if (!has_symmetry && has_transitivity) { + res->add_rule(rv[i_transitivity]); + } + else if (has_symmetry && has_transitivity) { + TRACE("dl", tout << "updating predicate " << mk_pp(p, m) << " to partial equivalence\n";); + SASSERT(kind == null_family_id); + rm.set_predicate_kind(p, rm.get_table_plugin(symbol("equivalence"))->get_kind()); + } + } + + if (res->get_num_rules() == source.get_num_rules()) { + dealloc(res); + return 0; + } + res->inherit_predicates(source); + + return res; + } + +}; + + diff --git a/src/muz/rel/dl_mk_partial_equiv.h b/src/muz/rel/dl_mk_partial_equiv.h new file mode 100644 index 000000000..54a70b3c0 --- /dev/null +++ b/src/muz/rel/dl_mk_partial_equiv.h @@ -0,0 +1,50 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + dl_mk_partial_equiv.h + +Abstract: + + Rule transformer which identifies predicates that are partial equivalence relations. + +Author: + + Nikolaj Bjorner (nbjorner) 2012-05-14 + +Revision History: + +--*/ + + +#ifndef _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ +#define _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ + +#include "dl_context.h" +#include "dl_rule_transformer.h" + +namespace datalog { + + class mk_partial_equivalence_transformer : public rule_transformer::plugin { + ast_manager & m; + context & m_context; + public: + mk_partial_equivalence_transformer(context & ctx, unsigned priority=45000) + : plugin(priority), + m(ctx.get_manager()), + m_context(ctx) {} + + rule_set * operator()(rule_set const & source); + + private: + + bool is_symmetry(rule const* r); + bool is_transitivity(rule const* r); + }; + +}; + +#endif /* _DL_MK_PARTIAL_EQUIV_TRANSFORMER_H_ */ + + diff --git a/src/muz/rel/dl_mk_similarity_compressor.cpp b/src/muz/rel/dl_mk_similarity_compressor.cpp new file mode 100644 index 000000000..aa2fe8ab9 --- /dev/null +++ b/src/muz/rel/dl_mk_similarity_compressor.cpp @@ -0,0 +1,546 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_similarity_compressor.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-10-22. + +Revision History: + +--*/ + +#include +#include +#include"dl_mk_similarity_compressor.h" +#include"dl_relation_manager.h" + +namespace datalog { + + mk_similarity_compressor::mk_similarity_compressor(context & ctx) : + plugin(5000), + m_context(ctx), + m_manager(ctx.get_manager()), + m_threshold_count(ctx.similarity_compressor_threshold()), + m_result_rules(ctx.get_rule_manager()), + m_modified(false), + m_pinned(m_manager) { + SASSERT(m_threshold_count>1); + } + + void mk_similarity_compressor::reset() { + m_rules.reset(); + m_result_rules.reset(); + m_pinned.reset(); + } + + /** + Allows to traverse head and positive tails in a single for loop starting from -1 + */ + static app * get_by_tail_index(rule * r, int idx) { + if (idx < 0) { + return r->get_head(); + } + SASSERT(idx < static_cast(r->get_positive_tail_size())); + return r->get_tail(idx); + } + + template + static int aux_compare(T a, T b) { + return (a>b) ? 1 : ( (a==b) ? 0 : -1); + } + + template + static int aux_compare(T* a, T* b); + + static int compare_var_args(app* t1, app* t2) { + SASSERT(t1->get_num_args()==t2->get_num_args()); + int res; + unsigned n = t1->get_num_args(); + for (unsigned i = 0; i < n; i++) { + expr * a1 = t1->get_arg(i); + expr * a2 = t2->get_arg(i); + res = aux_compare(is_var(a1), is_var(a2)); + if (res != 0) { + return res; + } + if (is_var(a1)) { + res = aux_compare(to_var(a1)->get_idx(), to_var(a2)->get_idx()); + if (res != 0) { + return res; + } + } + } + return 0; + } + + static int compare_args(app* t1, app* t2, int & skip_countdown) { + SASSERT(t1->get_num_args()==t2->get_num_args()); + int res; + unsigned n = t1->get_num_args(); + for (unsigned i=0; iget_arg(i))) { + SASSERT(t1->get_arg(i) == t2->get_arg(i)); + continue; + } + if ((skip_countdown--) == 0) { + continue; + } + res = aux_compare(t1->get_arg(i)->get_id(), t2->get_arg(i)->get_id()); + if (res!=0) { return res; } + } + return 0; + } + + /** + \brief Return 0 if r1 and r2 could be similar. If the rough similarity + equaivelance class of r1 is greater than the one of r2, return 1; otherwise return -1. + + Two rules are in the same rough similarity class if they differ only in constant arguments + of positive uninterpreted predicates. + */ + static int rough_compare(rule * r1, rule * r2) { + int res = aux_compare(r1->get_tail_size(), r2->get_tail_size()); + if (res!=0) { return res; } + res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size()); + if (res!=0) { return res; } + res = aux_compare(r1->get_positive_tail_size(), r2->get_positive_tail_size()); + if (res!=0) { return res; } + + int pos_tail_sz = r1->get_positive_tail_size(); + for (int i=-1; iget_decl()->get_id(), t2->get_decl()->get_id()); + if (res!=0) { return res; } + res = compare_var_args(t1, t2); + if (res!=0) { return res; } + } + + unsigned tail_sz = r1->get_tail_size(); + for (unsigned i=pos_tail_sz; iget_tail(i)->get_id(), r2->get_tail(i)->get_id()); + if (res!=0) { return res; } + } + + return 0; + } + + /** + \c r1 and \c r2 must be equal according to the \c rough_compare function for this function + to be called. + */ + static int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) { + SASSERT(rough_compare(r1, r2)==0); + int pos_tail_sz = r1->get_positive_tail_size(); + for (int i=-1; i info_vector; + + static void collect_const_indexes(app * t, int tail_index, info_vector & res) { + unsigned n = t->get_num_args(); + for (unsigned i=0; iget_arg(i))) { + continue; + } + res.push_back(const_info(tail_index, i)); + } + } + + static void collect_const_indexes(rule * r, info_vector & res) { + collect_const_indexes(r->get_head(), -1, res); + unsigned pos_tail_sz = r->get_positive_tail_size(); + for (unsigned i=0; iget_tail(i), i, res); + } + } + + template + static void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) { + unsigned const_cnt = const_infos.size(); + tgt.reset(); + for (unsigned i=0; iget_arg(inf.arg_index()))); + SASSERT(tgt.back()->get_num_args()==0); + } + } + template + static void collect_orphan_sorts(rule * r, const info_vector & const_infos, T & tgt) { + unsigned const_cnt = const_infos.size(); + tgt.reset(); + for (unsigned i=0; iget_decl()->get_domain(inf.arg_index())); + } + } + + /** + \brief From the \c tail_indexes and \c arg_indexes remove elements corresponding to constants + that are the same in rules \c *first ... \c *(after_last-1). + */ + static void remove_stable_constants(rule_vector::iterator first, rule_vector::iterator after_last, + info_vector & const_infos) { + SASSERT(after_last-first>1); + unsigned const_cnt = const_infos.size(); + ptr_vector vals; + rule * r = *(first++); + collect_orphan_consts(r, const_infos, vals); + SASSERT(vals.size()==const_cnt); + rule_vector::iterator it = first; + for (; it!=after_last; ++it) { + for (unsigned i=0; iget_arg(const_infos[i].arg_index())); + if (vals[i]!=val) { + vals[i] = 0; + } + } + } + unsigned removed_cnt = 0; + for (unsigned i=0; i vals; + ptr_vector sorts; + rule * r = *(first++); + collect_orphan_consts(r, const_infos, vals); + collect_orphan_sorts(r, const_infos, sorts); + SASSERT(vals.size()==const_cnt); + vector possible_parents(const_cnt); + for (unsigned i=1; iget_head()->get_num_args() - count_variable_arguments(r->get_head()); + unsigned pos_tail_sz = r->get_positive_tail_size(); + for (unsigned i=0; iget_tail(i)->get_num_args() - count_variable_arguments(r->get_tail(i)); + } + return res; + } + + static bool initial_comparator(rule * r1, rule * r2) { + int res = rough_compare(r1, r2); + if (res!=0) { return res>0; } + return total_compare(r1, r2)>0; + } + + class arg_ignoring_comparator { + unsigned m_ignored_index; + public: + arg_ignoring_comparator(unsigned ignored_index) : m_ignored_index(ignored_index) {} + bool operator()(rule * r1, rule * r2) const { + return total_compare(r1, r2, m_ignored_index)>0; + } + bool eq(rule * r1, rule * r2) const { + return total_compare(r1, r2, m_ignored_index)==0; + } + }; + + void mk_similarity_compressor::merge_class(rule_vector::iterator first, + rule_vector::iterator after_last) { + SASSERT(after_last-first>1); + info_vector const_infos; + rule * r = *first; //an arbitrary representative of the class + collect_const_indexes(r, const_infos); + remove_stable_constants(first, after_last, const_infos); + + unsigned const_cnt = const_infos.size(); + SASSERT(const_cnt>0); + + detect_equal_constants(first, after_last, const_infos); + + + //The aux relation contains column for each constant which does not have an earlier constant + //that it is equal to (i.e. only has no parent) + ptr_vector aux_domain; + collect_orphan_sorts(r, const_infos, aux_domain); + + func_decl* head_pred = r->get_decl(); + symbol const& name_prefix = head_pred->get_name(); + std::string name_suffix = "sc_" + to_string(const_cnt); + func_decl * aux_pred = m_context.mk_fresh_head_predicate(name_prefix, symbol(name_suffix.c_str()), + aux_domain.size(), aux_domain.c_ptr(), head_pred); + m_pinned.push_back(aux_pred); + + relation_fact val_fact(m_manager, const_cnt); + rule_vector::iterator it = first; + for (; it!=after_last; ++it) { + collect_orphan_consts(*it, const_infos, val_fact); + m_context.add_fact(aux_pred, val_fact); + } + m_context.get_rel_context()->get_rmanager().mark_saturated(aux_pred); + + app * new_head = r->get_head(); + ptr_vector new_tail; + svector new_negs; + unsigned tail_sz = r->get_tail_size(); + for (unsigned i=0; iget_tail(i)); + new_negs.push_back(r->is_neg_tail(i)); + } + + rule_counter ctr; + ctr.count_rule_vars(m_manager, r); + unsigned max_var_idx, new_var_idx_base; + if (ctr.get_max_positive(max_var_idx)) { + new_var_idx_base = max_var_idx+1; + } + else { + new_var_idx_base = 0; + } + + ptr_vector const_vars; //variables at indexes of their corresponding constants + expr_ref_vector aux_vars(m_manager); //variables as arguments for the auxiliary predicate + + unsigned aux_column_index = 0; + + for (unsigned i=0; i mod_args(mod_tail->get_num_args(), mod_tail->get_args()); + + for (; iget_decl(), mod_args.c_ptr()); + m_pinned.push_back(upd_tail); + mod_tail = upd_tail; + } + + app_ref aux_tail(m_manager.mk_app(aux_pred, aux_vars.c_ptr()), m_manager); + new_tail.push_back(aux_tail); + new_negs.push_back(false); + + rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), + new_negs.c_ptr()); + m_result_rules.push_back(new_rule); + + //TODO: allow for a rule to have multiple parent objects + new_rule->set_accounting_parent_object(m_context, r); + m_modified = true; + } + + void mk_similarity_compressor::process_class(rule_set const& source, rule_vector::iterator first, + rule_vector::iterator after_last) { + SASSERT(first!=after_last); + //remove duplicates + { + rule_vector::iterator it = first; + rule_vector::iterator prev = it; + ++it; + while(it!=after_last) { + if (it!=after_last && total_compare(*prev, *it)==0) { + --after_last; + std::swap(*it, *after_last); + m_modified = true; + } + else { + prev = it; + ++it; + } + } + } + SASSERT(first!=after_last); + + unsigned const_cnt = get_constant_count(*first); +#if 0 + for (unsigned ignored_index=0; ignored_indexm_threshold_count) { + merge_class(grp_begin, it); + //group was processed, so we remove it from the class + if (it==after_last) { + after_last=grp_begin; + it=after_last; + } + else { + while(it!=grp_begin) { + std::swap(*--it, *--after_last); + } + } + } + grp_begin = it; + grp_size = 0; + } + } + } +#endif + //TODO: compress also rules with pairs (or tuples) of equal constants + +#if 1 + if (const_cnt>0 && !source.is_output_predicate((*first)->get_decl())) { + unsigned rule_cnt = static_cast(after_last-first); + if (rule_cnt>m_threshold_count) { + merge_class(first, after_last); + return; + } + } +#endif + + //put rules which weren't merged into result + rule_vector::iterator it = first; + for (; it!=after_last; ++it) { + m_result_rules.push_back(*it); + } + } + + rule_set * mk_similarity_compressor::operator()(rule_set const & source) { + // TODO mc + m_modified = false; + unsigned init_rule_cnt = source.get_num_rules(); + SASSERT(m_rules.empty()); + for (unsigned i=0; i(0); + if (m_modified) { + result = alloc(rule_set, m_context); + unsigned fin_rule_cnt = m_result_rules.size(); + for (unsigned i=0; iadd_rule(m_result_rules.get(i)); + } + result->inherit_predicates(source); + } + reset(); + return result; + } +}; diff --git a/src/muz/rel/dl_mk_similarity_compressor.h b/src/muz/rel/dl_mk_similarity_compressor.h new file mode 100644 index 000000000..34b76e7e1 --- /dev/null +++ b/src/muz/rel/dl_mk_similarity_compressor.h @@ -0,0 +1,78 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_similarity_compressor.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-10-22. + +Revision History: + +--*/ +#ifndef _DL_MK_SIMILARITY_COMPRESSOR_H_ +#define _DL_MK_SIMILARITY_COMPRESSOR_H_ + +#include + +#include"map.h" +#include"obj_pair_hashtable.h" + +#include"dl_context.h" +#include"dl_rule_set.h" +#include"dl_rule_transformer.h" + +namespace datalog { + + /** + \brief Functor for merging groups of similar rules. + + A rule sequence + + P("1",x):-Q(x). + ... + P("N",x):-Q(x). + + will be replaced by + + P(y,x):-Q(x), Aux(y). + + and a set of facts + + Aux("1"). + ... + Aux("N"). + + Similar transformation is performed when the varying constant appears in the positive tail. + */ + class mk_similarity_compressor : public rule_transformer::plugin { + + context & m_context; + ast_manager & m_manager; + /** number of similar rules necessary for a group to be introduced */ + unsigned m_threshold_count; + rule_vector m_rules; + rule_ref_vector m_result_rules; + bool m_modified; + ast_ref_vector m_pinned; + + void merge_class(rule_vector::iterator first, rule_vector::iterator after_last); + void process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last); + + void reset(); + public: + mk_similarity_compressor(context & ctx); + + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_SIMILARITY_COMPRESSOR_H_ */ + diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp new file mode 100644 index 000000000..4b9ce582a --- /dev/null +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -0,0 +1,742 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_simple_joins.cpp + +Abstract: + + + +Author: + + Leonardo de Moura (leonardo) 2010-05-20. + +Revision History: + +--*/ + +#include +#include +#include +#include"dl_mk_simple_joins.h" +#include"dl_relation_manager.h" +#include"ast_pp.h" +#include"trace.h" + + +namespace datalog { + + mk_simple_joins::mk_simple_joins(context & ctx): + plugin(1000), + m_context(ctx), + rm(ctx.get_rule_manager()) { + } + + class join_planner { + typedef float cost; + + class pair_info { + cost m_total_cost; + /** + \brief Number of rules longer than two that contain this pair. + + This number is being updated by \c add_rule and \remove rule. Even though between + adding a rule and removing it, the length of a rule can decrease without this pair + being notified about it, it will surely see the decrease from length 3 to 2 which + the threshold for rule being counted in this counter. + */ + unsigned m_consumers; + bool m_stratified; + unsigned m_src_stratum; + public: + var_idx_set m_all_nonlocal_vars; + rule_vector m_rules; + + pair_info() : m_consumers(0), m_stratified(true), m_src_stratum(0) {} + + bool can_be_joined() const { + return m_consumers>0; + } + + cost get_cost() const { + /*if(m_instantiated) { + return std::numeric_limits::min(); + }*/ + SASSERT(m_consumers>0); + cost amortized = m_total_cost/m_consumers; + if(m_stratified) { + return amortized * ( (amortized>0) ? (1/16.0f) : 16.0f); + } + else { + return amortized; + } + } + + /** + \brief Add rule \c r among rules interested in current predicate pair. + + The \c pl.m_rule_content entry of the rule has to be properly filled in + by the time of a call to this function + */ + void add_rule(join_planner & pl, app * t1, app * t2, rule * r, + const var_idx_set & non_local_vars_normalized) { + if(m_rules.empty()) { + m_total_cost = pl.compute_cost(t1, t2); + m_src_stratum = std::max(pl.get_stratum(t1->get_decl()), pl.get_stratum(t2->get_decl())); + } + m_rules.push_back(r); + if(pl.m_rules_content.find_core(r)->get_data().m_value.size()>2) { + m_consumers++; + } + if(m_stratified) { + unsigned head_stratum = pl.get_stratum(r->get_decl()); + SASSERT(head_stratum>=m_src_stratum); + if(head_stratum==m_src_stratum) { + m_stratified = false; + } + } + idx_set_union(m_all_nonlocal_vars, non_local_vars_normalized); + } + /** + \brief Remove rule from the pair record. Return true if no rules remain + in the pair, and so it should be removed. + */ + bool remove_rule(rule * r, unsigned original_length) { + TRUSTME( remove_from_vector(m_rules, r) ); + if(original_length>2) { + SASSERT(m_consumers>0); + m_consumers--; + } + SASSERT(!m_rules.empty() || m_consumers==0); + return m_rules.empty(); + } + private: + pair_info & operator=(const pair_info &); //to avoid the implicit one + }; + typedef std::pair app_pair; + typedef map, obj_ptr_hash >, default_eq > cost_map; + typedef map, ptr_hash, ptr_eq > rule_pred_map; + + context & m_context; + ast_manager & m; + rule_manager & rm; + var_subst & m_var_subst; + rule_set & m_rs_aux_copy; //reference to a rule_set that will allow to ask for stratum levels + + cost_map m_costs; + ptr_vector m_interpreted; + rule_pred_map m_rules_content; + rule_ref_vector m_introduced_rules; + ptr_hashtable, ptr_eq > m_modified_rules; + + ast_ref_vector m_pinned; + mutable ptr_vector m_vars; + + public: + join_planner(context & ctx, rule_set & rs_aux_copy) + : m_context(ctx), m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_var_subst(ctx.get_var_subst()), + m_rs_aux_copy(rs_aux_copy), + m_introduced_rules(ctx.get_rule_manager()), + m_pinned(ctx.get_manager()) + { + } + + ~join_planner() + { + cost_map::iterator it = m_costs.begin(); + cost_map::iterator end = m_costs.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + m_costs.reset(); + } + private: + + void get_normalizer(app * t, unsigned & next_var, expr_ref_vector & result) const { + SASSERT(result.size()>0); + unsigned res_ofs = result.size()-1; + unsigned n=t->get_num_args(); + for(unsigned i=0; iget_arg(i))); + var * v = to_var(t->get_arg(i)); + unsigned var_idx = v->get_idx(); + if(result[res_ofs-var_idx]==0) { + result[res_ofs-var_idx]=m.mk_var(next_var, v->get_sort()); + next_var++; + } + } + } + + void get_normalizer(app * t1, app * t2, expr_ref_vector & result) const { + SASSERT(result.empty()); + if(t1->get_num_args()==0 && t2->get_num_args()==0) { + return; //nothing to normalize + } + SASSERT(!t1->is_ground() || !t2->is_ground()); + + unsigned max_var_idx = 0; + { + var_idx_set& orig_var_set = rm.collect_vars(t1, t2); + var_idx_set::iterator ovit = orig_var_set.begin(); + var_idx_set::iterator ovend = orig_var_set.end(); + for(; ovit!=ovend; ++ovit) { + unsigned var_idx = *ovit; + if(var_idx>max_var_idx) { + max_var_idx = var_idx; + } + } + } + + if(t1->get_decl()!=t2->get_decl()) { + if(t1->get_decl()->get_id()get_decl()->get_id()) { + std::swap(t1, t2); + } + } + else { + int_vector norm1(max_var_idx+1, -1); + int_vector norm2(max_var_idx+1, -1); + unsigned n=t1->get_num_args(); + SASSERT(n==t2->get_num_args()); + for(unsigned i=0; iget_arg(i)); + var * v2 = to_var(t2->get_arg(i)); + if(v1->get_sort()!=v2->get_sort()) { + //different sorts mean we can distinguish the two terms + if(v1->get_sort()->get_id()get_sort()->get_id()) { + std::swap(t1, t2); + } + break; + } + unsigned v1_idx = v1->get_idx(); + unsigned v2_idx = v2->get_idx(); + //since the rules already went through the mk_filter_rules transformer, + //variables must be linear + SASSERT(norm1[v1_idx]==-1); + SASSERT(norm2[v2_idx]==-1); + + if(norm2[v1_idx]!=norm1[v2_idx]) { + //now we can distinguish the two terms + if(norm2[v1_idx](0)); + unsigned next_var = 0; + get_normalizer(t1, next_var, result); + get_normalizer(t2, next_var, result); + } + + app_pair get_key(app * t1, app * t2) { + expr_ref_vector norm_subst(m); + get_normalizer(t1, t2, norm_subst); + expr_ref t1n_ref(m); + expr_ref t2n_ref(m); + m_var_subst(t1, norm_subst.size(), norm_subst.c_ptr(), t1n_ref); + m_var_subst(t2, norm_subst.size(), norm_subst.c_ptr(), t2n_ref); + app * t1n = to_app(t1n_ref); + app * t2n = to_app(t2n_ref); + if(t1n>t2n) { + std::swap(t1n, t2n); + } + m_pinned.push_back(t1n); + m_pinned.push_back(t2n); + + /* + IF_VERBOSE(0, + print_renaming(norm_subst, verbose_stream()); + display_predicate(m_context, t1, verbose_stream()); + display_predicate(m_context, t2, verbose_stream()); + display_predicate(m_context, t1n, verbose_stream()); + display_predicate(m_context, t2n, verbose_stream());); + */ + return app_pair(t1n, t2n); + } + + /** + \brief Add rule \c r among rules interested in predicate pair \c t1, \c t2. + + The \c m_rule_content entry of the rule \c r has to be properly filled in + by the time of a call to this function + */ + void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) { + TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << "\n"; + r->display(m_context, tout); tout << "\n";); + SASSERT(t1!=t2); + cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), 0); + pair_info * & ptr_inf = e->get_data().m_value; + if(ptr_inf==0) { + ptr_inf = alloc(pair_info); + } + pair_info & inf = *ptr_inf; + + expr_ref_vector normalizer(m); + get_normalizer(t1, t2, normalizer); + unsigned norm_ofs = normalizer.size()-1; + var_idx_set normalized_vars; + var_idx_set::iterator vit = non_local_vars.begin(); + var_idx_set::iterator vend = non_local_vars.end(); + for(; vit!=vend; ++vit) { + unsigned norm_var = to_var(normalizer.get(norm_ofs-*vit))->get_idx(); + normalized_vars.insert(norm_var); + } + + inf.add_rule(*this, t1, t2, r, normalized_vars); + } + + pair_info & get_pair(app_pair key) const { + cost_map::entry * e = m_costs.find_core(key); + SASSERT(e); + return *e->get_data().m_value; + } + + void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) { + pair_info * ptr = &get_pair(key); + if(ptr->remove_rule(r, original_len)) { + SASSERT(ptr->m_rules.empty()); + m_costs.remove(key); + dealloc(ptr); + } + } + + void register_rule(rule * r) { + rule_counter counter; + counter.count_rule_vars(m, r, 1); + + ptr_vector & rule_content = + m_rules_content.insert_if_not_there2(r, ptr_vector())->get_data().m_value; + SASSERT(rule_content.empty()); + + unsigned pos_tail_size=r->get_positive_tail_size(); + for(unsigned i=0; iget_tail(i)); + } + for(unsigned i=0; iget_tail(i); + var_idx_set t1_vars = rm.collect_vars(t1); + counter.count_vars(m, t1, -1); //temporarily remove t1 variables from counter + for(unsigned j=i+1; jget_tail(j); + counter.count_vars(m, t2, -1); //temporarily remove t2 variables from counter + var_idx_set scope_vars = rm.collect_vars(t2); + scope_vars |= t1_vars; + var_idx_set non_local_vars; + counter.collect_positive(non_local_vars); + counter.count_vars(m, t2, 1); //restore t2 variables in counter + set_intersection(non_local_vars, scope_vars); + register_pair(t1, t2, r, non_local_vars); + } + counter.count_vars(m, t1, 1); //restore t1 variables in counter + } + } + + bool extract_argument_info(unsigned var_idx, app * t, expr_ref_vector & args, + ptr_vector & domain) { + unsigned n=t->get_num_args(); + for(unsigned i=0; iget_arg(i)); + if(v->get_idx()==var_idx) { + args.push_back(v); + domain.push_back(m.get_sort(v)); + return true; + } + } + return false; + } + + void join_pair(app_pair pair_key) { + app * t1 = pair_key.first; + app * t2 = pair_key.second; + pair_info & inf = get_pair(pair_key); + SASSERT(!inf.m_rules.empty()); + var_idx_set & output_vars = inf.m_all_nonlocal_vars; + expr_ref_vector args(m); + ptr_vector domain; + + unsigned arity = output_vars.num_elems(); + idx_set::iterator ovit=output_vars.begin(); + idx_set::iterator ovend=output_vars.end(); + //TODO: improve quadratic complexity + for(;ovit!=ovend;++ovit) { + unsigned var_idx=*ovit; + + bool found=extract_argument_info(var_idx, t1, args, domain); + if(!found) { + found=extract_argument_info(var_idx, t2, args, domain); + } + SASSERT(found); + } + + SASSERT(args.size()==arity); + SASSERT(domain.size()==arity); + + rule * one_parent = inf.m_rules.back(); + + func_decl* parent_head = one_parent->get_decl(); + const char * one_parent_name = parent_head->get_name().bare_str(); + std::string parent_name; + if(inf.m_rules.size()>1) { + parent_name = one_parent_name + std::string("_and_") + to_string(inf.m_rules.size()-1); + } + else { + parent_name = one_parent_name; + } + + func_decl * decl = m_context.mk_fresh_head_predicate( + symbol(parent_name.c_str()), symbol("split"), + arity, domain.c_ptr(), parent_head); + + app_ref head(m.mk_app(decl, arity, args.c_ptr()), m); + + app * tail[] = {t1, t2}; + + rule * new_rule = m_context.get_rule_manager().mk(head, 2, tail, 0); + + //TODO: update accounting so that it can handle multiple parents + new_rule->set_accounting_parent_object(m_context, one_parent); + + m_introduced_rules.push_back(new_rule); + + //here we copy the inf.m_rules vector because inf.m_rules will get changed + //in the iteration. Also we use hashtable instead of vector because we do + //not want to process one rule twice. + typedef ptr_hashtable, default_eq > rule_hashtable; + rule_hashtable relevant_rules; + insert_into_set(relevant_rules, inf.m_rules); + rule_hashtable::iterator rit = relevant_rules.begin(); + rule_hashtable::iterator rend = relevant_rules.end(); + for(; rit!=rend; ++rit) { + apply_binary_rule(*rit, pair_key, head); + } + + // SASSERT(!m_costs.contains(pair_key)); + } + + void replace_edges(rule * r, const ptr_vector & removed_tails, + const ptr_vector & added_tails0, const ptr_vector & rule_content) { + SASSERT(removed_tails.size()>=added_tails0.size()); + unsigned len = rule_content.size(); + unsigned original_len = len+removed_tails.size()-added_tails0.size(); + ptr_vector added_tails(added_tails0); //we need a copy since we'll be modifying it + + unsigned rt_sz = removed_tails.size(); + //remove edges between removed tails + for(unsigned i=0; iget_head(); + + var_counter counter; + counter.count_vars(m, head, 1); + + unsigned tail_size=r->get_tail_size(); + unsigned pos_tail_size=r->get_positive_tail_size(); + + for(unsigned i=pos_tail_size; iget_tail(i), 1); + } + for(unsigned i=0; i & rule_content = m_rules_content.find_core(r)->get_data().m_value; + unsigned len = rule_content.size(); + if(len==1) { + return; + } + + func_decl * t1_pred = t1->get_decl(); + func_decl * t2_pred = t2->get_decl(); + ptr_vector removed_tails; + ptr_vector added_tails; + for(unsigned i1=0; i1get_decl()!=t1_pred) { + continue; + } + unsigned i2start = (t1_pred==t2_pred) ? (i1+1) : 0; + for(unsigned i2=i2start; i2get_decl()!=t2_pred) { + continue; + } + if(get_key(rt1, rt2)!=pair_key) { + continue; + } + expr_ref_vector normalizer(m); + get_normalizer(rt1, rt2, normalizer); + expr_ref_vector denormalizer(m); + reverse_renaming(m, normalizer, denormalizer); + expr_ref new_transf(m); + m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr(), new_transf); + app * new_lit = to_app(new_transf); + + m_pinned.push_back(new_lit); + rule_content[i1]=new_lit; + rule_content[i2]=rule_content.back(); + rule_content.pop_back(); + len--; //here the bound of both loops changes!!! + removed_tails.push_back(rt1); + removed_tails.push_back(rt2); + added_tails.push_back(new_lit); + //this exits the inner loop, the outer one continues in case there will + //be other matches + break; + } + } + SASSERT(!removed_tails.empty()); + SASSERT(!added_tails.empty()); + m_modified_rules.insert(r); + replace_edges(r, removed_tails, added_tails, rule_content); + } + + cost get_domain_size(func_decl * pred, unsigned arg_index) const { + relation_sort sort = pred->get_domain(arg_index); + return static_cast(m_context.get_sort_size_estimate(sort)); + //unsigned sz; + //if(!m_context.get_sort_size(sort, sz)) { + // sz=UINT_MAX; + //} + //return static_cast(sz); + } + + unsigned get_stratum(func_decl * pred) const { + return m_rs_aux_copy.get_predicate_strat(pred); + } + + cost estimate_size(app * t) const { + func_decl * pred = t->get_decl(); + unsigned n=pred->get_arity(); + rel_context_base* rel = m_context.get_rel_context(); + if (!rel) { + return cost(1); + } + relation_manager& rm = rel->get_rmanager(); + if( (m_context.saturation_was_run() && rm.try_get_relation(pred)) + || rm.is_saturated(pred)) { + SASSERT(rm.try_get_relation(pred)); //if it is saturated, it should exist + unsigned rel_size_int = rel->get_relation(pred).get_size_estimate_rows(); + if(rel_size_int!=0) { + cost rel_size = static_cast(rel_size_int); + cost curr_size = rel_size; + for(unsigned i=0; iget_arg(i))) { + curr_size /= get_domain_size(pred, i); + } + } + return curr_size; + } + } + cost res = 1; + for(unsigned i=0; iget_arg(i))) { + res *= get_domain_size(pred, i); + } + } + return res; + } + + cost compute_cost(app * t1, app * t2) const { + func_decl * t1_pred = t1->get_decl(); + func_decl * t2_pred = t2->get_decl(); + cost inters_size = 1; + variable_intersection vi(m_context.get_manager()); + vi.populate(t1, t2); + unsigned n = vi.size(); + for(unsigned i=0; i0) { + res /= 2; + } + else { + res *= 2; + } + } + }*/ + + TRACE("report_costs", + display_predicate(m_context, t1, tout); + display_predicate(m_context, t2, tout); + tout << res << "\n";); + return res; + } + + + bool pick_best_pair(app_pair & p) { + app_pair best; + bool found = false; + cost best_cost; + + cost_map::iterator it = m_costs.begin(); + cost_map::iterator end = m_costs.end(); + for(; it!=end; ++it) { + app_pair key = it->m_key; + pair_info & inf = *it->m_value; + if(!inf.can_be_joined()) { + continue; + } + cost c = inf.get_cost(); + if(!found || cm_key; + ptr_vector content = rcit->m_value; + SASSERT(content.size()<=2); + if(content.size()==orig_r->get_positive_tail_size()) { + //rule did not change + result->add_rule(orig_r); + continue; + } + + ptr_vector tail(content); + svector negs(tail.size(), false); + unsigned or_len = orig_r->get_tail_size(); + for(unsigned i=orig_r->get_positive_tail_size(); iget_tail(i)); + negs.push_back(orig_r->is_neg_tail(i)); + } + + rule * new_rule = m_context.get_rule_manager().mk(orig_r->get_head(), tail.size(), tail.c_ptr(), + negs.c_ptr()); + + new_rule->set_accounting_parent_object(m_context, orig_r); + m_context.get_rule_manager().mk_rule_rewrite_proof(*orig_r, *new_rule); + result->add_rule(new_rule); + } + while (!m_introduced_rules.empty()) { + result->add_rule(m_introduced_rules.back()); + m_context.get_rule_manager().mk_rule_asserted_proof(*m_introduced_rules.back()); + m_introduced_rules.pop_back(); + } + result->inherit_predicates(source); + return result; + } + }; + + rule_set * mk_simple_joins::operator()(rule_set const & source) { + rule_set rs_aux_copy(m_context); + rs_aux_copy.replace_rules(source); + if(!rs_aux_copy.is_closed()) { + rs_aux_copy.close(); + } + + join_planner planner(m_context, rs_aux_copy); + + return planner.run(source); + } + + +}; + diff --git a/src/muz/rel/dl_mk_simple_joins.h b/src/muz/rel/dl_mk_simple_joins.h new file mode 100644 index 000000000..36eb08dd5 --- /dev/null +++ b/src/muz/rel/dl_mk_simple_joins.h @@ -0,0 +1,63 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_simple_joins.h + +Abstract: + + + +Author: + + Leonardo de Moura (leonardo) 2010-05-20. + +Revision History: + +--*/ +#ifndef _DL_MK_SIMPLE_JOINS_H_ +#define _DL_MK_SIMPLE_JOINS_H_ + +#include"map.h" +#include"obj_pair_hashtable.h" + +#include"dl_context.h" +#include"dl_rule_set.h" +#include"dl_rule_transformer.h" + +namespace datalog { + + /** + \brief Functor for creating rules that contain simple joins. + A simple join is the join of two tables. + + After applying this transformation, every rule has at most one join. + So, the rules will have the form + + HEAD :- TAIL. + HEAD :- TAIL_1, TAIL_2. + + We also assume a rule may contain interpreted expressions that work as filtering conditions. + So, we may also have: + + HEAD :- TAIL, C_1, ..., C_n. + HEAD :- TAIL_1, TAIL_2, C_1, ..., C_n. + + Where the C_i's are interpreted expressions. + + We say that a rule containing C_i's is a rule with a "big tail". + */ + class mk_simple_joins : public rule_transformer::plugin { + context & m_context; + rule_manager & rm; + public: + mk_simple_joins(context & ctx); + + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_SIMPLE_JOINS_H_ */ + diff --git a/src/muz/rel/dl_product_relation.cpp b/src/muz/rel/dl_product_relation.cpp new file mode 100644 index 000000000..48cd666e6 --- /dev/null +++ b/src/muz/rel/dl_product_relation.cpp @@ -0,0 +1,1117 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_product_relation.cpp + +Abstract: + + A Relation combinator. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-4-11 + +Revision History: + +Notes: + + join = + more refined version lets augment the product + relation as a consequence of join. + join Q = + join = + + + u = + more refined version: + < (R u R') n (R u S') n (R' u S), (S u S') n (S u R') n (S' u R)> + + + proj = < proj R, proj S> + + & phi = + attach S to [R & phi] whenever R & phi can propagate to S + + + [rename] = + + + +--*/ + + +#include "dl_sieve_relation.h" +#include "dl_table_relation.h" +#include "dl_product_relation.h" +#include "bool_rewriter.h" +#include "ast_pp.h" + +namespace datalog { + + // ----------------------------------- + // + // product_relation_plugin + // + // ----------------------------------- + + product_relation_plugin & product_relation_plugin::get_plugin(relation_manager & rmgr) { + product_relation_plugin * res = + static_cast(rmgr.get_relation_plugin(get_name())); + if(!res) { + res = alloc(product_relation_plugin, rmgr); + rmgr.register_plugin(res); + } + return *res; + } + + product_relation_plugin::product_relation_plugin(relation_manager& m): + relation_plugin(product_relation_plugin::get_name(), m, ST_PRODUCT_RELATION), + m_spec_store(*this) { + } + + void product_relation_plugin::initialize(family_id fid) { + relation_plugin::initialize(fid); + m_spec_store.add_available_kind(get_kind()); + } + + family_id product_relation_plugin::get_relation_kind(const relation_signature & sig, const rel_spec & spec) { + return m_spec_store.get_relation_kind(sig, spec); + } + + family_id product_relation_plugin::get_relation_kind(const product_relation & r) { + return get_relation_kind(r.get_signature(), r.m_spec); + } + + bool product_relation_plugin::can_handle_signature(const relation_signature & s) { + return m_spec_store.contains_signature(s); + } + + bool product_relation_plugin::can_handle_signature(const relation_signature & s, family_id k) { + return true; + } + + product_relation& product_relation_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + + product_relation const & product_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + product_relation* product_relation_plugin::get(relation_base* r) { + return dynamic_cast(r); + } + + product_relation const* product_relation_plugin::get(relation_base const* r) { + return dynamic_cast(r); + } + + bool product_relation_plugin::is_product_relation(relation_base const& r) { + return r.get_plugin().get_name() == product_relation_plugin::get_name(); + } + + bool product_relation_plugin::are_aligned(const product_relation& r1, const product_relation& r2) { + unsigned sz = r1.size(); + if(sz!=r2.size()) { + return false; + } + for(unsigned i=0; i & rels, + rel_spec & res) { + vector specs; + ptr_vector::const_iterator rit = rels.begin(); + ptr_vector::const_iterator rend = rels.end(); + for(; rit!=rend; ++rit) { + specs.push_back((*rit)->m_spec); + } + + vector::iterator sit = specs.begin(); + vector::iterator send = specs.end(); + for(; sit!=send; ++sit) { + rel_spec & s = *sit; + std::sort(s.begin(), s.end()); + } + + res.reset(); + for(;;) { + family_id next = -1; + + sit = specs.begin(); + for(; sit!=send; ++sit) { + rel_spec & s = *sit; + if(!s.empty() && s.back()>next) { + next = s.back(); + } + } + if(next==-1) { + //we're done + break; + } + res.push_back(next); + sit = specs.begin(); + for(; sit!=send; ++sit) { + rel_spec & s = *sit; + if(!s.empty() && s.back()==next) { + s.pop_back(); + } + } + } + } + + + relation_base * product_relation_plugin::mk_empty(const relation_signature & s) { + return alloc(product_relation,*this, s); + } + + relation_base * product_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { + rel_spec spec; + m_spec_store.get_relation_spec(s, kind, spec); + relation_vector inner_rels; + unsigned rel_cnt = spec.size(); + for(unsigned i=0; i m_joins; + ptr_vector m_full; + unsigned_vector m_offset1; + svector m_kind1; + unsigned_vector m_offset2; + svector m_kind2; + + const relation_base & get_nonsieve_relation(const relation_base & r) { + relation_plugin & rp = r.get_plugin(); + if(rp.is_sieve_relation()) { + return static_cast(r).get_inner(); + } + else { + return r; + } + } + + relation_plugin & get_nonsieve_plugin(const relation_base & r) { + return get_nonsieve_relation(r).get_plugin(); + } + + family_id get_nonsieve_kind(const relation_base & r) { + return get_nonsieve_relation(r).get_kind(); + } + + /** + A tableish relatio is either a table_relation or a sieve_relation with a table_relation inside. + */ + bool is_tableish_relation(const relation_base & r) { + return get_nonsieve_plugin(r).from_table(); + } + + relation_base * get_full_tableish_relation(const relation_signature & sig, func_decl* p, family_id kind) { + relation_manager& rmgr = m_plugin.get_manager(); + table_signature tsig; + if(rmgr.relation_signature_to_table(sig, tsig)) { + return rmgr.mk_table_relation(sig, rmgr.get_appropriate_plugin(tsig).mk_full(p, tsig, kind)); + } + unsigned sz = sig.size(); + tsig.reset(); + for(unsigned i=0; i relations; + unsigned sz = m_joins.size(); + relation_base* result = 0; + for (unsigned i = 0; i < sz; ++i) { + relation_base const& r1 = (m_kind1[i] == T_FULL)?(*m_full[m_offset1[i]]):access(m_offset1[i], _r1); + relation_base const& r2 = (m_kind2[i] == T_FULL)?(*m_full[m_offset2[i]]):access(m_offset2[i], _r2); + relations.push_back((*m_joins[i])(r1, r2)); + } + result = alloc(product_relation, m_plugin, get_result_signature(), sz, relations.c_ptr()); + TRACE("dl",result->display(tout);); + return result; + } + }; + + relation_join_fn * product_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (is_product_relation(r1) && is_product_relation(r2)) { + return alloc(join_fn, *this, get(r1), get(r2), col_cnt, cols1, cols2); + } + if (is_product_relation(r1)) { + return alloc(join_fn, *this, get(r1), r2, col_cnt, cols1, cols2); + } + if (is_product_relation(r2)) { + return alloc(join_fn, *this, r1, get(r2), col_cnt, cols1, cols2); + } + if (r1.get_kind() != r2.get_kind()) { + return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2); + } + return 0; + } + + + class product_relation_plugin::transform_fn : public relation_transformer_fn { + relation_signature m_sig; + ptr_vector m_transforms; + public: + transform_fn(relation_signature s, unsigned num_trans, relation_transformer_fn** trans): + m_sig(s), + m_transforms(num_trans, trans) {} + + ~transform_fn() { dealloc_ptr_vector_content(m_transforms); } + + virtual relation_base * operator()(const relation_base & _r) { + product_relation const& r = get(_r); + product_relation_plugin& p = r.get_plugin(); + SASSERT(m_transforms.size() == r.size()); + ptr_vector relations; + for (unsigned i = 0; i < r.size(); ++i) { + relations.push_back((*m_transforms[i])(r[i])); + } + relation_base* result = alloc(product_relation, p, m_sig, relations.size(), relations.c_ptr()); + TRACE("dl", _r.display(tout); result->display(tout);); + return result; + } + }; + + relation_transformer_fn * product_relation_plugin::mk_project_fn(const relation_base & _r, + unsigned col_cnt, const unsigned * removed_cols) { + if (is_product_relation(_r)) { + product_relation const& r = get(_r); + ptr_vector projs; + for (unsigned i = 0; i < r.size(); ++i) { + projs.push_back(get_manager().mk_project_fn(r[i], col_cnt, removed_cols)); + } + relation_signature s; + relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, s); + return alloc(transform_fn, s, projs.size(), projs.c_ptr()); + } + return 0; + } + + relation_transformer_fn * product_relation_plugin::mk_rename_fn(const relation_base & _r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if(is_product_relation(_r)) { + ptr_vector trans; + product_relation const& r = get(_r); + for (unsigned i = 0; i < r.size(); ++i) { + trans.push_back(get_manager().mk_rename_fn(r[i], cycle_len, permutation_cycle)); + } + relation_signature s; + relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, s); + return alloc(transform_fn, s, trans.size(), trans.c_ptr()); + } + return 0; + } + + class product_relation_plugin::aligned_union_fn : public relation_union_fn { + relation_manager & m_rmgr; + bool m_is_widen; + + //m_union[i][j] is union between i-th and j-th relation. + //It can be zero which means that particular union should be skipped. + vector > m_unions; + + void mk_union_fn(unsigned i, unsigned j, relation_base const& r1, relation_base const& r2, + const relation_base* delta) { + relation_manager& rmgr = r1.get_manager(); + relation_union_fn* u = 0; + if (m_is_widen) { + u = rmgr.mk_widen_fn(r1, r2, delta); + } + else { + u = rmgr.mk_union_fn(r1, r2, delta); + } + m_unions.back().push_back(u); + } + + void init(const relation_vector & tgts, const relation_vector & srcs, const relation_vector * deltas) { + SASSERT(tgts.size()==srcs.size()); + unsigned num = tgts.size(); + for (unsigned i = 0; i < num; ++i) { + relation_base& r1 = *tgts[i]; + relation_base* delta = deltas ? (*deltas)[i] : 0; + m_unions.push_back(ptr_vector()); + for (unsigned j = 0; j < num; ++j) { + relation_base& r2 = *srcs[j]; + mk_union_fn(i, j, r1, r2, delta); + } + } + } + + bool can_do_inner_union(unsigned tgt_idx, unsigned src_idx) { + return m_unions[tgt_idx][src_idx]!=0; + } + + void do_inner_union(unsigned tgt_idx, unsigned src_idx, relation_base& tgt, + relation_base& src, relation_base * delta) { + SASSERT(m_unions[tgt_idx][src_idx]); + (*m_unions[tgt_idx][src_idx])(tgt, src, delta); + } + + /** + If tgt is zero, it is assumed to be a full relation. + */ + void do_destructive_intersection(scoped_rel& tgt, scoped_rel& src) { + if(!src) { + return; + } + if(!tgt) { + tgt=src.release(); + return; + } + do_intersection(*tgt, *src); + src = 0; + } + + void do_intersection(relation_base& tgt, relation_base& src) { + scoped_ptr intersect_fun = + m_rmgr.mk_filter_by_intersection_fn(tgt, src); + if(!intersect_fun) { + warning_msg("intersection does not exist"); + return; + } + (*intersect_fun)(tgt, src); + } + void do_delta_union(unsigned rel_idx, relation_base& tgt, relation_base& src) { + scoped_ptr union_fun = m_rmgr.mk_union_fn(tgt, src); + SASSERT(union_fun); + (*union_fun)(tgt, src); + } + public: + aligned_union_fn(product_relation const& tgt, product_relation const& src, product_relation const* delta, + bool is_widen) : + m_rmgr(tgt.get_manager()), + m_is_widen(is_widen) { + SASSERT(vectors_equal(tgt.m_spec, src.m_spec)); + SASSERT(!delta || vectors_equal(tgt.m_spec, delta->m_spec)); + init(tgt.m_relations, src.m_relations, delta ? &delta->m_relations : 0); + } + + ~aligned_union_fn() { + unsigned sz = m_unions.size(); + for(unsigned i=0; i side_results; + ptr_vector side_deltas; + + for (unsigned i = 0; i < num; ++i) { + relation_base& itgt = tgt[i]; + relation_base* idelta = delta ? &(*delta)[i] : 0; + + scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : 0; + scoped_rel side_result; + scoped_rel side_delta; + + //compute the side unions with which we will intersect the result of the basic one + for (unsigned j = 0; j < num; ++j) { + if (i == j) { + continue; //this is the basic union which we will perform later + } + if (can_do_inner_union(i, j)) { + TRACE("dl", itgt.display(tout << "tgt:\n"); src[j].display(tout << "src:\n");); + // union[i][j] + scoped_rel one_side_union = itgt.clone(); + scoped_rel one_side_delta = fresh_delta ? fresh_delta->clone() : 0; + TRACE("dl", one_side_union->display(tout << "union 1:\n"); src[j].display(tout);); + do_inner_union(i, j, *one_side_union, src[j], one_side_delta.get()); + TRACE("dl", one_side_union->display(tout << "union:\n");); + do_destructive_intersection(side_result, one_side_union); + TRACE("dl", + side_result->display(tout << "inner-union: " << i << " " << j << "\n"); + itgt.display(tout << "tgt:\n");); + if (one_side_delta) { + do_destructive_intersection(side_delta, one_side_delta); + } + + // union[j][i] + one_side_union = src[i].clone(); + one_side_delta = fresh_delta ? fresh_delta->clone() : 0; + TRACE("dl", one_side_union->display(tout << "union 2:\n"); tgt[j].display(tout);); + do_inner_union(i, j, *one_side_union, tgt[j], one_side_delta.get()); + TRACE("dl", one_side_union->display(tout << "union:\n");); + do_destructive_intersection(side_result, one_side_union); + TRACE("dl", + side_result->display(tout << "inner-union: " << i << " " << j << "\n"); + itgt.display(tout << "tgt:\n");); + if (one_side_delta) { + do_destructive_intersection(side_delta, one_side_delta); + } + } + } + side_results.push_back(side_result.release()); + side_deltas.push_back(side_delta.release()); + } + for (unsigned i = 0; i < num; ++i) { + relation_base& itgt = tgt[i]; + relation_base* idelta = delta ? &(*delta)[i] : 0; + scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : 0; + scoped_rel side_result(side_results[i]); + scoped_rel side_delta(side_deltas[i]); + + // perform the basic union + // assume a relation can always perform union with the relation of the same type + VERIFY(can_do_inner_union(i,i)); + do_inner_union(i, i, itgt, src[i], fresh_delta.get()); + + if (side_result) { + do_intersection(itgt, *side_result); + TRACE("dl", side_result->display(tout << "inner-union-end: " << i << "\n");); + } + if (fresh_delta) { + do_destructive_intersection(fresh_delta,side_delta); + SASSERT(idelta); + do_delta_union(i, *idelta, *fresh_delta); + } + } + if (num == 0) { + //we need to handle product relation of no relations separately + if (!src.m_default_empty && tgt.m_default_empty) { + tgt.m_default_empty = false; + if (delta) { + delta->m_default_empty = false; + } + } + } + TRACE("dl", _tgt.display(tout << "dst':\n"); + if (_delta) _delta->display(tout << "delta:\n"); ;); + } + }; + + class product_relation_plugin::unaligned_union_fn : public relation_union_fn { + bool m_is_widen; + rel_spec m_common_spec; + scoped_ptr m_aligned_union_fun; + public: + unaligned_union_fn(product_relation const& tgt, product_relation const& src, + product_relation const* delta, bool is_widen) : m_is_widen(is_widen) { + ptr_vector rels; + rels.push_back(&tgt); + rels.push_back(&src); + if(delta) { + rels.push_back(delta); + } + get_common_spec(rels, m_common_spec); + } + + + virtual void operator()(relation_base& _tgt, const relation_base& _src, relation_base* _delta) { + TRACE("dl", _tgt.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + product_relation& tgt = get(_tgt); + product_relation const& src0 = get(_src); + product_relation* delta = _delta ? get(_delta) : 0; + + tgt.convert_spec(m_common_spec); + if(delta) { + delta->convert_spec(m_common_spec); + } + scoped_rel src_scoped; + if(src0.get_kind()!=tgt.get_kind()) { + src_scoped = src0.clone(); + src_scoped->convert_spec(m_common_spec); + } + product_relation const& src = src_scoped ? *src_scoped : src0; + + if(!m_aligned_union_fun) { + m_aligned_union_fun = alloc(aligned_union_fn, tgt, src, delta, m_is_widen); + SASSERT(m_aligned_union_fun); + } + (*m_aligned_union_fun)(tgt, src, delta); + TRACE("dl", _tgt.display(tout << "dst':\n"); + if (_delta) _delta->display(tout << "delta:\n");); + } + }; + + class product_relation_plugin::single_non_transparent_src_union_fn : public relation_union_fn { + unsigned m_single_rel_idx; + scoped_ptr m_inner_union_fun; + public: + single_non_transparent_src_union_fn(unsigned single_rel_idx, relation_union_fn* inner_union_fun) + : m_single_rel_idx(single_rel_idx), + m_inner_union_fun(inner_union_fun) {} + + virtual void operator()(relation_base& tgt, const relation_base& _src, relation_base* delta) { + product_relation const& src = get(_src); + (*m_inner_union_fun)(tgt, src[m_single_rel_idx], delta); + } + }; + + relation_union_fn * product_relation_plugin::mk_union_w_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta, bool is_widen) { + if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { + if(are_aligned(get(tgt), get(src)) && (!delta || are_aligned(get(tgt), *get(delta)))) { + return alloc(aligned_union_fn, get(tgt), get(src), get(delta), is_widen); + } + return alloc(unaligned_union_fn, get(tgt), get(src), get(delta), is_widen); + } + if(check_kind(src)) { + const product_relation & p_src = get(src); + unsigned single_idx; + if(p_src.try_get_single_non_transparent(single_idx)) { + relation_union_fn * inner; + if(is_widen) { + inner = get_manager().mk_widen_fn(tgt, p_src[single_idx], delta); + } + else { + inner = get_manager().mk_union_fn(tgt, p_src[single_idx], delta); + } + if(inner) { + return alloc(single_non_transparent_src_union_fn, single_idx, inner); + } + } + } + return 0; + } + + relation_union_fn * product_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + return mk_union_w_fn(tgt, src, delta, false); + } + + relation_union_fn * product_relation_plugin::mk_widen_fn( + const relation_base & tgt, const relation_base & src, const relation_base * delta) { + return mk_union_w_fn(tgt, src, delta, true); + } + + class product_relation_plugin::mutator_fn : public relation_mutator_fn { + ptr_vector m_mutators; + public: + mutator_fn(unsigned sz, relation_mutator_fn** muts): + m_mutators(sz, muts) {} + + ~mutator_fn() { dealloc_ptr_vector_content(m_mutators); } + + virtual void operator()(relation_base & _r) { + TRACE("dl", _r.display(tout);); + product_relation& r = get(_r); + SASSERT(m_mutators.size() == r.size()); + for (unsigned i = 0; i < r.size(); ++i) { + relation_mutator_fn* m = m_mutators[i]; + if (m) { + (*m)(r[i]); + } + } + TRACE("dl", _r.display(tout);); + } + }; + + + relation_mutator_fn * product_relation_plugin::mk_filter_identical_fn( + const relation_base & _t, unsigned col_cnt, const unsigned * identical_cols) { + + if(is_product_relation(_t)) { + bool found = false; + product_relation const& r = get(_t); + ptr_vector mutators; + for (unsigned i = 0; i < r.size(); ++i) { + relation_mutator_fn* m = get_manager().mk_filter_identical_fn(r[i], col_cnt, identical_cols); + mutators.push_back(m); + if (m) found = true; + } + if (found) { + return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); + } + } + return 0; + } + + relation_mutator_fn * product_relation_plugin::mk_filter_equal_fn(const relation_base & _t, + const relation_element & value, unsigned col) { + if(is_product_relation(_t)) { + product_relation const& r = get(_t); + ptr_vector mutators; + bool found = false; + for (unsigned i = 0; i < r.size(); ++i) { + relation_mutator_fn* m = get_manager().mk_filter_equal_fn(r[i], value, col); + mutators.push_back(m); + if (m) found = true; + } + if (found) { + return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); + } + } + return 0; + } + + class product_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + ptr_vector m_mutators; + svector > m_attach; + public: + + filter_interpreted_fn(product_relation const& r, app* cond) { + for (unsigned i = 0; i < r.size(); ++i) { + m_mutators.push_back(r.get_manager().mk_filter_interpreted_fn(r[i], cond)); + } + for (unsigned i = 0; i < r.size(); ++i) { + relation_mutator_fn& m1 = *(m_mutators[i]); + for (unsigned j = i + 1; j < r.size(); ++j) { + relation_mutator_fn& m2 = *(m_mutators[j]); + if (m1.supports_attachment(r[j])) { + m_attach.push_back(std::make_pair(i,j)); + } + if (m2.supports_attachment(r[i])) { + m_attach.push_back(std::make_pair(j,i)); + } + } + } + } + + ~filter_interpreted_fn() { dealloc_ptr_vector_content(m_mutators); } + + void operator()(relation_base& _r) { + TRACE("dl", _r.display(tout);); + product_relation const& r = get(_r); + for (unsigned i = 0; i < m_attach.size(); ++i) { + m_mutators[m_attach[i].first]->attach(r[m_attach[i].second]); + } + for (unsigned i = 0; i < m_mutators.size(); ++i) { + (*m_mutators[i])(r[i]); + } + TRACE("dl", _r.display(tout);); + } + }; + + relation_mutator_fn * product_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + return alloc(filter_interpreted_fn, get(t), condition); + } + + + // ----------------------------------- + // + // product_relation + // + // ----------------------------------- + + product_relation::product_relation(product_relation_plugin& p, relation_signature const& s): + relation_base(p, s), + m_default_empty(true) { + ensure_correct_kind(); + } + + product_relation::product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations) : + relation_base(p, s), + m_default_empty(true) { + for (unsigned i = 0; i < num_relations; ++i) { + SASSERT(relations[i]->get_signature()==s); + m_relations.push_back(relations[i]); + } + ensure_correct_kind(); + } + + product_relation::~product_relation() { + unsigned num_relations = m_relations.size(); + for (unsigned i = 0; i < num_relations; ++i) { + m_relations[i]->deallocate(); + } + } + + product_relation_plugin& product_relation::get_plugin() const { + return dynamic_cast(relation_base::get_plugin()); + } + + void product_relation::ensure_correct_kind() { + unsigned rel_cnt = m_relations.size(); + //the rel_cnt==0 part makes us to update the kind also when the relation is newly created + bool spec_changed = rel_cnt!=m_spec.size() || rel_cnt==0; + if(spec_changed) { + m_spec.resize(rel_cnt); + } + for(unsigned i=0;iget_kind(); + if(spec_changed || m_spec[i]!=rkind) { + spec_changed = true; + m_spec[i]=rkind; + } + } + if(spec_changed) { + family_id new_kind = get_plugin().get_relation_kind(*this); + set_kind(new_kind); + } + } + + void product_relation::convert_spec(const rel_spec & spec) { + + func_decl* p = 0; + const relation_signature & sig = get_signature(); + family_id new_kind = get_plugin().get_relation_kind(sig, spec); + if(new_kind==get_kind()) { + return; + } + + unsigned old_sz = size(); + unsigned new_sz = spec.size(); + unsigned old_remain = old_sz; + relation_vector new_rels; + + //the loop is quadratic with the number of relations, maybe we want to fix it + for(unsigned i=0; iget_kind()==ikind) { + irel = m_relations[j]; + m_relations[j] = 0; + old_remain--; + break; + } + } + if(!irel) { + if(old_sz==0 && m_default_empty) { + //The relation didn't contain any inner relations but it was empty, + //so we make the newly added relations empty as well. + irel = get_manager().mk_empty_relation(sig, new_kind); + } + else { + irel = get_manager().mk_full_relation(sig, p, new_kind); + } + } + new_rels.push_back(irel); + } + SASSERT(old_remain==0); //the new specification must be a superset of the old one + m_relations = new_rels; + + set_kind(new_kind); + DEBUG_CODE( + ensure_correct_kind(); + SASSERT(get_kind()==new_kind); + ); + } + + bool product_relation::try_get_single_non_transparent(unsigned & idx) const { + unsigned sz = size(); + bool found = false; + unsigned candidate; + for(unsigned i=0; i relations; + for (unsigned i = 0; i < size(); ++i) { + relations.push_back((*this)[i].clone()); + } + product_relation_plugin& p = get_plugin(); + return alloc(product_relation, p, get_signature(), relations.size(), relations.c_ptr()); + } + + product_relation * product_relation::complement(func_decl*) const { + if(m_relations.empty()) { + product_relation * res = clone(); + res->m_default_empty = !m_default_empty; + return res; + } + UNREACHABLE(); + return 0; + } + + bool product_relation::empty() const { + if(m_relations.empty()) { + return m_default_empty; + } + for (unsigned i = 0; i < m_relations.size(); ++i) { + if (m_relations[i]->empty()) { + return true; + } + } + return false; + } + + void product_relation::to_formula(expr_ref& fml) const { + ast_manager& m = fml.get_manager(); + expr_ref_vector conjs(m); + expr_ref tmp(m); + for (unsigned i = 0; i < m_relations.size(); ++i) { + m_relations[i]->to_formula(tmp); + conjs.push_back(tmp); + } + bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), fml); + } + + void product_relation::display(std::ostream & out) const { + out<<"Product of the following relations:\n"; + for (unsigned i = 0; i < m_relations.size(); ++i) { + m_relations[i]->display(out); + } + } + +}; + + + diff --git a/src/muz/rel/dl_product_relation.h b/src/muz/rel/dl_product_relation.h new file mode 100644 index 000000000..0633ddbf1 --- /dev/null +++ b/src/muz/rel/dl_product_relation.h @@ -0,0 +1,191 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_product_relation.h + +Abstract: + + A Relation relation combinator. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-4-11 + +Revision History: + +--*/ +#ifndef _DL_PRODUCT_RELATION_H_ +#define _DL_PRODUCT_RELATION_H_ + + +#include "dl_context.h" +#include "dl_relation_manager.h" + +namespace datalog { + + class product_relation; + + class product_relation_plugin : public relation_plugin { + friend class product_relation; + public: + typedef svector rel_spec; + private: + class join_fn; + class transform_fn; + class mutator_fn; + class aligned_union_fn; + class unaligned_union_fn; + class single_non_transparent_src_union_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + struct fid_hash { + typedef family_id data; + unsigned operator()(data x) const { return static_cast(x); } + }; + + rel_spec_store > m_spec_store; + + family_id get_relation_kind(const product_relation & r); + + bool is_product_relation(relation_base * r) { return r->get_plugin().is_product_relation(); } + + public: + static product_relation_plugin& get_plugin(relation_manager & rmgr); + + product_relation_plugin(relation_manager& m); + + virtual void initialize(family_id fid); + + virtual bool can_handle_signature(const relation_signature & s); + virtual bool can_handle_signature(const relation_signature & s, family_id kind); + + static symbol get_name() { return symbol("product_relation"); } + + family_id get_relation_kind(const relation_signature & sig, const rel_spec & spec); + + virtual relation_base * mk_empty(const relation_signature & s); + virtual relation_base * mk_empty(const relation_signature & s, family_id kind); + + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + virtual relation_base * mk_full(func_decl* p, const relation_signature & s, family_id kind); + + protected: + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + + static bool is_product_relation(relation_base const& r); + + private: + static product_relation& get(relation_base& r); + static product_relation const & get(relation_base const& r); + static product_relation* get(relation_base* r); + static product_relation const* get(relation_base const* r); + + relation_union_fn * mk_union_w_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta, bool is_widen); + + bool are_aligned(const product_relation& r1, const product_relation& r2); + static void get_common_spec(const ptr_vector & rels, rel_spec & res); + }; + + + class product_relation : public relation_base { + friend class product_relation_plugin; + + friend class product_relation_plugin::join_fn; + friend class product_relation_plugin::transform_fn; + friend class product_relation_plugin::mutator_fn; + friend class product_relation_plugin::aligned_union_fn; + friend class product_relation_plugin::unaligned_union_fn; + friend class product_relation_plugin::single_non_transparent_src_union_fn; + friend class product_relation_plugin::filter_equal_fn; + friend class product_relation_plugin::filter_identical_fn; + friend class product_relation_plugin::filter_interpreted_fn; + + + + typedef product_relation_plugin::rel_spec rel_spec; + + /** + If m_relations is empty, value of this determines whether the relation is empty or full. + */ + bool m_default_empty; + + /** + There must not be two relations of the same kind + */ + ptr_vector m_relations; + + /** + Array of kinds of inner relations. + + If two product relations have equal signature and specification, their + m_relations arrays contain corresponding relations at the same indexes. + + The value returned by get_kind() depends uniquely on the specification. + */ + rel_spec m_spec; + + /** + \brief Ensure the kind assigned to this relation reflects the types of inner relations. + */ + void ensure_correct_kind(); + /** + The current specification must be a subset of the new one. + */ + void convert_spec(const rel_spec & spec); + public: + product_relation(product_relation_plugin& p, relation_signature const& s); + product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations); + + ~product_relation(); + + virtual bool empty() const; + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual product_relation * clone() const; + virtual product_relation * complement(func_decl* p) const; + virtual void display(std::ostream & out) const; + virtual void to_formula(expr_ref& fml) const; + product_relation_plugin& get_plugin() const; + + unsigned size() const { return m_relations.size(); } + relation_base& operator[](unsigned i) const { return *m_relations[i]; } + + /** + If all relations except one are sieve_relations with no inner columns, + return true and into \c idx assign index of that relation. Otherwise return + false. + */ + bool try_get_single_non_transparent(unsigned & idx) const; + + virtual bool is_precise() const { + for (unsigned i = 0; i < m_relations.size(); ++i) { + if (!m_relations[i]->is_precise()) { + return false; + } + } + return true; + } + }; + +}; + +#endif + diff --git a/src/muz/rel/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp new file mode 100644 index 000000000..457ef28c0 --- /dev/null +++ b/src/muz/rel/dl_relation_manager.cpp @@ -0,0 +1,1702 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_relation_manager.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-14. + +Revision History: + +--*/ + + +#include +#include"ast_pp.h" +#include"dl_check_table.h" +#include"dl_context.h" +#include"dl_finite_product_relation.h" +#include"dl_product_relation.h" +#include"dl_sieve_relation.h" +#include"dl_table_relation.h" +#include"dl_relation_manager.h" + +namespace datalog { + + relation_manager::~relation_manager() { + reset(); + } + + + void relation_manager::reset_relations() { + relation_map::iterator it=m_relations.begin(); + relation_map::iterator end=m_relations.end(); + for(;it!=end;++it) { + func_decl * pred = it->m_key; + get_context().get_manager().dec_ref(pred); //inc_ref in get_relation + relation_base * r=(*it).m_value; + r->deallocate(); + } + m_relations.reset(); + } + + void relation_manager::reset() { + reset_relations(); + + m_favourite_table_plugin = static_cast(0); + m_favourite_relation_plugin = static_cast(0); + dealloc_ptr_vector_content(m_table_plugins); + m_table_plugins.reset(); + dealloc_ptr_vector_content(m_relation_plugins); + m_relation_plugins.reset(); + m_next_table_fid = 0; + m_next_relation_fid = 0; + } + + dl_decl_util & relation_manager::get_decl_util() const { + return get_context().get_decl_util(); + } + + family_id relation_manager::get_next_relation_fid(relation_plugin & claimer) { + unsigned res = m_next_relation_fid++; + m_kind2plugin.insert(res, &claimer); + return res; + } + + void relation_manager::set_predicate_kind(func_decl * pred, family_id kind) { + SASSERT(!m_relations.contains(pred)); + m_pred_kinds.insert(pred, kind); + } + + family_id relation_manager::get_requested_predicate_kind(func_decl * pred) { + family_id res; + if(m_pred_kinds.find(pred, res)) { + return res; + } + else { + return null_family_id; + } + } + + relation_base & relation_manager::get_relation(func_decl * pred) { + relation_base * res = try_get_relation(pred); + if(!res) { + relation_signature sig; + from_predicate(pred, sig); + family_id rel_kind = get_requested_predicate_kind(pred); + res = mk_empty_relation(sig, rel_kind); + store_relation(pred, res); + } + return *res; + } + + relation_base * relation_manager::try_get_relation(func_decl * pred) const { + relation_base * res = 0; + if(!m_relations.find(pred, res)) { + return 0; + } + SASSERT(res); + return res; + } + + void relation_manager::store_relation(func_decl * pred, relation_base * rel) { + SASSERT(rel); + relation_map::entry * e = m_relations.insert_if_not_there2(pred, 0); + if (e->get_data().m_value) { + e->get_data().m_value->deallocate(); + } + else { + get_context().get_manager().inc_ref(pred); //dec_ref in reset + } + e->get_data().m_value = rel; + } + + void relation_manager::collect_non_empty_predicates(decl_set & res) const { + relation_map::iterator it = m_relations.begin(); + relation_map::iterator end = m_relations.end(); + for(; it!=end; ++it) { + if(!it->m_value->empty()) { + res.insert(it->m_key); + } + } + } + + void relation_manager::restrict_predicates(const decl_set & preds) { + typedef ptr_vector fd_vector; + fd_vector to_remove; + + relation_map::iterator rit = m_relations.begin(); + relation_map::iterator rend = m_relations.end(); + for(; rit!=rend; ++rit) { + func_decl * pred = rit->m_key; + if (!preds.contains(pred)) { + to_remove.insert(pred); + } + } + + fd_vector::iterator pit = to_remove.begin(); + fd_vector::iterator pend = to_remove.end(); + for(; pit!=pend; ++pit) { + func_decl * pred = *pit; + relation_base * rel; + VERIFY( m_relations.find(pred, rel) ); + rel->deallocate(); + m_relations.remove(pred); + get_context().get_manager().dec_ref(pred); + } + + set_intersection(m_saturated_rels, preds); + } + + void relation_manager::register_plugin(table_plugin * plugin) { + plugin->initialize(get_next_table_fid()); + m_table_plugins.push_back(plugin); + + if(plugin->get_name()==get_context().default_table()) { + m_favourite_table_plugin = plugin; + } + + table_relation_plugin * tr_plugin = alloc(table_relation_plugin, *plugin, *this); + register_relation_plugin_impl(tr_plugin); + m_table_relation_plugins.insert(plugin, tr_plugin); + + symbol checker_name = get_context().default_table_checker(); + if(get_context().default_table_checked() && get_table_plugin(checker_name)) { + if( m_favourite_table_plugin && + (plugin==m_favourite_table_plugin || plugin->get_name()==checker_name) ) { + symbol checked_name = get_context().default_table(); + //the plugins we need to create the checking plugin were just added + SASSERT(m_favourite_table_plugin->get_name()==get_context().default_table()); + table_plugin * checking_plugin = alloc(check_table_plugin, *this, checker_name, checked_name); + register_plugin(checking_plugin); + m_favourite_table_plugin = checking_plugin; + } + if(m_favourite_relation_plugin && m_favourite_relation_plugin->from_table()) { + table_relation_plugin * fav_rel_plugin = + static_cast(m_favourite_relation_plugin); + if(&fav_rel_plugin->get_table_plugin()==plugin || plugin->get_name()==checker_name) { + //the plugins we need to create the checking table_relation_plugin were just added + SASSERT(m_favourite_relation_plugin->get_name() == + get_context().default_relation()); + symbol checked_name = fav_rel_plugin->get_table_plugin().get_name(); + table_plugin * checking_plugin = alloc(check_table_plugin, *this, checker_name, checked_name); + register_plugin(checking_plugin); + + table_relation_plugin * checking_tr_plugin = + alloc(table_relation_plugin, *checking_plugin, *this); + register_relation_plugin_impl(checking_tr_plugin); + m_table_relation_plugins.insert(checking_plugin, checking_tr_plugin); + m_favourite_relation_plugin = checking_tr_plugin; + } + } + } + + } + + void relation_manager::register_relation_plugin_impl(relation_plugin * plugin) { + m_relation_plugins.push_back(plugin); + plugin->initialize(get_next_relation_fid(*plugin)); + if (plugin->get_name() == get_context().default_relation()) { + m_favourite_relation_plugin = plugin; + } + if(plugin->is_finite_product_relation()) { + finite_product_relation_plugin * fprp = static_cast(plugin); + relation_plugin * inner = &fprp->get_inner_plugin(); + m_finite_product_relation_plugins.insert(inner, fprp); + } + } + + relation_plugin * relation_manager::try_get_appropriate_plugin(const relation_signature & s) { + if(m_favourite_relation_plugin && m_favourite_relation_plugin->can_handle_signature(s)) { + return m_favourite_relation_plugin; + } + relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); + relation_plugin_vector::iterator rpend = m_relation_plugins.end(); + for(; rpit!=rpend; ++rpit) { + if((*rpit)->can_handle_signature(s)) { + return *rpit; + } + } + return 0; + } + + relation_plugin & relation_manager::get_appropriate_plugin(const relation_signature & s) { + relation_plugin * res = try_get_appropriate_plugin(s); + if (!res) { + throw default_exception("no suitable plugin found for given relation signature"); + } + return *res; + } + + table_plugin * relation_manager::try_get_appropriate_plugin(const table_signature & t) { + if (m_favourite_table_plugin && m_favourite_table_plugin->can_handle_signature(t)) { + return m_favourite_table_plugin; + } + table_plugin_vector::iterator tpit = m_table_plugins.begin(); + table_plugin_vector::iterator tpend = m_table_plugins.end(); + for(; tpit!=tpend; ++tpit) { + if((*tpit)->can_handle_signature(t)) { + return *tpit; + } + } + return 0; + } + + table_plugin & relation_manager::get_appropriate_plugin(const table_signature & t) { + table_plugin * res = try_get_appropriate_plugin(t); + if(!res) { + throw default_exception("no suitable plugin found for given table signature"); + } + return *res; + } + + relation_plugin * relation_manager::get_relation_plugin(symbol const& s) { + relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); + relation_plugin_vector::iterator rpend = m_relation_plugins.end(); + for(; rpit!=rpend; ++rpit) { + if((*rpit)->get_name()==s) { + return *rpit; + } + } + return 0; + } + + relation_plugin & relation_manager::get_relation_plugin(family_id kind) { + SASSERT(kind>=0); + SASSERT(kindget_name()==k) { + return *tpit; + } + } + return 0; + } + + table_relation_plugin & relation_manager::get_table_relation_plugin(table_plugin & tp) { + table_relation_plugin * res; + VERIFY( m_table_relation_plugins.find(&tp, res) ); + return *res; + } + + bool relation_manager::try_get_finite_product_relation_plugin(const relation_plugin & inner, + finite_product_relation_plugin * & res) { + return m_finite_product_relation_plugins.find(&inner, res); + } + + table_base * relation_manager::mk_empty_table(const table_signature & s) { + return get_appropriate_plugin(s).mk_empty(s); + } + + + bool relation_manager::is_non_explanation(relation_signature const& s) const { + dl_decl_util & decl_util = get_context().get_decl_util(); + unsigned n = s.size(); + for(unsigned i = 0; i < n; i++) { + if(decl_util.is_rule_sort(s[i])) { + return false; + } + } + return true; + } + + relation_base * relation_manager::mk_empty_relation(const relation_signature & s, func_decl* pred) { + return mk_empty_relation(s, get_requested_predicate_kind(pred)); + } + + relation_base * relation_manager::mk_empty_relation(const relation_signature & s, family_id kind) { + if (kind != null_family_id) { + relation_plugin & plugin = get_relation_plugin(kind); + if (plugin.can_handle_signature(s, kind)) + return plugin.mk_empty(s, kind); + } + relation_base * res; + relation_plugin* p = m_favourite_relation_plugin; + + if (p && p->can_handle_signature(s)) { + return p->mk_empty(s); + } + + if (mk_empty_table_relation(s, res)) { + return res; + } + + for (unsigned i = 0; i < m_relation_plugins.size(); ++i) { + p = m_relation_plugins[i]; + if (p->can_handle_signature(s)) { + return p->mk_empty(s); + } + } + + //If there is no plugin to handle the signature, we just create an empty product relation and + //stuff will be added to it by later operations. + return product_relation_plugin::get_plugin(*this).mk_empty(s); + } + + + relation_base * relation_manager::mk_table_relation(const relation_signature & s, table_base * table) { + SASSERT(s.size()==table->get_signature().size()); + return get_table_relation_plugin(table->get_plugin()).mk_from_table(s, table); + } + + bool relation_manager::mk_empty_table_relation(const relation_signature & s, relation_base * & result) { + table_signature tsig; + if(!relation_signature_to_table(s, tsig)) { + return false; + } + table_base * table = mk_empty_table(tsig); + result = mk_table_relation(s, table); + return true; + } + + + relation_base * relation_manager::mk_full_relation(const relation_signature & s, func_decl* p, family_id kind) { + if (kind != null_family_id) { + relation_plugin & plugin = get_relation_plugin(kind); + if (plugin.can_handle_signature(s, kind)) { + return plugin.mk_full(p, s, kind); + } + } + return get_appropriate_plugin(s).mk_full(p, s, null_family_id); + } + + relation_base * relation_manager::mk_full_relation(const relation_signature & s, func_decl* pred) { + family_id kind = get_requested_predicate_kind(pred); + return mk_full_relation(s, pred, kind); + } + + void relation_manager::relation_to_table(const relation_sort & sort, const relation_element & from, + table_element & to) { + SASSERT(from->get_num_args()==0); + VERIFY(get_context().get_decl_util().is_numeral_ext(from, to)); + } + + void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, + relation_element & to) { + to = get_decl_util().mk_numeral(from, sort); + } + + void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, + relation_element_ref & to) { + relation_element rel_el; + table_to_relation(sort, from, rel_el); + to = rel_el; + } + + void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, + const relation_fact::el_proxy & to) { + relation_element rel_el; + table_to_relation(sort, from, rel_el); + to = rel_el; + } + + bool relation_manager::relation_sort_to_table(const relation_sort & from, table_sort & to) { + return get_context().get_decl_util().try_get_size(from, to); + } + + void relation_manager::from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result) { + result = pred->get_domain(arg_index); + } + + void relation_manager::from_predicate(func_decl * pred, relation_signature & result) { + result.reset(); + unsigned arg_num=pred->get_arity(); + for(unsigned i=0;iset_cancel(f); + } + } + + std::string relation_manager::to_nice_string(const relation_element & el) const { + uint64 val; + std::stringstream stm; + if(get_context().get_decl_util().is_numeral_ext(el, val)) { + stm << val; + } + else { + stm << mk_pp(el, get_context().get_manager()); + } + return stm.str(); + } + + std::string relation_manager::to_nice_string(const relation_sort & s, const relation_element & el) const { + std::stringstream stm; + uint64 val; + if(get_context().get_decl_util().is_numeral_ext(el, val)) { + get_context().print_constant_name(s, val, stm); + } + else { + stm << mk_pp(el, get_context().get_manager()); + } + return stm.str(); + } + + std::string relation_manager::to_nice_string(const relation_sort & s) const { + return std::string(s->get_name().bare_str()); + } + + std::string relation_manager::to_nice_string(const relation_signature & s) const { + std::string res("["); + bool first = true; + relation_signature::const_iterator it = s.begin(); + relation_signature::const_iterator end = s.end(); + for(; it!=end; ++it) { + if(first) { + first = false; + } + else { + res+=','; + } + res+=to_nice_string(*it); + } + res+=']'; + + return res; + } + + void relation_manager::display(std::ostream & out) const { + relation_map::iterator it=m_relations.begin(); + relation_map::iterator end=m_relations.end(); + for(;it!=end;++it) { + out << "Table " << it->m_key->get_name() << "\n"; + it->m_value->display(out); + } + } + + void relation_manager::display_relation_sizes(std::ostream & out) const { + relation_map::iterator it=m_relations.begin(); + relation_map::iterator end=m_relations.end(); + for(;it!=end;++it) { + out << "Relation " << it->m_key->get_name() << " has size " + << it->m_value->get_size_estimate_rows() << "\n"; + } + } + + void relation_manager::display_output_tables(rule_set const& rules, std::ostream & out) const { + const decl_set & output_preds = rules.get_output_predicates(); + decl_set::iterator it=output_preds.begin(); + decl_set::iterator end=output_preds.end(); + for(; it!=end; ++it) { + func_decl * pred = *it; + relation_base * rel = try_get_relation(pred); + if(!rel) { + out << "Tuples in " << pred->get_name() << ": \n"; + continue; + } + rel->display_tuples(*pred, out); + } + } + + + // ----------------------------------- + // + // relation operations + // + // ----------------------------------- + + class relation_manager::empty_signature_relation_join_fn : public relation_join_fn { + public: + virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + TRACE("dl", tout << r1.get_plugin().get_name() << " " << r2.get_plugin().get_name() << "\n";); + if(r1.get_signature().empty()) { + if(r1.empty()) { + return r2.get_manager().mk_empty_relation(r2.get_signature(), r2.get_kind()); + } + else { + return r2.clone(); + } + } + else { + SASSERT(r2.get_signature().empty()); + if(r2.empty()) { + return r1.get_manager().mk_empty_relation(r1.get_signature(), r1.get_kind()); + } + else { + return r1.clone(); + } + } + } + }; + + relation_join_fn * relation_manager::mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation) { + relation_plugin * p1 = &t1.get_plugin(); + relation_plugin * p2 = &t2.get_plugin(); + + relation_join_fn * res = p1->mk_join_fn(t1, t2, col_cnt, cols1, cols2); + if(!res && p1!=p2) { + res = p2->mk_join_fn(t1, t2, col_cnt, cols1, cols2); + } + + if(!res && (t1.get_signature().empty() || t2.get_signature().empty())) { + res = alloc(empty_signature_relation_join_fn); + } + + finite_product_relation_plugin * fprp; + if(!res && p1->from_table() && try_get_finite_product_relation_plugin(*p2, fprp)) { + //we downcast here to relation_plugin so that we don't have to declare + //relation_manager as a friend class of finite_product_relation_plugin + res = static_cast(fprp)->mk_join_fn(t1, t2, col_cnt, cols1, cols2); + } + if(!res && p2->from_table() && try_get_finite_product_relation_plugin(*p1, fprp)) { + res = static_cast(fprp)->mk_join_fn(t1, t2, col_cnt, cols1, cols2); + } + + if(!res && allow_product_relation) { + relation_plugin & product_plugin = product_relation_plugin::get_plugin(*this); + res = product_plugin.mk_join_fn(t1, t2, col_cnt, cols1, cols2); + } + + return res; + } + + relation_transformer_fn * relation_manager::mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + return t.get_plugin().mk_project_fn(t, col_cnt, removed_cols); + } + + class relation_manager::default_relation_filter_interpreted_and_project_fn : public relation_transformer_fn { + scoped_ptr m_filter; + scoped_ptr m_project; + unsigned_vector m_removed_cols; + public: + /** + This constructor should be used only if we know that the projection operation + exists for the result of the join. + */ + default_relation_filter_interpreted_and_project_fn( + relation_mutator_fn* filter, + unsigned removed_col_cnt, + const unsigned * removed_cols) + : m_filter(filter), + m_project(0), + m_removed_cols(removed_col_cnt, removed_cols) {} + + virtual relation_base * operator()(const relation_base & t) { + scoped_rel t1 = t.clone(); + (*m_filter)(*t1); + if( !m_project) { + relation_manager & rmgr = t1->get_plugin().get_manager(); + m_project = rmgr.mk_project_fn(*t1, m_removed_cols.size(), m_removed_cols.c_ptr()); + if (!m_project) { + throw default_exception("projection does not exist"); + } + } + return (*m_project)(*t1); + } + }; + + relation_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn( + const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols) { + + relation_transformer_fn* res = + t.get_plugin().mk_filter_interpreted_and_project_fn( + t, + condition, + removed_col_cnt, + removed_cols); + + if (!res) { + relation_mutator_fn* filter_fn = mk_filter_interpreted_fn(t, condition); + if (filter_fn) { + res = alloc(default_relation_filter_interpreted_and_project_fn, + filter_fn, + removed_col_cnt, + removed_cols); + } + } + return res; + } + + + class relation_manager::default_relation_join_project_fn : public relation_join_fn { + scoped_ptr m_join; + scoped_ptr m_project; + + unsigned_vector m_removed_cols; + public: + /** + This constructor should be used only if we know that the projection operation + exists for the result of the join. + */ + default_relation_join_project_fn(join_fn * join, unsigned removed_col_cnt, + const unsigned * removed_cols) + : m_join(join), m_project(0), m_removed_cols(removed_col_cnt, removed_cols) {} + + virtual relation_base * operator()(const relation_base & t1, const relation_base & t2) { + scoped_rel aux = (*m_join)(t1, t2); + if(!m_project) { + relation_manager & rmgr = aux->get_plugin().get_manager(); + m_project = rmgr.mk_project_fn(*aux, m_removed_cols.size(), m_removed_cols.c_ptr()); + if(!m_project) { + throw default_exception("projection does not exist"); + } + } + relation_base * res = (*m_project)(*aux); + return res; + } + }; + + + relation_join_fn * relation_manager::mk_join_project_fn(const relation_base & t1, const relation_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join) { + relation_join_fn * res = t1.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + if(!res && &t1.get_plugin()!=&t2.get_plugin()) { + res = t2.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, + removed_cols); + } + if(!res) { + relation_join_fn * join = mk_join_fn(t1, t2, joined_col_cnt, cols1, cols2, allow_product_relation_join); + if(join) { + res = alloc(default_relation_join_project_fn, join, removed_col_cnt, removed_cols); + } + } + return res; + + } + + relation_transformer_fn * relation_manager::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { + return t.get_plugin().mk_rename_fn(t, permutation_cycle_len, permutation_cycle); + } + + relation_transformer_fn * relation_manager::mk_permutation_rename_fn(const relation_base & t, + const unsigned * permutation) { + relation_transformer_fn * res = t.get_plugin().mk_permutation_rename_fn(t, permutation); + if(!res) { + res = alloc(default_relation_permutation_rename_fn, t, permutation); + } + return res; + } + + + relation_union_fn * relation_manager::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + relation_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); + if(!res && &tgt.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_union_fn(tgt, src, delta); + } + if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { + res = delta->get_plugin().mk_union_fn(tgt, src, delta); + } + return res; + } + + relation_union_fn * relation_manager::mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + relation_union_fn * res = tgt.get_plugin().mk_widen_fn(tgt, src, delta); + if(!res && &tgt.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_widen_fn(tgt, src, delta); + } + if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { + res = delta->get_plugin().mk_widen_fn(tgt, src, delta); + } + if(!res) { + res = mk_union_fn(tgt, src, delta); + } + return res; + } + + relation_mutator_fn * relation_manager::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) { + return t.get_plugin().mk_filter_identical_fn(t, col_cnt, identical_cols); + } + + relation_mutator_fn * relation_manager::mk_filter_equal_fn(const relation_base & t, + const relation_element & value, unsigned col) { + + return t.get_plugin().mk_filter_equal_fn(t, value, col); + } + + relation_mutator_fn * relation_manager::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + return t.get_plugin().mk_filter_interpreted_fn(t, condition); + } + + class relation_manager::default_relation_select_equal_and_project_fn : public relation_transformer_fn { + scoped_ptr m_filter; + scoped_ptr m_project; + public: + default_relation_select_equal_and_project_fn(relation_mutator_fn * filter, relation_transformer_fn * project) + : m_filter(filter), m_project(project) {} + + virtual relation_base * operator()(const relation_base & t1) { + TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); + scoped_rel aux = t1.clone(); + (*m_filter)(*aux); + relation_base * res = (*m_project)(*aux); + return res; + } + }; + + relation_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const relation_base & t, + const relation_element & value, unsigned col) { + relation_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); + if(!res) { + relation_mutator_fn * selector = mk_filter_equal_fn(t, value, col); + if(selector) { + relation_transformer_fn * projector = mk_project_fn(t, 1, &col); + if(projector) { + res = alloc(default_relation_select_equal_and_project_fn, selector, projector); + } + else { + dealloc(selector); + } + } + } + return res; + } + + + class relation_manager::default_relation_intersection_filter_fn : public relation_intersection_filter_fn { + scoped_ptr m_join_fun; + scoped_ptr m_union_fun; + public: + + default_relation_intersection_filter_fn(relation_join_fn * join_fun, relation_union_fn * union_fun) + : m_join_fun(join_fun), m_union_fun(union_fun) {} + + virtual void operator()(relation_base & tgt, const relation_base & intersected_obj) { + scoped_rel filtered_rel = (*m_join_fun)(tgt, intersected_obj); + TRACE("dl", + tgt.display(tout << "tgt:\n"); + intersected_obj.display(tout << "intersected:\n"); + filtered_rel->display(tout << "filtered:\n"); + ); + if(!m_union_fun) { + SASSERT(tgt.can_swap(*filtered_rel)); + tgt.swap(*filtered_rel); + } + tgt.reset(); + TRACE("dl", tgt.display(tout << "target reset:\n"); ); + (*m_union_fun)(tgt, *filtered_rel); + TRACE("dl", tgt.display(tout << "intersected target:\n"); ); + } + + }; + + relation_intersection_filter_fn * relation_manager::try_mk_default_filter_by_intersection_fn( + const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, + const unsigned * tgt_cols, const unsigned * src_cols) { + TRACE("dl_verbose", tout << tgt.get_plugin().get_name() << "\n";); + unsigned_vector join_removed_cols; + add_sequence(tgt.get_signature().size(), src.get_signature().size(), join_removed_cols); + scoped_rel join_fun = mk_join_project_fn(tgt, src, joined_col_cnt, tgt_cols, src_cols, + join_removed_cols.size(), join_removed_cols.c_ptr(), false); + if(!join_fun) { + return 0; + } + //we perform the join operation here to see what the result is + scoped_rel join_res = (*join_fun)(tgt, src); + if(tgt.can_swap(*join_res)) { + return alloc(default_relation_intersection_filter_fn, join_fun.release(), 0); + } + if(join_res->get_plugin().is_product_relation()) { + //we cannot have the product relation here, since it uses the intersection operation + //for unions and therefore we would get into an infinite recursion + return 0; + } + scoped_rel union_fun = mk_union_fn(tgt, *join_res); + if(!union_fun) { + return 0; + } + return alloc(default_relation_intersection_filter_fn, join_fun.release(), union_fun.release()); + } + + + relation_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const relation_base & t, + const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { + TRACE("dl_verbose", tout << t.get_plugin().get_name() << "\n";); + relation_intersection_filter_fn * res = t.get_plugin().mk_filter_by_intersection_fn(t, src, joined_col_cnt, + t_cols, src_cols); + if(!res && &t.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); + } + if(!res) { + res = try_mk_default_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); + } + return res; + } + + relation_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const relation_base & tgt, + const relation_base & src) { + TRACE("dl_verbose", tout << tgt.get_plugin().get_name() << "\n";); + SASSERT(tgt.get_signature()==src.get_signature()); + unsigned sz = tgt.get_signature().size(); + unsigned_vector cols; + add_sequence(0, sz, cols); + return mk_filter_by_intersection_fn(tgt, src, cols, cols); + } + + + relation_intersection_filter_fn * relation_manager::mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + TRACE("dl", tout << t.get_plugin().get_name() << "\n";); + relation_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, + t_cols, negated_cols); + if(!res && &t.get_plugin()!=&negated_obj.get_plugin()) { + res = negated_obj.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, + negated_cols); + } + return res; + } + + + + + + // ----------------------------------- + // + // table operations + // + // ----------------------------------- + + class relation_manager::default_table_join_fn : public convenient_table_join_fn { + unsigned m_col_cnt; + public: + default_table_join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), m_col_cnt(col_cnt) {} + + virtual table_base * operator()(const table_base & t1, const table_base & t2) { + table_plugin * plugin = &t1.get_plugin(); + + const table_signature & res_sign = get_result_signature(); + if (!plugin->can_handle_signature(res_sign)) { + plugin = &t2.get_plugin(); + if (!plugin->can_handle_signature(res_sign)) { + plugin = &t1.get_manager().get_appropriate_plugin(res_sign); + } + } + SASSERT(plugin->can_handle_signature(res_sign)); + table_base * res = plugin->mk_empty(res_sign); + + unsigned t1cols = t1.get_signature().size(); + unsigned t2cols = t2.get_signature().size(); + unsigned t1first_func = t1.get_signature().first_functional(); + unsigned t2first_func = t2.get_signature().first_functional(); + + table_base::iterator els1it = t1.begin(); + table_base::iterator els1end = t1.end(); + table_base::iterator els2end = t2.end(); + + table_fact acc; + + for(; els1it!=els1end; ++els1it) { + const table_base::row_interface & row1 = *els1it; + + table_base::iterator els2it = t2.begin(); + for(; els2it!=els2end; ++els2it) { + const table_base::row_interface & row2 = *els2it; + + bool match=true; + for(unsigned i=0; iadd_fact(acc); + } + } + return res; + } + }; + + table_join_fn * relation_manager::mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + table_join_fn * res = t1.get_plugin().mk_join_fn(t1, t2, col_cnt, cols1, cols2); + if(!res && &t1.get_plugin()!=&t2.get_plugin()) { + res = t2.get_plugin().mk_join_fn(t1, t2, col_cnt, cols1, cols2); + } + if(!res) { + table_signature sig; + table_signature::from_join(t1.get_signature(), t2.get_signature(), + col_cnt, cols1, cols2, sig); + res = alloc(default_table_join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); + } + return res; + } + + + class relation_manager::auxiliary_table_transformer_fn { + table_fact m_row; + public: + virtual ~auxiliary_table_transformer_fn() {} + virtual const table_signature & get_result_signature() const = 0; + virtual void modify_fact(table_fact & f) const = 0; + + table_base * operator()(const table_base & t) { + table_plugin & plugin = t.get_plugin(); + const table_signature & res_sign = get_result_signature(); + SASSERT(plugin.can_handle_signature(res_sign)); + table_base * res = plugin.mk_empty(res_sign); + + table_base::iterator it = t.begin(); + table_base::iterator end = t.end(); + + for(; it!=end; ++it) { + it->get_fact(m_row); + modify_fact(m_row); + res->add_fact(m_row); + } + return res; + } + }; + + class relation_manager::default_table_project_fn + : public convenient_table_project_fn, auxiliary_table_transformer_fn { + public: + default_table_project_fn(const table_signature & orig_sig, unsigned removed_col_cnt, + const unsigned * removed_cols) + : convenient_table_project_fn(orig_sig, removed_col_cnt, removed_cols) { + SASSERT(removed_col_cnt>0); + } + + virtual const table_signature & get_result_signature() const { + return convenient_table_project_fn::get_result_signature(); + } + + virtual void modify_fact(table_fact & f) const { + project_out_vector_columns(f, m_removed_cols); + } + + virtual table_base * operator()(const table_base & t) { + return auxiliary_table_transformer_fn::operator()(t); + } + }; + + class relation_manager::null_signature_table_project_fn : public table_transformer_fn { + const table_signature m_empty_sig; + public: + null_signature_table_project_fn() : m_empty_sig() {} + virtual table_base * operator()(const table_base & t) { + relation_manager & m = t.get_plugin().get_manager(); + table_base * res = m.mk_empty_table(m_empty_sig); + if(!t.empty()) { + table_fact el; + res->add_fact(el); + } + return res; + } + }; + + + + table_transformer_fn * relation_manager::mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + table_transformer_fn * res = t.get_plugin().mk_project_fn(t, col_cnt, removed_cols); + if(!res && col_cnt==t.get_signature().size()) { + //all columns are projected out + res = alloc(null_signature_table_project_fn); + } + if(!res) { + res = alloc(default_table_project_fn, t.get_signature(), col_cnt, removed_cols); + } + return res; + } + + + class relation_manager::default_table_join_project_fn : public convenient_table_join_project_fn { + scoped_ptr m_join; + scoped_ptr m_project; + + unsigned_vector m_removed_cols; + public: + default_table_join_project_fn(join_fn * join, const table_base & t1, const table_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) + : convenient_table_join_project_fn(t1.get_signature(), t2.get_signature(), joined_col_cnt, cols1, + cols2, removed_col_cnt, removed_cols), + m_join(join), + m_removed_cols(removed_col_cnt, removed_cols) {} + + class unreachable_reducer : public table_row_pair_reduce_fn { + virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { + //we do project_with_reduce only if we are sure there will be no reductions + //(see code of the table_signature::from_join_project function) + UNREACHABLE(); + } + }; + + virtual table_base * operator()(const table_base & t1, const table_base & t2) { + table_base * aux = (*m_join)(t1, t2); + if(m_project==0) { + relation_manager & rmgr = aux->get_plugin().get_manager(); + if(get_result_signature().functional_columns()!=0) { + //to preserve functional columns we need to do the project_with_reduction + unreachable_reducer * reducer = alloc(unreachable_reducer); + m_project = rmgr.mk_project_with_reduce_fn(*aux, m_removed_cols.size(), m_removed_cols.c_ptr(), reducer); + } + else { + m_project = rmgr.mk_project_fn(*aux, m_removed_cols); + } + if(!m_project) { + throw default_exception("projection for table does not exist"); + } + } + table_base * res = (*m_project)(*aux); + aux->deallocate(); + return res; + } + }; + + table_join_fn * relation_manager::mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { + table_join_fn * res = t1.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + if(!res && &t1.get_plugin()!=&t2.get_plugin()) { + res = t2.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, + removed_cols); + } + if(!res) { + table_join_fn * join = mk_join_fn(t1, t2, joined_col_cnt, cols1, cols2); + if(join) { + res = alloc(default_table_join_project_fn, join, t1, t2, joined_col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + } + } + return res; + + } + + class relation_manager::default_table_rename_fn + : public convenient_table_rename_fn, auxiliary_table_transformer_fn { + public: + default_table_rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) + : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { + SASSERT(permutation_cycle_len>=2); + } + + virtual const table_signature & get_result_signature() const { + return convenient_table_rename_fn::get_result_signature(); + } + + virtual void modify_fact(table_fact & f) const { + permutate_by_cycle(f, m_cycle); + } + + virtual table_base * operator()(const table_base & t) { + return auxiliary_table_transformer_fn::operator()(t); + } + + }; + + table_transformer_fn * relation_manager::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { + table_transformer_fn * res = t.get_plugin().mk_rename_fn(t, permutation_cycle_len, permutation_cycle); + if(!res) { + res = alloc(default_table_rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); + } + return res; + } + + table_transformer_fn * relation_manager::mk_permutation_rename_fn(const table_base & t, + const unsigned * permutation) { + table_transformer_fn * res = t.get_plugin().mk_permutation_rename_fn(t, permutation); + if(!res) { + res = alloc(default_table_permutation_rename_fn, t, permutation); + } + return res; + } + + + class relation_manager::default_table_union_fn : public table_union_fn { + table_fact m_row; + public: + virtual void operator()(table_base & tgt, const table_base & src, table_base * delta) { + table_base::iterator it = src.begin(); + table_base::iterator iend = src.end(); + + for(; it!=iend; ++it) { + it->get_fact(m_row); + + if(delta) { + if(!tgt.contains_fact(m_row)) { + tgt.add_new_fact(m_row); + delta->add_fact(m_row); + } + } + else { + //if there's no delta, we don't need to know whether we are actually adding a new fact + tgt.add_fact(m_row); + } + } + } + }; + + table_union_fn * relation_manager::mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta) { + table_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); + if(!res && &tgt.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_union_fn(tgt, src, delta); + } + if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { + res = delta->get_plugin().mk_union_fn(tgt, src, delta); + } + if(!res) { + res = alloc(default_table_union_fn); + } + return res; + } + + table_union_fn * relation_manager::mk_widen_fn(const table_base & tgt, const table_base & src, + const table_base * delta) { + table_union_fn * res = tgt.get_plugin().mk_widen_fn(tgt, src, delta); + if(!res && &tgt.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_widen_fn(tgt, src, delta); + } + if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { + res = delta->get_plugin().mk_widen_fn(tgt, src, delta); + } + if(!res) { + res = mk_union_fn(tgt, src, delta); + } + return res; + } + + + /** + An auixiliary class for functors that perform filtering. It performs the table traversal + and only asks for each individual row whether it should be removed. + + When using this class in multiple inheritance, this class should not be inherited publicly + and should be mentioned as last. This should ensure that deteletion of the object will + go well when initiated from a pointer to the first ancestor. + */ + class relation_manager::auxiliary_table_filter_fn { + table_fact m_row; + svector m_to_remove; + public: + virtual ~auxiliary_table_filter_fn() {} + virtual bool should_remove(const table_fact & f) const = 0; + + void operator()(table_base & r) { + m_to_remove.reset(); + unsigned sz = 0; + table_base::iterator it = r.begin(); + table_base::iterator iend = r.end(); + for(; it!=iend; ++it) { + it->get_fact(m_row); + if(should_remove(m_row)) { + m_to_remove.append(m_row.size(), m_row.c_ptr()); + ++sz; + } + } + r.remove_facts(sz, m_to_remove.c_ptr()); + } + }; + + class relation_manager::default_table_filter_identical_fn : public table_mutator_fn, auxiliary_table_filter_fn { + const unsigned m_col_cnt; + const unsigned_vector m_identical_cols; + public: + default_table_filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) + : m_col_cnt(col_cnt), + m_identical_cols(col_cnt, identical_cols) { + SASSERT(col_cnt>=2); + } + + virtual bool should_remove(const table_fact & f) const { + table_element val=f[m_identical_cols[0]]; + for(unsigned i=1; iget_arg(0); + if (!m.is_eq(condition)) { + return 0; + } + expr* x = to_app(condition)->get_arg(0); + expr* y = to_app(condition)->get_arg(1); + if (!is_var(x)) { + std::swap(x, y); + } + if (!is_var(x)) { + return 0; + } + dl_decl_util decl_util(m); + uint64 value = 0; + if (!decl_util.is_numeral_ext(y, value)) { + return 0; + } + return alloc(default_table_filter_not_equal_fn, ctx, to_var(x)->get_idx(), value); + } + }; + + + + class relation_manager::default_table_filter_interpreted_fn + : public table_mutator_fn, auxiliary_table_filter_fn { + ast_manager & m_ast_manager; + var_subst & m_vs; + dl_decl_util & m_decl_util; + th_rewriter & m_simp; + app_ref m_condition; + ptr_vector m_var_sorts; + expr_ref_vector m_args; + public: + default_table_filter_interpreted_fn(context & ctx, unsigned col_cnt, app* condition) + : m_ast_manager(ctx.get_manager()), + m_vs(ctx.get_var_subst()), + m_decl_util(ctx.get_decl_util()), + m_simp(ctx.get_rewriter()), + m_condition(condition, ctx.get_manager()), + m_args(ctx.get_manager()) { + m_var_sorts.resize(col_cnt); + get_free_vars(m_condition, m_var_sorts); + } + + virtual bool should_remove(const table_fact & f) const { + expr_ref_vector& args = const_cast(m_args); + + args.reset(); + //arguments need to be in reverse order for the substitution + unsigned col_cnt = f.size(); + for(int i=col_cnt-1;i>=0;i--) { + sort * var_sort = m_var_sorts[i]; + if(!var_sort) { + args.push_back(0); + continue; //this variable does not occur in the condition; + } + + table_element el = f[i]; + args.push_back(m_decl_util.mk_numeral(el, var_sort)); + } + + expr_ref ground(m_ast_manager); + m_vs(m_condition.get(), args.size(), args.c_ptr(), ground); + m_simp(ground); + + return m_ast_manager.is_false(ground); + } + + virtual void operator()(table_base & t) { + auxiliary_table_filter_fn::operator()(t); + } + }; + + table_mutator_fn * relation_manager::mk_filter_interpreted_fn(const table_base & t, app * condition) { + context & ctx = get_context(); + table_mutator_fn * res = t.get_plugin().mk_filter_interpreted_fn(t, condition); + if (!res) { + res = default_table_filter_not_equal_fn::mk(ctx, condition); + } + if(!res) { + res = alloc(default_table_filter_interpreted_fn, ctx, t.get_signature().size(), condition); + } + return res; + } + + + class relation_manager::default_table_filter_interpreted_and_project_fn + : public table_transformer_fn { + scoped_ptr m_filter; + scoped_ptr m_project; + app_ref m_condition; + unsigned_vector m_removed_cols; + public: + default_table_filter_interpreted_and_project_fn(context & ctx, table_mutator_fn * filter, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) + : m_filter(filter), m_condition(condition, ctx.get_manager()), + m_removed_cols(removed_col_cnt, removed_cols) {} + + virtual table_base* operator()(const table_base & tb) { + table_base *t2 = tb.clone(); + (*m_filter)(*t2); + if (!m_project) { + relation_manager & rmgr = t2->get_plugin().get_manager(); + m_project = rmgr.mk_project_fn(*t2, m_removed_cols.size(), m_removed_cols.c_ptr()); + if (!m_project) { + throw default_exception("projection does not exist"); + } + } + return (*m_project)(*t2); + } + }; + + table_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + table_transformer_fn * res = t.get_plugin().mk_filter_interpreted_and_project_fn(t, condition, removed_col_cnt, removed_cols); + if (res) + return res; + + table_mutator_fn * filter = mk_filter_interpreted_fn(t, condition); + SASSERT(filter); + res = alloc(default_table_filter_interpreted_and_project_fn, get_context(), filter, condition, removed_col_cnt, removed_cols); + return res; + } + + + table_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const table_base & t, + const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { + table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, + t_cols, src_cols); + if(!res && &t.get_plugin()!=&src.get_plugin()) { + res = src.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, t_cols, src_cols); + } + return res; + } + + + + class relation_manager::default_table_negation_filter_fn : public convenient_table_negation_filter_fn, + auxiliary_table_filter_fn { + const table_base * m_negated_table; + mutable table_fact m_aux_fact; + public: + default_table_negation_filter_fn(const table_base & tgt, const table_base & neg_t, + unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) + : convenient_table_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), + m_negated_table(0) { + m_aux_fact.resize(neg_t.get_signature().size()); + } + + virtual bool should_remove(const table_fact & f) const { + if(!m_all_neg_bound || m_overlap) { + table_base::iterator nit = m_negated_table->begin(); + table_base::iterator nend = m_negated_table->end(); + for(; nit!=nend; ++nit) { + const table_base::row_interface & nrow = *nit; + if(bindings_match(nrow, f)) { + return true; + } + } + return false; + } + else { + make_neg_bindings(m_aux_fact, f); + return m_negated_table->contains_fact(m_aux_fact); + } + } + + virtual void operator()(table_base & tgt, const table_base & negated_table) { + SASSERT(m_negated_table==0); + flet flet_neg_table(m_negated_table, &negated_table); + auxiliary_table_filter_fn::operator()(tgt); + } + + }; + + table_intersection_filter_fn * relation_manager::mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, + t_cols, negated_cols); + if(!res && &t.get_plugin()!=&negated_obj.get_plugin()) { + res = negated_obj.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, + negated_cols); + } + if(!res) { + res = alloc(default_table_negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); + } + return res; + } + + + class relation_manager::default_table_select_equal_and_project_fn : public table_transformer_fn { + scoped_ptr m_filter; + scoped_ptr m_project; + public: + default_table_select_equal_and_project_fn(table_mutator_fn * filter, table_transformer_fn * project) + : m_filter(filter), m_project(project) {} + + virtual table_base * operator()(const table_base & t1) { + TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); + scoped_rel aux = t1.clone(); + (*m_filter)(*aux); + table_base * res = (*m_project)(*aux); + return res; + } + }; + + table_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col) { + table_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); + if(!res) { + table_mutator_fn * selector = mk_filter_equal_fn(t, value, col); + SASSERT(selector); + table_transformer_fn * projector = mk_project_fn(t, 1, &col); + SASSERT(projector); + res = alloc(default_table_select_equal_and_project_fn, selector, projector); + } + return res; + } + + + class relation_manager::default_table_map_fn : public table_mutator_fn { + scoped_ptr m_mapper; + unsigned m_first_functional; + scoped_rel m_aux_table; + scoped_ptr m_union_fn; + table_fact m_curr_fact; + public: + default_table_map_fn(const table_base & t, table_row_mutator_fn * mapper) + : m_mapper(mapper), m_first_functional(t.get_signature().first_functional()) { + SASSERT(t.get_signature().functional_columns()>0); + table_plugin & plugin = t.get_plugin(); + m_aux_table = plugin.mk_empty(t.get_signature()); + m_union_fn = plugin.mk_union_fn(t, *m_aux_table, static_cast(0)); + } + + virtual void operator()(table_base & t) { + SASSERT(t.get_signature()==m_aux_table->get_signature()); + if(!m_aux_table->empty()) { + m_aux_table->reset(); + } + + + table_base::iterator it = t.begin(); + table_base::iterator iend = t.end(); + for(; it!=iend; ++it) { + it->get_fact(m_curr_fact); + if((*m_mapper)(m_curr_fact.c_ptr()+m_first_functional)) { + m_aux_table->add_fact(m_curr_fact); + } + } + + t.reset(); + (*m_union_fn)(t, *m_aux_table, static_cast(0)); + } + }; + + table_mutator_fn * relation_manager::mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { + SASSERT(t.get_signature().functional_columns()>0); + table_mutator_fn * res = t.get_plugin().mk_map_fn(t, mapper); + if(!res) { + res = alloc(default_table_map_fn, t, mapper); + } + return res; + } + + + class relation_manager::default_table_project_with_reduce_fn : public convenient_table_transformer_fn { + unsigned_vector m_removed_cols; + const unsigned m_inp_col_cnt; + const unsigned m_removed_col_cnt; + const unsigned m_result_col_cnt; + scoped_ptr m_reducer; + unsigned m_res_first_functional; + table_fact m_row; + table_fact m_former_row; + public: + default_table_project_with_reduce_fn(const table_signature & orig_sig, unsigned removed_col_cnt, + const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) + : m_removed_cols(removed_col_cnt, removed_cols), + m_inp_col_cnt(orig_sig.size()), + m_removed_col_cnt(removed_col_cnt), + m_result_col_cnt(orig_sig.size()-removed_col_cnt), + m_reducer(reducer) { + SASSERT(removed_col_cnt>0); + table_signature::from_project_with_reduce(orig_sig, removed_col_cnt, removed_cols, + get_result_signature()); + m_res_first_functional = get_result_signature().first_functional(); + m_row.resize(get_result_signature().size()); + m_former_row.resize(get_result_signature().size()); + } + + virtual void modify_fact(table_fact & f) const { + unsigned ofs=1; + unsigned r_i=1; + for(unsigned i=m_removed_cols[0]+1; isuggest_fact(m_former_row)) { + (*m_reducer)(m_former_row.c_ptr()+m_res_first_functional, m_row.c_ptr()+m_res_first_functional); + res->ensure_fact(m_former_row); + } + } + return res; + } + }; + + table_transformer_fn * relation_manager::mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { + SASSERT(t.get_signature().functional_columns()>0); + table_transformer_fn * res = t.get_plugin().mk_project_with_reduce_fn(t, col_cnt, removed_cols, reducer); + if(!res) { + res = alloc(default_table_project_with_reduce_fn, t.get_signature(), col_cnt, removed_cols, reducer); + } + return res; + } + +}; + diff --git a/src/muz/rel/dl_relation_manager.h b/src/muz/rel/dl_relation_manager.h new file mode 100644 index 000000000..9f12b4bb6 --- /dev/null +++ b/src/muz/rel/dl_relation_manager.h @@ -0,0 +1,688 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_relation_manager.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-24. + +Revision History: + +--*/ +#ifndef _DL_RELATION_MANAGER_H_ +#define _DL_RELATION_MANAGER_H_ + + +#include"map.h" +#include"vector.h" +#include"dl_base.h" + +namespace datalog { + + class context; + class dl_decl_util; + class table_relation; + class table_relation_plugin; + class finite_product_relation; + class finite_product_relation_plugin; + class sieve_relation; + class sieve_relation_plugin; + class rule_set; + + + class relation_manager { + class empty_signature_relation_join_fn; + class default_relation_join_project_fn; + class default_relation_select_equal_and_project_fn; + class default_relation_intersection_filter_fn; + class default_relation_filter_interpreted_and_project_fn; + + class auxiliary_table_transformer_fn; + class auxiliary_table_filter_fn; + + class default_table_join_fn; + class default_table_project_fn; + class null_signature_table_project_fn; + class default_table_join_project_fn; + class default_table_rename_fn; + class default_table_union_fn; + class default_table_filter_equal_fn; + class default_table_filter_identical_fn; + class default_table_filter_interpreted_fn; + class default_table_filter_interpreted_and_project_fn; + class default_table_negation_filter_fn; + class default_table_filter_not_equal_fn; + class default_table_select_equal_and_project_fn; + class default_table_map_fn; + class default_table_project_with_reduce_fn; + + typedef obj_map decl2kind_map; + + typedef u_map kind2plugin_map; + + typedef map, + ptr_eq > tp2trp_map; + typedef map, + ptr_eq > rp2fprp_map; + + typedef map, ptr_eq > relation_map; + typedef ptr_vector table_plugin_vector; + typedef ptr_vector relation_plugin_vector; + + context & m_context; + table_plugin_vector m_table_plugins; + relation_plugin_vector m_relation_plugins; + //table_relation_plugins corresponding to table_plugins + tp2trp_map m_table_relation_plugins; + rp2fprp_map m_finite_product_relation_plugins; + + kind2plugin_map m_kind2plugin; + + table_plugin * m_favourite_table_plugin; + + relation_plugin * m_favourite_relation_plugin; + + relation_map m_relations; + + decl_set m_saturated_rels; + + family_id m_next_table_fid; + family_id m_next_relation_fid; + + /** + Map specifying what kind of relation should be used to represent particular predicate. + */ + decl2kind_map m_pred_kinds; + + void register_relation_plugin_impl(relation_plugin * plugin); + + relation_manager(const relation_manager &); //private and undefined copy constructor + relation_manager & operator=(const relation_manager &); //private and undefined operator= + public: + relation_manager(context & ctx) : + m_context(ctx), + m_favourite_table_plugin(0), + m_favourite_relation_plugin(0), + m_next_table_fid(0), + m_next_relation_fid(0) {} + + virtual ~relation_manager(); + + void reset(); + void reset_relations(); + + context & get_context() const { return m_context; } + dl_decl_util & get_decl_util() const; + + family_id get_next_table_fid() { return m_next_table_fid++; } + family_id get_next_relation_fid(relation_plugin & claimer); + + + /** + Set what kind of relation is going to be used to represent the predicate \c pred. + + This function can be called only before the relation object for \c pred is created + (i.e. before the \c get_relation function is called with \c pred as argument for the + first time). + */ + void set_predicate_kind(func_decl * pred, family_id kind); + /** + Return the relation kind that was requested to represent the predicate \c pred by + \c set_predicate_kind. If there was no such request, return \c null_family_id. + */ + family_id get_requested_predicate_kind(func_decl * pred); + relation_base & get_relation(func_decl * pred); + relation_base * try_get_relation(func_decl * pred) const; + /** + \brief Store the relation \c rel under the predicate \c pred. The \c relation_manager + takes over the relation object. + */ + void store_relation(func_decl * pred, relation_base * rel); + + bool is_saturated(func_decl * pred) const { return m_saturated_rels.contains(pred); } + void mark_saturated(func_decl * pred) { m_saturated_rels.insert(pred); } + void reset_saturated_marks() { + if(!m_saturated_rels.empty()) { + m_saturated_rels.reset(); + } + } + + void collect_non_empty_predicates(decl_set & res) const; + void restrict_predicates(const decl_set & preds); + + void register_plugin(table_plugin * plugin); + /** + table_relation_plugins should not be passed to this function since they are + created automatically when registering a table plugin. + */ + void register_plugin(relation_plugin * plugin) { + SASSERT(!plugin->from_table()); + register_relation_plugin_impl(plugin); + } + + table_plugin & get_appropriate_plugin(const table_signature & t); + relation_plugin & get_appropriate_plugin(const relation_signature & t); + table_plugin * try_get_appropriate_plugin(const table_signature & t); + relation_plugin * try_get_appropriate_plugin(const relation_signature & t); + + table_plugin * get_table_plugin(symbol const& s); + relation_plugin * get_relation_plugin(symbol const& s); + relation_plugin & get_relation_plugin(family_id kind); + table_relation_plugin & get_table_relation_plugin(table_plugin & tp); + bool try_get_finite_product_relation_plugin(const relation_plugin & inner, + finite_product_relation_plugin * & res); + + table_base * mk_empty_table(const table_signature & s); + relation_base * mk_implicit_relation(const relation_signature & s, app * expr); + + relation_base * mk_empty_relation(const relation_signature & s, family_id kind); + relation_base * mk_empty_relation(const relation_signature & s, func_decl* pred); + + relation_base * mk_full_relation(const relation_signature & s, func_decl* pred, family_id kind); + relation_base * mk_full_relation(const relation_signature & s, func_decl* pred); + + relation_base * mk_table_relation(const relation_signature & s, table_base * table); + bool mk_empty_table_relation(const relation_signature & s, relation_base * & result); + + bool is_non_explanation(relation_signature const& s) const; + + + /** + \brief Convert relation value to table one. + + This function can be called only for the relation sorts that have a table counterpart. + */ + void relation_to_table(const relation_sort & sort, const relation_element & from, table_element & to); + + void table_to_relation(const relation_sort & sort, const table_element & from, relation_element & to); + void table_to_relation(const relation_sort & sort, const table_element & from, + const relation_fact::el_proxy & to); + void table_to_relation(const relation_sort & sort, const table_element & from, + relation_element_ref & to); + + bool relation_sort_to_table(const relation_sort & from, table_sort & to); + void from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result); + void from_predicate(func_decl * pred, relation_signature & result); + + /** + \brief Convert relation signature to table signature and return true if successful. If false + is returned, the value of \c to is undefined. + */ + bool relation_signature_to_table(const relation_signature & from, table_signature & to); + + void relation_fact_to_table(const relation_signature & s, const relation_fact & from, + table_fact & to); + void table_fact_to_relation(const relation_signature & s, const table_fact & from, + relation_fact & to); + + + void set_cancel(bool f); + + + // ----------------------------------- + // + // relation operations + // + // ----------------------------------- + + //TODO: If multiple operation implementations are available, we may want to do something to + //select the best one here. + + /** + If \c allow_product_relation is true, we will create a join that builds a product relation, + if there is no other way to do the join. If \c allow_product_relation is false, we will return + zero in that case. + */ + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation=true); + + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + const unsigned_vector & cols1, const unsigned_vector & cols2, bool allow_product_relation=true) { + SASSERT(cols1.size()==cols2.size()); + return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), allow_product_relation); + } + + /** + \brief Return functor that transforms a table into one that lacks columns listed in + \c removed_cols array. + + The \c removed_cols cotains columns of table \c t in strictly ascending order. + */ + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + + relation_transformer_fn * mk_project_fn(const relation_base & t, const unsigned_vector & removed_cols) { + return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr()); + } + + /** + \brief Return an operation that is a composition of a join an a project operation. + */ + relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join=true); + + relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, + const unsigned_vector & cols1, const unsigned_vector & cols2, + const unsigned_vector & removed_cols, bool allow_product_relation_join=true) { + return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(), + removed_cols.c_ptr(), allow_product_relation_join); + } + + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + relation_transformer_fn * mk_rename_fn(const relation_base & t, const unsigned_vector & permutation_cycle) { + return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr()); + } + + /** + Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array + of column number. + */ + relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, + const unsigned * permutation); + relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, + const unsigned_vector permutation) { + SASSERT(t.get_signature().size()==permutation.size()); + return mk_permutation_rename_fn(t, permutation.c_ptr()); + } + + + /** + The post-condition for an ideal union operation is be + + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 ) + + A required post-condition is + + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + tgt_1==tgt_0 => delta_1==delta_0 + delta_0 \subset delta_1 + delta_1 \subset (delta_0 \union tgt_1) + ( tgt_1 \setminus tgt_0 ) \subset delta_1 + + So that a sufficient implementation is + + Union(tgt, src, delta) { + oldTgt:=tgt.clone(); + tgt:=tgt \union src + if(tgt!=oldTgt) { + delta:=delta \union src //also ?delta \union tgt? would work + } + } + + If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient + post-condition is + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + (tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty + */ + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src) { + return mk_union_fn(tgt, src, static_cast(0)); + } + + /** + Similar to union, but this one should be used inside loops to allow for abstract + domain convergence. + */ + relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, const unsigned_vector identical_cols) { + return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); + } + + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + + relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols); + + /** + \brief Operations that returns all rows of \c t for which is column \c col equal to \c value + with the column \c col removed. + + This operation can often be efficiently implemented and is useful for evaluating rules + of the form + + F(x):-P("c",x). + */ + relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, + const relation_element & value, unsigned col); + + + relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, + const relation_base & src, unsigned joined_col_cnt, + const unsigned * tgt_cols, const unsigned * src_cols); + relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, + const relation_base & src, const unsigned_vector & tgt_cols, const unsigned_vector & src_cols) { + SASSERT(tgt_cols.size()==src_cols.size()); + return mk_filter_by_intersection_fn(tgt, src, tgt_cols.size(), tgt_cols.c_ptr(), src_cols.c_ptr()); + } + relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, + const relation_base & src); + + /** + The filter_by_negation postcondition: + filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, + corresponding columns in neg: d1,...,dN): + tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } + */ + relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, const unsigned_vector & t_cols, + const unsigned_vector & negated_cols) { + SASSERT(t_cols.size()==negated_cols.size()); + return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); + } + + + // ----------------------------------- + // + // table operations + // + // ----------------------------------- + + + table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + + table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + const unsigned_vector & cols1, const unsigned_vector & cols2) { + SASSERT(cols1.size()==cols2.size()); + return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr()); + } + + /** + \brief Return functor that transforms a table into one that lacks columns listed in + \c removed_cols array. + + The \c removed_cols cotains columns of table \c t in strictly ascending order. + + If a project operation removes a non-functional column, all functional columns become + non-functional (so that none of the values in functional columns are lost) + */ + table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols); + + table_transformer_fn * mk_project_fn(const table_base & t, const unsigned_vector & removed_cols) { + return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr()); + } + + /** + \brief Return an operation that is a composition of a join an a project operation. + + This operation is equivalent to the two operations performed separately, unless functional + columns are involved. + + The ordinary project would make all of the functional columns into non-functional if any + non-functional column was removed. In function, however, we group columns into equivalence + classes (according to the equalities in \c cols1 and \c cols2) and make everything non-functional + only if some equivalence class of non-functional columns would have no non-functional columns + remain after the removal. + + This behavior is implemented in the \c table_signature::from_join_project function. + */ + table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols); + + table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + const unsigned_vector & cols1, const unsigned_vector & cols2, + const unsigned_vector & removed_cols) { + return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(), + removed_cols.c_ptr()); + } + + table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + table_transformer_fn * mk_rename_fn(const table_base & t, const unsigned_vector & permutation_cycle) { + return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr()); + } + + /** + Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array + of column number. + */ + table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned * permutation); + table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned_vector permutation) { + SASSERT(t.get_signature().size()==permutation.size()); + return mk_permutation_rename_fn(t, permutation.c_ptr()); + } + + + /** + The post-condition for an ideal union operation is be + + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 ) + + A required post-condition is + + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + tgt_1==tgt_0 => delta_1==delta_0 + delta_0 \subset delta_1 + delta_1 \subset (delta_0 \union tgt_1) + ( tgt_1 \setminus tgt_0 ) \subset delta_1 + + So that a sufficient implementation is + + Union(tgt, src, delta) { + oldTgt:=tgt.clone(); + tgt:=tgt \union src + if(tgt!=oldTgt) { + delta:=delta \union src //also ?delta \union tgt? would work + } + } + + If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient + post-condition is + Union(tgt, src, delta): + tgt_1==tgt_0 \union src + (tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty + */ + table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta); + + table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src) { + return mk_union_fn(tgt, src, static_cast(0)); + } + + /** + Similar to union, but this one should be used inside loops to allow for abstract + domain convergence. + */ + table_union_fn * mk_widen_fn(const table_base & tgt, const table_base & src, + const table_base * delta); + + table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, + const unsigned * identical_cols); + table_mutator_fn * mk_filter_identical_fn(const table_base & t, const unsigned_vector identical_cols) { + return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); + } + + table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, + unsigned col); + + table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); + + table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols); + + /** + \brief Operations that returns all rows of \c t for which is column \c col equal to \c value + with the column \c col removed. + + This operation can often be efficiently implemented and is useful for evaluating rules + of the form + + F(x):-P("c",x). + */ + table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col); + + table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t, + const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); + table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t, + const table_base & src, const unsigned_vector & t_cols, const unsigned_vector & src_cols) { + SASSERT(t_cols.size()==src_cols.size()); + return mk_filter_by_intersection_fn(t, src, t_cols.size(), t_cols.c_ptr(), src_cols.c_ptr()); + } + + /** + The filter_by_negation postcondition: + filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, + corresponding columns in neg: d1,...,dN): + tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } + */ + table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, + unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); + table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, + const unsigned_vector & t_cols, const unsigned_vector & negated_cols) { + SASSERT(t_cols.size()==negated_cols.size()); + return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); + } + + /** + \c t must contain at least one functional column. + + Created object takes ownership of the \c mapper object. + */ + virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper); + + /** + \c t must contain at least one functional column. + + Created object takes ownership of the \c mapper object. + */ + virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols, table_row_pair_reduce_fn * reducer); + + + + + // ----------------------------------- + // + // output functions + // + // ----------------------------------- + + + std::string to_nice_string(const relation_element & el) const; + /** + This one may give a nicer representation of \c el than the + \c to_nice_string(const relation_element & el) function, by unsing the information about the sort + of the element. + */ + std::string to_nice_string(const relation_sort & s, const relation_element & el) const; + std::string to_nice_string(const relation_sort & s) const; + std::string to_nice_string(const relation_signature & s) const; + + void display(std::ostream & out) const; + void display_relation_sizes(std::ostream & out) const; + void display_output_tables(rule_set const& rules, std::ostream & out) const; + + private: + relation_intersection_filter_fn * try_mk_default_filter_by_intersection_fn(const relation_base & t, + const relation_base & src, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * src_cols); + + }; + + /** + This is a helper class for relation_plugins whose relations can be of various kinds. + */ + template > + class rel_spec_store { + typedef relation_signature::hash r_hash; + typedef relation_signature::eq r_eq; + + typedef map family_id_idx_store; + typedef map sig2store; + + typedef u_map family_id2spec; + typedef map sig2spec_store; + + relation_plugin & m_parent; + svector m_allocated_kinds; + sig2store m_kind_assignment; + sig2spec_store m_kind_specs; + + + relation_manager & get_manager() { return m_parent.get_manager(); } + + void add_new_kind() { + add_available_kind(get_manager().get_next_relation_fid(m_parent)); + } + + public: + rel_spec_store(relation_plugin & parent) : m_parent(parent) {} + + ~rel_spec_store() { + reset_dealloc_values(m_kind_assignment); + reset_dealloc_values(m_kind_specs); + } + + void add_available_kind(family_id k) { + m_allocated_kinds.push_back(k); + } + + bool contains_signature(relation_signature const& sig) const { + return m_kind_assignment.contains(sig); + } + + family_id get_relation_kind(const relation_signature & sig, const Spec & spec) { + typename sig2store::entry * e = m_kind_assignment.find_core(sig); + if(!e) { + e = m_kind_assignment.insert_if_not_there2(sig, alloc(family_id_idx_store)); + m_kind_specs.insert(sig, alloc(family_id2spec)); + } + family_id_idx_store & ids = *e->get_data().m_value; + + unsigned res_idx; + if(!ids.find(spec, res_idx)) { + res_idx = ids.size(); + if(res_idx==m_allocated_kinds.size()) { + add_new_kind(); + } + SASSERT(res_idxinsert(m_allocated_kinds[res_idx], spec); + } + return m_allocated_kinds[res_idx]; + } + + void get_relation_spec(const relation_signature & sig, family_id kind, Spec & spec) { + family_id2spec * idspecs = m_kind_specs.find(sig); + spec = idspecs->find(kind); + } + + }; + +}; + +#endif /* _DL_RELATION_MANAGER_H_ */ + diff --git a/src/muz/rel/dl_sieve_relation.cpp b/src/muz/rel/dl_sieve_relation.cpp new file mode 100644 index 000000000..9f9419089 --- /dev/null +++ b/src/muz/rel/dl_sieve_relation.cpp @@ -0,0 +1,666 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_explanations.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-11-08. + +Revision History: + +--*/ + +#include +#include"ast_pp.h" +#include"dl_sieve_relation.h" + +namespace datalog { + + // ----------------------------------- + // + // sieve_relation + // + // ----------------------------------- + + sieve_relation::sieve_relation(sieve_relation_plugin & p, const relation_signature & s, + const bool * inner_columns, relation_base * inner) + : relation_base(p, s), m_inner_cols(s.size(), inner_columns), m_inner(inner) { + unsigned n = s.size(); + for(unsigned i=0; i 0; ) { + --i; + unsigned idx = m_inner2sig[i]; + s.push_back(m.mk_var(idx, sig[i])); + } + get_inner().to_formula(tmp); + get_plugin().get_context().get_var_subst()(tmp, sz, s.c_ptr(), fml); + } + + + void sieve_relation::display(std::ostream & out) const { + out << "Sieve relation "; + print_container(m_inner_cols, out); + out <<"\n"; + get_inner().display(out); + } + + + // ----------------------------------- + // + // sieve_relation_plugin + // + // ----------------------------------- + + sieve_relation_plugin & sieve_relation_plugin::get_plugin(relation_manager & rmgr) { + sieve_relation_plugin * res = static_cast(rmgr.get_relation_plugin(get_name())); + if(!res) { + res = alloc(sieve_relation_plugin, rmgr); + rmgr.register_plugin(res); + } + return *res; + } + + sieve_relation& sieve_relation_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + + sieve_relation const & sieve_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + sieve_relation* sieve_relation_plugin::get(relation_base* r) { + return dynamic_cast(r); + } + + sieve_relation const* sieve_relation_plugin::get(relation_base const* r) { + return dynamic_cast(r); + } + + sieve_relation_plugin::sieve_relation_plugin(relation_manager & manager) + : relation_plugin(get_name(), manager, ST_SIEVE_RELATION), + m_spec_store(*this) {} + + void sieve_relation_plugin::initialize(family_id fid) { + relation_plugin::initialize(fid); + m_spec_store.add_available_kind(get_kind()); + } + + family_id sieve_relation_plugin::get_relation_kind(const relation_signature & sig, + const bool * inner_columns, family_id inner_kind) { + rel_spec spec(sig.size(), inner_columns, inner_kind); + return m_spec_store.get_relation_kind(sig, spec); + } + + family_id sieve_relation_plugin::get_relation_kind(sieve_relation & r, const bool * inner_columns) { + const relation_signature & sig = r.get_signature(); + return get_relation_kind(sig, inner_columns, r.get_inner().get_kind()); + } + + void sieve_relation_plugin::extract_inner_columns(const relation_signature & s, relation_plugin & inner, + svector & inner_columns) { + SASSERT(inner_columns.size()==s.size()); + unsigned n = s.size(); + relation_signature inner_sig_singleton; + for(unsigned i=0; i & inner_columns, relation_signature & inner_sig) { + SASSERT(inner_columns.size()==s.size()); + inner_sig.reset(); + unsigned n = s.size(); + for(unsigned i=0; i inner_cols(s.size()); + extract_inner_columns(s, inner_cols.c_ptr()); + collect_inner_signature(s, inner_cols, inner_sig); +#endif + } + + bool sieve_relation_plugin::can_handle_signature(const relation_signature & s) { + //we do not want this plugin to handle anything by default + return false; +#if 0 + relation_signature inner_sig; + extract_inner_signature(s, inner_sig); + SASSERT(inner_sig.size()<=s.size()); + return !inner_sig.empty() && inner_sig.size()!=s.size(); +#endif + } + + sieve_relation * sieve_relation_plugin::mk_from_inner(const relation_signature & s, const bool * inner_columns, + relation_base * inner_rel) { + SASSERT(!inner_rel->get_plugin().is_sieve_relation()); //it does not make sense to make a sieve of a sieve + return alloc(sieve_relation, *this, s, inner_columns, inner_rel); + } + + sieve_relation * sieve_relation_plugin::mk_empty(const sieve_relation & original) { + return static_cast(mk_empty(original.get_signature(), original.get_kind())); + } + + relation_base * sieve_relation_plugin::mk_empty(const relation_base & original) { + return mk_empty(static_cast(original)); + } + + relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { + rel_spec spec; + m_spec_store.get_relation_spec(s, kind, spec); + relation_signature inner_sig; + collect_inner_signature(s, spec.m_inner_cols, inner_sig); + relation_base * inner = get_manager().mk_empty_relation(inner_sig, spec.m_inner_kind); + return mk_from_inner(s, spec.m_inner_cols.c_ptr(), inner); + } + + + relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s) { + UNREACHABLE(); + return 0; +#if 0 + svector inner_cols(s.size()); + extract_inner_columns(s, inner_cols.c_ptr()); + return mk_empty(s, inner_cols.c_ptr()); +#endif + } + + sieve_relation * sieve_relation_plugin::mk_empty(const relation_signature & s, relation_plugin & inner_plugin) { + SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve + svector inner_cols(s.size()); + extract_inner_columns(s, inner_plugin, inner_cols); + relation_signature inner_sig; + collect_inner_signature(s, inner_cols, inner_sig); + relation_base * inner_rel = inner_plugin.mk_empty(inner_sig); + return mk_from_inner(s, inner_cols, inner_rel); + } + + relation_base * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + relation_signature empty_sig; + relation_plugin& plugin = get_manager().get_appropriate_plugin(s); + relation_base * inner = plugin.mk_full(p, empty_sig, null_family_id); + svector inner_cols; + inner_cols.resize(s.size(), false); + return mk_from_inner(s, inner_cols, inner); + } + + sieve_relation * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin) { + SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve + svector inner_cols(s.size()); + extract_inner_columns(s, inner_plugin, inner_cols); + relation_signature inner_sig; + collect_inner_signature(s, inner_cols, inner_sig); + relation_base * inner_rel = inner_plugin.mk_full(p, inner_sig, null_family_id); + return mk_from_inner(s, inner_cols, inner_rel); + } + + class sieve_relation_plugin::join_fn : public convenient_relation_join_fn { + sieve_relation_plugin & m_plugin; + unsigned_vector m_inner_cols_1; + unsigned_vector m_inner_cols_2; + svector m_result_inner_cols; + + scoped_ptr m_inner_join_fun; + public: + join_fn(sieve_relation_plugin & p, const relation_base & r1, const relation_base & r2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, relation_join_fn * inner_join_fun) + : convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2), + m_plugin(p), + m_inner_join_fun(inner_join_fun) { + bool r1_sieved = r1.get_plugin().is_sieve_relation(); + bool r2_sieved = r2.get_plugin().is_sieve_relation(); + const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; + const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; + if(r1_sieved) { + m_result_inner_cols.append(sr1->m_inner_cols); + } + else { + m_result_inner_cols.resize(r1.get_signature().size(), true); + } + if(r2_sieved) { + m_result_inner_cols.append(sr2->m_inner_cols); + } + else { + m_result_inner_cols.resize(m_result_inner_cols.size() + r2.get_signature().size(), true); + } + } + + virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + bool r1_sieved = r1.get_plugin().is_sieve_relation(); + bool r2_sieved = r2.get_plugin().is_sieve_relation(); + SASSERT(r1_sieved || r2_sieved); + const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; + const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; + const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; + const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; + + relation_base * inner_res = (*m_inner_join_fun)(inner1, inner2); + + return m_plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.c_ptr(), inner_res); + } + }; + + relation_join_fn * sieve_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if( &r1.get_plugin()!=this && &r2.get_plugin()!=this ) { + //we create just operations that involve the current plugin + return 0; + } + bool r1_sieved = r1.get_plugin().is_sieve_relation(); + bool r2_sieved = r2.get_plugin().is_sieve_relation(); + const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; + const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; + const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; + const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; + + unsigned_vector inner_cols1; + unsigned_vector inner_cols2; + + for(unsigned i=0; iis_inner_col(cols1[i])) { + continue; + } + if(r2_sieved && !sr2->is_inner_col(cols2[i])) { + continue; + } + inner_cols1.push_back( r1_sieved ? sr1->get_inner_col(cols1[i]) : cols1[i] ); + inner_cols2.push_back( r2_sieved ? sr2->get_inner_col(cols2[i]) : cols2[i] ); + } + + relation_join_fn * inner_join_fun = get_manager().mk_join_fn(inner1, inner2, inner_cols1, inner_cols2, false); + if(!inner_join_fun) { + return 0; + } + return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2, inner_join_fun); + } + + + class sieve_relation_plugin::transformer_fn : public convenient_relation_transformer_fn { + svector m_result_inner_cols; + + scoped_ptr m_inner_fun; + public: + transformer_fn(relation_transformer_fn * inner_fun, const relation_signature & result_sig, + const bool * result_inner_cols) + : m_result_inner_cols(result_sig.size(), result_inner_cols), m_inner_fun(inner_fun) { + get_result_signature() = result_sig; + } + + virtual relation_base * operator()(const relation_base & r0) { + SASSERT(r0.get_plugin().is_sieve_relation()); + const sieve_relation & r = static_cast(r0); + sieve_relation_plugin & plugin = r.get_plugin(); + + relation_base * inner_res = (*m_inner_fun)(r.get_inner()); + + return plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.c_ptr(), inner_res); + } + }; + + relation_transformer_fn * sieve_relation_plugin::mk_project_fn(const relation_base & r0, unsigned col_cnt, + const unsigned * removed_cols) { + if(&r0.get_plugin()!=this) { + return 0; + } + const sieve_relation & r = static_cast(r0); + unsigned_vector inner_removed_cols; + + for(unsigned i=0; i result_inner_cols = r.m_inner_cols; + project_out_vector_columns(result_inner_cols, col_cnt, removed_cols); + + relation_signature result_sig; + relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, result_sig); + + relation_transformer_fn * inner_fun; + if(inner_removed_cols.empty()) { + inner_fun = alloc(identity_relation_transformer_fn); + } + else { + inner_fun = get_manager().mk_project_fn(r.get_inner(), inner_removed_cols); + } + + if(!inner_fun) { + return 0; + } + return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); + } + + relation_transformer_fn * sieve_relation_plugin::mk_rename_fn(const relation_base & r0, + unsigned cycle_len, const unsigned * permutation_cycle) { + if(&r0.get_plugin()!=this) { + return 0; + } + const sieve_relation & r = static_cast(r0); + + unsigned sig_sz = r.get_signature().size(); + unsigned_vector permutation; + add_sequence(0, sig_sz, permutation); + permutate_by_cycle(permutation, cycle_len, permutation_cycle); + + bool inner_identity; + unsigned_vector inner_permutation; + collect_sub_permutation(permutation, r.m_sig2inner, inner_permutation, inner_identity); + + svector result_inner_cols = r.m_inner_cols; + permutate_by_cycle(result_inner_cols, cycle_len, permutation_cycle); + + relation_signature result_sig; + relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, result_sig); + + relation_transformer_fn * inner_fun = + get_manager().mk_permutation_rename_fn(r.get_inner(), inner_permutation); + if(!inner_fun) { + return 0; + } + return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); + } + + + class sieve_relation_plugin::union_fn : public relation_union_fn { + scoped_ptr m_union_fun; + public: + union_fn(relation_union_fn * union_fun) : m_union_fun(union_fun) {} + + virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { + bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); + bool src_sieved = src.get_plugin().is_sieve_relation(); + bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); + sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : 0; + const sieve_relation * ssrc = src_sieved ? static_cast(&src) : 0; + sieve_relation * sdelta = delta_sieved ? static_cast(delta) : 0; + relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; + const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; + relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; + + (*m_union_fun)(itgt, isrc, idelta); + } + }; + + relation_union_fn * sieve_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if(&tgt.get_plugin()!=this && &src.get_plugin()!=this && (delta && &delta->get_plugin()!=this)) { + //we create the operation only if it involves this plugin + return 0; + } + + bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); + bool src_sieved = src.get_plugin().is_sieve_relation(); + bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); + const sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : 0; + const sieve_relation * ssrc = src_sieved ? static_cast(&src) : 0; + const sieve_relation * sdelta = delta_sieved ? static_cast(delta) : 0; + const relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; + const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; + const relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; + + //Now we require that the sieved and inner columns must match on all relations. + //We may want to allow for some cases of misalignment even though it could introcude imprecision + if( tgt_sieved && src_sieved && (!delta || delta_sieved) ) { + if( !vectors_equal(stgt->m_inner_cols, ssrc->m_inner_cols) + || (delta && !vectors_equal(stgt->m_inner_cols, sdelta->m_inner_cols)) ) { + return 0; + } + } + else { + if( (stgt && !stgt->no_sieved_columns()) + || (ssrc && !ssrc->no_sieved_columns()) + || (sdelta && !sdelta->no_sieved_columns()) ) { + //We have an unsieved relation and then some relation with some sieved columns, + //which means there is an misalignment. + return 0; + } + } + + relation_union_fn * union_fun = get_manager().mk_union_fn(itgt, isrc, idelta); + if(!union_fun) { + return 0; + } + + return alloc(union_fn, union_fun); + } + + + class sieve_relation_plugin::filter_fn : public relation_mutator_fn { + scoped_ptr m_inner_fun; + public: + filter_fn(relation_mutator_fn * inner_fun) + : m_inner_fun(inner_fun) {} + + virtual void operator()(relation_base & r0) { + SASSERT(r0.get_plugin().is_sieve_relation()); + sieve_relation & r = static_cast(r0); + + (*m_inner_fun)(r.get_inner()); + } + }; + + relation_mutator_fn * sieve_relation_plugin::mk_filter_identical_fn(const relation_base & r0, + unsigned col_cnt, const unsigned * identical_cols) { + if(&r0.get_plugin()!=this) { + return 0; + } + const sieve_relation & r = static_cast(r0); + unsigned_vector inner_icols; + + //we ignore the columns which do not belong to the inner relation (which introduces imprecision) + for(unsigned i=0; i(r0); + if(!r.is_inner_col(col)) { + //if the column which do not belong to the inner relation, we do nothing (which introduces imprecision) + return alloc(identity_relation_mutator_fn); + } + unsigned inner_col = r.get_inner_col(col); + + relation_mutator_fn * inner_fun = get_manager().mk_filter_equal_fn(r.get_inner(), value, inner_col); + if(!inner_fun) { + return 0; + } + return alloc(filter_fn, inner_fun); + } + + relation_mutator_fn * sieve_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, + app * condition) { + if(&rb.get_plugin()!=this) { + return 0; + } + ast_manager & m = get_ast_manager(); + const sieve_relation & r = static_cast(rb); + const relation_signature sig = r.get_signature(); + unsigned sz = sig.size(); + + var_idx_set& cond_vars = get_context().get_rule_manager().collect_vars(condition); + expr_ref_vector subst_vect(m); + subst_vect.resize(sz); + unsigned subst_ofs = sz-1; + for(unsigned i=0; i m_inner_fun; + public: + negation_filter_fn(relation_intersection_filter_fn * inner_fun) + : m_inner_fun(inner_fun) {} + + virtual void operator()(relation_base & r, const relation_base & neg) { + bool r_sieved = r.get_plugin().is_sieve_relation(); + bool neg_sieved = neg.get_plugin().is_sieve_relation(); + SASSERT(r_sieved || neg_sieved); + sieve_relation * sr = r_sieved ? static_cast(&r) : 0; + const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : 0; + relation_base & inner_r = r_sieved ? sr->get_inner() : r; + const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; + + (*m_inner_fun)(inner_r, inner_neg); + } + }; + + relation_intersection_filter_fn * sieve_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, + const relation_base & neg, unsigned col_cnt, const unsigned * r_cols, + const unsigned * neg_cols) { + if(&r.get_plugin()!=this && &neg.get_plugin()!=this) { + //we create just operations that involve the current plugin + return 0; + } + bool r_sieved = r.get_plugin().is_sieve_relation(); + bool neg_sieved = neg.get_plugin().is_sieve_relation(); + SASSERT(r_sieved || neg_sieved); + const sieve_relation * sr = r_sieved ? static_cast(&r) : 0; + const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : 0; + const relation_base & inner_r = r_sieved ? sr->get_inner() : r; + const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; + + unsigned_vector ir_cols; + unsigned_vector ineg_cols; + + for(unsigned i=0; iis_inner_col(r_cols[i]); + bool neg_col_inner = neg_sieved && !sneg->is_inner_col(neg_cols[i]); + if(r_col_inner && neg_col_inner) { + ir_cols.push_back( r_sieved ? sr->get_inner_col(i) : i ); + ineg_cols.push_back( neg_sieved ? sneg->get_inner_col(i) : i ); + } + else if(!r_col_inner && neg_col_inner) { + //Sieved (i.e. full) column in r is matched on an inner column in neg. + //If we assume the column in neg is not full, no rows from the inner relation of + //r would be removed. So in this case we perform no operation at cost of a little + //impresicion. + return alloc(identity_relation_intersection_filter_fn); + } + else { + //Inner or sieved column in r must match a sieved column in neg. + //Since sieved columns are full, this is always true so we can skip the equality. + continue; + } + } + + relation_intersection_filter_fn * inner_fun = + get_manager().mk_filter_by_negation_fn(inner_r, inner_neg, ir_cols, ineg_cols); + if(!inner_fun) { + return 0; + } + return alloc(negation_filter_fn, inner_fun); + } + + +}; diff --git a/src/muz/rel/dl_sieve_relation.h b/src/muz/rel/dl_sieve_relation.h new file mode 100644 index 000000000..48402cd6d --- /dev/null +++ b/src/muz/rel/dl_sieve_relation.h @@ -0,0 +1,198 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_sieve_relation.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-11-11. + +Revision History: + +--*/ + +#ifndef _DL_SIEVE_RELATION_H_ +#define _DL_SIEVE_RELATION_H_ + +#include "dl_context.h" +#include "dl_relation_manager.h" + +namespace datalog { + + class sieve_relation; + + class sieve_relation_plugin : public relation_plugin { + friend class sieve_relation; + public: + struct rel_spec { + svector m_inner_cols; + family_id m_inner_kind; + + /** + Create uninitialized rel_spec. + */ + rel_spec() {} + /** + \c inner_kind==null_family_id means we will not specify a relation kind when requesting + the relation object from the relation_manager. + + \c inner_kind==null_family_id cannot hold in a specification of existing relation object. + */ + rel_spec(unsigned sig_sz, const bool * inner_cols, family_id inner_kind=null_family_id) + : m_inner_cols(sig_sz, inner_cols), m_inner_kind(inner_kind) {} + + bool operator==(const rel_spec & o) const { + return m_inner_kind==o.m_inner_kind && vectors_equal(m_inner_cols, o.m_inner_cols); + } + + struct hash { + unsigned operator()(const rel_spec & s) const { + return svector_hash()(s.m_inner_cols)^s.m_inner_kind; + } + }; + }; + private: + + class join_fn; + class transformer_fn; + class union_fn; + class filter_fn; + class negation_filter_fn; + + rel_spec_store > m_spec_store; + + family_id get_relation_kind(sieve_relation & r, const bool * inner_columns); + + void extract_inner_columns(const relation_signature & s, relation_plugin & inner, + svector & inner_columns); + void extract_inner_signature(const relation_signature & s, relation_signature & inner_sig); + void collect_inner_signature(const relation_signature & s, const svector & inner_columns, + relation_signature & inner_sig); + public: + static symbol get_name() { return symbol("sieve_relation"); } + static sieve_relation_plugin& get_plugin(relation_manager & rmgr); + + static sieve_relation& get(relation_base& r); + static sieve_relation const & get(relation_base const& r); + static sieve_relation* get(relation_base* r); + static sieve_relation const* get(relation_base const* r); + + sieve_relation_plugin(relation_manager & manager); + + virtual void initialize(family_id fid); + + family_id get_relation_kind(const relation_signature & sig, const bool * inner_columns, + family_id inner_kind); + family_id get_relation_kind(const relation_signature & sig, const svector & inner_columns, + family_id inner_kind) { + SASSERT(sig.size()==inner_columns.size()); + return get_relation_kind(sig, inner_columns.c_ptr(), inner_kind); + } + + virtual bool can_handle_signature(const relation_signature & s); + + virtual relation_base * mk_empty(const relation_signature & s); + sieve_relation * mk_empty(const sieve_relation & original); + virtual relation_base * mk_empty(const relation_base & original); + virtual relation_base * mk_empty(const relation_signature & s, family_id kind); + sieve_relation * mk_empty(const relation_signature & s, relation_plugin & inner_plugin); + + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + sieve_relation * mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin); + + + sieve_relation * mk_from_inner(const relation_signature & s, const bool * inner_columns, + relation_base * inner_rel); + sieve_relation * mk_from_inner(const relation_signature & s, const svector inner_columns, + relation_base * inner_rel) { + SASSERT(inner_columns.size()==s.size()); + return mk_from_inner(s, inner_columns.c_ptr(), inner_rel); + } + + protected: + + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + }; + + + // ----------------------------------- + // + // sieve_relation + // + // ----------------------------------- + + class sieve_relation : public relation_base { + friend class sieve_relation_plugin; + friend class sieve_relation_plugin::join_fn; + friend class sieve_relation_plugin::transformer_fn; + friend class sieve_relation_plugin::union_fn; + friend class sieve_relation_plugin::filter_fn; + + svector m_inner_cols; + + unsigned_vector m_sig2inner; + unsigned_vector m_inner2sig; + unsigned_vector m_ignored_cols; //in ascending order, so that it can be used in project-like functions + + scoped_rel m_inner; + + + sieve_relation(sieve_relation_plugin & p, const relation_signature & s, + const bool * inner_columns, relation_base * inner); + + public: + sieve_relation_plugin & get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + bool is_inner_col(unsigned idx) const { return m_sig2inner[idx]!=UINT_MAX; } + unsigned get_inner_col(unsigned idx) const { + SASSERT(is_inner_col(idx)); + return m_sig2inner[idx]; + } + bool no_sieved_columns() const { return m_ignored_cols.size()==0; } + bool no_inner_columns() const { return m_ignored_cols.size()==get_signature().size(); } + + relation_base & get_inner() { return *m_inner; } + const relation_base & get_inner() const { return *m_inner; } + + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual sieve_relation * clone() const; + virtual relation_base * complement(func_decl*p) const; + virtual void to_formula(expr_ref& fml) const; + + virtual bool empty() const { return get_inner().empty(); } + virtual void reset() { get_inner().reset(); } + virtual unsigned get_size_estimate_rows() const { return get_inner().get_size_estimate_rows(); } + virtual unsigned get_size_estimate_bytes() const { return get_inner().get_size_estimate_bytes(); } + + virtual void display(std::ostream & out) const; + }; + + +}; + +#endif /* _DL_SIEVE_RELATION_H_ */ + diff --git a/src/muz/rel/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp new file mode 100644 index 000000000..52d9618b8 --- /dev/null +++ b/src/muz/rel/dl_sparse_table.cpp @@ -0,0 +1,1246 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_sparse_table.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-24. + +Revision History: + +--*/ + +#include +#include"dl_context.h" +#include"dl_util.h" +#include"dl_sparse_table.h" + +namespace datalog { + + // ----------------------------------- + // + // entry_storage + // + // ----------------------------------- + + entry_storage::store_offset entry_storage::insert_or_get_reserve_content() { + SASSERT(has_reserve()); + store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); + if (m_reserve == entry_ofs) { + //entry inserted, so reserve is no longer a reserve + m_reserve = NO_RESERVE; + } + return entry_ofs; + } + bool entry_storage::insert_reserve_content() { + SASSERT(has_reserve()); + store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); + if (m_reserve == entry_ofs) { + //entry inserted, so reserve is no longer a reserve + m_reserve = NO_RESERVE; + return true; + } + return false; + } + + bool entry_storage::remove_reserve_content() { + SASSERT(has_reserve()); + store_offset entry_ofs; + if (!find_reserve_content(entry_ofs)) { + //the fact was not in the table + return false; + } + remove_offset(entry_ofs); + return true; + } + + void entry_storage::remove_offset(store_offset ofs) { + m_data_indexer.remove(ofs); + store_offset last_ofs = after_last_offset() - m_entry_size; + if (ofs!=last_ofs) { + SASSERT(ofs + m_entry_size <= last_ofs); + //we don't want any holes, so we put the last element at the place + //of the removed one + m_data_indexer.remove(last_ofs); + char * base = &m_data.get(0); + memcpy(base+ofs, base+last_ofs, m_entry_size); + m_data_indexer.insert(ofs); + } + if (has_reserve()) { + //we already have a reserve, so we need to shrink a little to keep having just one + resize_data(m_data_size-m_entry_size); + } + m_reserve=last_ofs; + } + + unsigned entry_storage::get_size_estimate_bytes() const { + unsigned sz = m_data.capacity(); + sz += m_data_indexer.capacity()*sizeof(storage_indexer::entry); + return sz; + } + + // ----------------------------------- + // + // sparse_table::column_layout + // + // ----------------------------------- + + unsigned get_domain_length(uint64 dom_size) { + SASSERT(dom_size>0); + + unsigned length = 0; + + unsigned dom_size_sm; + if (dom_size>UINT_MAX) { + dom_size_sm = static_cast(dom_size>>32); + length += 32; + if ( (dom_size&UINT_MAX)!=0 && dom_size_sm!=UINT_MAX ) { + dom_size_sm++; + } + } + else { + dom_size_sm=static_cast(dom_size); + } + if (dom_size_sm == 1) { + length += 1; //unary domains + } + else if (dom_size_sm > 0x80000000u) { + length += 32; + } + else { + length += get_num_1bits(next_power_of_two(dom_size_sm)-1); //ceil(log2(dom_size)) + } + return length; + } + + sparse_table::column_layout::column_layout(const table_signature & sig) + : m_functional_col_cnt(sig.functional_columns()) { + SASSERT(sig.size() > 0); + unsigned ofs = 0; + unsigned sig_sz = sig.size(); + unsigned first_functional = sig_sz-m_functional_col_cnt; + for (unsigned i=0; i0); + SASSERT(length<=64); + + if (size() > 0 && (length > 54 || i == first_functional)) { + //large domains must start byte-aligned, as well as functional columns + make_byte_aligned_end(size()-1); + ofs = back().next_ofs(); + } + + push_back(column_info(ofs, length)); + ofs += length; + } + make_byte_aligned_end(size()-1); + SASSERT(back().next_ofs()%8 == 0);//the entries must be aligned to whole bytes + m_entry_size = back().next_ofs()/8; + if (m_functional_col_cnt) { + SASSERT((*this)[first_functional].m_offset%8 == 0); + m_functional_part_size = m_entry_size - (*this)[first_functional].m_offset/8; + } + else { + m_functional_part_size = 0; + } + } + + void sparse_table::column_layout::make_byte_aligned_end(unsigned col_index0) { + unsigned ofs = (*this)[col_index0].next_ofs(); + unsigned ofs_bit_part = ofs%8; + unsigned rounded_ofs = (ofs_bit_part == 0) ? ofs : (ofs+8-ofs_bit_part); + + if (rounded_ofs!=ofs) { + SASSERT(rounded_ofs>ofs); + int diff = rounded_ofs-ofs; + unsigned col_idx = col_index0+1; + while(diff!=0) { + //we should always be able to fix the alignment by the time we reach zero + SASSERT(col_idx>0); + col_idx--; + column_info & ci = (*this)[col_idx]; + unsigned new_length = ci.m_length; + if (ci.m_length < 64) { + unsigned swallowed = std::min(64-static_cast(ci.m_length), diff); + diff -= swallowed; + new_length += swallowed; + } + unsigned new_ofs = ci.m_offset+diff; + ci = column_info(new_ofs, new_length); + } + } + + SASSERT(rounded_ofs%8 == 0); + SASSERT((*this)[col_index0].next_ofs()%8 == 0); + } + + // ----------------------------------- + // + // sparse_table + // + // ----------------------------------- + + class sparse_table::our_iterator_core : public iterator_core { + + class our_row : public row_interface { + const our_iterator_core & m_parent; + public: + our_row(const sparse_table & t, const our_iterator_core & parent) : + row_interface(t), + m_parent(parent) {} + + virtual table_element operator[](unsigned col) const { + return m_parent.m_layout.get(m_parent.m_ptr, col); + } + + }; + + const char * m_end; + const char * m_ptr; + unsigned m_fact_size; + our_row m_row_obj; + const column_layout & m_layout; + + public: + our_iterator_core(const sparse_table & t, bool finished) : + m_end(t.m_data.after_last()), + m_ptr(finished ? m_end : t.m_data.begin()), + m_fact_size(t.m_fact_size), + m_row_obj(t, *this), + m_layout(t.m_column_layout) {} + + virtual bool is_finished() const { + return m_ptr == m_end; + } + + virtual row_interface & operator*() { + SASSERT(!is_finished()); + return m_row_obj; + } + virtual void operator++() { + SASSERT(!is_finished()); + m_ptr+=m_fact_size; + } + }; + + class sparse_table::key_indexer { + protected: + unsigned_vector m_key_cols; + public: + typedef const store_offset * offset_iterator; + + /** + Iterators returned by \c begin() and \c end() are valid only as long as the \c query_result + object that returned them exists. + */ + struct query_result { + private: + bool m_singleton; + union { + store_offset m_single_result; + struct { + offset_iterator begin; + offset_iterator end; + } m_many; + }; + public: + /** + \brief Empty result. + */ + query_result() : m_singleton(false) { + m_many.begin = 0; + m_many.end = 0; + } + query_result(offset_iterator begin, offset_iterator end) : m_singleton(false) { + m_many.begin = begin; + m_many.end = end; + } + query_result(store_offset single_result) : m_singleton(true), m_single_result(single_result) {} + + offset_iterator begin() const { return m_singleton ? &m_single_result : m_many.begin; } + offset_iterator end() const { return m_singleton ? (&m_single_result+1) : m_many.end; } + bool empty() const { return begin() == end(); } + }; + + key_indexer(unsigned key_len, const unsigned * key_cols) + : m_key_cols(key_len, key_cols) {} + + virtual ~key_indexer() {} + + virtual void update(const sparse_table & t) {} + + virtual query_result get_matching_offsets(const key_value & key) const = 0; + }; + + + class sparse_table::general_key_indexer : public key_indexer { + typedef svector offset_vector; + typedef u_map index_map; + + index_map m_map; + mutable entry_storage m_keys; + store_offset m_first_nonindexed; + + + void key_to_reserve(const key_value & key) const { + m_keys.ensure_reserve(); + m_keys.write_into_reserve(reinterpret_cast(key.c_ptr())); + } + + offset_vector & get_matching_offset_vector(const key_value & key) { + key_to_reserve(key); + store_offset ofs = m_keys.insert_or_get_reserve_content(); + index_map::entry * e = m_map.find_core(ofs); + if (!e) { + TRACE("dl_table_relation", tout << "inserting\n";); + e = m_map.insert_if_not_there2(ofs, offset_vector()); + } + return e->get_data().m_value; + } + public: + general_key_indexer(unsigned key_len, const unsigned * key_cols) + : key_indexer(key_len, key_cols), + m_keys(key_len*sizeof(table_element)), + m_first_nonindexed(0) {} + + virtual void update(const sparse_table & t) { + if (m_first_nonindexed == t.m_data.after_last_offset()) { + return; + } + SASSERT(m_first_nonindexedinsert(ofs); + } + + m_first_nonindexed = t.m_data.after_last_offset(); + } + + virtual query_result get_matching_offsets(const key_value & key) const { + key_to_reserve(key); + store_offset ofs; + if (!m_keys.find_reserve_content(ofs)) { + return query_result(); + } + index_map::entry * e = m_map.find_core(ofs); + if (!e) { + return query_result(); + } + const offset_vector & res = e->get_data().m_value; + return query_result(res.begin(), res.end()); + } + }; + + /** + When doing lookup using this index, the content of the reserve in sparse_table::m_data changes. + */ + class sparse_table::full_signature_key_indexer : public key_indexer { + const sparse_table & m_table; + + /** + Permutation of key columns to make it into table facts. If empty, no permutation is necessary. + */ + unsigned_vector m_permutation; + mutable table_fact m_key_fact; + public: + + static bool can_handle(unsigned key_len, const unsigned * key_cols, const sparse_table & t) { + unsigned non_func_cols = t.get_signature().first_functional(); + if (key_len!=non_func_cols) { + return false; + } + counter ctr; + ctr.count(key_len, key_cols); + if (ctr.get_max_counter_value()!=1 || ctr.get_max_positive()!=non_func_cols-1) { + return false; + } + SASSERT(ctr.get_positive_count() == non_func_cols); + return true; + } + + full_signature_key_indexer(unsigned key_len, const unsigned * key_cols, const sparse_table & t) + : key_indexer(key_len, key_cols), + m_table(t) { + SASSERT(can_handle(key_len, key_cols, t)); + + m_permutation.resize(key_len); + for (unsigned i=0; i(m_table); + t.write_into_reserve(m_key_fact.c_ptr()); + + store_offset res; + if (!t.m_data.find_reserve_content(res)) { + return query_result(); + } + return query_result(res); + } + }; + + sparse_table::sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity) + : table_base(p, sig), + m_column_layout(sig), + m_fact_size(m_column_layout.m_entry_size), + m_data(m_fact_size, m_column_layout.m_functional_part_size, init_capacity) {} + + sparse_table::sparse_table(const sparse_table & t) + : table_base(t.get_plugin(), t.get_signature()), + m_column_layout(t.m_column_layout), + m_fact_size(t.m_fact_size), + m_data(t.m_data) {} + + table_base * sparse_table::clone() const { + return get_plugin().mk_clone(*this); + } + + sparse_table::~sparse_table() { + reset_indexes(); + } + + void sparse_table::reset() { + reset_indexes(); + m_data.reset(); + } + + table_base::iterator sparse_table::begin() const { + return mk_iterator(alloc(our_iterator_core, *this, false)); + } + + table_base::iterator sparse_table::end() const { + return mk_iterator(alloc(our_iterator_core, *this, true)); + } + + sparse_table::key_indexer& sparse_table::get_key_indexer(unsigned key_len, + const unsigned * key_cols) const { +#if Z3DEBUG + //We allow indexes only on non-functional columns because we want to be able to modify them + //without having to worry about updating indexes. + //Maybe we might keep a list of indexes that contain functional columns and on an update reset + //only those. + SASSERT(key_len == 0 || + counter().count(key_len, key_cols).get_max_positive()get_data().m_value) { + if (full_signature_key_indexer::can_handle(key_len, key_cols, *this)) { + key_map_entry->get_data().m_value = alloc(full_signature_key_indexer, key_len, key_cols, *this); + } + else { + key_map_entry->get_data().m_value = alloc(general_key_indexer, key_len, key_cols); + } + } + key_indexer & indexer = *key_map_entry->get_data().m_value; + indexer.update(*this); + return indexer; + } + + void sparse_table::reset_indexes() { + key_index_map::iterator kmit = m_key_indexes.begin(); + key_index_map::iterator kmend = m_key_indexes.end(); + for (; kmit!=kmend; ++kmit) { + dealloc((*kmit).m_value); + } + m_key_indexes.reset(); + } + + void sparse_table::write_into_reserve(const table_element* f) { + TRACE("dl_table_relation", tout << "\n";); + m_data.ensure_reserve(); + char * reserve = m_data.get_reserve_ptr(); + unsigned col_cnt = m_column_layout.size(); + for (unsigned i = 0; i < col_cnt; ++i) { + SASSERT(f[i] < get_signature()[i]); //the value fits into the table signature + m_column_layout.set(reserve, i, f[i]); + } + } + + bool sparse_table::add_fact(const char * data) { + m_data.write_into_reserve(data); + return add_reserve_content(); + } + + void sparse_table::add_fact(const table_fact & f) { + write_into_reserve(f.c_ptr()); + add_reserve_content(); + } + + bool sparse_table::add_reserve_content() { + return m_data.insert_reserve_content(); + } + + bool sparse_table::contains_fact(const table_fact & f) const { + sparse_table & t = const_cast(*this); + t.write_into_reserve(f.c_ptr()); + unsigned func_col_cnt = get_signature().functional_columns(); + if (func_col_cnt == 0) { + return t.m_data.reserve_content_already_present(); + } + else { + store_offset ofs; + if (!t.m_data.find_reserve_content(ofs)) { + return false; + } + unsigned sz = get_signature().size(); + for (unsigned i=func_col_cnt; i(*this); + t.write_into_reserve(f.c_ptr()); + store_offset ofs; + if (!t.m_data.find_reserve_content(ofs)) { + return false; + } + unsigned sz = sig.size(); + for (unsigned i=sig.first_functional(); ipre_projection_idx); + dest_layout.set(dest, dest_idx++, src_layout.get(src, i)); + } + } + + void sparse_table::concatenate_rows(const column_layout & layout1, const column_layout & layout2, + const column_layout & layout_res, const char * ptr1, const char * ptr2, char * res, + const unsigned * removed_cols) { + unsigned t1non_func = layout1.size()-layout1.m_functional_col_cnt; + unsigned t2non_func = layout2.size()-layout2.m_functional_col_cnt; + unsigned t1cols = layout1.size(); + unsigned t2cols = layout2.size(); + unsigned orig_i = 0; + unsigned res_i = 0; + const unsigned * next_removed = removed_cols; + copy_columns(layout1, layout_res, 0, t1non_func, ptr1, res, res_i, orig_i, next_removed); + copy_columns(layout2, layout_res, 0, t2non_func, ptr2, res, res_i, orig_i, next_removed); + copy_columns(layout1, layout_res, t1non_func, t1cols, ptr1, res, res_i, orig_i, next_removed); + copy_columns(layout2, layout_res, t2non_func, t2cols, ptr2, res, res_i, orig_i, next_removed); + } + + void sparse_table::garbage_collect() { + if (memory::above_high_watermark()) { + get_plugin().garbage_collect(); + } + if (memory::above_high_watermark()) { + IF_VERBOSE(1, verbose_stream() << "Ran out of memory while filling table of size: " << get_size_estimate_rows() << " rows " << get_size_estimate_bytes() << " bytes\n";); + throw out_of_memory_error(); + } + } + + void sparse_table::self_agnostic_join_project(const sparse_table & t1, const sparse_table & t2, + unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, + const unsigned * removed_cols, bool tables_swapped, sparse_table & result) { + + unsigned t1_entry_size = t1.m_fact_size; + unsigned t2_entry_size = t2.m_fact_size; + + unsigned t1idx = 0; + unsigned t1end = t1.m_data.after_last_offset(); + + TRACE("dl_table_relation", + tout << "joined_col_cnt: " << joined_col_cnt << "\n"; + tout << "t1_entry_size: " << t1_entry_size << "\n"; + tout << "t2_entry_size: " << t2_entry_size << "\n"; + t1.display(tout); + t2.display(tout); + tout << (&t1) << " " << (&t2) << " " << (&result) << "\n"; + ); + + if (joined_col_cnt == 0) { + unsigned t2idx = 0; + unsigned t2end = t2.m_data.after_last_offset(); + + for (; t1idx!=t1end; t1idx+=t1_entry_size) { + for (t2idx = 0; t2idx != t2end; t2idx += t2_entry_size) { + result.m_data.ensure_reserve(); + result.garbage_collect(); + char * res_reserve = result.m_data.get_reserve_ptr(); + char const* t1ptr = t1.get_at_offset(t1idx); + char const* t2ptr = t2.get_at_offset(t2idx); + if (tables_swapped) { + concatenate_rows(t2.m_column_layout, t1.m_column_layout, result.m_column_layout, + t2ptr, t1ptr, res_reserve, removed_cols); + } else { + concatenate_rows(t1.m_column_layout, t2.m_column_layout, result.m_column_layout, + t1ptr, t2ptr, res_reserve, removed_cols); + } + result.add_reserve_content(); + } + } + return; + } + + key_value t1_key; + t1_key.resize(joined_col_cnt); + key_indexer& t2_indexer = t2.get_key_indexer(joined_col_cnt, t2_joined_cols); + + bool key_modified = true; + key_indexer::query_result t2_offsets; + + for (; t1idx != t1end; t1idx += t1_entry_size) { + for (unsigned i = 0; i < joined_col_cnt; i++) { + table_element val = t1.m_column_layout.get(t1.get_at_offset(t1idx), t1_joined_cols[i]); + TRACE("dl_table_relation", tout << "val: " << val << " " << t1idx << " " << t1_joined_cols[i] << "\n";); + if (t1_key[i] != val) { + t1_key[i] = val; + key_modified = true; + } + } + if (key_modified) { + t2_offsets = t2_indexer.get_matching_offsets(t1_key); + key_modified = false; + } + + if (t2_offsets.empty()) { + continue; + } + + key_indexer::offset_iterator t2ofs_it = t2_offsets.begin(); + key_indexer::offset_iterator t2ofs_end = t2_offsets.end(); + for (; t2ofs_it != t2ofs_end; ++t2ofs_it) { + store_offset t2ofs = *t2ofs_it; + result.m_data.ensure_reserve(); + result.garbage_collect(); + char * res_reserve = result.m_data.get_reserve_ptr(); + char const * t1ptr = t1.get_at_offset(t1idx); + char const * t2ptr = t2.get_at_offset(t2ofs); + if (tables_swapped) { + concatenate_rows(t2.m_column_layout, t1.m_column_layout, result.m_column_layout, + t2ptr, t1ptr, res_reserve, removed_cols); + } else { + concatenate_rows(t1.m_column_layout, t2.m_column_layout, result.m_column_layout, + t1ptr, t2ptr, res_reserve, removed_cols); + } + result.add_reserve_content(); + } + } + } + + + // ----------------------------------- + // + // sparse_table_plugin + // + // ----------------------------------- + + sparse_table_plugin::sparse_table_plugin(relation_manager & manager) + : table_plugin(symbol("sparse"), manager) {} + + sparse_table_plugin::~sparse_table_plugin() { + reset(); + } + + void sparse_table_plugin::reset() { + table_pool::iterator it = m_pool.begin(); + table_pool::iterator end = m_pool.end(); + for (; it!=end; ++it) { + sp_table_vector * vect = it->m_value; + sp_table_vector::iterator it = vect->begin(); + sp_table_vector::iterator end = vect->end(); + for (; it!=end; ++it) { + (*it)->destroy(); //calling deallocate() would only put the table back into the pool + } + dealloc(vect); + } + m_pool.reset(); + } + + void sparse_table_plugin::garbage_collect() { + IF_VERBOSE(2, verbose_stream() << "garbage collecting "<< memory::get_allocation_size() << " bytes down to ";); + reset(); + IF_VERBOSE(2, verbose_stream() << memory::get_allocation_size() << " bytes\n";); + } + + void sparse_table_plugin::recycle(sparse_table * t) { + const table_signature & sig = t->get_signature(); + t->reset(); + + table_pool::entry * e = m_pool.insert_if_not_there2(sig, 0); + sp_table_vector * & vect = e->get_data().m_value; + if (vect == 0) { + vect = alloc(sp_table_vector); + } + IF_VERBOSE(12, verbose_stream() << "Recycle: " << t->get_size_estimate_bytes() << "\n";); + + vect->push_back(t); + } + + table_base * sparse_table_plugin::mk_empty(const table_signature & s) { + SASSERT(can_handle_signature(s)); + + sp_table_vector * vect; + if (!m_pool.find(s, vect) || vect->empty()) { + return alloc(sparse_table, *this, s); + } + sparse_table * res = vect->back(); + vect->pop_back(); + return res; + } + + sparse_table * sparse_table_plugin::mk_clone(const sparse_table & t) { + sparse_table * res = static_cast(mk_empty(t.get_signature())); + res->m_data = t.m_data; + return res; + } + + + bool sparse_table_plugin::join_involves_functional(const table_signature & s1, const table_signature & s2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (col_cnt == 0) { + return false; + } + return counter().count(col_cnt, cols1).get_max_positive()>=s1.first_functional() + || counter().count(col_cnt, cols2).get_max_positive()>=s2.first_functional(); + } + + + class sparse_table_plugin::join_project_fn : public convenient_table_join_project_fn { + public: + join_project_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) + : convenient_table_join_project_fn(t1_sig, t2_sig, col_cnt, cols1, cols2, + removed_col_cnt, removed_cols) { + m_removed_cols.push_back(UINT_MAX); + } + + virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { + + const sparse_table & t1 = static_cast(tb1); + const sparse_table & t2 = static_cast(tb2); + + sparse_table_plugin & plugin = t1.get_plugin(); + + sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + + //If we join with some intersection, want to iterate over the smaller table and + //do indexing into the bigger one. If we simply do a product, we want the bigger + //one to be at the outer iteration (then the small one will hopefully fit into + //the cache) + if ( (t1.row_count() > t2.row_count()) == (!m_cols1.empty()) ) { + sparse_table::self_agnostic_join_project(t2, t1, m_cols1.size(), m_cols2.c_ptr(), + m_cols1.c_ptr(), m_removed_cols.c_ptr(), true, *res); + } + else { + sparse_table::self_agnostic_join_project(t1, t2, m_cols1.size(), m_cols1.c_ptr(), + m_cols2.c_ptr(), m_removed_cols.c_ptr(), false, *res); + } + TRACE("dl_table_relation", tb1.display(tout); tb2.display(tout); res->display(tout); ); + return res; + } + }; + + table_join_fn * sparse_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + const table_signature & sig1 = t1.get_signature(); + const table_signature & sig2 = t2.get_signature(); + if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() + || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { + //We also don't allow indexes on functional columns (and they are needed for joins) + return 0; + } + return mk_join_project_fn(t1, t2, col_cnt, cols1, cols2, 0, static_cast(0)); + } + + table_join_fn * sparse_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) { + const table_signature & sig1 = t1.get_signature(); + const table_signature & sig2 = t2.get_signature(); + if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() + || removed_col_cnt == t1.get_signature().size()+t2.get_signature().size() + || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { + //We don't allow sparse tables with zero signatures (and project on all columns leads to such) + //We also don't allow indexes on functional columns. + return 0; + } + return alloc(join_project_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + } + + class sparse_table_plugin::union_fn : public table_union_fn { + public: + virtual void operator()(table_base & tgt0, const table_base & src0, table_base * delta0) { + + sparse_table & tgt = static_cast(tgt0); + const sparse_table & src = static_cast(src0); + sparse_table * delta = static_cast(delta0); + + unsigned fact_size = tgt.m_fact_size; + const char* ptr = src.m_data.begin(); + const char* after_last=src.m_data.after_last(); + for (; ptradd_fact(ptr); + } + } + } + }; + + table_union_fn * sparse_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta) { + if (tgt.get_kind()!=get_kind() || src.get_kind()!=get_kind() + || (delta && delta->get_kind()!=get_kind()) + || tgt.get_signature()!=src.get_signature() + || (delta && delta->get_signature()!=tgt.get_signature())) { + return 0; + } + return alloc(union_fn); + } + + class sparse_table_plugin::project_fn : public convenient_table_project_fn { + const unsigned m_inp_col_cnt; + const unsigned m_removed_col_cnt; + const unsigned m_result_col_cnt; + public: + project_fn(const table_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_table_project_fn(orig_sig, removed_col_cnt, removed_cols), + m_inp_col_cnt(orig_sig.size()), + m_removed_col_cnt(removed_col_cnt), + m_result_col_cnt(orig_sig.size()-removed_col_cnt) { + SASSERT(removed_col_cnt>0); + } + + virtual void transform_row(const char * src, char * tgt, + const sparse_table::column_layout & src_layout, + const sparse_table::column_layout & tgt_layout) { + unsigned r_idx=0; + unsigned tgt_i=0; + for (unsigned i=0; i(tb); + + unsigned t_fact_size = t.m_fact_size; + + sparse_table_plugin & plugin = t.get_plugin(); + sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + + const sparse_table::column_layout & src_layout = t.m_column_layout; + const sparse_table::column_layout & tgt_layout = res->m_column_layout; + + const char* t_ptr = t.m_data.begin(); + const char* t_end = t.m_data.after_last(); + for (; t_ptr!=t_end; t_ptr+=t_fact_size) { + SASSERT(t_ptrm_data.ensure_reserve(); + char * res_ptr = res->m_data.get_reserve_ptr(); + transform_row(t_ptr, res_ptr, src_layout, tgt_layout); + res->m_data.insert_reserve_content(); + } + return res; + } + }; + + table_transformer_fn * sparse_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + if (col_cnt == t.get_signature().size()) { + return 0; + } + return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); + } + + + class sparse_table_plugin::select_equal_and_project_fn : public convenient_table_transformer_fn { + const unsigned m_col; + sparse_table::key_value m_key; + public: + select_equal_and_project_fn(const table_signature & orig_sig, table_element val, unsigned col) + : m_col(col) { + table_signature::from_project(orig_sig, 1, &col, get_result_signature()); + m_key.push_back(val); + } + + virtual table_base * operator()(const table_base & tb) { + const sparse_table & t = static_cast(tb); + + sparse_table_plugin & plugin = t.get_plugin(); + sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + + const sparse_table::column_layout & t_layout = t.m_column_layout; + const sparse_table::column_layout & res_layout = res->m_column_layout; + unsigned t_cols = t_layout.size(); + + sparse_table::key_indexer & indexer = t.get_key_indexer(1, &m_col); + sparse_table::key_indexer::query_result t_offsets = indexer.get_matching_offsets(m_key); + if (t_offsets.empty()) { + //no matches + return res; + } + sparse_table::key_indexer::offset_iterator ofs_it=t_offsets.begin(); + sparse_table::key_indexer::offset_iterator ofs_end=t_offsets.end(); + + for (; ofs_it!=ofs_end; ++ofs_it) { + sparse_table::store_offset t_ofs = *ofs_it; + const char * t_ptr = t.get_at_offset(t_ofs); + + res->m_data.ensure_reserve(); + char * res_reserve = res->m_data.get_reserve_ptr(); + + unsigned res_i = 0; + for (unsigned i=0; iadd_reserve_content(); + } + return res; + } + }; + + table_transformer_fn * sparse_table_plugin::mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col) { + if (t.get_kind()!=get_kind() || t.get_signature().size() == 1 || col>=t.get_signature().first_functional()) { + //We don't allow sparse tables with zero signatures (and project on a single + //column table produces one). + //We also don't allow indexes on functional columns. And our implementation of + //select_equal_and_project uses index on \c col. + return 0; + } + return alloc(select_equal_and_project_fn, t.get_signature(), value, col); + } + + + class sparse_table_plugin::rename_fn : public convenient_table_rename_fn { + unsigned_vector m_out_of_cycle; + public: + rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) + : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { + SASSERT(permutation_cycle_len>=2); + idx_set cycle_cols; + for (unsigned i=0; i < permutation_cycle_len; ++i) { + cycle_cols.insert(permutation_cycle[i]); + } + for (unsigned i=0; i < orig_sig.size(); ++i) { + if (!cycle_cols.contains(i)) { + m_out_of_cycle.push_back(i); + } + } + } + + void transform_row(const char * src, char * tgt, + const sparse_table::column_layout & src_layout, + const sparse_table::column_layout & tgt_layout) { + + for (unsigned i=1; i < m_cycle.size(); ++i) { + tgt_layout.set(tgt, m_cycle[i-1], src_layout.get(src, m_cycle[i])); + } + tgt_layout.set(tgt, m_cycle[m_cycle.size()-1], src_layout.get(src, m_cycle[0])); + + unsigned_vector::const_iterator it = m_out_of_cycle.begin(); + unsigned_vector::const_iterator end = m_out_of_cycle.end(); + for (; it!=end; ++it) { + unsigned col = *it; + tgt_layout.set(tgt, col, src_layout.get(src, col)); + } + } + + virtual table_base * operator()(const table_base & tb) { + + const sparse_table & t = static_cast(tb); + + unsigned t_fact_size = t.m_fact_size; + + sparse_table_plugin & plugin = t.get_plugin(); + sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + + unsigned res_fact_size = res->m_fact_size; + unsigned res_data_size = res_fact_size*t.row_count(); + + res->m_data.resize_data(res_data_size); + + //here we can separate data creatin and insertion into hashmap, since we know + //that no row will become duplicit + + //create the data + const char* t_ptr = t.m_data.begin(); + char* res_ptr = res->m_data.begin(); + char* res_end = res_ptr+res_data_size; + for (; res_ptr!=res_end; t_ptr+=t_fact_size, res_ptr+=res_fact_size) { + transform_row(t_ptr, res_ptr, t.m_column_layout, res->m_column_layout); + } + + //and insert them into the hash-map + for (unsigned i=0; i!=res_data_size; i+=res_fact_size) { + TRUSTME(res->m_data.insert_offset(i)); + } + + return res; + } + }; + + table_transformer_fn * sparse_table_plugin::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { + if (t.get_kind()!=get_kind()) { + return 0; + } + return alloc(rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); + } + + class sparse_table_plugin::negation_filter_fn : public convenient_table_negation_filter_fn { + typedef sparse_table::store_offset store_offset; + typedef sparse_table::key_value key_value; + typedef sparse_table::key_indexer key_indexer; + + bool m_joining_neg_non_functional; + + /** + Used by \c collect_intersection_offsets function. + If tgt_is_first is false, contains the same items as \c res. + */ + idx_set m_intersection_content; + + public: + negation_filter_fn(const table_base & tgt, const table_base & neg, + unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) + : convenient_table_negation_filter_fn(tgt, neg, joined_col_cnt, t_cols, negated_cols) { + unsigned neg_fisrt_func = neg.get_signature().first_functional(); + counter ctr; + ctr.count(m_cols2); + m_joining_neg_non_functional = ctr.get_max_counter_value() == 1 + && ctr.get_positive_count() == neg_fisrt_func + && (neg_fisrt_func == 0 || ctr.get_max_positive() == neg_fisrt_func-1); + } + + /** + Collect offsets of rows in \c t1 or \c t2 (depends on whether \c tgt_is_first is true or false) + that have a match in the other table into \c res. Offsets in \c res are in ascending order. + */ + void collect_intersection_offsets(const sparse_table & t1, const sparse_table & t2, + bool tgt_is_first, svector & res) { + SASSERT(res.empty()); + + if (!tgt_is_first) { + m_intersection_content.reset(); + } + + unsigned joined_col_cnt = m_cols1.size(); + unsigned t1_entry_size = t1.m_data.entry_size(); + + const unsigned * cols1 = tgt_is_first ? m_cols1.c_ptr() : m_cols2.c_ptr(); + const unsigned * cols2 = tgt_is_first ? m_cols2.c_ptr() : m_cols1.c_ptr(); + + key_value t1_key; + t1_key.resize(joined_col_cnt); + key_indexer & t2_indexer = t2.get_key_indexer(joined_col_cnt, cols2); + + bool key_modified=true; + key_indexer::query_result t2_offsets; + store_offset t1_after_last = t1.m_data.after_last_offset(); + for (store_offset t1_ofs=0; t1_ofs(tgt0); + const sparse_table & neg = static_cast(neg0); + + if (m_cols1.size() == 0) { + if (!neg.empty()) { + tgt.reset(); + } + return; + } + + svector to_remove; //offsets here are in increasing order + + //We don't do just the simple tgt.row_count()>neg.row_count() because the swapped case is + //more expensive. The constant 4 is, however, just my guess what the ratio might be. + if (tgt.row_count()/4>neg.row_count()) { + collect_intersection_offsets(neg, tgt, false, to_remove); + } + else { + collect_intersection_offsets(tgt, neg, true, to_remove); + } + + if (to_remove.empty()) { + return; + } + + //the largest offsets are at the end, so we can remove them one by one + while(!to_remove.empty()) { + store_offset removed_ofs = to_remove.back(); + to_remove.pop_back(); + tgt.m_data.remove_offset(removed_ofs); + } + tgt.reset_indexes(); + } + + }; + + table_intersection_filter_fn * sparse_table_plugin::mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + if (!check_kind(t) || !check_kind(negated_obj) + || join_involves_functional(t.get_signature(), negated_obj.get_signature(), joined_col_cnt, + t_cols, negated_cols) ) { + return 0; + } + return alloc(negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); + } + + unsigned sparse_table::get_size_estimate_bytes() const { + unsigned sz = 0; + sz += m_data.get_size_estimate_bytes(); + sz += m_key_indexes.capacity()*8; // TBD + return sz; + } + + +}; + diff --git a/src/muz/rel/dl_sparse_table.h b/src/muz/rel/dl_sparse_table.h new file mode 100644 index 000000000..010277b6b --- /dev/null +++ b/src/muz/rel/dl_sparse_table.h @@ -0,0 +1,480 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_table.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-01. + +Revision History: + +--*/ + +#ifndef _DL_SPARSE_TABLE_H_ +#define _DL_SPARSE_TABLE_H_ + +#include +#include +#include + +#include "ast.h" +#include "bit_vector.h" +#include "buffer.h" +#include "hashtable.h" +#include "map.h" +#include "ref_vector.h" +#include "vector.h" + +#include "dl_base.h" + + +namespace datalog { + class sparse_table; + + class sparse_table_plugin : public table_plugin { + friend class sparse_table; + protected: + class join_project_fn; + class union_fn; + class transformer_fn; + class rename_fn; + class project_fn; + class negation_filter_fn; + class select_equal_and_project_fn; + + typedef ptr_vector sp_table_vector; + typedef map table_pool; + + table_pool m_pool; + + void recycle(sparse_table * t); + + void garbage_collect(); + + void reset(); + + static bool join_involves_functional(const table_signature & s1, const table_signature & s2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + + public: + typedef sparse_table table; + + sparse_table_plugin(relation_manager & manager); + ~sparse_table_plugin(); + + virtual bool can_handle_signature(const table_signature & s) + { return s.size()>0; } + + virtual table_base * mk_empty(const table_signature & s); + sparse_table * mk_clone(const sparse_table & t); + + protected: + virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols); + virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta); + virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col); + virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + }; + + class entry_storage { + public: + typedef unsigned store_offset; + private: + typedef svector storage; + + class offset_hash_proc { + storage & m_storage; + unsigned m_unique_entry_size; + public: + offset_hash_proc(storage & s, unsigned unique_entry_sz) + : m_storage(s), m_unique_entry_size(unique_entry_sz) {} + unsigned operator()(store_offset ofs) const { + return string_hash(m_storage.c_ptr()+ofs, m_unique_entry_size, 0); + } + }; + + class offset_eq_proc { + storage & m_storage; + unsigned m_unique_entry_size; + public: + offset_eq_proc(storage & s, unsigned unique_entry_sz) + : m_storage(s), m_unique_entry_size(unique_entry_sz) {} + bool operator()(store_offset o1, store_offset o2) const { + const char * base = m_storage.c_ptr(); + return memcmp(base+o1, base+o2, m_unique_entry_size)==0; + } + }; + + typedef hashtable storage_indexer; + + static const store_offset NO_RESERVE = UINT_MAX; + + unsigned m_entry_size; + unsigned m_unique_part_size; + unsigned m_data_size; + /** + Invariant: Every or all but one blocks of length \c m_entry_size in the \c m_data vector + are unique sequences of bytes and have their offset stored in the \c m_data_indexer hashtable. + If the offset of the last block is not stored in the hashtable, it is stored in the \c m_reserve + variable. Otherwise \c m_reserve==NO_RESERVE. + + The size of m_data is actually 8 bytes larger than stated in m_data_size, so that we may + deref an uint64 pointer at the end of the array. + */ + storage m_data; + storage_indexer m_data_indexer; + store_offset m_reserve; + public: + entry_storage(unsigned entry_size, unsigned functional_size = 0, unsigned init_size = 0) + : m_entry_size(entry_size), + m_unique_part_size(entry_size-functional_size), + m_data_indexer(next_power_of_two(std::max(8u,init_size)), + offset_hash_proc(m_data, m_unique_part_size), offset_eq_proc(m_data, m_unique_part_size)), + m_reserve(NO_RESERVE) { + SASSERT(entry_size>0); + SASSERT(functional_size<=entry_size); + resize_data(init_size); + resize_data(0); + } + entry_storage(const entry_storage &s) + : m_entry_size(s.m_entry_size), + m_unique_part_size(s.m_unique_part_size), + m_data_size(s.m_data_size), + m_data(s.m_data), + m_data_indexer(next_power_of_two(std::max(8u,s.entry_count())), + offset_hash_proc(m_data, m_unique_part_size), offset_eq_proc(m_data, m_unique_part_size)), + m_reserve(s.m_reserve) { + store_offset after_last=after_last_offset(); + for(store_offset i=0; i(this)->get(ofs); } + + unsigned entry_count() const { return m_data_indexer.size(); } + + store_offset after_last_offset() const { + return (m_reserve==NO_RESERVE) ? m_data_size : m_reserve; + } + + char * begin() { return get(0); } + const char * begin() const { return get(0); } + const char * after_last() const { return get(after_last_offset()); } + + + bool has_reserve() const { return m_reserve!=NO_RESERVE; } + store_offset reserve() const { SASSERT(has_reserve()); return m_reserve; } + + void ensure_reserve() { + if(has_reserve()) { + SASSERT(m_reserve==m_data_size-m_entry_size); + return; + } + m_reserve=m_data_size; + resize_data(m_data_size+m_entry_size); + } + + /** + \brief Return pointer to the reserve. + + The reserve must exist when the function is called. + */ + char * get_reserve_ptr() { + SASSERT(has_reserve()); + return &m_data.get(reserve()); + } + + bool reserve_content_already_present() const { + SASSERT(has_reserve()); + return m_data_indexer.contains(reserve()); + } + + bool find_reserve_content(store_offset & result) const { + SASSERT(has_reserve()); + storage_indexer::entry * indexer_entry = m_data_indexer.find_core(reserve()); + if(!indexer_entry) { + return false; + } + result = indexer_entry->get_data(); + return true; + } + + /** + \brief Write fact \c f into the reserve at the end of the \c m_data storage. + + If the reserve does not exist, this function creates it. + */ + void write_into_reserve(const char * data) { + ensure_reserve(); + memcpy(get_reserve_ptr(), data, m_entry_size); + } + + /** + \brief If the fact in reserve is not in the table, insert it there and return true; + otherwise return false. + + When a fact is inserted into the table, the reserve becomes part of the table and + is no longer a reserve. + */ + bool insert_reserve_content(); + store_offset insert_or_get_reserve_content(); + bool remove_reserve_content(); + /** + Remove data at the offset \c ofs. + + Data with offset lower than \c ofs are not be modified by this function, data with + higher offset may be moved. + */ + void remove_offset(store_offset ofs); + + + //the following two operations allow breaking of the object invariant! + void resize_data(unsigned sz) { + m_data_size = sz; + m_data.resize(sz + sizeof(uint64)); + } + + bool insert_offset(store_offset ofs) { + return m_data_indexer.insert_if_not_there(ofs)==ofs; + } + }; + + class sparse_table : public table_base { + friend class sparse_table_plugin; + friend class sparse_table_plugin::join_project_fn; + friend class sparse_table_plugin::union_fn; + friend class sparse_table_plugin::transformer_fn; + friend class sparse_table_plugin::rename_fn; + friend class sparse_table_plugin::project_fn; + friend class sparse_table_plugin::negation_filter_fn; + friend class sparse_table_plugin::select_equal_and_project_fn; + + class our_iterator_core; + class key_indexer; + class general_key_indexer; + class full_signature_key_indexer; + typedef entry_storage::store_offset store_offset; + + + class column_info { + unsigned m_big_offset; + unsigned m_small_offset; + uint64 m_mask; + uint64 m_write_mask; + public: + unsigned m_offset; //!< in bits + unsigned m_length; //!< in bits + + column_info(unsigned offset, unsigned length) \ + : m_big_offset(offset/8), + m_small_offset(offset%8), + m_mask( length==64 ? ULLONG_MAX : (static_cast(1)<(rec+m_big_offset); + uint64 res = *ptr; + res>>=m_small_offset; + res&=m_mask; + return res; + } + void set(char * rec, table_element val) const { + SASSERT( (val&~m_mask)==0 ); //the value fits into the column + uint64 * ptr = reinterpret_cast(rec+m_big_offset); + *ptr&=m_write_mask; + *ptr|=val< { + + void make_byte_aligned_end(unsigned col_index); + public: + + unsigned m_entry_size; + /** + Number of last bytes which correspond to functional columns in the signature. + */ + unsigned m_functional_part_size; + unsigned m_functional_col_cnt; + + column_layout(const table_signature & sig); + + table_element get(const char * rec, unsigned col) const { + return (*this)[col].get(rec); + } + void set(char * rec, unsigned col, table_element val) const { + return (*this)[col].set(rec, val); + } + }; + + + typedef svector key_spec; //sequence of columns in a key + typedef svector key_value; //values of key columns + typedef map, + vector_eq_proc > key_index_map; + + static const store_offset NO_RESERVE = UINT_MAX; + + column_layout m_column_layout; + unsigned m_fact_size; + entry_storage m_data; + mutable key_index_map m_key_indexes; + + + const char * get_at_offset(store_offset i) const { + return m_data.get(i); + } + + table_element get_cell(store_offset ofs, unsigned column) const { + return m_column_layout.get(m_data.get(ofs), column); + } + + void set_cell(store_offset ofs, unsigned column, table_element val) { + m_column_layout.set(m_data.get(ofs), column, val); + } + + void write_into_reserve(const table_element* f); + + /** + \brief Return reference to an indexer over columns in \c key_cols. + + An indexer can retrieve a sequence of offsets that with \c key_cols columns equal to + the specified key. Indexers are populated lazily -- they remember the position of the + last fact they contain, and when an indexer is retrieved by the \c get_key_indexer function, + all the new facts are added into the indexer. + + When a fact is removed from the table, all indexers are destroyed. This is not an extra + expense in the current use scenario, because we first perform all fact removals and do the + joins only after that (joins are the only operations that lead to index construction). + */ + key_indexer& get_key_indexer(unsigned key_len, const unsigned * key_cols) const; + + void reset_indexes(); + + static void copy_columns(const column_layout & src_layout, const column_layout & dest_layout, + unsigned start_index, unsigned after_last, const char * src, char * dest, + unsigned & dest_idx, unsigned & pre_projection_idx, const unsigned * & next_removed); + + /** + \c array \c removed_cols contains column indexes to be removed in ascending order and + is terminated by a number greated than the highest column index of a join the the two tables. + This is to simplify the traversal of the array when building facts. + */ + static void concatenate_rows(const column_layout & layout1, const column_layout & layout2, + const column_layout & layout_res, const char * ptr1, const char * ptr2, char * res, + const unsigned * removed_cols); + + /** + \brief Perform join-project between t1 and t2 iterating through t1 and retrieving relevant + columns from t2 using indexing. + + \c array \c removed_cols contains column indexes to be removed in ascending order and + is terminated by a number greated than the highest column index of a join the the two tables. + This is to simplify the traversal of the array when building facts. + + \c tables_swapped value means that the resulting facts should contain facts from t2 first, + instead of the default behavior that would concatenate the two facts as \c (t1,t2). + + \remark The function is called \c self_agnostic_join since, unlike the virtual method + \c join, it is static and therefore allows to easily swap the roles of the two joined + tables (the indexed and iterated one) in a way that is expected to give better performance. + */ + static void self_agnostic_join_project(const sparse_table & t1, const sparse_table & t2, + unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, + const unsigned * removed_cols, bool tables_swapped, sparse_table & result); + + + /** + If the fact at \c data (in table's native representation) is not in the table, + add it and return true. Otherwise return false. + */ + bool add_fact(const char * data); + + bool add_reserve_content(); + + void garbage_collect(); + + sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity=0); + sparse_table(const sparse_table & t); + virtual ~sparse_table(); + public: + + virtual void deallocate() { + get_plugin().recycle(this); + } + + unsigned row_count() const { return m_data.entry_count(); } + + sparse_table_plugin & get_plugin() const + { return static_cast(table_base::get_plugin()); } + + virtual bool empty() const { return row_count()==0; } + virtual void add_fact(const table_fact & f); + virtual bool contains_fact(const table_fact & f) const; + virtual bool fetch_fact(table_fact & f) const; + virtual void ensure_fact(const table_fact & f); + virtual void remove_fact(const table_element* fact); + virtual void reset(); + + virtual table_base * clone() const; + + virtual table_base::iterator begin() const; + virtual table_base::iterator end() const; + + virtual unsigned get_size_estimate_rows() const { return row_count(); } + virtual unsigned get_size_estimate_bytes() const; + virtual bool knows_exact_size() const { return true; } + }; + + }; + + #endif /* _DL_SPARSE_TABLE_H_ */ diff --git a/src/muz/rel/dl_table.cpp b/src/muz/rel/dl_table.cpp new file mode 100644 index 000000000..0b8fc0388 --- /dev/null +++ b/src/muz/rel/dl_table.cpp @@ -0,0 +1,773 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_table.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-01. + +Revision History: + +--*/ + +#include"dl_context.h" +#include"dl_util.h" +#include"dl_table.h" +#include"dl_relation_manager.h" + +namespace datalog { + + // ----------------------------------- + // + // hashtable_table + // + // ----------------------------------- + + table_base * hashtable_table_plugin::mk_empty(const table_signature & s) { + SASSERT(can_handle_signature(s)); + return alloc(hashtable_table, *this, s); + } + + + class hashtable_table_plugin::join_fn : public convenient_table_join_fn { + unsigned m_joined_col_cnt; + public: + join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) + : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), + m_joined_col_cnt(col_cnt) {} + + virtual table_base * operator()(const table_base & t1, const table_base & t2) { + + const hashtable_table & ht1 = static_cast(t1); + const hashtable_table & ht2 = static_cast(t2); + + hashtable_table_plugin & plugin = ht1.get_plugin(); + + hashtable_table * res = static_cast(plugin.mk_empty(get_result_signature())); + + hashtable_table::storage::iterator els1it = ht1.m_data.begin(); + hashtable_table::storage::iterator els1end = ht1.m_data.end(); + hashtable_table::storage::iterator els2end = ht2.m_data.end(); + + table_fact acc; + + for(; els1it!=els1end; ++els1it) { + const table_fact & row1 = *els1it; + + hashtable_table::storage::iterator els2it = ht2.m_data.begin(); + for(; els2it!=els2end; ++els2it) { + const table_fact & row2 = *els2it; + + bool match=true; + for(unsigned i=0; im_data.insert(acc); + } + } + return res; + } + }; + + table_join_fn * hashtable_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if(t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind()) { + return 0; + } + return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); + } + + + class hashtable_table::our_iterator_core : public iterator_core { + const hashtable_table & m_parent; + storage::iterator m_inner; + storage::iterator m_end; + + class our_row : public row_interface { + const our_iterator_core & m_parent; + public: + our_row(const our_iterator_core & parent) : row_interface(parent.m_parent), m_parent(parent) {} + + virtual void get_fact(table_fact & result) const { + result = *m_parent.m_inner; + } + virtual table_element operator[](unsigned col) const { + return (*m_parent.m_inner)[col]; + } + + }; + + our_row m_row_obj; + + public: + our_iterator_core(const hashtable_table & t, bool finished) : + m_parent(t), m_inner(finished ? t.m_data.end() : t.m_data.begin()), + m_end(t.m_data.end()), m_row_obj(*this) {} + + virtual bool is_finished() const { + return m_inner==m_end; + } + + virtual row_interface & operator*() { + SASSERT(!is_finished()); + return m_row_obj; + } + virtual void operator++() { + SASSERT(!is_finished()); + ++m_inner; + } + }; + + + + table_base::iterator hashtable_table::begin() const { + return mk_iterator(alloc(our_iterator_core, *this, false)); + } + + table_base::iterator hashtable_table::end() const { + return mk_iterator(alloc(our_iterator_core, *this, true)); + } + + // ----------------------------------- + // + // bitvector_table + // + // ----------------------------------- + + bool bitvector_table_plugin::can_handle_signature(const table_signature & sig) { + if(sig.functional_columns()!=0) { + return false; + } + unsigned cols = sig.size(); + unsigned shift = 0; + for (unsigned i = 0; i < cols; ++i) { + unsigned s = static_cast(sig[i]); + if (s != sig[i] || !is_power_of_two(s)) { + return false; + } + unsigned num_bits = 0; + unsigned bit_pos = 1; + for (num_bits = 1; num_bits < 32; ++num_bits) { + if (bit_pos & s) { + break; + } + bit_pos <<= 1; + } + shift += num_bits; + if (shift >= 32) { + return false; + } + } + return true; + } + + table_base * bitvector_table_plugin::mk_empty(const table_signature & s) { + SASSERT(can_handle_signature(s)); + return alloc(bitvector_table, *this, s); + } + + class bitvector_table::bv_iterator : public iterator_core { + + bitvector_table const& m_bv; + unsigned m_offset; + + class our_row : public caching_row_interface { + const bv_iterator& m_parent; + public: + our_row(const bv_iterator & p) : caching_row_interface(p.m_bv), m_parent(p) {} + virtual void get_fact(table_fact& result) const { + if (result.size() < size()) { + result.resize(size(), 0); + } + m_parent.m_bv.offset2fact(m_parent.m_offset, result); + } + }; + our_row m_row_obj; + + public: + bv_iterator(const bitvector_table& bv, bool end): + m_bv(bv), m_offset(end?m_bv.m_bv.size():0), m_row_obj(*this) + { + if (!is_finished() && !m_bv.m_bv.get(m_offset)) { + ++(*this); + } + } + + virtual bool is_finished() const { + return m_offset == m_bv.m_bv.size(); + } + + virtual row_interface & operator*() { + SASSERT(!is_finished()); + return m_row_obj; + } + virtual void operator++() { + SASSERT(!is_finished()); + ++m_offset; + while (!is_finished() && !m_bv.m_bv.get(m_offset)) { + ++m_offset; + } + m_row_obj.reset(); + } + }; + + bitvector_table::bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig) + : table_base(plugin, sig) { + SASSERT(plugin.can_handle_signature(sig)); + + m_num_cols = sig.size(); + unsigned shift = 0; + for (unsigned i = 0; i < m_num_cols; ++i) { + unsigned s = static_cast(sig[i]); + if (s != sig[i] || !is_power_of_two(s)) { + throw default_exception("bit-vector table is specialized to small domains that are powers of two"); + } + m_shift.push_back(shift); + m_mask.push_back(s - 1); + unsigned num_bits = 0; + unsigned bit_pos = 1; + for (num_bits = 1; num_bits < 32; ++num_bits) { + if (bit_pos & s) { + break; + } + bit_pos <<= 1; + } + shift += num_bits; + if (shift >= 32) { + throw default_exception("bit-vector table is specialized to small domains that are powers of two"); + } + m_bv.reserve(1 << shift); + } + } + + unsigned bitvector_table::fact2offset(const table_element* f) const { + unsigned result = 0; + for (unsigned i = 0; i < m_num_cols; ++i) { + SASSERT(f[i]> m_shift[i]); + } + } + + void bitvector_table::add_fact(const table_fact & f) { + m_bv.set(fact2offset(f.c_ptr())); + } + + void bitvector_table::remove_fact(const table_element* fact) { + m_bv.unset(fact2offset(fact)); + } + + bool bitvector_table::contains_fact(const table_fact & f) const { + return m_bv.get(fact2offset(f.c_ptr())); + } + + table_base::iterator bitvector_table::begin() const { + return mk_iterator(alloc(bv_iterator, *this, false)); + } + + table_base::iterator bitvector_table::end() const { + return mk_iterator(alloc(bv_iterator, *this, true)); + } + + + + + // ----------------------------------- + // + // equivalence_table + // + // ----------------------------------- + + bool equivalence_table_plugin::can_handle_signature(const table_signature & sig) { + return sig.functional_columns() == 0 && sig.size() == 2 && sig[0] < UINT_MAX && sig[0] == sig[1]; + } + + bool equivalence_table_plugin::is_equivalence_table(table_base const& tbl) const { + if (tbl.get_kind() != get_kind()) return false; + equivalence_table const& t = static_cast(tbl); + return !t.is_sparse(); + } + + table_base * equivalence_table_plugin::mk_empty(const table_signature & s) { + TRACE("dl", for (unsigned i = 0; i < s.size(); ++i) tout << s[i] << " "; tout << "\n";); + SASSERT(can_handle_signature(s)); + return alloc(equivalence_table, *this, s); + } + + class equivalence_table_plugin::select_equal_and_project_fn : public table_transformer_fn { + unsigned m_val; + table_sort m_sort; + public: + select_equal_and_project_fn(const table_signature & sig, table_element val, unsigned col) + : m_val(static_cast(val)), + m_sort(sig[0]) { + SASSERT(val <= UINT_MAX); + SASSERT(col == 0 || col == 1); + SASSERT(sig.functional_columns() == 0); + SASSERT(sig.size() == 2); + SASSERT(sig[0] < UINT_MAX && sig[0] == sig[1]); + } + + virtual table_base* operator()(const table_base& tb) { + TRACE("dl", tout << "\n";); + table_plugin & plugin = tb.get_plugin(); + table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse")); + SASSERT(rp); + table_signature sig; + sig.push_back(m_sort); + table_base* result = rp->mk_empty(sig); + equivalence_table const& eq_table = static_cast(tb); + if (eq_table.is_valid(m_val)) { + table_fact fact; + fact.resize(1); + unsigned r = m_val; + do { + fact[0] = r; + result->add_fact(fact); + r = eq_table.m_uf.next(r); + } + while (r != m_val); + } + TRACE("dl", tb.display(tout << "src:\n"); result->display(tout << "result\n");); + return result; + } + }; + + table_transformer_fn * equivalence_table_plugin::mk_select_equal_and_project_fn( + const table_base & t, const table_element & value, unsigned col) { + return alloc(select_equal_and_project_fn, t.get_signature(), value, col); + } + + class equivalence_table_plugin::union_fn : public table_union_fn { + + equivalence_table_plugin& m_plugin; + + + void mk_union1(equivalence_table & tgt, const equivalence_table & src, table_base * delta) { + unsigned num_vars = src.m_uf.get_num_vars(); + table_fact fact; + fact.resize(2); + for (unsigned i = 0; i < num_vars; ++i) { + if (src.is_valid(i) && src.m_uf.find(i) == i) { + fact[0] = i; + equivalence_table::class_iterator it = src.class_begin(i); + equivalence_table::class_iterator end = src.class_end(i); + for (; it != end; ++it) { + fact[1] = *it; + if (!tgt.contains_fact(fact)) { + tgt.add_fact(fact); + if (delta) { + delta->add_fact(fact); + } + } + } + } + } + } + + void mk_union2(equivalence_table & tgt, const table_base & src, table_base * delta) { + table_fact fact; + table_base::iterator it = src.begin(), end = src.end(); + for (; it != end; ++it) { + it->get_fact(fact); + if (!tgt.contains_fact(fact)) { + tgt.add_fact(fact); + if (delta) { + delta->add_fact(fact); + TRACE("dl", + tout << "Add: "; + for (unsigned i = 0; i < fact.size(); ++i) tout << fact[i] << " "; + tout << "\n";); + } + } + } + } + + public: + union_fn(equivalence_table_plugin& p) : m_plugin(p) {} + + virtual void operator()(table_base & tgt0, const table_base & src, table_base * delta) { + TRACE("dl", tout << "union\n";); + equivalence_table & tgt = static_cast(tgt0); + if (m_plugin.is_equivalence_table(src)) { + mk_union1(tgt, static_cast(src), delta); + } + else { + mk_union2(tgt, src, delta); + } + TRACE("dl", src.display(tout << "src\n"); tgt.display(tout << "tgt\n"); + if (delta) delta->display(tout << "delta\n");); + } + }; + + table_union_fn * equivalence_table_plugin::mk_union_fn( + const table_base & tgt, const table_base & src, const table_base * delta) { + if (!is_equivalence_table(tgt) || + tgt.get_signature() != src.get_signature() || + (delta && delta->get_signature() != tgt.get_signature())) { + return 0; + } + return alloc(union_fn,*this); + } + + class equivalence_table_plugin::join_project_fn : public convenient_table_join_project_fn { + equivalence_table_plugin& m_plugin; + public: + join_project_fn( + equivalence_table_plugin& plugin, const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) + : convenient_table_join_project_fn(t1_sig, t2_sig, col_cnt, cols1, cols2, removed_col_cnt, removed_cols), + m_plugin(plugin) { + m_removed_cols.push_back(UINT_MAX); + } + + virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { + SASSERT(m_cols1.size() == 1); + const table_signature & res_sign = get_result_signature(); + table_plugin * plugin = &tb1.get_plugin(); + if (!plugin->can_handle_signature(res_sign)) { + plugin = &tb2.get_plugin(); + if (!plugin->can_handle_signature(res_sign)) { + plugin = &tb1.get_manager().get_appropriate_plugin(res_sign); + } + } + SASSERT(plugin->can_handle_signature(res_sign)); + table_base * result = plugin->mk_empty(res_sign); + + if (m_plugin.is_equivalence_table(tb1)) { + mk_join(0, m_cols1[0], static_cast(tb1), + 2, m_cols2[0], tb2, result); + } + else if (m_plugin.is_equivalence_table(tb2)) { + mk_join(tb1.get_signature().size(), m_cols2[0], static_cast(tb2), + 0, m_cols1[0], tb1, result); + } + else { + UNREACHABLE(); + } + TRACE("dl", tb1.display(tout << "tb1\n"); tb2.display(tout << "tb2\n"); result->display(tout << "result\n");); + return result; + } + + private: + table_base * mk_join(unsigned offs1, unsigned col1, equivalence_table const & t1, + unsigned offs2, unsigned col2, table_base const& t2, table_base* res) { + table_base::iterator els2it = t2.begin(); + table_base::iterator els2end = t2.end(); + + table_fact acc, proj; + acc.resize(t1.get_signature().size() + t2.get_signature().size()); + + for(; els2it != els2end; ++els2it) { + const table_base::row_interface & row2 = *els2it; + table_element const& e2 = row2[col2]; + equivalence_table::class_iterator it = t1.class_begin(e2); + equivalence_table::class_iterator end = t1.class_end(e2); + if (it != end) { + for (unsigned i = 0; i < row2.size(); ++i) { + acc[i+offs2] = row2[i]; + } + } + for (; it != end; ++it) { + acc[offs1+col1] = e2; + acc[offs1+1-col1] = *it; + mk_project(acc, proj); + TRACE("dl", for (unsigned i = 0; i < proj.size(); ++i) tout << proj[i] << " "; tout << "\n";); + res->add_fact(proj); + } + } + return res; + } + + virtual void mk_project(table_fact const & f, table_fact & p) const { + unsigned sz = f.size(); + p.reset(); + for (unsigned i = 0, r = 0; i < sz; ++i) { + if (r < m_removed_cols.size() && m_removed_cols[r] == i) { + ++r; + } + else { + p.push_back(f[i]); + } + } + } + + + }; + + table_join_fn * equivalence_table_plugin::mk_join_project_fn( + const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) { + if (col_cnt != 1) { + TRACE("dl", tout << "WARNING: join_project on multiple columns is not implemented\n";); + return 0; + } + if (is_equivalence_table(t1) || is_equivalence_table(t2)) { + return alloc(join_project_fn, *this, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + } + return 0; + } + + class equivalence_table::eq_iterator : public iterator_core { + + equivalence_table const& m_eq; + unsigned m_last; + unsigned m_current; + unsigned m_next; + + class our_row : public caching_row_interface { + const eq_iterator& m_parent; + public: + our_row(const eq_iterator & p) : caching_row_interface(p.m_eq), m_parent(p) {} + + virtual void get_fact(table_fact& result) const { + if (result.size() < size()) { + result.resize(size(), 0); + } + result[0] = m_parent.m_current; + result[1] = m_parent.m_next; + } + + virtual table_element operator[](unsigned col) const { + if (col == 0) return m_parent.m_current; + if (col == 1) return m_parent.m_next; + UNREACHABLE(); + return 0; + } + + }; + our_row m_row_obj; + + public: + eq_iterator(const equivalence_table& eq, bool end): + m_eq(eq), + m_last(eq.m_uf.get_num_vars()), + m_current(end?m_last:0), + m_next(0), + m_row_obj(*this) + { + while (m_current < m_last && !m_eq.is_valid(m_current)) { + m_current++; + m_next = m_current; + } + } + + virtual bool is_finished() const { + return m_current == m_last; + } + + virtual row_interface & operator*() { + SASSERT(!is_finished()); + return m_row_obj; + } + + virtual void operator++() { + SASSERT(!is_finished()); + m_next = m_eq.m_uf.next(m_next); + if (m_next == m_current) { + do { + m_current++; + m_next = m_current; + } + while (m_current < m_last && !m_eq.is_valid(m_current)); + } + } + }; + + equivalence_table::equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig) + : table_base(plugin, sig), m_uf(m_ctx), m_sparse(0) { + SASSERT(plugin.can_handle_signature(sig)); + } + + equivalence_table::~equivalence_table() { + if (is_sparse()) { + m_sparse->deallocate(); + } + } + + + void equivalence_table::add_fact(const table_fact & f) { + if (is_sparse()) { + add_fact_sparse(f); + } + else { + TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";); + while (first(f) >= m_uf.get_num_vars()) m_uf.mk_var(); + while (second(f) >= m_uf.get_num_vars()) m_uf.mk_var(); + m_uf.merge(first(f), second(f)); + m_valid.reserve(m_uf.get_num_vars()); + m_valid.set(first(f)); + m_valid.set(second(f)); + } + } + + void equivalence_table::remove_fact(const table_element* fact) { + mk_sparse(); + m_sparse->remove_fact(fact); + } + + void equivalence_table::mk_sparse() { + if (m_sparse) return; + + TRACE("dl",tout << "\n";); + table_plugin & plugin = get_plugin(); + table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse")); + SASSERT(rp); + table_base* result = rp->mk_empty(get_signature()); + table_base::iterator it = begin(), e = end(); + table_fact fact; + for (; it != e; ++it) { + it->get_fact(fact); + result->add_fact(fact); + } + m_sparse = result; + } + + void equivalence_table::add_fact_sparse(table_fact const& f) { + table_base::iterator it = m_sparse->begin(), end = m_sparse->end(); + vector to_add; + to_add.push_back(f); + table_fact f1(f); + + f1[0] = f[1]; + f1[1] = f[0]; + to_add.push_back(f1); + + f1[0] = f[1]; + f1[1] = f[1]; + to_add.push_back(f1); + + f1[0] = f[0]; + f1[1] = f[0]; + to_add.push_back(f1); + + for (; it != end; ++it) { + if ((*it)[0] == f[0]) { + f1[0] = f[1]; + f1[1] = (*it)[1]; + to_add.push_back(f1); + std::swap(f1[0],f1[1]); + to_add.push_back(f1); + } + } + for (unsigned i = 0; i < to_add.size(); ++i) { + m_sparse->add_fact(to_add[i]); + } + } + + bool equivalence_table::contains_fact(const table_fact & f) const { + TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";); + if (is_sparse()) { + return m_sparse->contains_fact(f); + } + return + is_valid(first(f)) && + is_valid(second(f)) && + m_uf.find(first(f)) == m_uf.find(second(f)); + } + + table_base* equivalence_table::clone() const { + if (is_sparse()) { + return m_sparse->clone(); + } + TRACE("dl",tout << "\n";); + table_plugin & plugin = get_plugin(); + table_base* result = plugin.mk_empty(get_signature()); + table_fact fact; + fact.resize(2); + for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) { + if (m_valid.get(i) && m_uf.find(i) == i) { + unsigned n = m_uf.next(i); + fact[0] = i; + while (n != i) { + fact[1] = n; + result->add_fact(fact); + n = m_uf.next(n); + } + } + } + return result; + } + + table_base::iterator equivalence_table::begin() const { + if (is_sparse()) return m_sparse->begin(); + return mk_iterator(alloc(eq_iterator, *this, false)); + } + + table_base::iterator equivalence_table::end() const { + if (is_sparse()) return m_sparse->end(); + return mk_iterator(alloc(eq_iterator, *this, true)); + } + + equivalence_table::class_iterator equivalence_table::class_begin(table_element const& _e) const { + SASSERT(!is_sparse()); + unsigned e = static_cast(_e); + return class_iterator(*this, e, !is_valid(e)); + } + + equivalence_table::class_iterator equivalence_table::class_end(table_element const& _e) const { + SASSERT(!is_sparse()); + unsigned e = static_cast(_e); + return class_iterator(*this, e, true); + } + + void equivalence_table::display(std::ostream& out) const { + if (is_sparse()) { + m_sparse->display(out); + return; + } + for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) { + if (is_valid(i) && m_uf.find(i) == i) { + unsigned j = i, last = i; + do { + out << "<" << i << " " << j << ">\n"; + j = m_uf.next(j); + } + while (last != j); + } + } + } + + unsigned equivalence_table::get_size_estimate_rows() const { + if (is_sparse()) return m_sparse->get_size_estimate_rows(); + return static_cast(get_signature()[0]); + } + + unsigned equivalence_table::get_size_estimate_bytes() const { + if (is_sparse()) return m_sparse->get_size_estimate_bytes(); + return static_cast(get_signature()[0]); + } + + bool equivalence_table::knows_exact_size() const { + return (!is_sparse() || m_sparse->knows_exact_size()); + } + +}; + diff --git a/src/muz/rel/dl_table.h b/src/muz/rel/dl_table.h new file mode 100644 index 000000000..3a240c337 --- /dev/null +++ b/src/muz/rel/dl_table.h @@ -0,0 +1,265 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_table.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-01. + +Revision History: + +--*/ +#ifndef _DL_TABLE_H_ +#define _DL_TABLE_H_ + +#include +#include +#include + +#include "ast.h" +#include "bit_vector.h" +#include "buffer.h" +#include "hashtable.h" +#include "map.h" +#include "ref_vector.h" +#include "vector.h" +#include "union_find.h" +#include "dl_base.h" +#include "dl_util.h" +#include "bit_vector.h" + + +namespace datalog { + + class context; + class variable_intersection; + + + + // ----------------------------------- + // + // hashtable_table + // + // ----------------------------------- + + class hashtable_table; + + class hashtable_table_plugin : public table_plugin { + friend class hashtable_table; + protected: + class join_fn; + public: + typedef hashtable_table table; + + hashtable_table_plugin(relation_manager & manager) + : table_plugin(symbol("hashtable"), manager) {} + + virtual table_base * mk_empty(const table_signature & s); + + virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + }; + + class hashtable_table : public table_base { + friend class hashtable_table_plugin; + friend class hashtable_table_plugin::join_fn; + + class our_iterator_core; + + typedef hashtable, + vector_eq_proc > storage; + + storage m_data; + + hashtable_table(hashtable_table_plugin & plugin, const table_signature & sig) + : table_base(plugin, sig) {} + public: + hashtable_table_plugin & get_plugin() const + { return static_cast(table_base::get_plugin()); } + + virtual void add_fact(const table_fact & f) { + m_data.insert(f); + } + virtual void remove_fact(const table_element* fact) { + table_fact f(get_signature().size(), fact); + m_data.remove(f); + } + virtual bool contains_fact(const table_fact & f) const { + return m_data.contains(f); + } + + virtual iterator begin() const; + virtual iterator end() const; + + virtual unsigned get_size_estimate_rows() const { return m_data.size(); } + virtual unsigned get_size_estimate_bytes() const { return m_data.size()*get_signature().size()*8; } + virtual bool knows_exact_size() const { return true; } + }; + + // ----------------------------------- + // + // bitvector_table + // + // ----------------------------------- + + class bitvector_table; + + class bitvector_table_plugin : public table_plugin { + public: + typedef bitvector_table table; + + bitvector_table_plugin(relation_manager & manager) + : table_plugin(symbol("bitvector"), manager) {} + + virtual bool can_handle_signature(const table_signature & s); + + virtual table_base * mk_empty(const table_signature & s); + }; + + class bitvector_table : public table_base { + friend class bitvector_table_plugin; + + class bv_iterator; + bit_vector m_bv; + unsigned m_num_cols; + unsigned_vector m_shift; + unsigned_vector m_mask; + + unsigned fact2offset(const table_element* f) const; + void offset2fact(unsigned offset, table_fact& f) const; + + bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig); + public: + virtual void add_fact(const table_fact & f); + virtual void remove_fact(const table_element* fact); + virtual bool contains_fact(const table_fact & f) const; + virtual iterator begin() const; + virtual iterator end() const; + }; + + // ------------------------------------------- + // Equivalence table. + // Really: partial equivalence relation table. + // ------------------------------------------- + + class equivalence_table; + + class equivalence_table_plugin : public table_plugin { + class union_fn; + class select_equal_and_project_fn; + class join_project_fn; + + bool is_equivalence_table(table_base const& tbl) const; + + public: + typedef equivalence_table table; + + equivalence_table_plugin(relation_manager & manager) + : table_plugin(symbol("equivalence"), manager) {} + + virtual bool can_handle_signature(const table_signature & s); + + virtual table_base * mk_empty(const table_signature & s); + + protected: + virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta); + virtual table_transformer_fn * mk_select_equal_and_project_fn( + const table_base & t, + const table_element & value, unsigned col); + virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols); + + +#if 0 + virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + const table_element & value, unsigned col); + virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); +#endif + }; + + class equivalence_table : public table_base { + friend class equivalence_table_plugin; + + class eq_iterator; + union_find_default_ctx m_ctx; + bit_vector m_valid; + union_find<> m_uf; + table_base* m_sparse; + + equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig); + virtual ~equivalence_table(); + + unsigned first(table_fact const& f) const { return static_cast(f[0]); } + unsigned second(table_fact const& f) const { return static_cast(f[1]); } + + bool is_valid(unsigned entry) const { return entry < m_valid.size() && m_valid.get(entry); } + bool is_sparse() const { return m_sparse != 0; } + + // iterator over equivalence class of 'n'. + class class_iterator { + equivalence_table const& m_parent; + unsigned m_current; + unsigned m_last; + bool m_end; + public: + class_iterator(equivalence_table const& s, unsigned n, bool end): + m_parent(s), m_current(n), m_last(n), m_end(end) {} + + unsigned operator*() { return m_current; } + + class_iterator& operator++() { + m_current = m_parent.m_uf.next(m_current); + m_end = (m_current == m_last); + return *this; + } + + bool operator==(const class_iterator & it) const { + return + (m_end && it.m_end) || + (!m_end && !it.m_end && m_current == it.m_current); + } + bool operator!=(const class_iterator & it) const { return !operator==(it); } + + }; + class_iterator class_begin(table_element const& e) const; + class_iterator class_end(table_element const& e) const; + + void add_fact_sparse(table_fact const& f); + void mk_sparse(); + + + public: + virtual void add_fact(const table_fact & f); + virtual void remove_fact(const table_element* fact); + virtual bool contains_fact(const table_fact & f) const; + virtual table_base* clone() const; + virtual iterator begin() const; + virtual iterator end() const; + virtual unsigned get_size_estimate_rows() const; + virtual unsigned get_size_estimate_bytes() const; + virtual bool knows_exact_size() const; + virtual void display(std::ostream & out) const; + + }; + + +}; + +#endif /* _DL_TABLE_H_ */ + diff --git a/src/muz/rel/dl_table_plugin.h b/src/muz/rel/dl_table_plugin.h new file mode 100644 index 000000000..134389b61 --- /dev/null +++ b/src/muz/rel/dl_table_plugin.h @@ -0,0 +1,193 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_table_plugin.h + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-23. + +Revision History: + +--*/ +#ifndef _DL_TABLE_PLUGIN_H_ +#define _DL_TABLE_PLUGIN_H_ + +#include"ast.h" +#include"map.h" +#include"vector.h" + +#include"dl_table_ops.h" + +namespace datalog { + + /** + Termplate class containing common infrastructure for relations and tables + */ + template + struct tr_infrastructure { + + typedef typename Traits::base_object base_object; + typedef typename Traits::signature signature; + typedef typename Traits::element element; + typedef typename Traits::fact fact; + typedef typename Traits::kind kind; + + class base_fn { + public: + virtual ~base_fn() {} + }; + + class join_fn : public base_fn { + public: + virtual base_object * operator()(const base_object & t1, const base_object & t2); + }; + + class transformer_fn : public base_fn { + public: + virtual base_object * operator()(const base_object & t); + }; + + class union_fn : public base_fn { + public: + virtual void operator()(base_object & tgt, const base_object & src, base_object * delta); + }; + + class mutator_fn : public base_fn { + public: + virtual void operator()(base_object & t); + }; + + class negation_filter_fn : public base_fn { + public: + virtual void operator()(base_object & t, const base_object & negated_obj); + }; + + class plugin_object { + const kind m_kind; + protected: + plugin_object(kind k) : m_kind(k) {} + public: + kind get_kind(); + + virtual base_object * mk_empty(const signature & s) = 0; + + virtual join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + NOT_IMPLEMENTED_YET(); + } + + virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, + const unsigned * removed_cols) = 0 + + virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) = 0; + + virtual union_fn * mk_union_fn(base_object & tgt, const base_object & src, base_object * delta) = 0; + + virtual mutator_fn * mk_filter_identical_fn(base_object & t, unsigned col_cnt, + const unsigned * identical_cols) = 0; + + virtual mutator_fn * mk_filter_equal_fn(base_object & t, const element & value, + unsigned col) = 0; + + virtual mutator_fn * mk_filter_interpreted_fn(base_object & t, app * condition) = 0; + + virtual negation_filter_fn * mk_filter_interpreted_fn(base_object & t, + const base_object & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) = 0; + + }; + + class base_ancestor { + const kind m_kind; + protected: + relation_manager & m_manager; + signature m_signature; + + base_ancestor(kind k, relation_manager & m, const signature & s) + : m_kind(k), m_manager(m), m_signature(s) {} + public: + virtual ~base_ancestor() {} + + kind get_kind() const { return m_kind; } + relation_manager & get_manager() const { return m_manager; } + const signature & get_signature() const { return m_signature; } + + virtual bool empty() const = 0; + virtual void add_fact(const fact & f) = 0; + virtual bool contains_fact(const fact & f) const = 0; + + /** + \brief Return table that contains the same data as the current one. + */ + virtual base_object * clone() const; + + }; + }; + + + // ----------------------------------- + // + // relation_base + // + // ----------------------------------- + + class relation_base1; + + enum relation_kind { + RK_UNKNOWN, + RK_TABLE + }; + + struct relation_traits { + typedef relation_base1 base_object; + typedef relation_signature signature; + typedef app * element; + typedef ptr_vector fact; + typedef relation_kind kind; + }; + + typedef tr_infrastructure relation_infrastructure; + + typedef relation_infrastructure::plugin_object relation_plugin_base; + + class relation_base1 : public relation_infrastructure::base_ancestor { + + }; + + + // ----------------------------------- + // + // table_base + // + // ----------------------------------- + + class table_base1; + + struct table_traits { + typedef table_base1 base_object; + typedef table_signature signature; + typedef unsigned element; + typedef unsigned_vector fact; + typedef table_kind kind; + }; + + typedef tr_infrastructure table_infrastructure; + + typedef table_infrastructure::plugin_object table_plugin_base; + + class table_base1 : public table_infrastructure::base_ancestor { + + }; + +}; + +#endif /* _DL_TABLE_PLUGIN_H_ */ + diff --git a/src/muz/rel/dl_table_relation.cpp b/src/muz/rel/dl_table_relation.cpp new file mode 100644 index 000000000..3c30c58bb --- /dev/null +++ b/src/muz/rel/dl_table_relation.cpp @@ -0,0 +1,490 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_table_relation.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-14. + +Revision History: + +--*/ + + +#include +#include"dl_context.h" +#include"dl_relation_manager.h" +#include"dl_table_relation.h" + + +namespace datalog { + + // ----------------------------------- + // + // table_relation_plugin + // + // ----------------------------------- + + symbol table_relation_plugin::create_plugin_name(const table_plugin &p) { + std::string name = std::string("tr_") + p.get_name().bare_str(); + return symbol(name.c_str()); + } + + bool table_relation_plugin::can_handle_signature(const relation_signature & s) { + table_signature tsig; + if(!get_manager().relation_signature_to_table(s, tsig)) { + return false; + } + return m_table_plugin.can_handle_signature(tsig); + } + + + relation_base * table_relation_plugin::mk_empty(const relation_signature & s) { + table_signature tsig; + if(!get_manager().relation_signature_to_table(s, tsig)) { + return 0; + } + table_base * t = m_table_plugin.mk_empty(tsig); + return alloc(table_relation, *this, s, t); + } + + relation_base * table_relation_plugin::mk_full(const relation_signature & s, func_decl* p, family_id kind) { + table_signature tsig; + if(!get_manager().relation_signature_to_table(s, tsig)) { + return 0; + } + table_base * t = m_table_plugin.mk_full(p, tsig, kind); + return alloc(table_relation, *this, s, t); + } + + relation_base * table_relation_plugin::mk_from_table(const relation_signature & s, table_base * t) { + if (&t->get_plugin() == &m_table_plugin) + return alloc(table_relation, *this, s, t); + table_relation_plugin& other = t->get_manager().get_table_relation_plugin(t->get_plugin()); + return alloc(table_relation, other, s, t); + } + + class table_relation_plugin::tr_join_project_fn : public convenient_relation_join_project_fn { + scoped_ptr m_tfun; + public: + tr_join_project_fn(const relation_signature & s1, const relation_signature & s2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols, table_join_fn * tfun) + : convenient_relation_join_project_fn(s1, s2, col_cnt, cols1, cols2, removed_col_cnt, + removed_cols), m_tfun(tfun) {} + + virtual relation_base * operator()(const relation_base & t1, const relation_base & t2) { + SASSERT(t1.from_table()); + SASSERT(t2.from_table()); + table_relation_plugin & plugin = static_cast(t1.get_plugin()); + + const table_relation & tr1 = static_cast(t1); + const table_relation & tr2 = static_cast(t2); + + table_base * tres = (*m_tfun)(tr1.get_table(), tr2.get_table()); + + TRACE("dl_table_relation", tout << "# join => "; tres->display(tout);); + if(&tres->get_plugin()!=&plugin.m_table_plugin) { + //Operation returned a table of different type than the one which is associated with + //this plugin. We need to get a correct table_relation_plugin and create the relation + //using it. + return plugin.get_manager().get_table_relation_plugin(tres->get_plugin()) + .mk_from_table(get_result_signature(), tres); + } + return plugin.mk_from_table(get_result_signature(), tres); + } + }; + + relation_join_fn * table_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if(!r1.from_table() || !r2.from_table()) { + return 0; + } + const table_relation & tr1 = static_cast(r1); + const table_relation & tr2 = static_cast(r2); + + table_join_fn * tfun = get_manager().mk_join_fn(tr1.get_table(), tr2.get_table(), col_cnt, cols1, cols2); + if(!tfun) { + return 0; + } + + return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, + cols2, 0, static_cast(0), tfun); + } + + relation_join_fn * table_relation_plugin::mk_join_project_fn(const relation_base & r1, + const relation_base & r2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { + if(!r1.from_table() || !r2.from_table()) { + return 0; + } + const table_relation & tr1 = static_cast(r1); + const table_relation & tr2 = static_cast(r2); + + table_join_fn * tfun = get_manager().mk_join_project_fn(tr1.get_table(), tr2.get_table(), joined_col_cnt, + cols1, cols2, removed_col_cnt, removed_cols); + SASSERT(tfun); + + return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), joined_col_cnt, cols1, + cols2, removed_col_cnt, removed_cols, tfun); + } + + + class table_relation_plugin::tr_transformer_fn : public convenient_relation_transformer_fn { + scoped_ptr m_tfun; + public: + tr_transformer_fn(const relation_signature & rsig, table_transformer_fn * tfun) + : m_tfun(tfun) { get_result_signature() = rsig; } + + virtual relation_base * operator()(const relation_base & t) { + SASSERT(t.from_table()); + table_relation_plugin & plugin = static_cast(t.get_plugin()); + + const table_relation & tr = static_cast(t); + + table_base * tres = (*m_tfun)(tr.get_table()); + + TRACE("dl_table_relation", tout << "# transform => "; tres->display(tout);); + if(&tres->get_plugin()!=&plugin.m_table_plugin) { + //Transformation returned a table of different type than the one which is associated with this plugin. + //We need to get a correct table_relation_plugin and create the relation using it. + return plugin.get_manager().get_table_relation_plugin(tres->get_plugin()) + .mk_from_table(get_result_signature(), tres); + } + return plugin.mk_from_table(get_result_signature(), tres); + } + }; + + relation_transformer_fn * table_relation_plugin::mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_transformer_fn * tfun = get_manager().mk_project_fn(tr.get_table(), col_cnt, removed_cols); + SASSERT(tfun); + + relation_signature sig; + relation_signature::from_project(t.get_signature(), col_cnt, removed_cols, sig); + + return alloc(tr_transformer_fn, sig, tfun); + } + + relation_transformer_fn * table_relation_plugin::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_transformer_fn * tfun = get_manager().mk_rename_fn(tr.get_table(), permutation_cycle_len, permutation_cycle); + SASSERT(tfun); + + relation_signature sig; + relation_signature::from_rename(t.get_signature(), permutation_cycle_len, permutation_cycle, sig); + + return alloc(tr_transformer_fn, sig, tfun); + } + + relation_transformer_fn * table_relation_plugin::mk_permutation_rename_fn(const relation_base & t, + const unsigned * permutation) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_transformer_fn * tfun = get_manager().mk_permutation_rename_fn(tr.get_table(), permutation); + SASSERT(tfun); + + relation_signature sig; + relation_signature::from_permutation_rename(t.get_signature(), permutation, sig); + + return alloc(tr_transformer_fn, sig, tfun); + } + + relation_transformer_fn * table_relation_plugin::mk_select_equal_and_project_fn(const relation_base & t, + const relation_element & value, unsigned col) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_element tvalue; + get_manager().relation_to_table(tr.get_signature()[col], value, tvalue); + + table_transformer_fn * tfun = get_manager().mk_select_equal_and_project_fn(tr.get_table(), tvalue, col); + SASSERT(tfun); + relation_signature res_sig; + relation_signature::from_project(t.get_signature(), 1, &col, res_sig); + return alloc(tr_transformer_fn, res_sig, tfun); + } + + /** + Union functor that can unite table relation into any other relation (using any delta relation) + by iterating through the table and calling \c add_fact of the target relation. + */ + class table_relation_plugin::universal_target_union_fn : public relation_union_fn { + virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { + SASSERT(src.from_table()); + + const table_relation & tr_src = static_cast(src); + relation_manager & rmgr = tr_src.get_manager(); + relation_signature sig = tr_src.get_signature(); + SASSERT(tgt.get_signature()==sig); + SASSERT(!delta || delta->get_signature()==sig); + + table_base::iterator it = tr_src.get_table().begin(); + table_base::iterator end = tr_src.get_table().end(); + + table_fact tfact; + relation_fact rfact(rmgr.get_context()); + for (; it != end; ++it) { + it->get_fact(tfact); + rmgr.table_fact_to_relation(sig, tfact, rfact); + if(delta) { + if(!tgt.contains_fact(rfact)) { + tgt.add_new_fact(rfact); + delta->add_fact(rfact); + } + } + else { + tgt.add_fact(rfact); + } + } + TRACE("dl_table_relation", tout << "# universal union => "; tgt.display(tout);); + } + }; + + class table_relation_plugin::tr_union_fn : public relation_union_fn { + scoped_ptr m_tfun; + public: + tr_union_fn(table_union_fn * tfun) : m_tfun(tfun) {} + + virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { + SASSERT(tgt.from_table()); + SASSERT(src.from_table()); + SASSERT(!delta || delta->from_table()); + + table_relation & tr_tgt = static_cast(tgt); + const table_relation & tr_src = static_cast(src); + table_relation * tr_delta = static_cast(delta); + + (*m_tfun)(tr_tgt.get_table(), tr_src.get_table(), tr_delta ? &tr_delta->get_table() : 0); + + TRACE("dl_table_relation", tout << "# union => "; tr_tgt.get_table().display(tout);); + } + }; + + relation_union_fn * table_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if(!src.from_table()) { + return 0; + } + if(!tgt.from_table() || (delta && !delta->from_table())) { + return alloc(universal_target_union_fn); + } + const table_relation & tr_tgt = static_cast(tgt); + const table_relation & tr_src = static_cast(src); + const table_relation * tr_delta = static_cast(delta); + + table_union_fn * tfun = get_manager().mk_union_fn(tr_tgt.get_table(), tr_src.get_table(), + tr_delta ? &tr_delta->get_table() : 0); + SASSERT(tfun); + + return alloc(tr_union_fn, tfun); + } + + + class table_relation_plugin::tr_mutator_fn : public relation_mutator_fn { + scoped_ptr m_tfun; + public: + tr_mutator_fn(table_mutator_fn * tfun) : m_tfun(tfun) {} + + virtual void operator()(relation_base & r) { + SASSERT(r.from_table()); + table_relation & tr = static_cast(r); + (*m_tfun)(tr.get_table()); + TRACE("dl_table_relation", tout << "# mutator => "; tr.get_table().display(tout);); + } + }; + + relation_mutator_fn * table_relation_plugin::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_mutator_fn * tfun = get_manager().mk_filter_identical_fn(tr.get_table(), col_cnt, identical_cols); + SASSERT(tfun); + return alloc(tr_mutator_fn, tfun); + } + + relation_mutator_fn * table_relation_plugin::mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) { + if(!t.from_table()) { + return 0; + } + const table_relation & tr = static_cast(t); + + table_element tvalue; + get_manager().relation_to_table(tr.get_signature()[col], value, tvalue); + + table_mutator_fn * tfun = get_manager().mk_filter_equal_fn(tr.get_table(), tvalue, col); + SASSERT(tfun); + return alloc(tr_mutator_fn, tfun); + } + + relation_mutator_fn * table_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + bool condition_needs_transforming = false; + if(!t.from_table() || condition_needs_transforming) { + return 0; + } + const table_relation & tr = static_cast(t); + table_mutator_fn * tfun = get_manager().mk_filter_interpreted_fn(tr.get_table(), condition); + SASSERT(tfun); + return alloc(tr_mutator_fn, tfun); + } + + relation_transformer_fn * table_relation_plugin::mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + if (!t.from_table()) + return 0; + + const table_relation & tr = static_cast(t); + table_transformer_fn * tfun = get_manager().mk_filter_interpreted_and_project_fn(tr.get_table(), + condition, removed_col_cnt, removed_cols); + SASSERT(tfun); + + relation_signature sig; + relation_signature::from_project(t.get_signature(), removed_col_cnt, removed_cols, sig); + return alloc(tr_transformer_fn, sig, tfun); + } + + class table_relation_plugin::tr_intersection_filter_fn : public relation_intersection_filter_fn { + scoped_ptr m_tfun; + public: + tr_intersection_filter_fn(table_intersection_filter_fn * tfun) : m_tfun(tfun) {} + + virtual void operator()(relation_base & r, const relation_base & src) { + SASSERT(r.from_table()); + SASSERT(src.from_table()); + + table_relation & tr = static_cast(r); + const table_relation & tr_src = static_cast(src); + + (*m_tfun)(tr.get_table(), tr_src.get_table()); + TRACE("dl_table_relation", tout << "# negation_filter => "; tr.get_table().display(tout);); + } + }; + + relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_intersection_fn(const relation_base & r, + const relation_base & src, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * src_cols) { + if(!r.from_table() || !src.from_table()) { + return 0; + } + const table_relation & tr = static_cast(r); + const table_relation & tr_neg = static_cast(src); + table_intersection_filter_fn * tfun = get_manager().mk_filter_by_intersection_fn(tr.get_table(), + tr_neg.get_table(), joined_col_cnt, r_cols, src_cols); + if(!tfun) { + return 0; + } + + return alloc(tr_intersection_filter_fn, tfun); + } + + + relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, + const relation_base & negated_rel, unsigned joined_col_cnt, + const unsigned * r_cols, const unsigned * negated_cols) { + if(!r.from_table() || !negated_rel.from_table()) { + return 0; + } + const table_relation & tr = static_cast(r); + const table_relation & tr_neg = static_cast(negated_rel); + table_intersection_filter_fn * tfun = get_manager().mk_filter_by_negation_fn(tr.get_table(), + tr_neg.get_table(), joined_col_cnt, r_cols, negated_cols); + SASSERT(tfun); + + return alloc(tr_intersection_filter_fn, tfun); + } + + + // ----------------------------------- + // + // table_relation + // + // ----------------------------------- + + void table_relation::add_table_fact(const table_fact & f) { + get_table().add_fact(f); + } + + void table_relation::add_fact(const relation_fact & f) { + SASSERT(f.size()==get_signature().size()); + table_fact vals; + get_manager().relation_fact_to_table(get_signature(), f, vals); + get_table().add_fact(vals); + TRACE("dl_table_relation", tout << "# add fact => "; get_table().display(tout);); + } + + bool table_relation::contains_fact(const relation_fact & f) const { + table_fact vals; + get_manager().relation_fact_to_table(get_signature(), f, vals); + return get_table().contains_fact(vals); + } + + relation_base * table_relation::clone() const { + table_base * tres = get_table().clone(); + return get_plugin().mk_from_table(get_signature(), tres); + } + + relation_base * table_relation::complement(func_decl* p) const { + table_base * tres = get_table().complement(p); + return get_plugin().mk_from_table(get_signature(), tres); + } + + void table_relation::display_tuples(func_decl & pred, std::ostream & out) const { + context & ctx = get_manager().get_context(); + unsigned arity = pred.get_arity(); + + out << "Tuples in " << pred.get_name() << ": \n"; + + table_base::iterator it = get_table().begin(); + table_base::iterator end = get_table().end(); + + table_fact fact; + for (; it != end; ++it) { + it->get_fact(fact); + + out << "\t("; + + for(unsigned i=0;i + +Author: + + Krystof Hoder (t-khoder) 2010-09-24. + +Revision History: + +--*/ +#ifndef _DL_TABLE_RELATION_H_ +#define _DL_TABLE_RELATION_H_ + + +#include "dl_base.h" +#include "dl_util.h" + +namespace datalog { + + class table_relation; + + class table_relation_plugin : public relation_plugin { + friend class table_relation; + + class tr_join_project_fn; + class tr_transformer_fn; + class universal_target_union_fn; + class tr_union_fn; + class tr_mutator_fn; + class tr_intersection_filter_fn; + + table_plugin & m_table_plugin; + + static symbol create_plugin_name(const table_plugin & p); + public: + table_relation_plugin(table_plugin & tp, relation_manager & manager) + : relation_plugin(create_plugin_name(tp), manager, ST_TABLE_RELATION), m_table_plugin(tp) {} + + table_plugin & get_table_plugin() { return m_table_plugin; } + + virtual bool can_handle_signature(const relation_signature & s); + + virtual relation_base * mk_empty(const relation_signature & s); + virtual relation_base * mk_full(const relation_signature & s, func_decl* p, family_id kind); + relation_base * mk_from_table(const relation_signature & s, table_base * t); + + protected: + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, + const unsigned * permutation); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); + virtual relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t, + const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); + virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + virtual relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, + const relation_element & value, unsigned col); + }; + + class table_relation : public relation_base { + friend class table_relation_plugin; + friend class table_relation_plugin::tr_join_project_fn; + friend class table_relation_plugin::tr_transformer_fn; + + scoped_rel m_table; + + /** + \brief Create a \c table_relation object. + + The newly created object takes ownership of the \c table object. + */ + table_relation(table_relation_plugin & p, const relation_signature & s, table_base * table) + : relation_base(p, s), m_table(table) { + SASSERT(s.size()==table->get_signature().size()); + } + public: + + table_relation_plugin & get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + table_base & get_table() { return *m_table; } + const table_base & get_table() const { return *m_table; } + + virtual bool empty() const { return m_table->empty(); } + + void add_table_fact(const table_fact & f); + + virtual void add_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual relation_base * clone() const; + virtual relation_base * complement(func_decl* p) const; + virtual void to_formula(expr_ref& fml) const { get_table().to_formula(get_signature(), fml); } + + virtual void display(std::ostream & out) const { + get_table().display(out); + } + virtual void display_tuples(func_decl & pred, std::ostream & out) const; + + virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); } + virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); } + virtual bool knows_exact_size() const { return m_table->knows_exact_size(); } + }; + +}; + +#endif /* _DL_TABLE_RELATION_H_ */ + diff --git a/src/muz/rel/dl_vector_relation.h b/src/muz/rel/dl_vector_relation.h new file mode 100644 index 000000000..114f4ca43 --- /dev/null +++ b/src/muz/rel/dl_vector_relation.h @@ -0,0 +1,407 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + dl_vector_relation.h + +Abstract: + + Basic relation with equivalences. + +Author: + + Nikolaj Bjorner (nbjorner) 2010-2-11 + +Revision History: + +--*/ +#ifndef _DL_VECTOR_RELATION_H_ +#define _DL_VECTOR_RELATION_H_ + +#include "ast_pp.h" +#include "dl_context.h" +#include "union_find.h" + +namespace datalog { + + typedef std::pair u_pair; + + template + class vector_relation_helper { + public: + static void mk_project_t(T& t, unsigned_vector const& renaming) {} + }; + + template > + class vector_relation : public relation_base { + protected: + T m_default; + vector* m_elems; + bool m_empty; + union_find_default_ctx m_ctx; + union_find<>* m_eqs; + + friend class vector_relation_plugin; + + public: + vector_relation(relation_plugin& p, relation_signature const& s, bool is_empty, T const& t = T()): + relation_base(p, s), + m_default(t), + m_elems(alloc(vector)), + m_empty(is_empty), + m_eqs(alloc(union_find<>, m_ctx)) { + m_elems->resize(s.size(), t); + for (unsigned i = 0; i < s.size(); ++i) { + m_eqs->mk_var(); + } + } + + virtual ~vector_relation() { + dealloc(m_eqs); + dealloc(m_elems); + } + + virtual bool can_swap() const { return true; } + + virtual void swap(relation_base& other) { + vector_relation& o = dynamic_cast(other); + if (&o == this) return; + std::swap(o.m_eqs, m_eqs); + std::swap(o.m_empty, m_empty); + std::swap(o.m_elems, m_elems); + } + + void copy(vector_relation const& other) { + SASSERT(get_signature() == other.get_signature()); + if (other.empty()) { + set_empty(); + return; + } + m_empty = false; + for (unsigned i = 0; i < m_elems->size(); ++i) { + (*this)[i] = other[i]; + SASSERT(find(i) == i); + } + for (unsigned i = 0; i < m_elems->size(); ++i) { + merge(i, find(i)); + } + } + + + virtual bool empty() const { return m_empty; } + + T& operator[](unsigned i) { return (*m_elems)[find(i)]; } + + T const& operator[](unsigned i) const { return (*m_elems)[find(i)]; } + + virtual void display_index(unsigned i, T const& t, std::ostream& out) const = 0; + + virtual void display(std::ostream & out) const { + if (empty()) { + out << "empty\n"; + return; + } + for (unsigned i = 0; i < m_elems->size(); ++i) { + if (i == find(i)) { + display_index(i, (*m_elems)[i], out); + } + else { + out << i << " = " << find(i) << "\n"; + } + } + } + + + bool is_subset_of(vector_relation const& other) const { + if (empty()) return true; + if (other.empty()) return false; + for (unsigned i = 0; i < get_signature().size(); ++i) { + if (!is_subset_of((*this)[i], other[i])) { + return false; + } + } + return true; + } + + void set_empty() { + unsigned sz = m_elems->size(); + m_empty = true; + m_elems->reset(); + m_elems->resize(sz, m_default); + dealloc(m_eqs); + m_eqs = alloc(union_find<>,m_ctx); + for (unsigned i = 0; i < sz; ++i) { + m_eqs->mk_var(); + } + } + + + virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const = 0; + + virtual T mk_widen(T const& t1, T const& t2) const = 0; + + virtual T mk_unite(T const& t1, T const& t2) const = 0; + + virtual bool is_subset_of(T const& t1, T const& t2) const = 0; + + virtual bool is_full(T const& t) const = 0; + + virtual bool is_empty(unsigned i, T const& t) const = 0; + + virtual void mk_rename_elem(T& t, unsigned col_cnt, unsigned const* cycle) = 0; + + virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& neq_eqs, T const& t) const { return t; } + + void equate(unsigned i, unsigned j) { + SASSERT(i < get_signature().size()); + SASSERT(j < get_signature().size()); + if (!empty() && find(i) != find(j)) { + bool isempty; + T r = mk_intersect((*this)[i], (*this)[j], isempty); + if (isempty || is_empty(find(i),r)) { + m_empty = true; + } + else { + merge(i, j); + (*this)[i] = r; + } + } + } + + bool is_full() const { + for (unsigned i = 0; i < m_elems->size(); ++i) { + if (!is_full((*this)[i])) { + return false; + } + } + return true; + } + + void mk_join(vector_relation const& r1, vector_relation const& r2, + unsigned num_cols, unsigned const* cols1, unsigned const* cols2) { + SASSERT(is_full()); + bool is_empty = r1.empty() || r2.empty(); + if (is_empty) { + m_empty = true; + return; + } + unsigned sz1 = r1.get_signature().size(); + unsigned sz2 = r2.get_signature().size(); + for (unsigned i = 0; i < sz1; ++i) { + (*this)[i] = r1[i]; + } + for (unsigned i = 0; i < sz2; ++i) { + (*this)[sz1+i] = r2[i]; + } + for (unsigned i = 0; i < num_cols; ++i) { + unsigned col1 = cols1[i]; + unsigned col2 = cols2[i]; + equate(col1, sz1 + col2); + } + + TRACE("dl_relation", + r1.display(tout << "r1:\n"); + r2.display(tout << "r2:\n"); + display(tout << "dst:\n"); + ); + } + + void mk_project(vector_relation const& r, unsigned col_cnt, unsigned const* removed_cols) { + SASSERT(is_full()); + unsigned_vector classRep, repNode; + unsigned result_size = get_signature().size(); + unsigned input_size = r.get_signature().size(); + repNode.resize(input_size, UINT_MAX); + + // initialize vector entries and set class representatives. + for (unsigned i = 0, j = 0, c = 0; i < input_size; ++i) { + if (c < col_cnt && removed_cols[c] == i) { + ++c; + } + else { + (*this)[j] = r[i]; + classRep.push_back(r.find(i)); + ++j; + } + } + + // merge remaining equivalence classes. + for (unsigned i = 0; i < result_size; ++i) { + unsigned rep = classRep[i]; + if (repNode[rep] == UINT_MAX) { + repNode[rep] = i; + } + else { + merge(repNode[rep], i); + } + } + + // rename columns in image of vector relation. + unsigned_vector renaming; + for (unsigned i = 0, j = 0, c = 0; i < input_size; ++i) { + if (c < col_cnt && removed_cols[c] == i) { + renaming.push_back(UINT_MAX); + ++c; + } + else { + renaming.push_back(find(j)); + ++j; + } + } + for (unsigned k = 0; k < result_size; ++k) { + Helper::mk_project_t((*this)[k], renaming); + } + + + TRACE("dl_relation", + ast_manager& m = r.get_plugin().get_ast_manager(); + tout << "Signature: "; + for (unsigned i = 0; i < r.get_signature().size(); ++i) { + tout << mk_pp(r.get_signature()[i], m) << " "; + } + tout << "Remove: "; + for (unsigned i = 0; i < col_cnt; ++i) { + tout << removed_cols[i] << " "; + } + tout << "\n"; + r.display(tout); + tout << " --> \n"; + display(tout);); + } + + void mk_rename(vector_relation const& r, unsigned col_cnt, unsigned const* cycle) { + unsigned col1, col2; + SASSERT(is_full()); + + // roundabout way of creating permuted relation. + unsigned_vector classRep, repNode; + for (unsigned i = 0; i < r.m_elems->size(); ++i) { + classRep.push_back(r.find(i)); + repNode.push_back(UINT_MAX); + (*this)[i] = r[i]; + } + for (unsigned i = 0; i + 1 < col_cnt; ++i) { + col1 = cycle[i]; + col2 = cycle[i+1]; + (*this)[col2] = (*r.m_elems)[col1]; + classRep[col2] = r.find(col1); + } + col1 = cycle[col_cnt-1]; + col2 = cycle[0]; + (*this)[col2] = (*r.m_elems)[col1]; + classRep[col2] = r.find(col1); + + for (unsigned i = 0; i < r.m_elems->size(); ++i) { + unsigned rep = classRep[i]; + if (repNode[rep] == UINT_MAX) { + repNode[rep] = i; + } + else { + merge(repNode[rep], i); + } + } + + for (unsigned i = 0; i < r.m_elems->size(); ++i) { + mk_rename_elem((*m_elems)[i], col_cnt, cycle); + } + + TRACE("dl_relation", + ast_manager& m = r.get_plugin().get_ast_manager(); + tout << "cycle: "; + for (unsigned i = 0; i < col_cnt; ++i) { + tout << cycle[i] << " "; + } + tout << "\nold_sig: "; + for (unsigned i = 0; i < r.get_signature().size(); ++i) { + tout << mk_pp(r.get_signature()[i], m) << " "; + } + tout << "\nnew_sig: "; + for (unsigned i = 0; i < get_signature().size(); ++i) { + tout << mk_pp(get_signature()[i], m) << " "; + } + tout << "\n"; + r.display(tout << "src:\n"); + ); + } + + void mk_union(vector_relation const& src, vector_relation* delta, bool is_widen) { + TRACE("dl_relation", display(tout << "dst:\n"); src.display(tout << "src:\n");); + + if (src.empty()) { + if (delta) { + delta->copy(src); + } + return; + } + + if (empty()) { + copy(src); + if (delta) { + delta->copy(src); + } + return; + } + + // find coarsest equivalence class containing joint equalities + union_find<>* uf = alloc(union_find<>, m_ctx); + unsigned size = get_signature().size(); + map, default_eq > mp; + bool change = false; + bit_vector finds; + finds.resize(size, false); + for (unsigned i = 0; i < size; ++i) { + uf->mk_var(); + unsigned w; + u_pair p(std::make_pair(find(i), src.find(i))); + if (mp.find(p, w)) { + uf->merge(i, w); + } + else { + mp.insert(p, i); + // detect change + if (finds.get(find(i))) { + change = true; + } + else { + finds.set(find(i), true); + } + } + } + vector* elems = alloc(vector); + for (unsigned i = 0; i < size; ++i) { + T t1 = mk_eq(*m_eqs, *uf, (*this)[i]); + T t2 = mk_eq(*src.m_eqs, *uf, src[i]); + if (is_widen) { + elems->push_back(mk_widen(t1, t2)); + } + else { + elems->push_back(mk_unite(t1, t2)); + } + TRACE("dl_relation", tout << t1 << " u " << t2 << " = " << elems->back() << "\n";); + change = delta && (change || !((*elems)[i] == (*this)[i])); + } + dealloc(m_eqs); + dealloc(m_elems); + m_eqs = uf; + m_elems = elems; + if (delta && change) { + delta->copy(*this); + } + TRACE("dl_relation", display(tout << "dst':\n");); + } + + unsigned find(unsigned i) const { + return m_eqs->find(i); + } + + void merge(unsigned i, unsigned j) { + m_eqs->merge(i, j); + } + + }; + +}; + +#endif + diff --git a/src/muz/rel/karr_relation.cpp b/src/muz/rel/karr_relation.cpp new file mode 100644 index 000000000..436cd8598 --- /dev/null +++ b/src/muz/rel/karr_relation.cpp @@ -0,0 +1,790 @@ +#include "karr_relation.h" +#include "bool_rewriter.h" + +namespace datalog { + class karr_relation : public relation_base { + friend class karr_relation_plugin; + friend class karr_relation_plugin::filter_equal_fn; + + karr_relation_plugin& m_plugin; + ast_manager& m; + mutable arith_util a; + func_decl_ref m_fn; + mutable bool m_empty; + mutable matrix m_ineqs; + mutable bool m_ineqs_valid; + mutable matrix m_basis; + mutable bool m_basis_valid; + + public: + karr_relation(karr_relation_plugin& p, func_decl* f, relation_signature const& s, bool is_empty): + relation_base(p, s), + m_plugin(p), + m(p.get_ast_manager()), + a(m), + m_fn(f, m), + m_empty(is_empty), + m_ineqs_valid(!is_empty), + m_basis_valid(false) + { + } + + virtual bool empty() const { + return m_empty; + } + + virtual bool is_precise() const { return false; } + + virtual void add_fact(const relation_fact & f) { + SASSERT(m_empty); + SASSERT(!m_basis_valid); + m_empty = false; + m_ineqs_valid = true; + for (unsigned i = 0; i < f.size(); ++i) { + rational n; + if (a.is_numeral(f[i], n) && n.is_int()) { + vector row; + row.resize(f.size()); + row[i] = rational(1); + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(-n); + m_ineqs.eq.push_back(true); + } + } + } + + virtual bool contains_fact(const relation_fact & f) const { + UNREACHABLE(); + return false; + } + + virtual void display(std::ostream & out) const { + if (m_fn) { + out << m_fn->get_name() << "\n"; + } + if (empty()) { + out << "empty\n"; + } + else { + if (m_ineqs_valid) { + m_ineqs.display(out << "ineqs:\n"); + } + if (m_basis_valid) { + m_basis.display(out << "basis:\n"); + } + } + } + + virtual karr_relation * clone() const { + karr_relation* result = alloc(karr_relation, m_plugin, m_fn, get_signature(), m_empty); + result->copy(*this); + return result; + } + + virtual karr_relation * complement(func_decl*) const { + UNREACHABLE(); + return 0; + } + + virtual void to_formula(expr_ref& fml) const { + if (empty()) { + fml = m.mk_false(); + } + else { + matrix const& M = get_ineqs(); + expr_ref_vector conj(m); + for (unsigned i = 0; i < M.size(); ++i) { + to_formula(M.A[i], M.b[i], M.eq[i], conj); + } + bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), fml); + } + } + + karr_relation_plugin& get_plugin() const { return m_plugin; } + + void filter_interpreted(app* cond) { + rational one(1), mone(-1); + expr* e1, *e2, *en; + var* v, *w; + rational n1, n2; + expr_ref_vector conjs(m); + qe::flatten_and(cond, conjs); + matrix& M = get_ineqs(); + unsigned num_columns = get_signature().size(); + + for (unsigned i = 0; i < conjs.size(); ++i) { + expr* e = conjs[i].get(); + rational b(0); + vector row; + row.resize(num_columns, rational(0)); + bool processed = true; + if (m.is_eq(e, e1, e2) && is_linear(e1, row, b, one) && is_linear(e2, row, b, mone)) { + M.A.push_back(row); + M.b.push_back(b); + M.eq.push_back(true); + } + else if ((a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b); + M.eq.push_back(false); + } + else if ((a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b - rational(1)); + M.eq.push_back(false); + } + else if (m.is_not(e, en) && (a.is_lt(en, e2, e1) || a.is_gt(en, e1, e2)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b); + M.eq.push_back(false); + } + else if (m.is_not(e, en) && (a.is_le(en, e2, e1) || a.is_ge(en, e1, e2)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b - rational(1)); + M.eq.push_back(false); + } + else if (m.is_or(e, e1, e2) && is_eq(e1, v, n1) && is_eq(e2, w, n2) && v == w) { + if (n1 > n2) { + std::swap(n1, n2); + } + SASSERT(n1 <= n2); + row[v->get_idx()] = rational(1); + // v - n1 >= 0 + M.A.push_back(row); + M.b.push_back(-n1); + M.eq.push_back(false); + // -v + n2 >= 0 + row[v->get_idx()] = rational(-1); + M.A.push_back(row); + M.b.push_back(n2); + M.eq.push_back(false); + } + else { + processed = false; + } + TRACE("dl", tout << (processed?"+ ":"- ") << mk_pp(e, m) << "\n"; + if (processed) matrix::display_ineq(tout, row, M.b.back(), M.eq.back()); + ); + } + TRACE("dl", display(tout);); + } + + void mk_join(karr_relation const& r1, karr_relation const& r2, + unsigned col_cnt, unsigned const* cols1, unsigned const* cols2) { + if (r1.empty() || r2.empty()) { + m_empty = true; + return; + } + matrix const& M1 = r1.get_ineqs(); + matrix const& M2 = r2.get_ineqs(); + unsigned sig1_size = r1.get_signature().size(); + unsigned sig_size = get_signature().size(); + m_ineqs.reset(); + for (unsigned i = 0; i < M1.size(); ++i) { + vector row; + row.append(M1.A[i]); + row.resize(sig_size); + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(M1.b[i]); + m_ineqs.eq.push_back(M1.eq[i]); + } + for (unsigned i = 0; i < M2.size(); ++i) { + vector row; + row.resize(sig_size); + for (unsigned j = 0; j < M2.A[i].size(); ++j) { + row[sig1_size + j] = M2.A[i][j]; + } + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(M2.b[i]); + m_ineqs.eq.push_back(M2.eq[i]); + } + for (unsigned i = 0; i < col_cnt; ++i) { + vector row; + row.resize(sig_size); + row[cols1[i]] = rational(1); + row[sig1_size + cols2[i]] = rational(-1); + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(rational(0)); + m_ineqs.eq.push_back(true); + } + m_ineqs_valid = true; + m_basis_valid = false; + m_empty = false; + if (r1.m_fn) { + m_fn = r1.m_fn; + } + if (r2.m_fn) { + m_fn = r2.m_fn; + } + } + + void mk_project(karr_relation const& r, unsigned cnt, unsigned const* cols) { + if (r.m_empty) { + m_empty = true; + return; + } + matrix const& M = r.get_basis(); + m_basis.reset(); + for (unsigned i = 0; i < M.size(); ++i) { + vector row; + unsigned k = 0; + for (unsigned j = 0; j < M.A[i].size(); ++j) { + if (k < cnt && j == cols[k]) { + ++k; + } + else { + row.push_back(M.A[i][j]); + } + } + SASSERT(row.size() + cnt == M.A[i].size()); + SASSERT(M.eq[i]); + m_basis.A.push_back(row); + m_basis.b.push_back(M.b[i]); + m_basis.eq.push_back(true); + } + m_basis_valid = true; + m_ineqs_valid = false; + m_empty = false; + m_fn = r.m_fn; + + TRACE("dl", + for (unsigned i = 0; i < cnt; ++i) { + tout << cols[i] << " "; + } + tout << "\n"; + r.display(tout); + display(tout);); + } + + void mk_rename(const karr_relation & r, unsigned col_cnt, const unsigned * cols) { + if (r.empty()) { + m_empty = true; + return; + } + m_ineqs.reset(); + m_basis.reset(); + m_ineqs_valid = r.m_ineqs_valid; + m_basis_valid = r.m_basis_valid; + if (m_ineqs_valid) { + m_ineqs.append(r.m_ineqs); + mk_rename(m_ineqs, col_cnt, cols); + } + if (m_basis_valid) { + m_basis.append(r.m_basis); + mk_rename(m_basis, col_cnt, cols); + } + m_fn = r.m_fn; + TRACE("dl", r.display(tout); display(tout);); + } + + void mk_union(karr_relation const& src, karr_relation* delta) { + if (src.empty()) { + if (delta) { + delta->m_empty = true; + } + return; + } + matrix const& M = src.get_basis(); + if (empty()) { + m_basis = M; + m_basis_valid = true; + m_empty = false; + m_ineqs_valid = false; + if (delta) { + delta->copy(*this); + } + return; + } + matrix& N = get_basis(); + unsigned N_size = N.size(); + for (unsigned i = 0; i < M.size(); ++i) { + bool found = false; + for (unsigned j = 0; !found && j < N_size; ++j) { + found = + same_row(M.A[i], N.A[j]) && + M.b[i] == N.b[j] && + M.eq[i] == N.eq[j]; + } + if (!found) { + N.A.push_back(M.A[i]); + N.b.push_back(M.b[i]); + N.eq.push_back(M.eq[i]); + } + } + m_ineqs_valid = false; + if (N_size != N.size()) { + if (delta) { + delta->copy(*this); + } + } + } + + matrix const& get_basis() const { + init_basis(); + return m_basis; + } + + matrix& get_basis() { + init_basis(); + return m_basis; + } + + matrix const& get_ineqs() const { + init_ineqs(); + return m_ineqs; + } + + matrix & get_ineqs() { + init_ineqs(); + return m_ineqs; + } + + private: + + void copy(karr_relation const& other) { + m_ineqs = other.m_ineqs; + m_basis = other.m_basis; + m_basis_valid = other.m_basis_valid; + m_ineqs_valid = other.m_ineqs_valid; + m_empty = other.m_empty; + } + + bool same_row(vector const& r1, vector const& r2) const { + SASSERT(r1.size() == r2.size()); + for (unsigned i = 0; i < r1.size(); ++i) { + if (r1[i] != r2[i]) { + return false; + } + } + return true; + } + + void mk_rename(matrix& M, unsigned col_cnt, unsigned const* cols) { + for (unsigned j = 0; j < M.size(); ++j) { + vector & row = M.A[j]; + rational tmp = row[cols[0]]; + for (unsigned i = 0; i + 1 < col_cnt; ++i) { + row[cols[i]] = row[cols[i+1]]; + } + row[cols[col_cnt-1]] = tmp; + } + } + + bool is_eq(expr* e, var*& v, rational& n) { + expr* e1, *e2; + if (!m.is_eq(e, e1, e2)) { + return false; + } + if (!is_var(e1)) { + std::swap(e1, e2); + } + if (!is_var(e1)) { + return false; + } + v = to_var(e1); + if (!a.is_numeral(e2, n)) { + return false; + } + return true; + } + + bool is_linear(expr* e, vector& row, rational& b, rational const& mul) { + if (!a.is_int(e)) { + return false; + } + if (is_var(e)) { + row[to_var(e)->get_idx()] += mul; + return true; + } + if (!is_app(e)) { + return false; + } + rational n; + if (a.is_numeral(e, n)) { + b += mul*n; + return true; + } + if (a.is_add(e)) { + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + if (!is_linear(to_app(e)->get_arg(i), row, b, mul)) { + return false; + } + } + return true; + } + expr* e1, *e2; + if (a.is_sub(e, e1, e2)) { + return is_linear(e1, row, b, mul) && is_linear(e2, row, b, -mul); + } + if (a.is_mul(e, e1, e2) && a.is_numeral(e1, n)) { + return is_linear(e2, row, b, mul*n); + } + if (a.is_mul(e, e1, e2) && a.is_numeral(e2, n)) { + return is_linear(e1, row, b, mul*n); + } + if (a.is_uminus(e, e1)) { + return is_linear(e1, row, b, -mul); + } + return false; + } + + void init_ineqs() const { + if (!m_ineqs_valid) { + SASSERT(m_basis_valid); + m_plugin.dualizeH(m_ineqs, m_basis); + m_ineqs_valid = true; + } + } + + void init_basis() const { + if (!m_basis_valid) { + SASSERT(m_ineqs_valid); + if (m_plugin.dualizeI(m_basis, m_ineqs)) { + m_basis_valid = true; + } + else { + m_empty = true; + } + } + } + + void to_formula(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) const { + expr_ref_vector sum(m); + expr_ref zero(m), lhs(m); + zero = a.mk_numeral(rational(0), true); + + for (unsigned i = 0; i < row.size(); ++i) { + if (row[i].is_zero()) { + continue; + } + var* var = m.mk_var(i, a.mk_int()); + if (row[i].is_one()) { + sum.push_back(var); + } + else { + sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); + } + } + if (!b.is_zero()) { + sum.push_back(a.mk_numeral(b, true)); + } + lhs = a.mk_add(sum.size(), sum.c_ptr()); + if (is_eq) { + conj.push_back(m.mk_eq(lhs, zero)); + } + else { + conj.push_back(a.mk_ge(lhs, zero)); + } + } + }; + + + karr_relation& karr_relation_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + + karr_relation const & karr_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + void karr_relation_plugin::set_cancel(bool f) { + m_hb.set_cancel(f); + } + + relation_base * karr_relation_plugin::mk_empty(const relation_signature & s) { + return alloc(karr_relation, *this, 0, s, true); + } + + relation_base * karr_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + return alloc(karr_relation, *this, p, s, false); + } + + class karr_relation_plugin::join_fn : public convenient_relation_join_fn { + public: + join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ + } + + virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + karr_relation const& r1 = get(_r1); + karr_relation const& r2 = get(_r2); + karr_relation_plugin& p = r1.get_plugin(); + karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); + return result; + } + }; + + relation_join_fn * karr_relation_plugin::mk_join_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(t1) || !check_kind(t2)) { + return 0; + } + return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); + } + + + class karr_relation_plugin::project_fn : public convenient_relation_project_fn { + public: + project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { + } + + virtual relation_base * operator()(const relation_base & _r) { + karr_relation const& r = get(_r); + karr_relation_plugin& p = r.get_plugin(); + karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); + return result; + } + }; + + relation_transformer_fn * karr_relation_plugin::mk_project_fn(const relation_base & r, + unsigned col_cnt, const unsigned * removed_cols) { + return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); + } + + class karr_relation_plugin::rename_fn : public convenient_relation_rename_fn { + public: + rename_fn(karr_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {} + + virtual relation_base * operator()(const relation_base & _r) { + karr_relation const& r = get(_r); + karr_relation_plugin& p = r.get_plugin(); + karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); + return result; + } + }; + + relation_transformer_fn * karr_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if (!check_kind(r)) { + return 0; + } + return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle); + } + + bool karr_relation_plugin::dualizeI(matrix& dst, matrix const& src) { + dst.reset(); + m_hb.reset(); + for (unsigned i = 0; i < src.size(); ++i) { + if (src.eq[i]) { + m_hb.add_eq(src.A[i], -src.b[i]); + } + else { + m_hb.add_ge(src.A[i], -src.b[i]); + } + } + for (unsigned i = 0; !src.A.empty() && i < src.A[0].size(); ++i) { + m_hb.set_is_int(i); + } + lbool is_sat = l_undef; + + try { + is_sat = m_hb.saturate(); + } + catch (...) { + is_sat = l_undef; + } + TRACE("dl_verbose", m_hb.display(tout);); + if (is_sat == l_false) { + return false; + } + if (is_sat == l_undef) { + return true; + } + unsigned basis_size = m_hb.get_basis_size(); + bool first_initial = true; + for (unsigned i = 0; i < basis_size; ++i) { + bool is_initial; + vector soln; + m_hb.get_basis_solution(i, soln, is_initial); + if (is_initial && first_initial) { + dst.A.push_back(soln); + dst.b.push_back(rational(1)); + dst.eq.push_back(true); + first_initial = false; + } + else if (!is_initial) { + dst.A.push_back(soln); + dst.b.push_back(rational(0)); + dst.eq.push_back(true); + } + } + return true; + } + + void karr_relation_plugin::dualizeH(matrix& dst, matrix const& src) { + dst.reset(); + if (src.size() == 0) { + return; + } + m_hb.reset(); + for (unsigned i = 0; i < src.size(); ++i) { + vector v(src.A[i]); + v.push_back(src.b[i]); + if (src.eq[i]) { + m_hb.add_eq(v, rational(0)); + } + else { + m_hb.add_ge(v, rational(0)); + } + } + for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) { + m_hb.set_is_int(i); + } + lbool is_sat = l_undef; + try { + is_sat = m_hb.saturate(); + } + catch (...) { + is_sat = l_undef; + } + if (is_sat != l_true) { + return; + } + TRACE("dl_verbose", m_hb.display(tout);); + SASSERT(is_sat == l_true); + unsigned basis_size = m_hb.get_basis_size(); + for (unsigned i = 0; i < basis_size; ++i) { + bool is_initial; + vector soln; + m_hb.get_basis_solution(i, soln, is_initial); + if (!is_initial) { + dst.b.push_back(soln.back()); + dst.eq.push_back(true); + soln.pop_back(); + dst.A.push_back(soln); + } + } + } + + + class karr_relation_plugin::union_fn : public relation_union_fn { + public: + union_fn() {} + + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + + karr_relation& r = get(_r); + karr_relation const& src = get(_src); + TRACE("dl", r.display(tout << "dst:\n"); src.display(tout << "src:\n");); + + if (_delta) { + karr_relation& d = get(*_delta); + r.mk_union(src, &d); + } + else { + r.mk_union(src, 0); + } + TRACE("dl", r.display(tout << "result:\n");); + } + }; + + relation_union_fn * karr_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn); + } + + class karr_relation_plugin::filter_identical_fn : public relation_mutator_fn { + unsigned_vector m_identical_cols; + public: + filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) + : m_identical_cols(col_cnt, identical_cols) {} + + virtual void operator()(relation_base & _r) { + karr_relation & r = get(_r); + TRACE("dl", r.display(tout << "src:\n");); + r.get_ineqs(); + for (unsigned i = 1; i < m_identical_cols.size(); ++i) { + unsigned c1 = m_identical_cols[0]; + unsigned c2 = m_identical_cols[i]; + vector row; + row.resize(r.get_signature().size()); + row[c1] = rational(1); + row[c2] = rational(-1); + r.m_ineqs.A.push_back(row); + r.m_ineqs.b.push_back(rational(0)); + r.m_ineqs.eq.push_back(true); + r.m_basis_valid = false; + } + TRACE("dl", r.display(tout << "result:\n");); + } + }; + + relation_mutator_fn * karr_relation_plugin::mk_filter_identical_fn( + const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { + if(!check_kind(t)) { + return 0; + } + return alloc(filter_identical_fn, col_cnt, identical_cols); + } + + + class karr_relation_plugin::filter_equal_fn : public relation_mutator_fn { + unsigned m_col; + rational m_value; + bool m_valid; + public: + filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) + : m_col(col) { + arith_util arith(m.get_context().get_manager()); + m_valid = arith.is_numeral(value, m_value) && m_value.is_int(); + } + + virtual void operator()(relation_base & _r) { + karr_relation & r = get(_r); + if (m_valid) { + r.get_ineqs(); + vector row; + row.resize(r.get_signature().size()); + row[m_col] = rational(1); + r.m_ineqs.A.push_back(row); + r.m_ineqs.b.push_back(rational(-1)); + r.m_ineqs.eq.push_back(true); + r.m_basis_valid = false; + } + TRACE("dl", tout << m_value << "\n"; r.display(tout);); + } + }; + + relation_mutator_fn * karr_relation_plugin::mk_filter_equal_fn(const relation_base & r, + const relation_element & value, unsigned col) { + if (check_kind(r)) { + return alloc(filter_equal_fn, get_manager(), value, col); + } + return 0; + } + + + class karr_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + app_ref m_cond; + public: + filter_interpreted_fn(karr_relation const& t, app* cond): + m_cond(cond, t.get_plugin().get_ast_manager()) { + } + + void operator()(relation_base& t) { + get(t).filter_interpreted(m_cond); + TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); + } + }; + + relation_mutator_fn * karr_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + if (check_kind(t)) { + return alloc(filter_interpreted_fn, get(t), condition); + } + return 0; + } +}; diff --git a/src/muz/rel/karr_relation.h b/src/muz/rel/karr_relation.h new file mode 100644 index 000000000..00a92748a --- /dev/null +++ b/src/muz/rel/karr_relation.h @@ -0,0 +1,88 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + karr_relation.h + +Abstract: + + Extract integer linear invariants. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-08 + +Revision History: + +--*/ +#ifndef _KARR_RELATION_H_ +#define _KARR_RELATION_H_ + +#include"dl_mk_karr_invariants.h" +#include"dl_relation_manager.h" + +namespace datalog { + + class karr_relation; + + class karr_relation_plugin : public relation_plugin { + arith_util a; + hilbert_basis m_hb; + + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + friend class karr_relation; + public: + karr_relation_plugin(relation_manager& rm): + relation_plugin(karr_relation_plugin::get_name(), rm), + a(get_ast_manager()) + {} + + virtual bool can_handle_signature(const relation_signature & sig) { + return true; + } + + static symbol get_name() { return symbol("karr_relation"); } + + virtual void set_cancel(bool f); + + virtual relation_base * mk_empty(const relation_signature & s); + + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + + static karr_relation& get(relation_base& r); + static karr_relation const & get(relation_base const& r); + + virtual relation_join_fn * mk_join_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + + private: + bool dualizeI(matrix& dst, matrix const& src); + void dualizeH(matrix& dst, matrix const& src); + + + }; + + +}; + +#endif /* _DL_MK_KARR_INVARIANTS_H_ */ + From add96bc98f74a029fae98679b965d5bf7e3580d1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2013 21:24:34 -0700 Subject: [PATCH 130/509] re-organize muz_qe into separate units Signed-off-by: Nikolaj Bjorner --- src/muz/fp/dl_cmds.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/muz/fp/dl_cmds.h diff --git a/src/muz/fp/dl_cmds.h b/src/muz/fp/dl_cmds.h new file mode 100644 index 000000000..d71b319c4 --- /dev/null +++ b/src/muz/fp/dl_cmds.h @@ -0,0 +1,37 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + dl_cmds.h + +Abstract: + Datalog commands for SMT2 front-end. + +Author: + + Nikolaj Bjorner (nbjorner) 2012-11-17 + +Notes: + +--*/ +#ifndef _DL_CMDS_H_ +#define _DL_CMDS_H_ + +#include "ast.h" + +class cmd_context; + +struct dl_collected_cmds { + expr_ref_vector m_rules; + svector m_names; + expr_ref_vector m_queries; + func_decl_ref_vector m_rels; + dl_collected_cmds(ast_manager& m) : m_rules(m), m_queries(m), m_rels(m) {} +}; + +void install_dl_cmds(cmd_context & ctx); +void install_dl_collect_cmds(dl_collected_cmds& collected_cmds, cmd_context& ctx); + + +#endif From 9e61820125fc16e6d596f379ad5cc8195adcd496 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2013 21:49:53 -0700 Subject: [PATCH 131/509] re-organizing muz Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 2 +- src/muz/bmc/dl_bmc_engine.cpp | 1 + src/muz/dl_context.cpp | 3 +- src/muz/dl_rule.cpp | 1 + src/muz/dl_util.cpp | 3 +- src/muz/dl_util.h | 30 ---------- src/muz/equiv_proof_converter.cpp | 4 +- src/muz/{ => fp}/datalog_parser.cpp | 0 src/muz/{ => fp}/datalog_parser.h | 0 src/muz/pdr/pdr_context.cpp | 5 +- src/muz/pdr/pdr_dl_interface.cpp | 5 +- src/muz/pdr/pdr_util.cpp | 5 +- src/muz/scoped_proof.h | 55 +++++++++++++++++++ src/muz/tab/tab_context.cpp | 1 + src/{muz => qe}/vsubst_tactic.cpp | 0 src/{muz => qe}/vsubst_tactic.h | 0 .../tactic}/unit_subsumption_tactic.cpp | 0 .../tactic}/unit_subsumption_tactic.h | 0 .../arith}/arith_bounds_tactic.cpp | 0 .../arith}/arith_bounds_tactic.h | 0 20 files changed, 73 insertions(+), 42 deletions(-) rename src/muz/{ => fp}/datalog_parser.cpp (100%) rename src/muz/{ => fp}/datalog_parser.h (100%) create mode 100644 src/muz/scoped_proof.h rename src/{muz => qe}/vsubst_tactic.cpp (100%) rename src/{muz => qe}/vsubst_tactic.h (100%) rename src/{muz => smt/tactic}/unit_subsumption_tactic.cpp (100%) rename src/{muz => smt/tactic}/unit_subsumption_tactic.h (100%) rename src/{muz => tactic/arith}/arith_bounds_tactic.cpp (100%) rename src/{muz => tactic/arith}/arith_bounds_tactic.h (100%) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index a2b4ef9b2..248d45382 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -57,7 +57,7 @@ def init_project_def(): add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe']) add_lib('transforms', ['muz'], 'muz/transforms') add_lib('rel', ['muz', 'transforms'], 'muz/rel') - add_lib('pdr', ['muz', 'transforms'], 'muz/pdr') + add_lib('pdr', ['muz', 'transforms', 'arith_tactics', 'smt_tactic'], 'muz/pdr') add_lib('clp', ['muz', 'transforms'], 'muz/clp') add_lib('tab', ['muz', 'transforms'], 'muz/tab') add_lib('bmc', ['muz', 'transforms'], 'muz/bmc') diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 72e40590e..60d285a55 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -31,6 +31,7 @@ Revision History: #include "rewriter_def.h" #include "dl_transforms.h" #include "dl_mk_rule_inliner.h" +#include "scoped_proof.h" namespace datalog { diff --git a/src/muz/dl_context.cpp b/src/muz/dl_context.cpp index 5e707e315..11cb48490 100644 --- a/src/muz/dl_context.cpp +++ b/src/muz/dl_context.cpp @@ -19,8 +19,6 @@ Revision History: #include #include -#include"arith_simplifier_plugin.h" -#include"basic_simplifier_plugin.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"dl_context.h" @@ -28,6 +26,7 @@ Revision History: #include"ast_smt_pp.h" #include"ast_smt2_pp.h" #include"datatype_decl_plugin.h" +#include"scoped_proof.h" namespace datalog { diff --git a/src/muz/dl_rule.cpp b/src/muz/dl_rule.cpp index 5ac580cb8..96e2b163a 100644 --- a/src/muz/dl_rule.cpp +++ b/src/muz/dl_rule.cpp @@ -42,6 +42,7 @@ Revision History: #include"bool_rewriter.h" #include"expr_safe_replace.h" #include"filter_model_converter.h" +#include"scoped_proof.h" namespace datalog { diff --git a/src/muz/dl_util.cpp b/src/muz/dl_util.cpp index e32999ddc..2f9eb519c 100644 --- a/src/muz/dl_util.cpp +++ b/src/muz/dl_util.cpp @@ -25,9 +25,10 @@ Revision History: #endif #include"ast_pp.h" #include"bool_rewriter.h" +#include"for_each_expr.h" +#include"scoped_proof.h" #include"dl_context.h" #include"dl_rule.h" -#include"for_each_expr.h" #include"dl_util.h" namespace datalog { diff --git a/src/muz/dl_util.h b/src/muz/dl_util.h index d805e683b..a6bd7f7f8 100644 --- a/src/muz/dl_util.h +++ b/src/muz/dl_util.h @@ -124,36 +124,6 @@ namespace datalog { */ void display_fact(context & ctx, app * f, std::ostream & out); - class scoped_proof_mode { - ast_manager& m; - proof_gen_mode m_mode; - public: - scoped_proof_mode(ast_manager& m, proof_gen_mode mode): m(m) { - m_mode = m.proof_mode(); - m.toggle_proof_mode(mode); - } - ~scoped_proof_mode() { - m.toggle_proof_mode(m_mode); - } - - }; - - class scoped_proof : public scoped_proof_mode { - public: - scoped_proof(ast_manager& m): scoped_proof_mode(m, PGM_FINE) {} - }; - - class scoped_no_proof : public scoped_proof_mode { - public: - scoped_no_proof(ast_manager& m): scoped_proof_mode(m, PGM_DISABLED) {} - }; - - class scoped_restore_proof : public scoped_proof_mode { - public: - scoped_restore_proof(ast_manager& m): scoped_proof_mode(m, m.proof_mode()) {} - }; - - class variable_intersection diff --git a/src/muz/equiv_proof_converter.cpp b/src/muz/equiv_proof_converter.cpp index 98ea88044..13b759f7e 100644 --- a/src/muz/equiv_proof_converter.cpp +++ b/src/muz/equiv_proof_converter.cpp @@ -19,11 +19,11 @@ Revision History: #include "equiv_proof_converter.h" #include "ast_pp.h" -#include "dl_util.h" +#include "scoped_proof.h" void equiv_proof_converter::insert(expr* fml1, expr* fml2) { if (fml1 != fml2) { - datalog::scoped_proof _sp(m); + scoped_proof _sp(m); proof_ref p1(m), p2(m), p3(m); p1 = m.mk_asserted(fml1); p2 = m.mk_rewrite(fml1, fml2); diff --git a/src/muz/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp similarity index 100% rename from src/muz/datalog_parser.cpp rename to src/muz/fp/datalog_parser.cpp diff --git a/src/muz/datalog_parser.h b/src/muz/fp/datalog_parser.h similarity index 100% rename from src/muz/datalog_parser.h rename to src/muz/fp/datalog_parser.h diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index db85bc5a9..f3e253071 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -46,6 +46,7 @@ Notes: #include "proof_utils.h" #include "dl_boogie_proof.h" #include "qe_util.h" +#include "scoped_proof.h" namespace pdr { @@ -1719,7 +1720,7 @@ namespace pdr { } proof_ref context::get_proof() const { - datalog::scoped_proof _sc(m); + scoped_proof _sc(m); proof_ref proof(m); SASSERT(m_last_result == l_true); proof = m_search.get_proof_trace(*this); @@ -1959,7 +1960,7 @@ namespace pdr { void context::create_children(model_node& n) { SASSERT(n.level() > 0); bool use_model_generalizer = m_params.use_model_generalizer(); - datalog::scoped_no_proof _sc(m); + scoped_no_proof _sc(m); pred_transformer& pt = n.pt(); model_ref M = n.get_model_ptr(); diff --git a/src/muz/pdr/pdr_dl_interface.cpp b/src/muz/pdr/pdr_dl_interface.cpp index ccd22d57f..45872fe99 100644 --- a/src/muz/pdr/pdr_dl_interface.cpp +++ b/src/muz/pdr/pdr_dl_interface.cpp @@ -31,8 +31,9 @@ Revision History: #include "dl_mk_slice.h" #include "dl_mk_unfold.h" #include "dl_mk_coalesce.h" -#include "model_smt2_pp.h" #include "dl_transforms.h" +#include "scoped_proof.h" +#include "model_smt2_pp.h" using namespace pdr; @@ -149,7 +150,7 @@ lbool dl_interface::query(expr * query) { m_ctx.reopen(); m_ctx.replace_rules(old_rules); - datalog::scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode. + scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode. m_context->set_proof_converter(m_ctx.get_proof_converter()); m_context->set_model_converter(m_ctx.get_model_converter()); diff --git a/src/muz/pdr/pdr_util.cpp b/src/muz/pdr/pdr_util.cpp index d23dc186e..4db835bed 100644 --- a/src/muz/pdr/pdr_util.cpp +++ b/src/muz/pdr/pdr_util.cpp @@ -45,6 +45,7 @@ Notes: #include "poly_rewriter.h" #include "poly_rewriter_def.h" #include "arith_rewriter.h" +#include "scoped_proof.h" @@ -1072,7 +1073,7 @@ namespace pdr { void hoist_non_bool_if(expr_ref& fml) { ast_manager& m = fml.get_manager(); - datalog::scoped_no_proof _sp(m); + scoped_no_proof _sp(m); params_ref p; ite_hoister_star ite_rw(m, p); expr_ref tmp(m); @@ -1419,7 +1420,7 @@ namespace pdr { void normalize_arithmetic(expr_ref& t) { ast_manager& m = t.get_manager(); - datalog::scoped_no_proof _sp(m); + scoped_no_proof _sp(m); params_ref p; arith_normalizer_star rw(m, p); expr_ref tmp(m); diff --git a/src/muz/scoped_proof.h b/src/muz/scoped_proof.h new file mode 100644 index 000000000..e37290c03 --- /dev/null +++ b/src/muz/scoped_proof.h @@ -0,0 +1,55 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + scoped_proof.h + +Abstract: + + Scoped proof environments. Toggles enabling proofs. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-08-28 + +Revision History: + +--*/ +#ifndef _SCOPED_PROOF__H_ +#define _SCOPED_PROOF_H_ + +#include "ast.h" + +class scoped_proof_mode { + ast_manager& m; + proof_gen_mode m_mode; +public: + scoped_proof_mode(ast_manager& m, proof_gen_mode mode): m(m) { + m_mode = m.proof_mode(); + m.toggle_proof_mode(mode); + } + ~scoped_proof_mode() { + m.toggle_proof_mode(m_mode); + } + +}; + +class scoped_proof : public scoped_proof_mode { +public: + scoped_proof(ast_manager& m): scoped_proof_mode(m, PGM_FINE) {} +}; + +class scoped_no_proof : public scoped_proof_mode { +public: + scoped_no_proof(ast_manager& m): scoped_proof_mode(m, PGM_DISABLED) {} +}; + +class scoped_restore_proof : public scoped_proof_mode { +public: + scoped_restore_proof(ast_manager& m): scoped_proof_mode(m, m.proof_mode()) {} +}; + + + +#endif diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index a56d16a0e..2201be73e 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -29,6 +29,7 @@ Revision History: #include "datatype_decl_plugin.h" #include "for_each_expr.h" #include "matcher.h" +#include "scoped_proof.h" namespace tb { diff --git a/src/muz/vsubst_tactic.cpp b/src/qe/vsubst_tactic.cpp similarity index 100% rename from src/muz/vsubst_tactic.cpp rename to src/qe/vsubst_tactic.cpp diff --git a/src/muz/vsubst_tactic.h b/src/qe/vsubst_tactic.h similarity index 100% rename from src/muz/vsubst_tactic.h rename to src/qe/vsubst_tactic.h diff --git a/src/muz/unit_subsumption_tactic.cpp b/src/smt/tactic/unit_subsumption_tactic.cpp similarity index 100% rename from src/muz/unit_subsumption_tactic.cpp rename to src/smt/tactic/unit_subsumption_tactic.cpp diff --git a/src/muz/unit_subsumption_tactic.h b/src/smt/tactic/unit_subsumption_tactic.h similarity index 100% rename from src/muz/unit_subsumption_tactic.h rename to src/smt/tactic/unit_subsumption_tactic.h diff --git a/src/muz/arith_bounds_tactic.cpp b/src/tactic/arith/arith_bounds_tactic.cpp similarity index 100% rename from src/muz/arith_bounds_tactic.cpp rename to src/tactic/arith/arith_bounds_tactic.cpp diff --git a/src/muz/arith_bounds_tactic.h b/src/tactic/arith/arith_bounds_tactic.h similarity index 100% rename from src/muz/arith_bounds_tactic.h rename to src/tactic/arith/arith_bounds_tactic.h From e4338f085b348365ef68a9949ccb10c8e256a4a9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2013 22:11:33 -0700 Subject: [PATCH 132/509] re-organization of muz Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 7 +- src/{muz => ast}/scoped_proof.h | 0 src/{muz => math/hilbert}/heap_trie.h | 0 src/{muz => math/hilbert}/hilbert_basis.cpp | 0 src/{muz => math/hilbert}/hilbert_basis.h | 0 src/{muz => model}/model2expr.cpp | 0 src/{muz => model}/model2expr.h | 0 src/muz/README | 3 +- src/muz/{ => base}/dl_boogie_proof.cpp | 0 src/muz/{ => base}/dl_boogie_proof.h | 0 src/muz/{ => base}/dl_context.cpp | 0 src/muz/{ => base}/dl_context.h | 0 src/muz/{ => base}/dl_costs.cpp | 0 src/muz/{ => base}/dl_costs.h | 0 src/muz/{ => base}/dl_engine_base.h | 0 src/muz/{ => base}/dl_rule.cpp | 0 src/muz/{ => base}/dl_rule.h | 0 src/muz/{ => base}/dl_rule_set.cpp | 0 src/muz/{ => base}/dl_rule_set.h | 0 .../{ => base}/dl_rule_subsumption_index.cpp | 0 .../{ => base}/dl_rule_subsumption_index.h | 0 src/muz/{ => base}/dl_rule_transformer.cpp | 0 src/muz/{ => base}/dl_rule_transformer.h | 0 src/muz/{ => base}/dl_util.cpp | 0 src/muz/{ => base}/dl_util.h | 0 src/muz/{ => base}/fixedpoint_params.pyg | 0 src/muz/{ => base}/hnf.cpp | 0 src/muz/{ => base}/hnf.h | 0 src/muz/{ => base}/proof_utils.cpp | 0 src/muz/{ => base}/proof_utils.h | 0 src/muz/skip_list_base.h | 871 ------------------ src/{muz => tactic}/equiv_proof_converter.cpp | 0 src/{muz => tactic}/equiv_proof_converter.h | 0 .../horn_subsume_model_converter.cpp | 0 .../horn_subsume_model_converter.h | 0 .../replace_proof_converter.cpp | 0 src/{muz => tactic}/replace_proof_converter.h | 0 37 files changed, 6 insertions(+), 875 deletions(-) rename src/{muz => ast}/scoped_proof.h (100%) rename src/{muz => math/hilbert}/heap_trie.h (100%) rename src/{muz => math/hilbert}/hilbert_basis.cpp (100%) rename src/{muz => math/hilbert}/hilbert_basis.h (100%) rename src/{muz => model}/model2expr.cpp (100%) rename src/{muz => model}/model2expr.h (100%) rename src/muz/{ => base}/dl_boogie_proof.cpp (100%) rename src/muz/{ => base}/dl_boogie_proof.h (100%) rename src/muz/{ => base}/dl_context.cpp (100%) rename src/muz/{ => base}/dl_context.h (100%) rename src/muz/{ => base}/dl_costs.cpp (100%) rename src/muz/{ => base}/dl_costs.h (100%) rename src/muz/{ => base}/dl_engine_base.h (100%) rename src/muz/{ => base}/dl_rule.cpp (100%) rename src/muz/{ => base}/dl_rule.h (100%) rename src/muz/{ => base}/dl_rule_set.cpp (100%) rename src/muz/{ => base}/dl_rule_set.h (100%) rename src/muz/{ => base}/dl_rule_subsumption_index.cpp (100%) rename src/muz/{ => base}/dl_rule_subsumption_index.h (100%) rename src/muz/{ => base}/dl_rule_transformer.cpp (100%) rename src/muz/{ => base}/dl_rule_transformer.h (100%) rename src/muz/{ => base}/dl_util.cpp (100%) rename src/muz/{ => base}/dl_util.h (100%) rename src/muz/{ => base}/fixedpoint_params.pyg (100%) rename src/muz/{ => base}/hnf.cpp (100%) rename src/muz/{ => base}/hnf.h (100%) rename src/muz/{ => base}/proof_utils.cpp (100%) rename src/muz/{ => base}/proof_utils.h (100%) delete mode 100644 src/muz/skip_list_base.h rename src/{muz => tactic}/equiv_proof_converter.cpp (100%) rename src/{muz => tactic}/equiv_proof_converter.h (100%) rename src/{muz => tactic}/horn_subsume_model_converter.cpp (100%) rename src/{muz => tactic}/horn_subsume_model_converter.h (100%) rename src/{muz => tactic}/replace_proof_converter.cpp (100%) rename src/{muz => tactic}/replace_proof_converter.h (100%) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 248d45382..4fec0a9e7 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -14,6 +14,7 @@ def init_project_def(): add_lib('polynomial', ['util'], 'math/polynomial') add_lib('sat', ['util']) add_lib('nlsat', ['polynomial', 'sat']) + add_lib('hilbert', ['util'], 'math/hilbert') add_lib('interval', ['util'], 'math/interval') add_lib('realclosure', ['interval'], 'math/realclosure') add_lib('subpaving', ['interval'], 'math/subpaving') @@ -54,8 +55,8 @@ def init_project_def(): add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') add_lib('qe', ['smt','sat'], 'qe') - add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe']) - add_lib('transforms', ['muz'], 'muz/transforms') + add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base') + add_lib('transforms', ['muz', 'hilbert'], 'muz/transforms') add_lib('rel', ['muz', 'transforms'], 'muz/rel') add_lib('pdr', ['muz', 'transforms', 'arith_tactics', 'smt_tactic'], 'muz/pdr') add_lib('clp', ['muz', 'transforms'], 'muz/clp') @@ -64,7 +65,7 @@ def init_project_def(): add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc'], 'muz/fp') add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') - add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'fp', 'muz', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') + add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h'] add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure'], diff --git a/src/muz/scoped_proof.h b/src/ast/scoped_proof.h similarity index 100% rename from src/muz/scoped_proof.h rename to src/ast/scoped_proof.h diff --git a/src/muz/heap_trie.h b/src/math/hilbert/heap_trie.h similarity index 100% rename from src/muz/heap_trie.h rename to src/math/hilbert/heap_trie.h diff --git a/src/muz/hilbert_basis.cpp b/src/math/hilbert/hilbert_basis.cpp similarity index 100% rename from src/muz/hilbert_basis.cpp rename to src/math/hilbert/hilbert_basis.cpp diff --git a/src/muz/hilbert_basis.h b/src/math/hilbert/hilbert_basis.h similarity index 100% rename from src/muz/hilbert_basis.h rename to src/math/hilbert/hilbert_basis.h diff --git a/src/muz/model2expr.cpp b/src/model/model2expr.cpp similarity index 100% rename from src/muz/model2expr.cpp rename to src/model/model2expr.cpp diff --git a/src/muz/model2expr.h b/src/model/model2expr.h similarity index 100% rename from src/muz/model2expr.h rename to src/model/model2expr.h diff --git a/src/muz/README b/src/muz/README index 5d6f433b8..03cd12bc7 100644 --- a/src/muz/README +++ b/src/muz/README @@ -1 +1,2 @@ -muZ and Quantifier Elimination modules \ No newline at end of file +muZ: routines related to solving satisfiability of Horn clauses and +solving Datalog programs. diff --git a/src/muz/dl_boogie_proof.cpp b/src/muz/base/dl_boogie_proof.cpp similarity index 100% rename from src/muz/dl_boogie_proof.cpp rename to src/muz/base/dl_boogie_proof.cpp diff --git a/src/muz/dl_boogie_proof.h b/src/muz/base/dl_boogie_proof.h similarity index 100% rename from src/muz/dl_boogie_proof.h rename to src/muz/base/dl_boogie_proof.h diff --git a/src/muz/dl_context.cpp b/src/muz/base/dl_context.cpp similarity index 100% rename from src/muz/dl_context.cpp rename to src/muz/base/dl_context.cpp diff --git a/src/muz/dl_context.h b/src/muz/base/dl_context.h similarity index 100% rename from src/muz/dl_context.h rename to src/muz/base/dl_context.h diff --git a/src/muz/dl_costs.cpp b/src/muz/base/dl_costs.cpp similarity index 100% rename from src/muz/dl_costs.cpp rename to src/muz/base/dl_costs.cpp diff --git a/src/muz/dl_costs.h b/src/muz/base/dl_costs.h similarity index 100% rename from src/muz/dl_costs.h rename to src/muz/base/dl_costs.h diff --git a/src/muz/dl_engine_base.h b/src/muz/base/dl_engine_base.h similarity index 100% rename from src/muz/dl_engine_base.h rename to src/muz/base/dl_engine_base.h diff --git a/src/muz/dl_rule.cpp b/src/muz/base/dl_rule.cpp similarity index 100% rename from src/muz/dl_rule.cpp rename to src/muz/base/dl_rule.cpp diff --git a/src/muz/dl_rule.h b/src/muz/base/dl_rule.h similarity index 100% rename from src/muz/dl_rule.h rename to src/muz/base/dl_rule.h diff --git a/src/muz/dl_rule_set.cpp b/src/muz/base/dl_rule_set.cpp similarity index 100% rename from src/muz/dl_rule_set.cpp rename to src/muz/base/dl_rule_set.cpp diff --git a/src/muz/dl_rule_set.h b/src/muz/base/dl_rule_set.h similarity index 100% rename from src/muz/dl_rule_set.h rename to src/muz/base/dl_rule_set.h diff --git a/src/muz/dl_rule_subsumption_index.cpp b/src/muz/base/dl_rule_subsumption_index.cpp similarity index 100% rename from src/muz/dl_rule_subsumption_index.cpp rename to src/muz/base/dl_rule_subsumption_index.cpp diff --git a/src/muz/dl_rule_subsumption_index.h b/src/muz/base/dl_rule_subsumption_index.h similarity index 100% rename from src/muz/dl_rule_subsumption_index.h rename to src/muz/base/dl_rule_subsumption_index.h diff --git a/src/muz/dl_rule_transformer.cpp b/src/muz/base/dl_rule_transformer.cpp similarity index 100% rename from src/muz/dl_rule_transformer.cpp rename to src/muz/base/dl_rule_transformer.cpp diff --git a/src/muz/dl_rule_transformer.h b/src/muz/base/dl_rule_transformer.h similarity index 100% rename from src/muz/dl_rule_transformer.h rename to src/muz/base/dl_rule_transformer.h diff --git a/src/muz/dl_util.cpp b/src/muz/base/dl_util.cpp similarity index 100% rename from src/muz/dl_util.cpp rename to src/muz/base/dl_util.cpp diff --git a/src/muz/dl_util.h b/src/muz/base/dl_util.h similarity index 100% rename from src/muz/dl_util.h rename to src/muz/base/dl_util.h diff --git a/src/muz/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg similarity index 100% rename from src/muz/fixedpoint_params.pyg rename to src/muz/base/fixedpoint_params.pyg diff --git a/src/muz/hnf.cpp b/src/muz/base/hnf.cpp similarity index 100% rename from src/muz/hnf.cpp rename to src/muz/base/hnf.cpp diff --git a/src/muz/hnf.h b/src/muz/base/hnf.h similarity index 100% rename from src/muz/hnf.h rename to src/muz/base/hnf.h diff --git a/src/muz/proof_utils.cpp b/src/muz/base/proof_utils.cpp similarity index 100% rename from src/muz/proof_utils.cpp rename to src/muz/base/proof_utils.cpp diff --git a/src/muz/proof_utils.h b/src/muz/base/proof_utils.h similarity index 100% rename from src/muz/proof_utils.h rename to src/muz/base/proof_utils.h diff --git a/src/muz/skip_list_base.h b/src/muz/skip_list_base.h deleted file mode 100644 index feaa00763..000000000 --- a/src/muz/skip_list_base.h +++ /dev/null @@ -1,871 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - skip_list_base.h - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-10-01. - -Revision History: - - WARNING: IT IS NOT SAFE TO STORE KEYS, VALUES in the SKIP_LIST that need non-default constructors/destructors. - ---*/ -#ifndef _SKIP_LIST_BASE_H_ -#define _SKIP_LIST_BASE_H_ - -#include -#include"util.h" -#include"memory_manager.h" -#include"small_object_allocator.h" -#include"trace.h" - -#ifdef _MSC_VER -#pragma warning(disable : 4200) -#endif - -/* - This file defines a base class for implementing skip-list like data-structures. - This base class is relies on a manager for providing some basic services. - The manager is a template parameter. - - A Skip-list manager is responsible for: - - - Providing primitives for allocating/deallocating memory - void * allocate(size_t size); - void deallocate(size_t size, void* p); - - Generating random skip-list levels efficiently - unsigned random_level(unsigned max_level); - - Call-backs that will be invoked when a reference for a "value" stored in the skip-list is incremented/decremented. - void inc_ref_eh(value const & v); - void dec_ref_eh(value const & h); -*/ - -/** - \brief Base class for generating random_levels. -*/ -class random_level_manager { -#define SL_BITS_IN_RANDOM 16 - unsigned m_random_data; - unsigned m_random_bits:16; - unsigned m_random_left:16; - - unsigned random_value() { - return ((m_random_data = m_random_data * 214013L + 2531011L) >> 16) & 0xffff; - } - - void init_random() { - m_random_data = 0; - m_random_bits = random_value(); - m_random_left = SL_BITS_IN_RANDOM/2; - } -public: - random_level_manager() { - init_random(); - } - - unsigned random_level(unsigned max_level) { - unsigned level = 1; - unsigned b; - do { - b = m_random_bits&3; - if (!b) - level++; - m_random_bits >>= 2; - m_random_left--; - if (m_random_left == 0) { - m_random_bits = random_value(); - m_random_left = SL_BITS_IN_RANDOM/2; - } - } while (!b); - return (level > max_level ? max_level : level); - } - -}; - -/** - \brief Basic skip-list manager. - The class is parametrized by the Value type that is stored in the skip-list. -*/ -template -class sl_manager_base : public random_level_manager { - typedef Value value; - - small_object_allocator m_alloc; - -public: - void * allocate(size_t size) { - return m_alloc.allocate(size); - } - - void deallocate(size_t size, void* p) { - m_alloc.deallocate(size, p); - } - - void inc_ref_eh(value const & v) { - /* do nothing */ - } - - void dec_ref_eh(value const & h) { - /* do nothing */ - } -}; - -#define SL_SIZE_NUM_BITS 12 -#define SL_CAPACITY_NUM_BITS SL_SIZE_NUM_BITS -#define SL_MAX_CAPACITY ((1 << SL_SIZE_NUM_BITS) - 1) -#define SL_LEVEL_NUM_BITS 8 -#define SL_MAX_LEVEL ((1 << SL_LEVEL_NUM_BITS) - 1) -COMPILE_TIME_ASSERT(SL_SIZE_NUM_BITS == SL_CAPACITY_NUM_BITS); -COMPILE_TIME_ASSERT(SL_SIZE_NUM_BITS + SL_CAPACITY_NUM_BITS + SL_LEVEL_NUM_BITS == 32); - -/** - \brief Base (template) class for implementing skip-list like data-structures where - entries are stored in buckets to improve cache behavior. - - The Traits template parameter must provide: - - - a definition for the class Traits::manager - - a definition for the class Traits::entry which provides: - - a definition for the types key and value - - the methods: - key const & begin_key() const - key const & end_key() const - value const & val() const - void set_begin_key(key const & k) - void set_end_key(key const & k) - void set_val(value const & v) - void display(ostream & out) const - - the maximal number of levels Traits::max_level - - the maximal capacity of each bucket Traits::max_capacity - - the initial capacity of the first bucket Traits::initial_capacity - - flag for reference counting support Traits::ref_count. If this flag is true - the methods inc_ref_eh and dec_ref_eh in the manager object will be invoked. - - the methods - bool lt(key const & k1, key const & k2) - bool eq(key const & k1, key const & k2) - bool val_eq(value const & v1, value const & v2) - key succ(key const & k) - key pred(key const & k) -*/ -template -class skip_list_base : protected Traits { -protected: - typedef typename Traits::entry entry; -public: - typedef typename Traits::manager manager; - typedef typename entry::key key; - typedef typename entry::value value; - - struct bucket { - unsigned m_size:SL_SIZE_NUM_BITS; //!< number of entries stored in the bucket. - unsigned m_capacity:SL_CAPACITY_NUM_BITS; //!< capacity (number of entries) that can be stored in the bucket. - unsigned m_level:SL_LEVEL_NUM_BITS; - char m_extra[0]; - - static unsigned get_obj_size(unsigned num_lvls, unsigned capacity) { - return sizeof(bucket) + num_lvls*sizeof(bucket*) + capacity*sizeof(entry); - } - - entry * get_entries() { return reinterpret_cast(m_extra); } - entry const * get_entries() const { return reinterpret_cast(m_extra); } - - bucket ** next_vect() { return reinterpret_cast(get_entries() + m_capacity); } - bucket * const * next_vect() const { return reinterpret_cast(get_entries() + m_capacity); } - - bucket(unsigned lvl, unsigned capacity = Traits::max_capacity): - m_size(0), - m_capacity(capacity), - m_level(lvl) { - memset(next_vect(), 0, sizeof(bucket*)*lvl); - } - - unsigned level() const { return m_level; } - unsigned size() const { return m_size; } - unsigned capacity() const { return m_capacity; } - bool empty() const { return size() == 0; } - void set_size(unsigned sz) { m_size = sz; } - void shrink(unsigned delta) { m_size -= delta; } - void expand(unsigned delta) { m_size += delta; } - entry & first_entry() { SASSERT(!empty()); return get_entries()[0]; } - entry & last_entry() { SASSERT(!empty()); return get_entries()[size() - 1]; } - entry const & first_entry() const { SASSERT(!empty()); return get_entries()[0]; } - entry const & last_entry() const { SASSERT(!empty()); return get_entries()[size() - 1]; } - entry const & get(unsigned idx) const { SASSERT(idx < size()); return get_entries()[idx]; } - entry & get(unsigned idx) { SASSERT(idx < size()); return get_entries()[idx]; } - void set(unsigned idx, entry const & e) { SASSERT(idx < capacity()); get_entries()[idx] = e; } - bucket * get_next(unsigned idx) const { return next_vect()[idx]; } - void set_next(unsigned idx, bucket * bt) { SASSERT(idx < level()); next_vect()[idx] = bt; } - }; - - // Only the header bucket has zero entries. - bucket * m_header; - - bucket * first_bucket() const { - return m_header->get_next(0); - } - -#ifdef Z3DEBUG - /** - \brief (debugging only) Return the predecessor bucket of the given bucket. - - \pre bt != m_header, and bt is a bucket of the list. - */ - bucket * pred_bucket(bucket * bt) const { - SASSERT(bt != m_header); - bucket * curr = m_header; - while (curr->get_next(0) != bt) { - curr = curr->get_next(0); - SASSERT(curr != 0); // bt is not in the list - } - return curr; - } -#endif - - bool lt(key const & k1, key const & k2) const { return Traits::lt(k1, k2); } - - bool gt(key const & k1, key const & k2) const { return lt(k2, k1); } - - bool geq(key const & k1, key const & k2) const { return !lt(k1, k2); } - - bool leq(key const & k1, key const & k2) const { return !gt(k1, k2); } - - /** - \brief Create a new bucket of the given level. - */ - static bucket * mk_bucket(manager & m, unsigned lvl, unsigned capacity = Traits::max_capacity) { - void * mem = m.allocate(bucket::get_obj_size(lvl, capacity)); - return new (mem) bucket(lvl, capacity); - } - - static bucket * mk_header(manager & m, unsigned lvl) { - return mk_bucket(m, lvl, 0); - } - - static void inc_ref(manager & m, value const & v) { - if (Traits::ref_count) - m.inc_ref_eh(v); - } - - static void dec_ref(manager & m, value const & v) { - if (Traits::ref_count) - m.dec_ref_eh(v); - } - - /** - \brief Invoke dec_ref_eh for each value stored in the bucket. - */ - static void dec_ref(manager & m, bucket * bt) { - if (Traits::ref_count) { - unsigned sz = bt->size(); - for (unsigned i = 0; i < sz; i++) - m.dec_ref_eh(bt->get(i).val()); - } - } - - /** - \brief Deallocate the given bucket. - - \remark This method invokes dec_ref_eh for each value in the bucket. - */ - template - static void deallocate_bucket(manager & m, bucket * bt) { - if (DecRef) - dec_ref(m, bt); - unsigned sz = bucket::get_obj_size(bt->level(), bt->capacity()); - bt->~bucket(); - m.deallocate(sz, bt); - } - - /** - \brief Deallocate all buckets in the skip list. - - \remark This method invokes dec_ref_eh for each value in the list. - */ - template - void deallocate_list(manager & m) { - bucket * curr = m_header; - while (curr != 0) { - bucket * old = curr; - curr = curr->get_next(0); - deallocate_bucket(m, old); - } - } - -#ifdef Z3DEBUG - /** - \brief Check the following property - - for all i \in [0, b->level()) . pred_vect[i]->get_next(i) == b - */ - bool check_pred_vect(bucket * bt, bucket * pred_vect[]) { - if (bt == 0) - return true; - for (unsigned i = 0; i < bt->level(); i++) { - SASSERT(pred_vect[i]->get_next(i) == bt); - } - return true; - } -#endif - - /** - \brief Delete the given buffer and update the forward/next pointer of the buckets in pred_vect. - - \remark This method invokes dec_ref_eh for each value in the bucket. - */ - void del_bucket(manager & m, bucket * bt, bucket * pred_vect[]) { - SASSERT(check_pred_vect(bt, pred_vect)); - for (unsigned i = 0; i < bt->level(); i++) - pred_vect[i]->set_next(i, bt->get_next(i)); - deallocate_bucket(m, bt); - } - - /** - \brief Update the \c pred_vect vector from levels [0, bt->level()). - That is, bt will be now the "predecessor" for these levels. - */ - static void update_predecessor_vector(bucket * pred_vect [], bucket * bt) { - unsigned lvl = bt->level(); - for (unsigned i = 0; i < lvl; i++) { - pred_vect[i] = bt; - } - } - - /** - \brief Similar to the previous method, but the updated vector is stored in new_pred_vect. - */ - void update_predecessor_vector(bucket * pred_vect[], bucket * bt, bucket * new_pred_vect[]) { - unsigned bt_lvl = bt->level(); - for (unsigned i = 0; i < bt_lvl; i++) { - new_pred_vect[i] = bt; - } - unsigned list_lvl = level(); - for (unsigned i = bt_lvl; i < list_lvl; i++) { - new_pred_vect[i] = pred_vect[i]; - } - } - - /** - \brief Return the list level. - */ - unsigned level() const { - return m_header->level(); - } - - /** - \brief Expand/Increase the number of levels in the header. - */ - void expand_header(manager & m, unsigned new_lvl) { - SASSERT(new_lvl > level()); - bucket * new_header = mk_header(m, new_lvl); - // copy forward pointers of the old header. - unsigned old_lvl = level(); - for (unsigned i = 0; i < old_lvl; i++) - new_header->set_next(i, m_header->get_next(i)); - // update header - deallocate_bucket(m, m_header); - m_header = new_header; - } - - /** - \brief Increase list level to lvl if lvl > level() - */ - void update_list_level(manager & m, unsigned lvl) { - if (lvl > level()) { - expand_header(m, lvl); - } - } - - /** - \brief Increase list level (and store m_header in the new levels in pred_vect) if lvl > level(). - */ - void update_list_level(manager & m, unsigned lvl, bucket * pred_vect[]) { - if (lvl > level()) { - bucket * old_header = m_header; - unsigned old_lvl = m_header->level(); - expand_header(m, lvl); - for (unsigned i = 0; i < old_lvl; i++) { - if (pred_vect[i] == old_header) - pred_vect[i] = m_header; - } - for (unsigned i = old_lvl; i < lvl; i++) { - pred_vect[i] = m_header; - } - SASSERT(level() == lvl); - } - } - - /** - \brief Add first entry to the list. - - \remark This method will invoke inc_ref_eh for e.val() - */ - void insert_first_entry(manager & m, entry const & e) { - unsigned lvl = m.random_level(Traits::max_level); - bucket * new_bucket = mk_bucket(m, lvl, Traits::initial_capacity); - update_list_level(m, lvl); - for (unsigned i = 0; i < lvl; i++) { - m_header->set_next(i, new_bucket); - } - inc_ref(m, e.val()); - new_bucket->set_size(1); - new_bucket->set(0, e); - } - - /** - \brief Expand the capacity of the first-bucket in a skip-list with only one bucket. - This method assumes the capacity of the first-bucket < Traits::max_capacity - */ - void expand_first_bucket(manager & m) { - bucket * f = first_bucket(); - SASSERT(f != 0); - SASSERT(f->get_next(0) == 0); - SASSERT(f->capacity() < Traits::max_capacity); - unsigned old_capacity = f->capacity(); - SASSERT(old_capacity > 0); - unsigned new_capacity = old_capacity * 2; - if (new_capacity > Traits::max_capacity) - new_capacity = Traits::max_capacity; - unsigned lvl = f->level(); - bucket * new_f = mk_bucket(m, lvl, new_capacity); - unsigned sz = f->size(); - new_f->set_size(sz); - for (unsigned i = 0; i < sz; i++) - new_f->set(i, f->get(i)); - for (unsigned i = 0; i < lvl; i++) - m_header->set_next(i, new_f); - deallocate_bucket(m, f); - SASSERT(first_bucket() == new_f); - } - - /** - \brief Create a new bucket and divide the elements in bt between bt and the new bucket. - */ - void splice(manager & m, bucket * bt, bucket * pred_vect[]) { - SASSERT(bt->capacity() == Traits::max_capacity); - unsigned bt_lvl = bt->level(); - unsigned new_bucket_lvl = m.random_level(Traits::max_level); - bucket * new_bucket = mk_bucket(m, new_bucket_lvl); - update_list_level(m, new_bucket_lvl, pred_vect); - unsigned _lvl = std::min(bt_lvl, new_bucket_lvl); - for (unsigned i = 0; i < _lvl; i++) { - new_bucket->set_next(i, bt->get_next(i)); - bt->set_next(i, new_bucket); - } - for (unsigned i = bt_lvl; i < new_bucket_lvl; i++) { - new_bucket->set_next(i, pred_vect[i]->get_next(i)); - pred_vect[i]->set_next(i, new_bucket); - } - unsigned old_size = bt->size(); - SASSERT(old_size >= 2); - unsigned mid = old_size/2; - new_bucket->set_size(old_size - mid); - unsigned i = mid; - unsigned j = 0; - for (; i < old_size; i++, j++) { - new_bucket->set(j, bt->get(i)); - } - bt->set_size(mid); - SASSERT(!bt->empty()); - SASSERT(!new_bucket->empty()); - } - - /** - \brief Open space at position idx. The number of entries in bt is increased by one. - - \remark This method will *NOT* invoke inc_ref_eh - */ - void open_space(bucket * bt, unsigned idx) { - SASSERT(bt->size() < bt->capacity()); - SASSERT(idx <= bt->size()); - unsigned i = bt->size(); - while (i > idx) { - bt->set(i, bt->get(i-1)); - i--; - } - bt->expand(1); - } - - /** - \brief Open two spaces at position idx. The number of entries in bt is increased by one. - - \remark This method will *NOT* invoke inc_ref_eh - */ - void open_2spaces(bucket * bt, unsigned idx) { - SASSERT(bt->size() < bt->capacity() - 1); - SASSERT(idx <= bt->size()); - unsigned i = bt->size() + 1; - unsigned end = idx + 1; - while (i > end) { - bt->set(i, bt->get(i-2)); - i--; - } - bt->expand(2); - } - - /** - \brief Delete entry at position idx. - - \remark This method will invoke dec_ref_eh for the value stored in entry at position idx. - */ - void del_entry(manager & m, bucket * bt, unsigned idx) { - SASSERT(!bt->empty()); - SASSERT(idx < bt->size()); - dec_ref(m, bt->get(idx).val()); - unsigned sz = bt->size(); - for (unsigned i = idx; i < sz - 1; i++) { - bt->set(i, bt->get(i+1)); - } - bt->shrink(1); - } - - /** - \brief Create a copy of the skip list. - - \remark This method will invoke inc_ref_eh for all values copied. - */ - void clone_core(manager & m, skip_list_base * new_list) const { - bucket * pred_vect[Traits::max_level]; - unsigned lvl = level(); - new_list->update_list_level(m, lvl); - bucket * new_header = new_list->m_header; - for (unsigned i = 0; i < lvl; i++) - pred_vect[i] = new_header; - bucket * curr = first_bucket(); - while (curr != 0) { - unsigned curr_lvl = curr->level(); - bucket * new_bucket = new_list->mk_bucket(m, curr_lvl, curr->capacity()); - for (unsigned i = 0; i < curr_lvl; i++) { - pred_vect[i]->set_next(i, new_bucket); - pred_vect[i] = new_bucket; - } - unsigned curr_sz = curr->size(); - for (unsigned i = 0; i < curr_sz; i++) { - entry const & curr_entry = curr->get(i); - inc_ref(m, curr_entry.val()); - new_bucket->set(i, curr_entry); - } - new_bucket->set_size(curr_sz); - curr = curr->get_next(0); - } - } - -public: - skip_list_base(): - m_header(0) { - SASSERT(Traits::max_capacity >= 2); - SASSERT(Traits::initial_capacity >= 2); - SASSERT(Traits::initial_capacity <= Traits::max_capacity); - SASSERT(Traits::max_level >= 1); - SASSERT(Traits::max_capacity <= SL_MAX_CAPACITY); - SASSERT(Traits::max_level <= SL_MAX_LEVEL); - } - - skip_list_base(manager & m): - m_header(0) { - SASSERT(Traits::max_capacity >= 2); - SASSERT(Traits::initial_capacity >= 2); - SASSERT(Traits::initial_capacity <= Traits::max_capacity); - SASSERT(Traits::max_level >= 1); - SASSERT(Traits::max_capacity <= SL_MAX_CAPACITY); - SASSERT(Traits::max_level <= SL_MAX_LEVEL); - init(m); - } - - ~skip_list_base() { - SASSERT(m_header == 0); - } - - void deallocate(manager & m) { - deallocate_list(m); - m_header = 0; - } - - /** - \brief Deallocate the list but do not invoke dec_ref_eh. - */ - void deallocate_no_decref(manager & m) { - deallocate_list(m); - m_header = 0; - } - - /** - \brief Initialize a list that was created using the default constructor. - It can be used also to initialized a list deallocated using the method #deallocate. - */ - void init(manager & m) { - SASSERT(m_header == 0); - m_header = mk_header(m, 1); - } - - /** - \brief Remove all elements from the skip-list. - */ - void reset(manager & m) { - deallocate_list(m); - m_header = mk_header(m, 1); - } - - /** - \brief Remove all elements from the skip-list without invoking dec_ref_eh. - */ - void reset_no_decref(manager & m) { - deallocate_list(m); - m_header = mk_header(m, 1); - } - - /** - \brief Return true if the list is empty. - */ - bool empty() const { - SASSERT(m_header != 0); - return first_bucket() == 0; - } - -protected: - /** - \brief Return the position of the bucket in the skip list. - */ - unsigned get_bucket_idx(bucket const * bt) const { - bucket * curr = m_header; - unsigned pos = 0; - while (curr != 0) { - if (curr == bt) - return pos; - pos++; - curr = curr->get_next(0); - } - UNREACHABLE(); - return pos; - } - - /** - \brief Display the given entry. - */ - void display(std::ostream & out, entry const & e) const { - e.display(out); - } - - /** - \brief Display a reference to the given bucket. - */ - void display_bucket_ref(std::ostream & out, bucket const * bt) const { - if (bt == 0) - out << "NIL"; - else - out << "#" << get_bucket_idx(bt); - } - - /** - \brief Display the predecessor vector. - */ - void display_predecessor_vector(std::ostream & out, bucket const * const pred_vect[]) const { - for (unsigned i = 0; i < level(); i++) { - out << i << ": "; - display_bucket_ref(out, pred_vect[i]); - if (pred_vect[i]) { - out << " -> "; - display_bucket_ref(out, pred_vect[i]->get_next(i)); - } - out << "\n"; - } - } - - /** - \brief Display the successors of the given bucket. - */ - void display_successors(std::ostream & out, bucket const * bt) const { - out << "["; - for (unsigned i = 0; i < bt->level(); i++) { - if (i > 0) out << ", "; - display_bucket_ref(out, bt->get_next(i)); - } - out << "]"; - } - - /** - \brief Display the given bucket. - */ - void display(std::ostream & out, bucket const * bt) const { - if (bt == 0) { - out << "NIL\n"; - return; - } - out << "bucket "; - display_bucket_ref(out, bt); - out << ", capacity: " << bt->capacity() << "\n"; - out << "successors: "; - display_successors(out, bt); - out << "\n"; - out << "entries:\n"; - for (unsigned i = 0; i < bt->size(); i++) { - display(out, bt->get(i)); - out << "\n"; - } - out << "----------\n"; - } - -public: - /** - \brief Dump the skip list for debugging purposes. - It assumes that key and value types implement operator <<. - */ - void display_physical(std::ostream & out) const { - out << "{\nskip-list level: " << m_header->level() << "\n"; - bucket * curr = m_header; - while (curr != 0) { - display(out, curr); - curr = curr->get_next(0); - } - out << "}\n"; - } - - void display(std::ostream & out) const { - bucket * curr = m_header; - while (curr != 0) { - unsigned sz = curr->size(); - for (unsigned i = 0; i < sz; i++) { - if (i > 0) - out << " "; - curr->get(i).display(out); - } - curr = curr->get_next(0); - } - } - -protected: - /** - \brief Return true if bucket b2 can be reached from b1 following get_next(i) pointers - */ - bool is_reachable_at_i(bucket const * bt1, bucket const * bt2, unsigned i) const { - bucket * curr = bt1->get_next(i); - while (curr != 0) { - if (curr == bt2) - return true; - curr = curr->get_next(i); - } - return false; - } - -protected: - static void display_size_info_core(std::ostream & out, unsigned cls_size) { - out << "sizeof root: " << cls_size << "\n"; - out << "bucket max capacity: " << Traits::max_capacity << "\n"; - out << "bucket max level: " << Traits::max_level << "\n"; - out << "sizeof(bucket): " << sizeof(bucket) << " + " << sizeof(bucket*) << "*lvl + " << sizeof(entry) << "*capacity\n"; - out << "sizeof(usual bucket): " << (sizeof(bucket) + sizeof(entry)*Traits::max_capacity) << " + " << sizeof(bucket*) << "*lvl\n"; - out << "sizeof(max. bucket): " << (sizeof(bucket) + sizeof(entry)*Traits::max_capacity + sizeof(bucket*)*Traits::max_level) << "\n"; - out << "sizeof(entry): " << sizeof(entry) << "\n"; - out << "sizeof empty: " << cls_size + bucket::get_obj_size(1, 0) << "\n";; - out << "sizeof singleton: [" - << (cls_size + bucket::get_obj_size(1, 0) + bucket::get_obj_size(1, Traits::initial_capacity)) << ", " - << (cls_size + - bucket::get_obj_size(Traits::max_level, 0) + - bucket::get_obj_size(Traits::max_level, Traits::max_capacity)) << "]\n"; - } - -public: - /** - \brief Return true if skip-list has more than k buckets (not considering the header). - - \remark This method is for debugging purposes. - */ - bool has_more_than_k_buckets(unsigned k) const { - bucket * curr = first_bucket(); - while (curr != 0 && k > 0) { - curr = curr->get_next(0); - k--; - } - return curr != 0; - } - - /** - \brief Return true if the skip-list has more than k entries. - */ - bool has_more_than_k_entries(unsigned k) const { - bucket * curr = first_bucket(); - while (curr != 0 && k >= curr->size()) { - k -= curr->size(); - curr = curr->get_next(0); - } - SASSERT(curr == 0 || curr->size() > k); - return curr != 0; - } - -protected: - /** - \brief Return the amount of memory consumed by the list. - */ - unsigned memory_core(unsigned cls_size) const { - unsigned r = 0; - r += cls_size; - bucket * curr = m_header; - while (curr != 0) { - r += bucket::get_obj_size(curr->level(), curr->capacity()); - curr = curr->get_next(0); - } - return r; - } - -public: - /** - \brief Compress the buckets of the skip-list. - Make sure that all, but the last bucket, have at least \c load entries. - - \remark If load > Traits::max_capacity, then it assumes load = Traits::max_capacity. - */ - void compress(manager & m, unsigned load = Traits::max_capacity/2) { - if (load > Traits::max_capacity) - load = Traits::max_capacity; - bucket * pred_vect[Traits::max_level]; - update_predecessor_vector(pred_vect, m_header); - bucket * curr = first_bucket(); - while (curr != 0) { - update_predecessor_vector(pred_vect, curr); - bucket * next = curr->get_next(0); - while (curr->size() < load && next != 0) { - // steal entries of the successor bucket. - unsigned deficit = load - curr->size(); - unsigned next_size = next->size(); - if (next_size <= deficit) { - for (unsigned i = 0, j = curr->size(); i < next_size; i++, j++) { - curr->set(j, next->get(i)); - } - curr->expand(next_size); - bucket * new_next = next->get_next(0); - del_bucket(m, next, pred_vect); - next = new_next; - SASSERT(curr->size() <= load); - } - else { - for (unsigned i = 0, j = curr->size(); i < deficit; i++, j++) { - curr->set(j, next->get(i)); - } - curr->expand(deficit); - for (unsigned i = deficit, j = 0; i < next_size; i++, j++) { - next->set(j, next->get(i)); - } - next->set_size(next_size - deficit); - SASSERT(curr->size() == load); - } - } - curr = curr->get_next(0); - } - } - - void swap(skip_list_base & other) { - bucket * tmp = m_header; - m_header = other.m_header; - other.m_header = tmp; - } -}; - - -#endif diff --git a/src/muz/equiv_proof_converter.cpp b/src/tactic/equiv_proof_converter.cpp similarity index 100% rename from src/muz/equiv_proof_converter.cpp rename to src/tactic/equiv_proof_converter.cpp diff --git a/src/muz/equiv_proof_converter.h b/src/tactic/equiv_proof_converter.h similarity index 100% rename from src/muz/equiv_proof_converter.h rename to src/tactic/equiv_proof_converter.h diff --git a/src/muz/horn_subsume_model_converter.cpp b/src/tactic/horn_subsume_model_converter.cpp similarity index 100% rename from src/muz/horn_subsume_model_converter.cpp rename to src/tactic/horn_subsume_model_converter.cpp diff --git a/src/muz/horn_subsume_model_converter.h b/src/tactic/horn_subsume_model_converter.h similarity index 100% rename from src/muz/horn_subsume_model_converter.h rename to src/tactic/horn_subsume_model_converter.h diff --git a/src/muz/replace_proof_converter.cpp b/src/tactic/replace_proof_converter.cpp similarity index 100% rename from src/muz/replace_proof_converter.cpp rename to src/tactic/replace_proof_converter.cpp diff --git a/src/muz/replace_proof_converter.h b/src/tactic/replace_proof_converter.h similarity index 100% rename from src/muz/replace_proof_converter.h rename to src/tactic/replace_proof_converter.h From f5b988aead99a9229eb51f6ba463fc7e49a863ca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 28 Aug 2013 22:15:16 -0700 Subject: [PATCH 133/509] update README Signed-off-by: Nikolaj Bjorner --- src/muz/README | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/muz/README b/src/muz/README index 03cd12bc7..c7d5a9665 100644 --- a/src/muz/README +++ b/src/muz/README @@ -1,2 +1,12 @@ muZ: routines related to solving satisfiability of Horn clauses and solving Datalog programs. + +- base - contains base routines and the main context for + maintaining fixedpoint solvers +- transforms - common rule transformations +- rel - relational algebra based Datalog engine +- pdr - PDR based Horn clause solver +- clp - Dart/Symbolic execution-based solver +- tab - Tabulation based solver +- bmc - Bounded model checking based solver +- fp - main exported routines \ No newline at end of file From 912d220e947591867d6a20591be4faacdaae5707 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 29 Aug 2013 13:40:42 -0700 Subject: [PATCH 134/509] working on generalizer Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_generalizers.cpp | 128 +++++++++++++++++++++++-------- src/muz/pdr/pdr_generalizers.h | 3 + 2 files changed, 101 insertions(+), 30 deletions(-) diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp index f1ab01070..1121243f2 100644 --- a/src/muz/pdr/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -23,6 +23,7 @@ Revision History: #include "pdr_generalizers.h" #include "expr_abstract.h" #include "var_subst.h" +#include "model_smt2_pp.h" namespace pdr { @@ -157,7 +158,7 @@ namespace pdr { } void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - // method3(n, core, uses_level, new_cores); + method3(n, core, uses_level, new_cores); method1(n, core, uses_level, new_cores); } @@ -199,6 +200,7 @@ namespace pdr { for (unsigned i = 0; i < fmls.size(); ++i) { fml = m.mk_not(fmls[i].get()); core2.reset(); + conv2.reset(); qe::flatten_and(fml, core2); if (!mk_convex(core2, 1, conv2)) { IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core2), m) << "\n";); @@ -304,10 +306,6 @@ namespace pdr { bool uses_level1; expr_ref_vector core1(m); core1.append(core); - obj_hashtable bs; - for (unsigned i = 0; i < core.size(); ++i) { - bs.insert(core[i]); - } expr_ref_vector consequences(m); { n.pt().get_solver().set_consequences(&consequences); @@ -325,6 +323,77 @@ namespace pdr { verbose_stream() << mk_pp(core1[i].get(), m) << "\n"; }); + // Create disjunction. + expr_ref tmp(m); + for (unsigned i = 0; i < consequences.size(); ++i) { + consequences[i] = m.mk_not(consequences[i].get()); + } + tmp = m.mk_and(core.size(), core.c_ptr()); + + if (!strengthen_consequences(n, consequences, tmp)) { + return; + } + + IF_VERBOSE(0, verbose_stream() << "consequences strengthened\n";); + // Use the resulting formula to find Farkas lemmas from core. + } + + bool core_convex_hull_generalizer::strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B) { + expr_ref A(m), tmp(m), convA(m); + unsigned sz = As.size(); + for (unsigned i = 0; i < As.size(); ++i) { + expr_ref_vector Hs(m); + Hs.push_back(As[i].get()); + for (unsigned j = i + 1; j < As.size(); ++j) { + Hs.push_back(Hs[j].get()); + bool unsat = false; + if (mk_closure(n, Hs, A)) { + tmp = As[i].get(); + As[i] = A; + unsat = is_unsat(As, B); + As[i] = tmp; + } + if (unsat) { + convA = A; + As[j] = As.back(); + As.pop_back(); + --j; + } + else { + Hs.pop_back(); + } + } + if (Hs.size() > 1) { + As[i] = convA; + } + } + return sz > As.size(); + } + + bool core_convex_hull_generalizer::mk_closure(model_node& n, expr_ref_vector const& Hs, expr_ref& A) { + expr_ref_vector fmls(m), es(m); + add_variables(n, Hs.size(), fmls); + for (unsigned i = 0; i < Hs.size(); ++i) { + es.reset(); + qe::flatten_and(Hs[i], es); + if (!mk_convex(es, i, fmls)) { + return false; + } + } + A = m.mk_and(fmls.size(), fmls.c_ptr()); + return true; + } + + bool core_convex_hull_generalizer::is_unsat(expr_ref_vector const& As, expr* B) { + smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); + for (unsigned i = 0; i < As.size(); ++i) { + ctx.assert_expr(As[i]); + } + ctx.assert_expr(B); + return l_false == ctx.check(); + } + +#if 0 // now create the convex closure of the consequences: expr_ref tmp(m), zero(m); expr_ref_vector conv(m), es(m), enabled(m); @@ -359,19 +428,26 @@ namespace pdr { smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); for (unsigned i = 0; i < conv.size(); ++i) { ctx.assert_expr(conv[i].get()); + IF_VERBOSE(0, verbose_stream() << "CC: " << mk_pp(conv[i].get(), m) << "\n";); } for (unsigned i = 0; i < core.size(); ++i) { ctx.assert_expr(core[i]); + IF_VERBOSE(0, verbose_stream() << "Co: " << mk_pp(core[i], m) << "\n";); } vector transversal; while (l_true == ctx.check()) { - IF_VERBOSE(0, ctx.display(verbose_stream());); model_ref md; ctx.get_model(md); + IF_VERBOSE(0, + ctx.display(verbose_stream()); + verbose_stream() << "\n"; + model_smt2_pp(verbose_stream(), m, *md.get(), 0);); expr_ref_vector lits(m); unsigned_vector pos; for (unsigned i = 0; i < consequences.size(); ++i) { if (md->eval(enabled[i].get(), tmp, false)) { + IF_VERBOSE(0, + verbose_stream() << mk_pp(enabled[i].get(), m) << " |-> " << mk_pp(tmp, m) << "\n";); if (m.is_true(tmp)) { lits.push_back(tmp); pos.push_back(i); @@ -387,44 +463,37 @@ namespace pdr { // // we could no longer satisfy core using a partition. // - - } + IF_VERBOSE(0, verbose_stream() << "TBD: tranverse\n";); +#endif + void core_convex_hull_generalizer::add_variables(model_node& n, unsigned num_vars, expr_ref_vector& fmls) { manager& pm = n.pt().get_pdr_manager(); + SASSERT(num_vars > 0); while (m_vars.size() < num_vars) { m_vars.resize(m_vars.size()+1); m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); } - if (!m_vars[0].contains(n.pt().head())) { - expr_ref var(m); - m_vars[0].insert(n.pt().head(), 0); - unsigned sz = n.pt().sig_size(); - for (unsigned i = 0; i < sz; ++i) { - func_decl* fn0 = n.pt().sig(i); - sort* srt = fn0->get_range(); - if (a.is_int_real(srt)) { - func_decl* fn1 = pm.o2n(fn0, 0); - for (unsigned j = 0; j < num_vars; ++j) { - var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); - m_vars[j].insert(fn1, var); - m_trail.push_back(var); - } - } - } - } unsigned sz = n.pt().sig_size(); + for (unsigned i = 0; i < sz; ++i) { expr* var; ptr_vector vars; func_decl* fn0 = n.pt().sig(i); func_decl* fn1 = pm.o2n(fn0, 0); - for (unsigned j = 0; j < num_vars; ++j) { - VERIFY (m_vars[j].find(fn1, var)); - vars.push_back(var); + sort* srt = fn0->get_range(); + if (a.is_int_real(srt)) { + for (unsigned j = 0; j < num_vars; ++j) { + if (!m_vars[j].find(fn1, var)) { + var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); + m_trail.push_back(var); + m_vars[j].insert(fn1, var); + } + vars.push_back(var); + } + fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr()))); } - fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr()))); } if (m_is_closure) { for (unsigned i = 0; i < num_vars; ++i) { @@ -476,7 +545,6 @@ namespace pdr { } bool core_convex_hull_generalizer::mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv) { - conv.reset(); for (unsigned i = 0; i < core.size(); ++i) { mk_convex(core[i], index, conv); } diff --git a/src/muz/pdr/pdr_generalizers.h b/src/muz/pdr/pdr_generalizers.h index 10aa5b978..e566148ce 100644 --- a/src/muz/pdr/pdr_generalizers.h +++ b/src/muz/pdr/pdr_generalizers.h @@ -90,6 +90,9 @@ namespace pdr { void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); void method2(model_node& n, expr_ref_vector& core, bool& uses_level); void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); + bool strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B); + bool is_unsat(expr_ref_vector const& As, expr* B); + bool mk_closure(model_node& n, expr_ref_vector const& Hs, expr_ref& A); void add_variables(model_node& n, unsigned num_vars, expr_ref_vector& fmls); public: core_convex_hull_generalizer(context& ctx, bool is_closure); From cdbdf60aaebcbc6d8195497f5fb6a31d9ec86868 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 29 Aug 2013 14:34:08 -0700 Subject: [PATCH 135/509] working on generalizer Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_farkas_learner.cpp | 1 + src/muz/pdr/pdr_generalizers.cpp | 280 +++++++++++++++-------------- src/muz/pdr/pdr_generalizers.h | 6 +- 3 files changed, 148 insertions(+), 139 deletions(-) diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index 6202c913d..3cd1932ba 100644 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ b/src/muz/pdr/pdr_farkas_learner.cpp @@ -423,6 +423,7 @@ namespace pdr { else { bool_rewriter rw(m); rw.mk_or(n, (expr*const*)(lits), res); + res = m.mk_not(res); } } diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp index 1121243f2..bd9a1b62e 100644 --- a/src/muz/pdr/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -158,7 +158,7 @@ namespace pdr { } void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - method3(n, core, uses_level, new_cores); + // method3(n, core, uses_level, new_cores); method1(n, core, uses_level, new_cores); } @@ -188,11 +188,7 @@ namespace pdr { return; } add_variables(n, 2, fmls); - if (!mk_convex(core, 0, conv1)) { - new_cores.push_back(std::make_pair(core, uses_level)); - IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core), m) << "\n";); - return; - } + mk_convex(core, 0, conv1); conv1.append(fmls); expr_ref fml = n.pt().get_formulas(n.level(), false); fmls.reset(); @@ -202,10 +198,7 @@ namespace pdr { core2.reset(); conv2.reset(); qe::flatten_and(fml, core2); - if (!mk_convex(core2, 1, conv2)) { - IF_VERBOSE(0, verbose_stream() << "Non-convex: " << mk_pp(pm.mk_and(core2), m) << "\n";); - continue; - } + mk_convex(core2, 1, conv2); conv2.append(conv1); expr_ref state = pm.mk_and(conv2); TRACE("pdr", @@ -323,13 +316,30 @@ namespace pdr { verbose_stream() << mk_pp(core1[i].get(), m) << "\n"; }); - // Create disjunction. expr_ref tmp(m); - for (unsigned i = 0; i < consequences.size(); ++i) { - consequences[i] = m.mk_not(consequences[i].get()); + + // Check that F(R) => \/ consequences + { + expr_ref_vector cstate(m); + for (unsigned i = 0; i < consequences.size(); ++i) { + cstate.push_back(m.mk_not(consequences[i].get())); + } + tmp = m.mk_and(cstate.size(), cstate.c_ptr()); + model_node nd(0, tmp, n.pt(), n.level()); + pred_transformer::scoped_farkas sf (n.pt(), false); + VERIFY(l_false == n.pt().is_reachable(nd, &core1, uses_level1)); } + + // Create disjunction. tmp = m.mk_and(core.size(), core.c_ptr()); + // Check that \/ consequences => not (core) + if (!is_unsat(consequences, tmp)) { + IF_VERBOSE(0, verbose_stream() << "Consequences don't contradict the core\n";); + return; + } + IF_VERBOSE(0, verbose_stream() << "Consequences contradict core\n";); + if (!strengthen_consequences(n, consequences, tmp)) { return; } @@ -345,15 +355,15 @@ namespace pdr { expr_ref_vector Hs(m); Hs.push_back(As[i].get()); for (unsigned j = i + 1; j < As.size(); ++j) { - Hs.push_back(Hs[j].get()); + Hs.push_back(As[j].get()); bool unsat = false; - if (mk_closure(n, Hs, A)) { - tmp = As[i].get(); - As[i] = A; - unsat = is_unsat(As, B); - As[i] = tmp; - } + mk_convex(n, Hs, A); + tmp = As[i].get(); + As[i] = A; + unsat = is_unsat(As, B); + As[i] = tmp; if (unsat) { + IF_VERBOSE(0, verbose_stream() << "New convex: " << mk_pp(convA, m) << "\n";); convA = A; As[j] = As.back(); As.pop_back(); @@ -370,103 +380,27 @@ namespace pdr { return sz > As.size(); } - bool core_convex_hull_generalizer::mk_closure(model_node& n, expr_ref_vector const& Hs, expr_ref& A) { + void core_convex_hull_generalizer::mk_convex(model_node& n, expr_ref_vector const& Hs, expr_ref& A) { expr_ref_vector fmls(m), es(m); add_variables(n, Hs.size(), fmls); for (unsigned i = 0; i < Hs.size(); ++i) { es.reset(); qe::flatten_and(Hs[i], es); - if (!mk_convex(es, i, fmls)) { - return false; - } + mk_convex(es, i, fmls); } A = m.mk_and(fmls.size(), fmls.c_ptr()); - return true; } bool core_convex_hull_generalizer::is_unsat(expr_ref_vector const& As, expr* B) { smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); - for (unsigned i = 0; i < As.size(); ++i) { - ctx.assert_expr(As[i]); - } + expr_ref disj(m); + disj = m.mk_or(As.size(), As.c_ptr()); + ctx.assert_expr(disj); ctx.assert_expr(B); + std::cout << "Checking\n" << mk_pp(disj, m) << "\n" << mk_pp(B, m) << "\n"; return l_false == ctx.check(); } -#if 0 - // now create the convex closure of the consequences: - expr_ref tmp(m), zero(m); - expr_ref_vector conv(m), es(m), enabled(m); - zero = a.mk_numeral(rational(0), a.mk_real()); - add_variables(n, consequences.size(), conv); - for (unsigned i = 0; i < consequences.size(); ++i) { - es.reset(); - tmp = m.mk_not(consequences[i].get()); - qe::flatten_and(tmp, es); - if (!mk_convex(es, i, conv)) { - IF_VERBOSE(0, verbose_stream() << "Failed to create convex closure\n";); - return; - } - es.reset(); - // - // enabled[i] = not (sigma_i = 0 and z_i1 = 0 and .. and z_im = 0) - // - es.push_back(m.mk_eq(m_sigma[i].get(), zero)); - for (unsigned j = 0; j < n.pt().sig_size(); ++j) { - func_decl* fn0 = n.pt().sig(j); - func_decl* fn1 = pm.o2n(fn0, 0); - expr* var; - VERIFY (m_vars[i].find(fn1, var)); - es.push_back(m.mk_eq(var, zero)); - } - - enabled.push_back(m.mk_not(m.mk_and(es.size(), es.c_ptr()))); - } - - // the convex closure was created of all consequences. - // now determine a subset of enabled constraints. - smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); - for (unsigned i = 0; i < conv.size(); ++i) { - ctx.assert_expr(conv[i].get()); - IF_VERBOSE(0, verbose_stream() << "CC: " << mk_pp(conv[i].get(), m) << "\n";); - } - for (unsigned i = 0; i < core.size(); ++i) { - ctx.assert_expr(core[i]); - IF_VERBOSE(0, verbose_stream() << "Co: " << mk_pp(core[i], m) << "\n";); - } - vector transversal; - while (l_true == ctx.check()) { - model_ref md; - ctx.get_model(md); - IF_VERBOSE(0, - ctx.display(verbose_stream()); - verbose_stream() << "\n"; - model_smt2_pp(verbose_stream(), m, *md.get(), 0);); - expr_ref_vector lits(m); - unsigned_vector pos; - for (unsigned i = 0; i < consequences.size(); ++i) { - if (md->eval(enabled[i].get(), tmp, false)) { - IF_VERBOSE(0, - verbose_stream() << mk_pp(enabled[i].get(), m) << " |-> " << mk_pp(tmp, m) << "\n";); - if (m.is_true(tmp)) { - lits.push_back(tmp); - pos.push_back(i); - } - } - } - transversal.push_back(pos); - SASSERT(!lits.empty()); - tmp = m.mk_not(m.mk_and(lits.size(), lits.c_ptr())); - TRACE("pdr", tout << "add block: " << mk_pp(tmp, m) << "\n";); - ctx.assert_expr(tmp); - } - // - // we could no longer satisfy core using a partition. - // - IF_VERBOSE(0, verbose_stream() << "TBD: tranverse\n";); -#endif - - void core_convex_hull_generalizer::add_variables(model_node& n, unsigned num_vars, expr_ref_vector& fmls) { manager& pm = n.pt().get_pdr_manager(); SASSERT(num_vars > 0); @@ -544,36 +478,42 @@ namespace pdr { return true; } - bool core_convex_hull_generalizer::mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv) { + void core_convex_hull_generalizer::mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv) { for (unsigned i = 0; i < core.size(); ++i) { mk_convex(core[i], index, conv); } - return !conv.empty() && mk_closure(conv); + mk_closure(conv); } void core_convex_hull_generalizer::mk_convex(expr* fml, unsigned index, expr_ref_vector& conv) { expr_ref result(m), r1(m), r2(m); expr* e1, *e2; bool is_not = m.is_not(fml, fml); - if (a.is_le(fml, e1, e2) && mk_convex(e1, index, false, r1) && mk_convex(e2, index, false, r2)) { + if (a.is_le(fml, e1, e2)) { + mk_convex(e1, index, false, r1); + mk_convex(e2, index, false, r2); result = a.mk_le(r1, r2); } - else if (a.is_ge(fml, e1, e2) && mk_convex(e1, index, false, r1) && mk_convex(e2, index, false, r2)) { + else if (a.is_ge(fml, e1, e2)) { + mk_convex(e1, index, false, r1); + mk_convex(e2, index, false, r2); result = a.mk_ge(r1, r2); } - else if (a.is_gt(fml, e1, e2) && mk_convex(e1, index, false, r1) && mk_convex(e2, index, false, r2)) { + else if (a.is_gt(fml, e1, e2)) { + mk_convex(e1, index, false, r1); + mk_convex(e2, index, false, r2); result = a.mk_gt(r1, r2); } - else if (a.is_lt(fml, e1, e2) && mk_convex(e1, index, false, r1) && mk_convex(e2, index, false, r2)) { + else if (a.is_lt(fml, e1, e2)) { + mk_convex(e1, index, false, r1); + mk_convex(e2, index, false, r2); result = a.mk_lt(r1, r2); } - else if (m.is_eq(fml, e1, e2) && a.is_int_real(e1) && mk_convex(e1, index, false, r1) && mk_convex(e2, index, false, r2)) { + else if (m.is_eq(fml, e1, e2) && a.is_int_real(e1)) { + mk_convex(e1, index, false, r1); + mk_convex(e2, index, false, r2); result = m.mk_eq(r1, r2); } - else { - TRACE("pdr", tout << "Did not handle " << mk_pp(fml, m) << "\n";); - return; - } if (is_not) { result = m.mk_not(result); } @@ -591,49 +531,46 @@ namespace pdr { } - bool core_convex_hull_generalizer::mk_convex(expr* term, unsigned index, bool is_mul, expr_ref& result) { + void core_convex_hull_generalizer::mk_convex(expr* term, unsigned index, bool is_mul, expr_ref& result) { if (!is_app(term)) { - return false; + result = term; + return; } app* app = to_app(term); expr* e1, *e2; expr_ref r1(m), r2(m); - if (translate(app->get_decl(), index, result)) { - return true; - } if (a.is_add(term)) { - bool ok = true; expr_ref_vector args(m); - for (unsigned i = 0; ok && i < app->get_num_args(); ++i) { - ok = mk_convex(app->get_arg(i), index, is_mul, r1); - if (ok) { - args.push_back(r1); - } + for (unsigned i = 0; i < app->get_num_args(); ++i) { + mk_convex(app->get_arg(i), index, is_mul, r1); + args.push_back(r1); } - if (ok) { - result = a.mk_add(args.size(), args.c_ptr()); - } - return ok; + result = a.mk_add(args.size(), args.c_ptr()); } - if (a.is_sub(term, e1, e2) && mk_convex(e1, index, is_mul, r1) && mk_convex(e2, index, is_mul, r2)) { + else if (a.is_sub(term, e1, e2)) { + mk_convex(e1, index, is_mul, r1); + mk_convex(e2, index, is_mul, r2); result = a.mk_sub(r1, r2); - return true; } - if (a.is_mul(term, e1, e2) && mk_convex(e1, index, true, r1) && mk_convex(e2, index, true, r2)) { + else if (a.is_mul(term, e1, e2)) { + mk_convex(e1, index, true, r1); + mk_convex(e2, index, true, r2); result = a.mk_mul(r1, r2); - return true; } - if (a.is_numeral(term)) { + else if (a.is_numeral(term)) { if (is_mul) { result = term; } else { result = a.mk_mul(m_sigma[index].get(), term); } - return true; } - IF_VERBOSE(0, verbose_stream() << "Not handled: " << mk_pp(term, m) << "\n";); - return false; + else if (translate(app->get_decl(), index, result)) { + // no-op + } + else { + result = term; + } } @@ -1088,3 +1025,74 @@ namespace pdr { } } }; + +#if 0 + // now create the convex closure of the consequences: + expr_ref tmp(m), zero(m); + expr_ref_vector conv(m), es(m), enabled(m); + zero = a.mk_numeral(rational(0), a.mk_real()); + add_variables(n, consequences.size(), conv); + for (unsigned i = 0; i < consequences.size(); ++i) { + es.reset(); + tmp = m.mk_not(consequences[i].get()); + qe::flatten_and(tmp, es); + mk_convex(es, i, conv); + es.reset(); + // + // enabled[i] = not (sigma_i = 0 and z_i1 = 0 and .. and z_im = 0) + // + es.push_back(m.mk_eq(m_sigma[i].get(), zero)); + for (unsigned j = 0; j < n.pt().sig_size(); ++j) { + func_decl* fn0 = n.pt().sig(j); + func_decl* fn1 = pm.o2n(fn0, 0); + expr* var; + VERIFY (m_vars[i].find(fn1, var)); + es.push_back(m.mk_eq(var, zero)); + } + + enabled.push_back(m.mk_not(m.mk_and(es.size(), es.c_ptr()))); + } + + // the convex closure was created of all consequences. + // now determine a subset of enabled constraints. + smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); + for (unsigned i = 0; i < conv.size(); ++i) { + ctx.assert_expr(conv[i].get()); + IF_VERBOSE(0, verbose_stream() << "CC: " << mk_pp(conv[i].get(), m) << "\n";); + } + for (unsigned i = 0; i < core.size(); ++i) { + ctx.assert_expr(core[i]); + IF_VERBOSE(0, verbose_stream() << "Co: " << mk_pp(core[i], m) << "\n";); + } + vector transversal; + while (l_true == ctx.check()) { + model_ref md; + ctx.get_model(md); + IF_VERBOSE(0, + ctx.display(verbose_stream()); + verbose_stream() << "\n"; + model_smt2_pp(verbose_stream(), m, *md.get(), 0);); + expr_ref_vector lits(m); + unsigned_vector pos; + for (unsigned i = 0; i < consequences.size(); ++i) { + if (md->eval(enabled[i].get(), tmp, false)) { + IF_VERBOSE(0, + verbose_stream() << mk_pp(enabled[i].get(), m) << " |-> " << mk_pp(tmp, m) << "\n";); + if (m.is_true(tmp)) { + lits.push_back(tmp); + pos.push_back(i); + } + } + } + transversal.push_back(pos); + SASSERT(!lits.empty()); + tmp = m.mk_not(m.mk_and(lits.size(), lits.c_ptr())); + TRACE("pdr", tout << "add block: " << mk_pp(tmp, m) << "\n";); + ctx.assert_expr(tmp); + } + // + // we could no longer satisfy core using a partition. + // + IF_VERBOSE(0, verbose_stream() << "TBD: tranverse\n";); +#endif + diff --git a/src/muz/pdr/pdr_generalizers.h b/src/muz/pdr/pdr_generalizers.h index e566148ce..e5f146d0d 100644 --- a/src/muz/pdr/pdr_generalizers.h +++ b/src/muz/pdr/pdr_generalizers.h @@ -83,16 +83,16 @@ namespace pdr { bool m_is_closure; expr_ref mk_closure(expr* e); bool mk_closure(expr_ref_vector& conj); - bool mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv); + void mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv); void mk_convex(expr* fml, unsigned index, expr_ref_vector& conv); - bool mk_convex(expr* term, unsigned index, bool is_mul, expr_ref& result); + void mk_convex(expr* term, unsigned index, bool is_mul, expr_ref& result); bool translate(func_decl* fn, unsigned index, expr_ref& result); void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); void method2(model_node& n, expr_ref_vector& core, bool& uses_level); void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); bool strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B); bool is_unsat(expr_ref_vector const& As, expr* B); - bool mk_closure(model_node& n, expr_ref_vector const& Hs, expr_ref& A); + void mk_convex(model_node& n, expr_ref_vector const& Hs, expr_ref& A); void add_variables(model_node& n, unsigned num_vars, expr_ref_vector& fmls); public: core_convex_hull_generalizer(context& ctx, bool is_closure); From 58b16c55850789277ed027e3992fe8c83a8f8464 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 31 Aug 2013 20:39:49 -0700 Subject: [PATCH 136/509] generalize mk_convex method to work with scaling Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_generalizers.cpp | 135 +++++++++++-------------------- src/muz/pdr/pdr_generalizers.h | 18 +++++ 2 files changed, 64 insertions(+), 89 deletions(-) diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp index bd9a1b62e..64766ea93 100644 --- a/src/muz/pdr/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -23,6 +23,7 @@ Revision History: #include "pdr_generalizers.h" #include "expr_abstract.h" #include "var_subst.h" +#include "expr_safe_replace.h" #include "model_smt2_pp.h" @@ -147,6 +148,49 @@ namespace pdr { m_farkas_learner.collect_statistics(st); } + expr_ref scaler::operator()(expr* e, expr* k, obj_map* translate) { + m_cache[0].reset(); + m_cache[1].reset(); + m_translate = translate; + m_k = k; + return scale(e, false); + } + + expr_ref scaler::scale(expr* e, bool is_mul) { + expr* r; + if (m_cache[is_mul].find(e, r)) { + return expr_ref(r, m); + } + if (!is_app(e)) { + return expr_ref(e, m); + } + app* ap = to_app(e); + if (m_translate && m_translate->find(ap->get_decl(), r)) { + return expr_ref(r, m); + } + if (!is_mul && a.is_numeral(e)) { + return expr_ref(a.mk_mul(m_k, e), m); + } + expr_ref_vector args(m); + bool is_mul_rec = is_mul || a.is_mul(e); + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + args.push_back(scale(ap->get_arg(i), is_mul_rec)); + } + expr_ref result(m); + result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); + m_cache[is_mul].insert(e, result); + return result; + } + + expr_ref scaler::undo_k(expr* e, expr* k) { + expr_safe_replace sub(m); + th_rewriter rw(m); + expr_ref result(e, m); + sub.insert(k, a.mk_numeral(rational(1), false)); + sub(result); + rw(result); + return result; + } core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx, bool is_closure): core_generalizer(ctx), @@ -479,100 +523,13 @@ namespace pdr { } void core_convex_hull_generalizer::mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv) { + scaler sc(m); for (unsigned i = 0; i < core.size(); ++i) { - mk_convex(core[i], index, conv); + conv.push_back(sc(core[i], m_sigma[index].get(), &m_vars[index])); } mk_closure(conv); } - void core_convex_hull_generalizer::mk_convex(expr* fml, unsigned index, expr_ref_vector& conv) { - expr_ref result(m), r1(m), r2(m); - expr* e1, *e2; - bool is_not = m.is_not(fml, fml); - if (a.is_le(fml, e1, e2)) { - mk_convex(e1, index, false, r1); - mk_convex(e2, index, false, r2); - result = a.mk_le(r1, r2); - } - else if (a.is_ge(fml, e1, e2)) { - mk_convex(e1, index, false, r1); - mk_convex(e2, index, false, r2); - result = a.mk_ge(r1, r2); - } - else if (a.is_gt(fml, e1, e2)) { - mk_convex(e1, index, false, r1); - mk_convex(e2, index, false, r2); - result = a.mk_gt(r1, r2); - } - else if (a.is_lt(fml, e1, e2)) { - mk_convex(e1, index, false, r1); - mk_convex(e2, index, false, r2); - result = a.mk_lt(r1, r2); - } - else if (m.is_eq(fml, e1, e2) && a.is_int_real(e1)) { - mk_convex(e1, index, false, r1); - mk_convex(e2, index, false, r2); - result = m.mk_eq(r1, r2); - } - if (is_not) { - result = m.mk_not(result); - } - conv.push_back(result); - } - - - bool core_convex_hull_generalizer::translate(func_decl* f, unsigned index, expr_ref& result) { - expr* tmp; - if (m_vars[index].find(f, tmp)) { - result = tmp; - return true; - } - return false; - } - - - void core_convex_hull_generalizer::mk_convex(expr* term, unsigned index, bool is_mul, expr_ref& result) { - if (!is_app(term)) { - result = term; - return; - } - app* app = to_app(term); - expr* e1, *e2; - expr_ref r1(m), r2(m); - if (a.is_add(term)) { - expr_ref_vector args(m); - for (unsigned i = 0; i < app->get_num_args(); ++i) { - mk_convex(app->get_arg(i), index, is_mul, r1); - args.push_back(r1); - } - result = a.mk_add(args.size(), args.c_ptr()); - } - else if (a.is_sub(term, e1, e2)) { - mk_convex(e1, index, is_mul, r1); - mk_convex(e2, index, is_mul, r2); - result = a.mk_sub(r1, r2); - } - else if (a.is_mul(term, e1, e2)) { - mk_convex(e1, index, true, r1); - mk_convex(e2, index, true, r2); - result = a.mk_mul(r1, r2); - } - else if (a.is_numeral(term)) { - if (is_mul) { - result = term; - } - else { - result = a.mk_mul(m_sigma[index].get(), term); - } - } - else if (translate(app->get_decl(), index, result)) { - // no-op - } - else { - result = term; - } - } - // --------------------------------- // core_arith_inductive_generalizer diff --git a/src/muz/pdr/pdr_generalizers.h b/src/muz/pdr/pdr_generalizers.h index e5f146d0d..ca9c5a969 100644 --- a/src/muz/pdr/pdr_generalizers.h +++ b/src/muz/pdr/pdr_generalizers.h @@ -73,6 +73,24 @@ namespace pdr { virtual void collect_statistics(statistics& st) const; }; + // Arithmetic scaling functor. + // Variables are replaced using + // m_translate. Constants are replaced by + // multiplication with a variable 'k' (scale factor). + class scaler { + ast_manager& m; + arith_util a; + obj_map m_cache[2]; + expr* m_k; + obj_map* m_translate; + public: + scaler(ast_manager& m): m(m), a(m), m_translate(0) {} + expr_ref operator()(expr* e, expr* k, obj_map* translate = 0); + expr_ref undo_k(expr* e, expr* k); + private: + expr_ref scale(expr* e, bool is_mul); + }; + class core_convex_hull_generalizer : public core_generalizer { ast_manager& m; arith_util a; From 06a858ef3d8e77158b9620eb8e4c1e31823b0ed5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 1 Sep 2013 13:43:19 -0700 Subject: [PATCH 137/509] refactor closure code Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_closure.cpp | 175 +++++++++++++++++ src/muz/pdr/pdr_closure.h | 67 +++++++ src/muz/pdr/pdr_generalizers.cpp | 315 ++----------------------------- src/muz/pdr/pdr_generalizers.h | 31 +-- src/qe/qe_util.cpp | 16 ++ src/qe/qe_util.h | 4 + 6 files changed, 283 insertions(+), 325 deletions(-) create mode 100644 src/muz/pdr/pdr_closure.cpp create mode 100644 src/muz/pdr/pdr_closure.h diff --git a/src/muz/pdr/pdr_closure.cpp b/src/muz/pdr/pdr_closure.cpp new file mode 100644 index 000000000..483b48932 --- /dev/null +++ b/src/muz/pdr/pdr_closure.cpp @@ -0,0 +1,175 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + pdr_closure.cpp + +Abstract: + + Utility functions for computing closures. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-9-1. + +Revision History: + +--*/ + +#include "pdr_closure.h" +#include "pdr_context.h" +#include "expr_safe_replace.h" + +namespace pdr { + + expr_ref scaler::operator()(expr* e, expr* k, obj_map* translate) { + m_cache[0].reset(); + m_cache[1].reset(); + m_translate = translate; + m_k = k; + return scale(e, false); + } + + expr_ref scaler::scale(expr* e, bool is_mul) { + expr* r; + if (m_cache[is_mul].find(e, r)) { + return expr_ref(r, m); + } + if (!is_app(e)) { + return expr_ref(e, m); + } + app* ap = to_app(e); + if (m_translate && m_translate->find(ap->get_decl(), r)) { + return expr_ref(r, m); + } + if (!is_mul && a.is_numeral(e)) { + return expr_ref(a.mk_mul(m_k, e), m); + } + expr_ref_vector args(m); + bool is_mul_rec = is_mul || a.is_mul(e); + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + args.push_back(scale(ap->get_arg(i), is_mul_rec)); + } + expr_ref result(m); + result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); + m_cache[is_mul].insert(e, result); + return result; + } + + expr_ref scaler::undo_k(expr* e, expr* k) { + expr_safe_replace sub(m); + th_rewriter rw(m); + expr_ref result(e, m); + sub.insert(k, a.mk_numeral(rational(1), false)); + sub(result); + rw(result); + return result; + } + + + closure::closure(pred_transformer& p, bool is_closure): + m(p.get_manager()), m_pt(p), a(m), + m_is_closure(is_closure), m_sigma(m), m_trail(m) {} + + + void closure::add_variables(unsigned num_vars, expr_ref_vector& fmls) { + manager& pm = m_pt.get_pdr_manager(); + SASSERT(num_vars > 0); + while (m_vars.size() < num_vars) { + m_vars.resize(m_vars.size()+1); + m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); + } + + unsigned sz = m_pt.sig_size(); + + for (unsigned i = 0; i < sz; ++i) { + expr* var; + ptr_vector vars; + func_decl* fn0 = m_pt.sig(i); + func_decl* fn1 = pm.o2n(fn0, 0); + sort* srt = fn0->get_range(); + if (a.is_int_real(srt)) { + for (unsigned j = 0; j < num_vars; ++j) { + if (!m_vars[j].find(fn1, var)) { + var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); + m_trail.push_back(var); + m_vars[j].insert(fn1, var); + } + vars.push_back(var); + } + fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr()))); + } + } + if (m_is_closure) { + for (unsigned i = 0; i < num_vars; ++i) { + fmls.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); + } + } + else { + // is interior: + for (unsigned i = 0; i < num_vars; ++i) { + fmls.push_back(a.mk_gt(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); + } + } + fmls.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(num_vars, m_sigma.c_ptr()))); + } + + expr_ref closure::close_fml(expr* e) { + expr* e0, *e1, *e2; + expr_ref result(m); + if (a.is_lt(e, e1, e2)) { + result = a.mk_le(e1, e2); + } + else if (a.is_gt(e, e1, e2)) { + result = a.mk_ge(e1, e2); + } + else if (m.is_not(e, e0) && a.is_ge(e0, e1, e2)) { + result = a.mk_le(e1, e2); + } + else if (m.is_not(e, e0) && a.is_le(e0, e1, e2)) { + result = a.mk_ge(e1, e2); + } + else if (a.is_ge(e) || a.is_le(e) || m.is_eq(e) || + (m.is_not(e, e0) && (a.is_gt(e0) || a.is_lt(e0)))) { + result = e; + } + else { + IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";); + } + return result; + } + + expr_ref closure::close_conjunction(expr* fml) { + expr_ref_vector fmls(m); + qe::flatten_and(fml, fmls); + for (unsigned i = 0; i < fmls.size(); ++i) { + fmls[i] = close_fml(fmls[i].get()); + } + return expr_ref(m.mk_and(fmls.size(), fmls.c_ptr()), m); + } + + expr_ref closure::relax(unsigned i, expr* fml) { + scaler sc(m); + expr_ref result = sc(fml, m_sigma[i].get(), &m_vars[i]); + return close_conjunction(result); + } + + expr_ref closure::operator()(expr_ref_vector const& As) { + if (As.empty()) { + return expr_ref(m.mk_false(), m); + } + if (As.size() == 1) { + return expr_ref(As[0], m); + } + expr_ref_vector fmls(m); + expr_ref B(m); + add_variables(As.size(), fmls); + for (unsigned i = 0; i < As.size(); ++i) { + fmls.push_back(relax(i, As[i])); + } + B = qe::mk_and(fmls); + return B; + } + +} diff --git a/src/muz/pdr/pdr_closure.h b/src/muz/pdr/pdr_closure.h new file mode 100644 index 000000000..885dbce8d --- /dev/null +++ b/src/muz/pdr/pdr_closure.h @@ -0,0 +1,67 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + pdr_closure.h + +Abstract: + + Utility functions for computing closures. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-9-1. + +Revision History: + +--*/ + +#ifndef _PDR_CLOSURE_H_ +#define _PDR_CLOSURE_H_ + +#include "arith_decl_plugin.h" + +namespace pdr { + + // Arithmetic scaling functor. + // Variables are replaced using + // m_translate. Constants are replaced by + // multiplication with a variable 'k' (scale factor). + class scaler { + ast_manager& m; + arith_util a; + obj_map m_cache[2]; + expr* m_k; + obj_map* m_translate; + public: + scaler(ast_manager& m): m(m), a(m), m_translate(0) {} + expr_ref operator()(expr* e, expr* k, obj_map* translate = 0); + expr_ref undo_k(expr* e, expr* k); + private: + expr_ref scale(expr* e, bool is_mul); + }; + + class pred_transformer; + + class closure { + ast_manager& m; + pred_transformer& m_pt; + arith_util a; + bool m_is_closure; + expr_ref_vector m_sigma; + expr_ref_vector m_trail; + vector > m_vars; + + expr_ref relax(unsigned i, expr* fml); + expr_ref close_conjunction(expr* fml); + expr_ref close_fml(expr* fml); + void add_variables(unsigned num_vars, expr_ref_vector& fmls); + public: + closure(pred_transformer& pt, bool is_closure); + expr_ref operator()(expr_ref_vector const& As); + + }; +} + +#endif diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp index 64766ea93..3abf320b2 100644 --- a/src/muz/pdr/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -148,56 +148,10 @@ namespace pdr { m_farkas_learner.collect_statistics(st); } - expr_ref scaler::operator()(expr* e, expr* k, obj_map* translate) { - m_cache[0].reset(); - m_cache[1].reset(); - m_translate = translate; - m_k = k; - return scale(e, false); - } - - expr_ref scaler::scale(expr* e, bool is_mul) { - expr* r; - if (m_cache[is_mul].find(e, r)) { - return expr_ref(r, m); - } - if (!is_app(e)) { - return expr_ref(e, m); - } - app* ap = to_app(e); - if (m_translate && m_translate->find(ap->get_decl(), r)) { - return expr_ref(r, m); - } - if (!is_mul && a.is_numeral(e)) { - return expr_ref(a.mk_mul(m_k, e), m); - } - expr_ref_vector args(m); - bool is_mul_rec = is_mul || a.is_mul(e); - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - args.push_back(scale(ap->get_arg(i), is_mul_rec)); - } - expr_ref result(m); - result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); - m_cache[is_mul].insert(e, result); - return result; - } - - expr_ref scaler::undo_k(expr* e, expr* k) { - expr_safe_replace sub(m); - th_rewriter rw(m); - expr_ref result(e, m); - sub.insert(k, a.mk_numeral(rational(1), false)); - sub(result); - rw(result); - return result; - } core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx, bool is_closure): core_generalizer(ctx), m(ctx.get_manager()), - a(m), - m_sigma(m), - m_trail(m), m_is_closure(is_closure) { } @@ -224,38 +178,35 @@ namespace pdr { // update with new core. // void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - manager& pm = n.pt().get_pdr_manager(); - expr_ref_vector conv1(m), conv2(m), core1(m), core2(m), fmls(m); - unsigned orig_size = new_cores.size(); + expr_ref_vector conv2(m), fmls(m), fml1_2(m); + bool change = false; + if (core.empty()) { new_cores.push_back(std::make_pair(core, uses_level)); return; } - add_variables(n, 2, fmls); - mk_convex(core, 0, conv1); - conv1.append(fmls); - expr_ref fml = n.pt().get_formulas(n.level(), false); - fmls.reset(); - qe::flatten_and(fml, fmls); + closure cl(n.pt(), m_is_closure); + + expr_ref fml1 = qe::mk_and(core); + expr_ref fml2 = n.pt().get_formulas(n.level(), false); + fml1_2.push_back(fml1); + fml1_2.push_back(0); + qe::flatten_and(fml2, fmls); for (unsigned i = 0; i < fmls.size(); ++i) { - fml = m.mk_not(fmls[i].get()); - core2.reset(); - conv2.reset(); - qe::flatten_and(fml, core2); - mk_convex(core2, 1, conv2); - conv2.append(conv1); - expr_ref state = pm.mk_and(conv2); + fml2 = m.mk_not(fmls[i].get()); + fml1_2[1] = fml2; + expr_ref state = cl(fml1_2); TRACE("pdr", tout << "Check states:\n" << mk_pp(state, m) << "\n"; - tout << "Old states:\n" << mk_pp(fml, m) << "\n"; + tout << "Old states:\n" << mk_pp(fml2, m) << "\n"; ); model_node nd(0, state, n.pt(), n.level()); pred_transformer::scoped_farkas sf(n.pt(), true); bool uses_level1 = uses_level; if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) { new_cores.push_back(std::make_pair(conv2, uses_level1)); - - expr_ref state1 = pm.mk_and(conv2); + change = true; + expr_ref state1 = qe::mk_and(conv2); TRACE("pdr", tout << mk_pp(state, m) << "\n"; tout << "Generalized to:\n" << mk_pp(state1, m) << "\n";); @@ -264,70 +215,9 @@ namespace pdr { verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";); } } - if (!m_is_closure || new_cores.size() == orig_size) { + if (!m_is_closure || !change) { new_cores.push_back(std::make_pair(core, uses_level)); } - - } - - // take as starting point two points from different regions. - void core_convex_hull_generalizer::method2(model_node& n, expr_ref_vector& core, bool& uses_level) { - expr_ref_vector conv1(m), conv2(m), core1(m), core2(m); - if (core.empty()) { - return; - } - manager& pm = n.pt().get_pdr_manager(); - smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); - expr_ref goal(pm.mk_and(core)); - ctx.assert_expr(goal); - lbool r = ctx.check(); - if (r != l_true) { - IF_VERBOSE(0, verbose_stream() << "unexpected result from satisfiability check\n";); - return; - } - add_variables(n, 2, conv1); - model_ref mdl; - ctx.get_model(mdl); - - unsigned sz = n.pt().sig_size(); - for (unsigned i = 0; i < sz; ++i) { - expr_ref_vector constr(m); - expr* left, *right; - func_decl* fn0 = n.pt().sig(i); - func_decl* fn1 = pm.o2n(fn0, 0); - if (m_vars[0].find(fn1, left) && m_vars[1].find(fn1, right)) { - expr_ref val(m); - mdl->eval(fn1, val); - if (val) { - conv1.push_back(m.mk_eq(left, val)); - constr.push_back(m.mk_eq(right, val)); - } - } - expr_ref new_model = pm.mk_and(constr); - m_trail.push_back(new_model); - m_trail.push_back(goal); - m_models.insert(goal, new_model); - } - obj_map::iterator it = m_models.begin(), end = m_models.end(); - for (; it != end; ++it) { - if (it->m_key == goal) { - continue; - } - conv1.push_back(it->m_value); - expr_ref state = pm.mk_and(conv1); - TRACE("pdr", tout << "Try:\n" << mk_pp(state, m) << "\n";); - model_node nd(0, state, n.pt(), n.level()); - pred_transformer::scoped_farkas sf(n.pt(), true); - if (l_false == n.pt().is_reachable(nd, &conv2, uses_level)) { - IF_VERBOSE(0, - verbose_stream() << mk_pp(state, m) << "\n"; - verbose_stream() << "Generalized to:\n" << mk_pp(pm.mk_and(conv2), m) << "\n";); - core.reset(); - core.append(conv2); - return; - } - conv1.pop_back(); - } } /* @@ -339,7 +229,6 @@ namespace pdr { for (unsigned i = 0; i < core.size(); ++i) { tout << "B:" << mk_pp(core[i], m) << "\n"; }); - manager& pm = n.pt().get_pdr_manager(); bool uses_level1; expr_ref_vector core1(m); core1.append(core); @@ -395,13 +284,14 @@ namespace pdr { bool core_convex_hull_generalizer::strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B) { expr_ref A(m), tmp(m), convA(m); unsigned sz = As.size(); + closure cl(n.pt(), m_is_closure); for (unsigned i = 0; i < As.size(); ++i) { expr_ref_vector Hs(m); Hs.push_back(As[i].get()); for (unsigned j = i + 1; j < As.size(); ++j) { Hs.push_back(As[j].get()); bool unsat = false; - mk_convex(n, Hs, A); + A = cl(Hs); tmp = As[i].get(); As[i] = A; unsat = is_unsat(As, B); @@ -424,16 +314,6 @@ namespace pdr { return sz > As.size(); } - void core_convex_hull_generalizer::mk_convex(model_node& n, expr_ref_vector const& Hs, expr_ref& A) { - expr_ref_vector fmls(m), es(m); - add_variables(n, Hs.size(), fmls); - for (unsigned i = 0; i < Hs.size(); ++i) { - es.reset(); - qe::flatten_and(Hs[i], es); - mk_convex(es, i, fmls); - } - A = m.mk_and(fmls.size(), fmls.c_ptr()); - } bool core_convex_hull_generalizer::is_unsat(expr_ref_vector const& As, expr* B) { smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); @@ -445,91 +325,6 @@ namespace pdr { return l_false == ctx.check(); } - void core_convex_hull_generalizer::add_variables(model_node& n, unsigned num_vars, expr_ref_vector& fmls) { - manager& pm = n.pt().get_pdr_manager(); - SASSERT(num_vars > 0); - while (m_vars.size() < num_vars) { - m_vars.resize(m_vars.size()+1); - m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); - } - - unsigned sz = n.pt().sig_size(); - - for (unsigned i = 0; i < sz; ++i) { - expr* var; - ptr_vector vars; - func_decl* fn0 = n.pt().sig(i); - func_decl* fn1 = pm.o2n(fn0, 0); - sort* srt = fn0->get_range(); - if (a.is_int_real(srt)) { - for (unsigned j = 0; j < num_vars; ++j) { - if (!m_vars[j].find(fn1, var)) { - var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); - m_trail.push_back(var); - m_vars[j].insert(fn1, var); - } - vars.push_back(var); - } - fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr()))); - } - } - if (m_is_closure) { - for (unsigned i = 0; i < num_vars; ++i) { - fmls.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); - } - } - else { - // is interior: - for (unsigned i = 0; i < num_vars; ++i) { - fmls.push_back(a.mk_gt(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); - } - } - fmls.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(num_vars, m_sigma.c_ptr()))); - } - - expr_ref core_convex_hull_generalizer::mk_closure(expr* e) { - expr* e0, *e1, *e2; - expr_ref result(m); - if (a.is_lt(e, e1, e2)) { - result = a.mk_le(e1, e2); - } - else if (a.is_gt(e, e1, e2)) { - result = a.mk_ge(e1, e2); - } - else if (m.is_not(e, e0) && a.is_ge(e0, e1, e2)) { - result = a.mk_le(e1, e2); - } - else if (m.is_not(e, e0) && a.is_le(e0, e1, e2)) { - result = a.mk_ge(e1, e2); - } - else if (a.is_ge(e) || a.is_le(e) || m.is_eq(e) || - (m.is_not(e, e0) && (a.is_gt(e0) || a.is_lt(e0)))) { - result = e; - } - else { - IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";); - } - return result; - } - - bool core_convex_hull_generalizer::mk_closure(expr_ref_vector& conj) { - for (unsigned i = 0; i < conj.size(); ++i) { - conj[i] = mk_closure(conj[i].get()); - if (!conj[i].get()) { - return false; - } - } - return true; - } - - void core_convex_hull_generalizer::mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv) { - scaler sc(m); - for (unsigned i = 0; i < core.size(); ++i) { - conv.push_back(sc(core[i], m_sigma[index].get(), &m_vars[index])); - } - mk_closure(conv); - } - // --------------------------------- // core_arith_inductive_generalizer @@ -800,7 +595,7 @@ namespace pdr { for (unsigned i = ut_size; i < t_size; i++) { conj.push_back(rule.get_tail(i)); } - result = pm.mk_and(conj); + result = qe::mk_and(conj); if (!sub.empty()) { expr_ref tmp = result; var_subst(m, false)(tmp, sub.size(), sub.c_ptr(), result); @@ -983,73 +778,3 @@ namespace pdr { } }; -#if 0 - // now create the convex closure of the consequences: - expr_ref tmp(m), zero(m); - expr_ref_vector conv(m), es(m), enabled(m); - zero = a.mk_numeral(rational(0), a.mk_real()); - add_variables(n, consequences.size(), conv); - for (unsigned i = 0; i < consequences.size(); ++i) { - es.reset(); - tmp = m.mk_not(consequences[i].get()); - qe::flatten_and(tmp, es); - mk_convex(es, i, conv); - es.reset(); - // - // enabled[i] = not (sigma_i = 0 and z_i1 = 0 and .. and z_im = 0) - // - es.push_back(m.mk_eq(m_sigma[i].get(), zero)); - for (unsigned j = 0; j < n.pt().sig_size(); ++j) { - func_decl* fn0 = n.pt().sig(j); - func_decl* fn1 = pm.o2n(fn0, 0); - expr* var; - VERIFY (m_vars[i].find(fn1, var)); - es.push_back(m.mk_eq(var, zero)); - } - - enabled.push_back(m.mk_not(m.mk_and(es.size(), es.c_ptr()))); - } - - // the convex closure was created of all consequences. - // now determine a subset of enabled constraints. - smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); - for (unsigned i = 0; i < conv.size(); ++i) { - ctx.assert_expr(conv[i].get()); - IF_VERBOSE(0, verbose_stream() << "CC: " << mk_pp(conv[i].get(), m) << "\n";); - } - for (unsigned i = 0; i < core.size(); ++i) { - ctx.assert_expr(core[i]); - IF_VERBOSE(0, verbose_stream() << "Co: " << mk_pp(core[i], m) << "\n";); - } - vector transversal; - while (l_true == ctx.check()) { - model_ref md; - ctx.get_model(md); - IF_VERBOSE(0, - ctx.display(verbose_stream()); - verbose_stream() << "\n"; - model_smt2_pp(verbose_stream(), m, *md.get(), 0);); - expr_ref_vector lits(m); - unsigned_vector pos; - for (unsigned i = 0; i < consequences.size(); ++i) { - if (md->eval(enabled[i].get(), tmp, false)) { - IF_VERBOSE(0, - verbose_stream() << mk_pp(enabled[i].get(), m) << " |-> " << mk_pp(tmp, m) << "\n";); - if (m.is_true(tmp)) { - lits.push_back(tmp); - pos.push_back(i); - } - } - } - transversal.push_back(pos); - SASSERT(!lits.empty()); - tmp = m.mk_not(m.mk_and(lits.size(), lits.c_ptr())); - TRACE("pdr", tout << "add block: " << mk_pp(tmp, m) << "\n";); - ctx.assert_expr(tmp); - } - // - // we could no longer satisfy core using a partition. - // - IF_VERBOSE(0, verbose_stream() << "TBD: tranverse\n";); -#endif - diff --git a/src/muz/pdr/pdr_generalizers.h b/src/muz/pdr/pdr_generalizers.h index ca9c5a969..be04ec646 100644 --- a/src/muz/pdr/pdr_generalizers.h +++ b/src/muz/pdr/pdr_generalizers.h @@ -21,6 +21,7 @@ Revision History: #define _PDR_GENERALIZERS_H_ #include "pdr_context.h" +#include "pdr_closure.h" #include "arith_decl_plugin.h" namespace pdr { @@ -73,45 +74,15 @@ namespace pdr { virtual void collect_statistics(statistics& st) const; }; - // Arithmetic scaling functor. - // Variables are replaced using - // m_translate. Constants are replaced by - // multiplication with a variable 'k' (scale factor). - class scaler { - ast_manager& m; - arith_util a; - obj_map m_cache[2]; - expr* m_k; - obj_map* m_translate; - public: - scaler(ast_manager& m): m(m), a(m), m_translate(0) {} - expr_ref operator()(expr* e, expr* k, obj_map* translate = 0); - expr_ref undo_k(expr* e, expr* k); - private: - expr_ref scale(expr* e, bool is_mul); - }; class core_convex_hull_generalizer : public core_generalizer { ast_manager& m; - arith_util a; - expr_ref_vector m_sigma; - expr_ref_vector m_trail; - vector > m_vars; obj_map m_models; bool m_is_closure; - expr_ref mk_closure(expr* e); - bool mk_closure(expr_ref_vector& conj); - void mk_convex(expr_ref_vector const& core, unsigned index, expr_ref_vector& conv); - void mk_convex(expr* fml, unsigned index, expr_ref_vector& conv); - void mk_convex(expr* term, unsigned index, bool is_mul, expr_ref& result); - bool translate(func_decl* fn, unsigned index, expr_ref& result); void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); - void method2(model_node& n, expr_ref_vector& core, bool& uses_level); void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); bool strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B); bool is_unsat(expr_ref_vector const& As, expr* B); - void mk_convex(model_node& n, expr_ref_vector const& Hs, expr_ref& A); - void add_variables(model_node& n, unsigned num_vars, expr_ref_vector& fmls); public: core_convex_hull_generalizer(context& ctx, bool is_closure); virtual ~core_convex_hull_generalizer() {} diff --git a/src/qe/qe_util.cpp b/src/qe/qe_util.cpp index 629fe4b56..77396ac49 100644 --- a/src/qe/qe_util.cpp +++ b/src/qe/qe_util.cpp @@ -1,4 +1,5 @@ #include "qe_util.h" +#include "bool_rewriter.h" namespace qe { void flatten_and(expr_ref_vector& result) { @@ -113,4 +114,19 @@ namespace qe { result.push_back(fml); flatten_or(result); } + + expr_ref mk_and(expr_ref_vector const& fmls) { + ast_manager& m = fmls.get_manager(); + expr_ref result(m); + bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), result); + return result; + } + + expr_ref mk_or(expr_ref_vector const& fmls) { + ast_manager& m = fmls.get_manager(); + expr_ref result(m); + bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), result); + return result; + } + } diff --git a/src/qe/qe_util.h b/src/qe/qe_util.h index 7e1fe7f79..f1a99ec6c 100644 --- a/src/qe/qe_util.h +++ b/src/qe/qe_util.h @@ -33,5 +33,9 @@ namespace qe { void flatten_or(expr* fml, expr_ref_vector& result); + expr_ref mk_and(expr_ref_vector const& fmls); + + expr_ref mk_or(expr_ref_vector const& fmls); + } #endif From 929d9f430bcc63efd42993d3ca9ed380067249ef Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 1 Sep 2013 13:45:02 -0700 Subject: [PATCH 138/509] refactor closure code Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_closure.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/muz/pdr/pdr_closure.cpp b/src/muz/pdr/pdr_closure.cpp index 483b48932..3a450a68d 100644 --- a/src/muz/pdr/pdr_closure.cpp +++ b/src/muz/pdr/pdr_closure.cpp @@ -135,6 +135,7 @@ namespace pdr { result = e; } else { + result = e; IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";); } return result; From fcc351eba66a05e8d130832d81a894b4cb01fa9f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 1 Sep 2013 13:50:18 -0700 Subject: [PATCH 139/509] refactor closure code Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_closure.cpp | 6 +++--- src/muz/pdr/pdr_generalizers.cpp | 15 +++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/muz/pdr/pdr_closure.cpp b/src/muz/pdr/pdr_closure.cpp index 3a450a68d..86af8b2f9 100644 --- a/src/muz/pdr/pdr_closure.cpp +++ b/src/muz/pdr/pdr_closure.cpp @@ -135,8 +135,8 @@ namespace pdr { result = e; } else { - result = e; IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";); + result = m.mk_true(); } return result; } @@ -145,9 +145,9 @@ namespace pdr { expr_ref_vector fmls(m); qe::flatten_and(fml, fmls); for (unsigned i = 0; i < fmls.size(); ++i) { - fmls[i] = close_fml(fmls[i].get()); + fmls[i] = close_fml(fmls[i].get()); } - return expr_ref(m.mk_and(fmls.size(), fmls.c_ptr()), m); + return qe::mk_and(fmls); } expr_ref closure::relax(unsigned i, expr* fml) { diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp index 3abf320b2..7c2557260 100644 --- a/src/muz/pdr/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -116,11 +116,10 @@ namespace pdr { void core_farkas_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { ast_manager& m = n.pt().get_manager(); - manager& pm = n.pt().get_pdr_manager(); if (core.empty()) return; - expr_ref A(m), B(pm.mk_and(core)), C(m); + expr_ref A(m), B(qe::mk_and(core)), C(m); expr_ref_vector Bs(m); - pm.get_or(B, Bs); + qe::flatten_or(B, Bs); A = n.pt().get_propagation_formula(m_ctx.get_pred_transformers(), n.level()); bool change = false; @@ -130,13 +129,13 @@ namespace pdr { if (m_farkas_learner.get_lemma_guesses(A, B, lemmas)) { TRACE("pdr", tout << "Old core:\n" << mk_pp(B, m) << "\n"; - tout << "New core:\n" << mk_pp(pm.mk_and(lemmas), m) << "\n";); - Bs[i] = pm.mk_and(lemmas); + tout << "New core:\n" << mk_pp(qe::mk_and(lemmas), m) << "\n";); + Bs[i] = qe::mk_and(lemmas); change = true; } } if (change) { - C = pm.mk_or(Bs); + C = qe::mk_or(Bs); TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";); core.reset(); qe::flatten_and(C, core); @@ -688,7 +687,7 @@ namespace pdr { for (unsigned i = 0; i < rules.size(); ++i) { fmls.push_back(m.mk_not(mk_transition_rule(reps, level, *rules[i]))); } - fml = pm.mk_and(fmls); + fml = qe::mk_and(fmls); TRACE("pdr", tout << mk_pp(fml, m) << "\n";); return fml; } @@ -744,7 +743,7 @@ namespace pdr { } } - expr_ref result = pm.mk_and(conjs); + expr_ref result = qe::mk_and(conjs); TRACE("pdr", tout << mk_pp(result, m) << "\n";); return result; } From 878905c13c3b2a1486dbd6d0511f313513517aab Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 2 Sep 2013 19:43:22 -0700 Subject: [PATCH 140/509] Adding overflow checks Signed-off-by: Nikolaj Bjorner --- src/muz/rel/dl_sparse_table.cpp | 7 +++++-- src/muz/rel/dl_sparse_table.h | 3 +++ src/test/vector.cpp | 18 +++++++++++++++--- src/util/vector.h | 8 +++++++- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/muz/rel/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp index 52d9618b8..c9bdec0b6 100644 --- a/src/muz/rel/dl_sparse_table.cpp +++ b/src/muz/rel/dl_sparse_table.cpp @@ -1066,11 +1066,14 @@ namespace datalog { unsigned res_fact_size = res->m_fact_size; unsigned res_data_size = res_fact_size*t.row_count(); + if (res_fact_size != 0 && (res_data_size / res_fact_size) != t.row_count()) { + throw default_exception("multiplication overflow"); + } res->m_data.resize_data(res_data_size); - //here we can separate data creatin and insertion into hashmap, since we know - //that no row will become duplicit + //here we can separate data creating and insertion into hashmap, since we know + //that no row will become duplicate //create the data const char* t_ptr = t.m_data.begin(); diff --git a/src/muz/rel/dl_sparse_table.h b/src/muz/rel/dl_sparse_table.h index 010277b6b..5c5f95a75 100644 --- a/src/muz/rel/dl_sparse_table.h +++ b/src/muz/rel/dl_sparse_table.h @@ -275,6 +275,9 @@ namespace datalog { //the following two operations allow breaking of the object invariant! void resize_data(unsigned sz) { m_data_size = sz; + if (sz + sizeof(uint64) < sz) { + throw default_exception("overflow resizing data section for sparse table"); + } m_data.resize(sz + sizeof(uint64)); } diff --git a/src/test/vector.cpp b/src/test/vector.cpp index 0a3904954..86ae997ca 100644 --- a/src/test/vector.cpp +++ b/src/test/vector.cpp @@ -19,7 +19,7 @@ Revision History: #include"vector.h" static void tst1() { - vector v1; + svector v1; SASSERT(v1.empty()); for (unsigned i = 0; i < 1000; i++) { v1.push_back(i + 3); @@ -30,8 +30,8 @@ static void tst1() { for (unsigned i = 0; i < 1000; i++) { SASSERT(static_cast(v1[i]) == i + 3); } - vector::iterator it = v1.begin(); - vector::iterator end = v1.end(); + svector::iterator it = v1.begin(); + svector::iterator end = v1.end(); for (int i = 0; it != end; ++it, ++i) { SASSERT(*it == i + 3); } @@ -42,6 +42,18 @@ static void tst1() { } SASSERT(v1.empty()); SASSERT(v1.size() == 0); + unsigned i = 1000000000; + while (true) { + std::cout << "resize " << i << "\n"; + try { + v1.resize(i); + } + catch (z3_exception& e) { + std::cout << e.msg() << "\n"; + break; + } + i *= 2; + } } void tst_vector() { diff --git a/src/util/vector.h b/src/util/vector.h index c9ed900a9..8bfe7c703 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -29,6 +29,7 @@ Revision History: #include #include"memory_manager.h" #include"hash.h" +#include"z3_exception.h" // disable warning for constant 'if' expressions. // these are used heavily in templates. @@ -67,9 +68,14 @@ class vector { else { SASSERT(capacity() > 0); unsigned old_capacity = reinterpret_cast(m_data)[CAPACITY_IDX]; + unsigned old_capacity_T = sizeof(T) * old_capacity + sizeof(unsigned) * 2; unsigned new_capacity = (3 * old_capacity + 1) >> 1; + unsigned new_capacity_T = sizeof(T) * new_capacity + sizeof(unsigned) * 2; unsigned size = reinterpret_cast(m_data)[SIZE_IDX]; - unsigned * mem = reinterpret_cast(memory::allocate(sizeof(T) * new_capacity + sizeof(unsigned) * 2)); + if (new_capacity <= old_capacity || new_capacity_T <= old_capacity_T) { + throw default_exception("Overflow encountered when expanding vector"); + } + unsigned * mem = reinterpret_cast(memory::allocate(new_capacity_T)); *mem = new_capacity; mem ++; *mem = size; From 1cf2b7c2d3ea69c459c8cd0c0bc19518559dce4e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 2 Sep 2013 21:22:44 -0700 Subject: [PATCH 141/509] remove unused reference to rm Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_subsumption_checker.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/muz/transforms/dl_mk_subsumption_checker.cpp b/src/muz/transforms/dl_mk_subsumption_checker.cpp index 9b2c5627b..1967fdc93 100644 --- a/src/muz/transforms/dl_mk_subsumption_checker.cpp +++ b/src/muz/transforms/dl_mk_subsumption_checker.cpp @@ -252,7 +252,6 @@ namespace datalog { if (!rel) { return; } - relation_manager& rm = rel->get_rmanager(); func_decl_set const& candidate_preds = m_context.get_predicates(); From 7c4b2b04a7f8a24c0338153781e2029eb575be69 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 4 Sep 2013 08:54:02 -0700 Subject: [PATCH 142/509] fix coi-filter to not ignore relational tables Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_coi_filter.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index fc4c411ff..8583fbe45 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -52,10 +52,14 @@ namespace datalog { ptr_vector todo; rule_set::decl2rules body2rules; // initialization for reachability + rel_context_base* rc = m_context.get_rel_context(); for (rule_set::iterator it = source.begin(); it != source.end(); ++it) { rule * r = *it; all.insert(r->get_decl()); - if (r->get_uninterpreted_tail_size() == 0) { + bool non_empty = + (rc && !rc->is_empty_relation(r->get_decl())) || + r->get_uninterpreted_tail_size() == 0; + if (non_empty) { if (!reached.contains(r->get_decl())) { reached.insert(r->get_decl()); todo.insert(r->get_decl()); From 5908e24728c4fef4017d66abd7d315137cee0fb2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Sep 2013 09:36:15 -0700 Subject: [PATCH 143/509] fix bug missing NNF of equality as IFF reported by Sticksel Signed-off-by: Nikolaj Bjorner --- src/qe/qe.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index d0aafb896..d63f0ae00 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -482,7 +482,7 @@ namespace qe { } void nnf_iff(app* a, bool p) { - SASSERT(m.is_iff(a) || m.is_xor(a)); + SASSERT(m.is_iff(a) || m.is_xor(a) || m.is_eq(a)); expr* a0 = a->get_arg(0); expr* a1 = a->get_arg(1); @@ -616,7 +616,7 @@ namespace qe { else if (m.is_ite(a)) { nnf_ite(a, p); } - else if (m.is_iff(a)) { + else if (m.is_iff(a) || (m.is_eq(a) && m.is_bool(a->get_arg(0)))) { nnf_iff(a, p); } else if (m.is_xor(a)) { @@ -1926,6 +1926,7 @@ namespace qe { plugin(x).get_num_branches(contains(x), fml, num_branches)) { return true; } + TRACE("qe", tout << "setting variable " << mk_pp(x, m) << " free\n";); m_free_vars.push_back(x); m_current->del_var(x); } @@ -2493,6 +2494,7 @@ namespace qe { // callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml' virtual void elim_var(unsigned idx, expr* fml, expr* def) { + TRACE("qe", tout << mk_pp(m_vars->get(idx), m) << " " << mk_pp(fml, m) << "\n";); *m_fml = fml; m_vars->set(idx, m_vars->get(m_vars->size()-1)); m_vars->pop_back(); From 457b22b00e23512113b2de5a1dd85f3e36cd1ad9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Sep 2013 21:49:00 -0700 Subject: [PATCH 144/509] add TPTP example Signed-off-by: Nikolaj Bjorner --- examples/tptp/README | 18 + examples/tptp/tptp5.cpp | 2480 +++++++++++++++++ examples/tptp/tptp5.h | 38 + examples/tptp/tptp5.lex.cpp | 2672 ++++++++++++++++++ examples/tptp/tptp5.tab.c | 4475 +++++++++++++++++++++++++++++++ examples/tptp/tptp5.tab.h | 138 + scripts/mk_project.py | 1 + scripts/mk_util.py | 13 +- src/api/c++/z3++.h | 29 + src/muz/base/dl_util.h | 2 +- src/muz/rel/dl_sparse_table.cpp | 32 +- src/muz/rel/dl_sparse_table.h | 10 +- src/util/vector.h | 114 +- 13 files changed, 9943 insertions(+), 79 deletions(-) create mode 100644 examples/tptp/README create mode 100644 examples/tptp/tptp5.cpp create mode 100644 examples/tptp/tptp5.h create mode 100644 examples/tptp/tptp5.lex.cpp create mode 100644 examples/tptp/tptp5.tab.c create mode 100644 examples/tptp/tptp5.tab.h diff --git a/examples/tptp/README b/examples/tptp/README new file mode 100644 index 000000000..c28a53da4 --- /dev/null +++ b/examples/tptp/README @@ -0,0 +1,18 @@ +TPTP front-end and utilities as a sample using the C++ bindings. +To build the example execute + make examples +in the build directory. + +This command will create the executable tptp. +On Windows, you can just execute it. +On OSX and Linux, you must install z3 first using + sudo make install +OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX) + with the build directory. You need that to be able to + find the Z3 shared library. + +The sample illustrates using Z3 from the TPTP language. +The TPTP language is documented on http://tptp.org +It also exposes utilities for converting between SMT-LIB +and TPTP format. + diff --git a/examples/tptp/tptp5.cpp b/examples/tptp/tptp5.cpp new file mode 100644 index 000000000..1f50bdfea --- /dev/null +++ b/examples/tptp/tptp5.cpp @@ -0,0 +1,2480 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z3++.h" + +struct alloc_region { + std::list m_alloc; + + void * allocate(size_t s) { + char * res = new char[s]; + m_alloc.push_back(res); + return res; + } + + ~alloc_region() { + std::list::iterator it = m_alloc.begin(), end = m_alloc.end(); + for (; it != end; ++it) { + delete *it; + } + } +}; + +template +class flet { + T & m_ref; + T m_old; +public: + flet(T& x, T const& y): m_ref(x), m_old(x) { x = y; } + ~flet() { m_ref = m_old; } +}; + +struct symbol_compare { + bool operator()(z3::symbol const& s1, z3::symbol const& s2) const { + return s1 < s2; + }; +}; + + +template +struct symbol_table { + typedef std::map map; + map m_map; + + void insert(z3::symbol s, T val) { + m_map.insert(std::pair(s, val)); + } + + bool find(z3::symbol const& s, T& val) { + typename map::iterator it = m_map.find(s); + if (it == m_map.end()) { + return false; + } + else { + val = it->second; + return true; + } + } +}; + + +typedef std::set symbol_set; + + +struct named_formulas { + std::vector m_formulas; + std::vector m_names; + std::vector m_files; + bool m_has_conjecture; + + named_formulas(): m_has_conjecture(false) {} + + void push_back(z3::expr fml, char const * name, char const* file) { + m_formulas.push_back(fml); + m_names.push_back(name); + m_files.push_back(file); + } + + void set_has_conjecture() { + m_has_conjecture = true; + } + + bool has_conjecture() const { + return m_has_conjecture; + } +}; + +inline void * operator new(size_t s, alloc_region & r) { return r.allocate(s); } + +inline void * operator new[](size_t s, alloc_region & r) { return r.allocate(s); } + +inline void operator delete(void *, alloc_region & ) { /* do nothing */ } + +inline void operator delete[](void *, alloc_region & ) { /* do nothing */ } + +struct failure_ex { + std::string msg; + failure_ex(char const* m):msg(m) {} +}; + + +extern char* tptp_lval[]; +extern int yylex(); + +static char* strdup(alloc_region& r, char const* s) { + size_t l = strlen(s) + 1; + char* result = new (r) char[l]; + memcpy(result, s, l); + return result; +} + +class TreeNode { + char const* m_symbol; + int m_symbol_index; + TreeNode** m_children; + +public: + TreeNode(alloc_region& r, char const* sym, + TreeNode* A, TreeNode* B, TreeNode* C, TreeNode* D, TreeNode* E, + TreeNode* F, TreeNode* G, TreeNode* H, TreeNode* I, TreeNode* J): + m_symbol(strdup(r, sym)), + m_symbol_index(-1) { + m_children = new (r) TreeNode*[10]; + m_children[0] = A; + m_children[1] = B; + m_children[2] = C; + m_children[3] = D; + m_children[4] = E; + m_children[5] = F; + m_children[6] = G; + m_children[7] = H; + m_children[8] = I; + m_children[9] = J; + + } + + char const* symbol() const { return m_symbol; } + TreeNode *const* children() const { return m_children; } + TreeNode* child(unsigned i) const { return m_children[i]; } + int index() const { return m_symbol_index; } + + void set_index(int idx) { m_symbol_index = idx; } +}; + +TreeNode* MkToken(alloc_region& r, char* token, int symbolIndex) { + TreeNode* ss; + char* symbol = tptp_lval[symbolIndex]; + ss = new (r) TreeNode(r, symbol, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); + ss->set_index(symbolIndex); + return ss; +} + + +// ------------------------------------------------------ +// Build Z3 formulas. + +class env { + z3::context& m_context; + z3::expr_vector m_bound; // vector of bound constants. + z3::sort m_univ; + symbol_table m_decls; + symbol_table m_defined_sorts; + static std::vector* m_nodes; + static alloc_region* m_region; + char const* m_filename; + + + enum binary_connective { + IFF, + IMPLIES, + IMPLIED, + LESS_TILDE_GREATER, + TILDE_VLINE + }; + + void mk_error(TreeNode* f, char const* msg) { + std::ostringstream strm; + strm << "expected: " << msg << "\n"; + strm << "got: " << f->symbol(); + throw failure_ex(strm.str().c_str()); + } + + void mk_not_handled(TreeNode* f, char const* msg) { + std::ostringstream strm; + strm << "Construct " << f->symbol() << " not handled: " << msg; + throw failure_ex(strm.str().c_str()); + } + + void mk_input(TreeNode* f, named_formulas& fmls) { + if (!strcmp(f->symbol(),"annotated_formula")) { + mk_annotated_formula(f->child(0), fmls); + } + else if (!strcmp(f->symbol(),"include")) { + mk_include(f->child(2), f->child(3), fmls); + } + else { + mk_error(f, "annotated formula or include"); + } + } + + void mk_annotated_formula(TreeNode* f, named_formulas& fmls) { + if (!strcmp(f->symbol(),"fof_annotated")) { + fof_annotated(f->child(2), f->child(4), f->child(6), f->child(7), fmls); + } + else if (!strcmp(f->symbol(),"tff_annotated")) { + fof_annotated(f->child(2), f->child(4), f->child(6), f->child(7), fmls); + } + else if (!strcmp(f->symbol(),"cnf_annotated")) { + cnf_annotated(f->child(2), f->child(4), f->child(6), f->child(7), fmls); + } + else if (!strcmp(f->symbol(),"thf_annotated")) { + mk_error(f, "annotated formula (not thf)"); + } + else { + mk_error(f, "annotated formula"); + } + } + + void check_arity(unsigned num_args, unsigned arity) { + if (num_args != arity) { + throw failure_ex("arity missmatch"); + } + } + + void mk_include(TreeNode* file_name, TreeNode* formula_selection, named_formulas& fmls) { + char const* fn = file_name->child(0)->symbol(); + TreeNode* name_list = formula_selection->child(2); + if (name_list && !strcmp("null",name_list->symbol())) { + name_list = 0; + } + std::string inc_name; + bool f_exists = false; + for (unsigned i = 1; !f_exists && i <= 3; ++i) { + inc_name.clear(); + f_exists = mk_filename(fn, i, inc_name); + + } + if (!f_exists) { + inc_name.clear(); + f_exists = mk_env_filename(fn, inc_name); + } + if (!f_exists) { + inc_name = fn; + } + + parse(inc_name.c_str(), fmls); + while (name_list) { + return mk_error(name_list, "name list (not handled)"); + char const* name = name_list->child(0)->symbol(); + name_list = name_list->child(2); + } + } + +#define CHECK(_node_) if (0 != strcmp(_node_->symbol(),#_node_)) return mk_error(_node_,#_node_); + + const char* get_name(TreeNode* name) { + if (!name->child(0)) { + mk_error(name, "node with a child"); + } + if (!name->child(0)->child(0)) { + return name->child(0)->symbol(); + } + return name->child(0)->child(0)->symbol(); + } + + z3::expr mk_forall(z3::expr_vector& bound, z3::expr body) { + return mk_quantifier(true, bound, body); + } + + z3::expr mk_quantifier(bool is_forall, z3::expr_vector& bound, z3::expr body) { + Z3_app* vars = new Z3_app[bound.size()]; + for (unsigned i = 0; i < bound.size(); ++i) { + vars[i] = (Z3_app) bound[i]; + } + Z3_ast r = Z3_mk_quantifier_const(m_context, is_forall, 1, bound.size(), vars, 0, 0, body); + delete[] vars; + return z3::expr(m_context, r); + } + + void cnf_annotated(TreeNode* name, TreeNode* formula_role, TreeNode* formula, TreeNode* annotations, named_formulas& fmls) { + symbol_set st; + get_cnf_variables(formula, st); + symbol_set::iterator it = st.begin(), end = st.end(); + std::vector names; + m_bound.resize(0); + for(; it != end; ++it) { + names.push_back(*it); + m_bound.push_back(m_context.constant(names.back(), m_univ)); + } + z3::expr r(m_context); + cnf_formula(formula, r); + if (!m_bound.empty()) { + r = mk_forall(m_bound, r); + } + char const* role = formula_role->child(0)->symbol(); + if (!strcmp(role,"conjecture")) { + fmls.set_has_conjecture(); + r = !r; + } + fmls.push_back(r, get_name(name), m_filename); + m_bound.resize(0); + } + + void cnf_formula(TreeNode* formula, z3::expr& r) { + std::vector disj; + if (formula->child(1)) { + disjunction(formula->child(1), disj); + } + else { + disjunction(formula->child(0), disj); + } + if (disj.size() > 0) { + r = disj[0]; + } + else { + r = m_context.bool_val(false); + } + for (unsigned i = 1; i < disj.size(); ++i) { + r = r || disj[i]; + } + } + + void disjunction(TreeNode* d, std::vector& r) { + z3::expr lit(m_context); + if (d->child(2)) { + disjunction(d->child(0), r); + literal(d->child(2), lit); + r.push_back(lit); + } + else { + literal(d->child(0), lit); + r.push_back(lit); + } + } + + void literal(TreeNode* l, z3::expr& lit) { + if (!strcmp(l->child(0)->symbol(),"~")) { + fof_formula(l->child(1), lit); + lit = !lit; + } + else { + fof_formula(l->child(0), lit); + } + } + + void fof_annotated(TreeNode* name, TreeNode* formula_role, TreeNode* formula, TreeNode* annotations, named_formulas& fmls) { + z3::expr fml(m_context); + //CHECK(fof_formula); + CHECK(formula_role); + fof_formula(formula->child(0), fml); + char const* role = formula_role->child(0)->symbol(); + if (!strcmp(role,"conjecture")) { + fmls.set_has_conjecture(); + fmls.push_back(!fml, get_name(name), m_filename); + } + else if (!strcmp(role,"type")) { + } + else { + fmls.push_back(fml, get_name(name), m_filename); + } + } + + void fof_formula(TreeNode* f, z3::expr& fml) { + z3::expr f1(m_context); + char const* name = f->symbol(); + if (!strcmp(name,"fof_logic_formula") || + !strcmp(name,"fof_binary_assoc") || + !strcmp(name,"fof_binary_formula") || + !strcmp(name,"tff_logic_formula") || + !strcmp(name,"tff_binary_assoc") || + !strcmp(name,"tff_binary_formula") || + !strcmp(name,"atomic_formula") || + !strcmp(name,"defined_atomic_formula")) { + fof_formula(f->child(0), fml); + } + else if (!strcmp(name, "fof_sequent") || + !strcmp(name, "tff_sequent")) { + fof_formula(f->child(0), f1); + fof_formula(f->child(2), fml); + fml = implies(f1, fml); + } + else if (!strcmp(name, "fof_binary_nonassoc") || + !strcmp(name, "tff_binary_nonassoc")) { + fof_formula(f->child(0), f1); + fof_formula(f->child(2), fml); + //SASSERT(!strcmp("binary_connective",f->child(1)->symbol())); + char const* conn = f->child(1)->child(0)->symbol(); + if (!strcmp(conn, "<=>")) { + fml = (f1 == fml); + } + else if (!strcmp(conn, "=>")) { + fml = implies(f1, fml); + } + else if (!strcmp(conn, "<=")) { + fml = implies(fml, f1); + } + else if (!strcmp(conn, "<~>")) { + fml = ! (f1 == fml); + } + else if (!strcmp(conn, "~|")) { + fml = !(f1 || fml); + } + else if (!strcmp(conn, "~&")) { + fml = ! (f1 && fml); + } + else { + mk_error(f->child(1)->child(0), "connective"); + } + } + else if (!strcmp(name,"fof_or_formula") || + !strcmp(name,"tff_or_formula")) { + fof_formula(f->child(0), f1); + fof_formula(f->child(2), fml); + fml = f1 || fml; + } + else if (!strcmp(name,"fof_and_formula") || + !strcmp(name,"tff_and_formula")) { + fof_formula(f->child(0), f1); + fof_formula(f->child(2), fml); + fml = f1 && fml; + } + else if (!strcmp(name,"fof_unitary_formula") || + !strcmp(name,"tff_unitary_formula")) { + if (f->child(1)) { + // parenthesis + fof_formula(f->child(1), fml); + } + else { + fof_formula(f->child(0), fml); + } + } + else if (!strcmp(name,"fof_quantified_formula") || + !strcmp(name,"tff_quantified_formula")) { + fof_quantified_formula(f->child(0), f->child(2), f->child(5), fml); + } + else if (!strcmp(name,"fof_unary_formula") || + !strcmp(name,"tff_unary_formula")) { + if (!f->child(1)) { + fof_formula(f->child(0), fml); + } + else { + fof_formula(f->child(1), fml); + char const* conn = f->child(0)->child(0)->symbol(); + if (!strcmp(conn,"~")) { + fml = !fml; + } + else { + mk_error(f->child(0)->child(0), "fof_unary_formula"); + } + } + } + else if (!strcmp(name,"fof_let")) { + mk_let(f->child(2), f->child(5), fml); + } + else if (!strcmp(name,"variable")) { + char const* v = f->child(0)->symbol(); + if (!find_bound(v, fml)) { + mk_error(f->child(0), "variable"); + } + } + else if (!strcmp(name,"fof_conditional")) { + z3::expr f2(m_context); + fof_formula(f->child(2), f1); + fof_formula(f->child(4), f2); + fof_formula(f->child(6), fml); + fml = ite(f1, f2, fml); + } + else if (!strcmp(name,"plain_atomic_formula") || + !strcmp(name,"defined_plain_formula") || + !strcmp(name,"system_atomic_formula")) { + z3::sort srt(m_context.bool_sort()); + term(f->child(0), srt, fml); + } + else if (!strcmp(name,"defined_infix_formula") || + !strcmp(name,"fol_infix_unary")) { + z3::expr t1(m_context), t2(m_context); + term(f->child(0), m_univ, t1); + term(f->child(2), m_univ, t2); + TreeNode* inf = f->child(1); + while (inf && strcmp(inf->symbol(),"=") && strcmp(inf->symbol(),"!=")) { + inf = inf->child(0); + } + if (!inf) { + mk_error(f->child(1), "defined_infix_formula"); + } + char const* conn = inf->symbol(); + if (!strcmp(conn,"=")) { + fml = t1 == t2; + } + else if (!strcmp(conn,"!=")) { + fml = ! (t1 == t2); + } + else { + mk_error(inf, "defined_infix_formula"); + } + } + else if (!strcmp(name, "tff_typed_atom")) { + while (!strcmp(f->child(0)->symbol(),"(")) { + f = f->child(1); + } + char const* id = 0; + z3::sort s(m_context); + z3::sort_vector sorts(m_context); + + mk_id(f->child(0), id); + if (is_ttype(f->child(2))) { + s = mk_sort(id); + m_defined_sorts.insert(symbol(id), s); + } + else { + mk_mapping_sort(f->child(2), sorts, s); + z3::func_decl fd(m_context.function(id, sorts, s)); + m_decls.insert(symbol(id), fd); + } + } + else { + mk_error(f, "fof_formula"); + } + } + + bool is_ttype(TreeNode* t) { + char const* name = t->symbol(); + if (!strcmp(name,"atomic_defined_word")) { + return !strcmp("$tType", t->child(0)->symbol()); + } + return false; + } + + void fof_quantified_formula(TreeNode* fol_quantifier, TreeNode* vl, TreeNode* formula, z3::expr& fml) { + unsigned l = m_bound.size(); + mk_variable_list(vl); + fof_formula(formula, fml); + bool is_forall = !strcmp(fol_quantifier->child(0)->symbol(),"!"); + z3::expr_vector bound(m_context); + for (unsigned i = l; i < m_bound.size(); ++i) { + bound.push_back(m_bound[i]); + } + fml = mk_quantifier(is_forall, bound, fml); + m_bound.resize(l); + } + + void mk_variable_list(TreeNode* variable_list) { + while (variable_list) { + TreeNode* var = variable_list->child(0); + if (!strcmp(var->symbol(),"tff_variable")) { + var = var->child(0); + } + if (!strcmp(var->symbol(),"variable")) { + char const* name = var->child(0)->symbol(); + m_bound.push_back(m_context.constant(name, m_univ)); + } + else if (!strcmp(var->symbol(),"tff_typed_variable")) { + z3::sort s(m_context); + char const* name = var->child(0)->child(0)->symbol(); + mk_sort(var->child(2), s); + m_bound.push_back(m_context.constant(name, s)); + } + else { + mk_error(var, "variable_list"); + } + variable_list = variable_list->child(2); + } + } + + void mk_sort(TreeNode* t, z3::sort& s) { + char const* name = t->symbol(); + if (!strcmp(name, "tff_atomic_type") || + !strcmp(name, "defined_type")) { + mk_sort(t->child(0), s); + } + else if (!strcmp(name, "atomic_defined_word")) { + z3::symbol sname = symbol(t->child(0)->symbol()); + z3::sort srt(m_context); + if (!strcmp("$tType", t->child(0)->symbol())) { + char const* id = 0; + s = mk_sort(id); + m_defined_sorts.insert(symbol(id), s); + } + else if (m_defined_sorts.find(sname, srt)) { + s = srt; + } + else { + s = mk_sort(sname); + if (sname == symbol("$rat")) { + throw failure_ex("rational sorts are not handled\n"); + } + mk_error(t, sname.str().c_str()); + } + } + else if (!strcmp(name,"atomic_word")) { + name = t->child(0)->symbol(); + z3::symbol symname = symbol(name); + s = mk_sort(symname); + } + else { + mk_error(t, "sort"); + } + } + + void mk_mapping_sort(TreeNode* t, z3::sort_vector& domain, z3::sort& s) { + char const* name = t->symbol(); + char const* id = 0; + if (!strcmp(name,"tff_top_level_type")) { + mk_mapping_sort(t->child(0), domain, s); + } + else if (!strcmp(name,"tff_atomic_type")) { + mk_sort(t->child(0), s); + } + else if (!strcmp(name,"tff_mapping_type")) { + TreeNode* t1 = t->child(0); + if (t1->child(1)) { + mk_xprod_sort(t1->child(1), domain); + } + else { + mk_sort(t1->child(0), s); + domain.push_back(s); + } + mk_sort(t->child(2), s); + } + else { + mk_error(t, "mapping sort"); + } + } + + void mk_xprod_sort(TreeNode* t, z3::sort_vector& sorts) { + char const* name = t->symbol(); + z3::sort s1(m_context), s2(m_context); + if (!strcmp(name, "tff_atomic_type")) { + mk_sort(t->child(0), s1); + sorts.push_back(s1); + } + else if (!strcmp(name, "tff_xprod_type")) { + name = t->child(0)->symbol(); + if (!strcmp(name, "tff_atomic_type") || + !strcmp(name, "tff_xprod_type")) { + mk_xprod_sort(t->child(0), sorts); + mk_xprod_sort(t->child(2), sorts); + } + else if (t->child(1)) { + mk_xprod_sort(t->child(1), sorts); + } + else { + mk_error(t, "xprod sort"); + } + } + else { + mk_error(t, "xprod sort"); + } + } + + void term(TreeNode* t, z3::sort const& s, z3::expr& r) { + char const* name = t->symbol(); + if (!strcmp(name, "defined_plain_term") || + !strcmp(name, "system_term") || + !strcmp(name, "plain_term")) { + if (!t->child(1)) { + term(t->child(0), s, r); + } + else { + apply_term(t->child(0), t->child(2), s, r); + } + } + else if (!strcmp(name, "constant") || + !strcmp(name, "functor") || + !strcmp(name, "defined_plain_formula") || + !strcmp(name, "defined_functor") || + !strcmp(name, "defined_constant") || + !strcmp(name, "system_constant") || + !strcmp(name, "defined_atomic_term") || + !strcmp(name, "system_functor") || + !strcmp(name, "function_term") || + !strcmp(name, "term") || + !strcmp(name, "defined_term")) { + term(t->child(0), s, r); + } + + + else if (!strcmp(name, "defined_atom")) { + char const* name0 = t->child(0)->symbol(); + if (!strcmp(name0,"number")) { + name0 = t->child(0)->child(0)->symbol(); + char const* per = strchr(name0, '.'); + bool is_real = 0 != per; + bool is_rat = 0 != strchr(name0, '/'); + bool is_int = !is_real && !is_rat; + if (is_int) { + r = m_context.int_val(name0); + } + else { + r = m_context.real_val(name0); + } + } + else if (!strcmp(name0, "distinct_object")) { + throw failure_ex("distinct object not handled"); + } + else { + mk_error(t->child(0), "number or distinct object"); + } + } + else if (!strcmp(name, "atomic_defined_word")) { + char const* ch = t->child(0)->symbol(); + z3::symbol s = symbol(ch); + z3::func_decl fd(m_context); + if (!strcmp(ch, "$true")) { + r = m_context.bool_val(true); + } + else if (!strcmp(ch, "$false")) { + r = m_context.bool_val(false); + } + else if (m_decls.find(s, fd)) { + r = fd(0,0); + } + else { + mk_error(t->child(0), "atomic_defined_word"); + } + } + else if (!strcmp(name, "atomic_word")) { + z3::func_decl f(m_context); + z3::symbol sym = symbol(t->child(0)->symbol()); + if (m_decls.find(sym, f)) { + r = f(0,0); + } + else { + r = m_context.constant(sym, s); + } + } + else if (!strcmp(name, "variable")) { + char const* v = t->child(0)->symbol(); + if (!find_bound(v, r)) { + mk_error(t->child(0), "variable not bound"); + } + } + else { + mk_error(t, "term not recognized"); + } + } + + void apply_term(TreeNode* f, TreeNode* args, z3::sort const& s, z3::expr& r) { + z3::expr_vector terms(m_context); + z3::sort_vector sorts(m_context); + mk_args(args, terms); + for (unsigned i = 0; i < terms.size(); ++i) { + sorts.push_back(terms[i].get_sort()); + } + if (!strcmp(f->symbol(),"functor") || + !strcmp(f->symbol(),"system_functor") || + !strcmp(f->symbol(),"defined_functor")) { + f = f->child(0); + } + bool atomic_word = !strcmp(f->symbol(),"atomic_word"); + if (atomic_word || + !strcmp(f->symbol(),"atomic_defined_word") || + !strcmp(f->symbol(),"atomic_system_word")) { + char const* ch = f->child(0)->symbol(); + z3::symbol fn = symbol(ch); + z3::func_decl fun(m_context); + z3::context& ctx = r.ctx(); + if (!strcmp(ch,"$less")) { + check_arity(terms.size(), 2); + r = terms[0] < terms[1]; + } + else if (!strcmp(ch,"$lesseq")) { + check_arity(terms.size(), 2); + r = terms[0] <= terms[1]; + } + else if (!strcmp(ch,"$greater")) { + check_arity(terms.size(), 2); + r = terms[0] > terms[1]; + } + else if (!strcmp(ch,"$greatereq")) { + check_arity(terms.size(), 2); + r = terms[0] >= terms[1]; + } + else if (!strcmp(ch,"$uminus")) { + check_arity(terms.size(), 1); + r = -terms[0]; + } + else if (!strcmp(ch,"$sum")) { + check_arity(terms.size(), 2); + r = terms[0] + terms[1]; + } + else if (!strcmp(ch,"$plus")) { + check_arity(terms.size(), 2); + r = terms[0] + terms[1]; + } + else if (!strcmp(ch,"$difference")) { + check_arity(terms.size(), 2); + r = terms[0] - terms[1]; + } + else if (!strcmp(ch,"$product")) { + check_arity(terms.size(), 2); + r = terms[0] * terms[1]; + } + else if (!strcmp(ch,"$quotient")) { + check_arity(terms.size(), 2); + r = terms[0] / terms[1]; + } + else if (!strcmp(ch,"$quotient_e")) { + check_arity(terms.size(), 2); + r = terms[0] / terms[1]; + } + else if (!strcmp(ch,"$distinct")) { + check_arity(terms.size(), 2); + r = terms[0] != terms[1]; + } + else if (!strcmp(ch,"$floor") || !strcmp(ch,"$to_int")) { + check_arity(terms.size(), 1); + r = to_real(to_int(terms[0])); + } + else if (!strcmp(ch,"$to_real")) { + check_arity(terms.size(), 1); + r = to_real(terms[0]); + } + else if (!strcmp(ch,"$is_int")) { + check_arity(terms.size(), 1); + r = z3::expr(ctx, Z3_mk_is_int(ctx, terms[0])); + } + else if (!strcmp(ch,"$true")) { + r = ctx.bool_val(true); + } + else if (!strcmp(ch,"$false")) { + r = ctx.bool_val(false); + } + // ceiling(x) = -floor(-x) + else if (!strcmp(ch,"$ceiling")) { + check_arity(terms.size(), 1); + r = ceiling(terms[0]); + } + // truncate - The nearest integral value with magnitude not greater than the absolute value of the argument. + // if x >= 0 floor(x) else ceiling(x) + else if (!strcmp(ch,"$truncate")) { + check_arity(terms.size(), 1); + r = truncate(terms[0]); + } + // The nearest integral number to the argument. When the argument + // is halfway between two integral numbers, the nearest even integral number to the argument. + else if (!strcmp(ch,"$round")) { + check_arity(terms.size(), 1); + z3::expr t = terms[0]; + z3::expr i = to_int(t); + z3::expr i2 = i + ctx.real_val(1,2); + r = ite(t > i2, i + 1, ite(t == i2, ite(is_even(i), i, i+1), i)); + } + // $quotient_e(N,D) - the Euclidean quotient, which has a non-negative remainder. + // If D is positive then $quotient_e(N,D) is the floor (in the type of N and D) of + // the real division N/D, and if D is negative then $quotient_e(N,D) is the ceiling of N/D. + + // $quotient_t(N,D) - the truncation of the real division N/D. + else if (!strcmp(ch,"$quotient_t")) { + check_arity(terms.size(), 2); + r = truncate(terms[0] / terms[1]); + } + // $quotient_f(N,D) - the floor of the real division N/D. + else if (!strcmp(ch,"$quotient_f")) { + check_arity(terms.size(), 2); + r = to_real(to_int(terms[0] / terms[1])); + } + // For t in {$int,$rat, $real}, x in {e, t,f}, $quotient_x and $remainder_x are related by + // ! [N:t,D:t] : $sum($product($quotient_x(N,D),D),$remainder_x(N,D)) = N + // For zero divisors the result is not specified. + else if (!strcmp(ch,"$remainder_t")) { + mk_not_handled(f, ch); + } + else if (!strcmp(ch,"$remainder_e")) { + check_arity(terms.size(), 2); + r = z3::expr(ctx, Z3_mk_mod(ctx, terms[0], terms[1])); + } + else if (!strcmp(ch,"$remainder_r")) { + mk_not_handled(f, ch); + } + else if (!strcmp(ch,"$to_rat") || + !strcmp(ch,"$is_rat")) { + mk_not_handled(f, ch); + } + else if (m_decls.find(fn, fun)) { + r = fun(terms); + } + else if (true) { + z3::func_decl func(m_context); + func = m_context.function(fn, sorts, s); + r = func(terms); + } + else { + mk_error(f->child(0), "atomic, defined or system word"); + } + return; + } + mk_error(f, "function"); + } + + z3::expr to_int(z3::expr e) { + return z3::expr(e.ctx(), Z3_mk_real2int(e.ctx(), e)); + } + + z3::expr to_real(z3::expr e) { + return z3::expr(e.ctx(), Z3_mk_int2real(e.ctx(), e)); + } + + z3::expr ceiling(z3::expr e) { + return -to_real(to_int(-e)); + } + + z3::expr is_even(z3::expr e) { + z3::context& ctx = e.ctx(); + z3::expr two = ctx.int_val(2); + z3::expr m = z3::expr(ctx, Z3_mk_mod(ctx, e, two)); + return m == 0; + } + + z3::expr truncate(z3::expr e) { + return ite(e >= 0, to_int(e), ceiling(e)); + } + + bool check_app(z3::func_decl& f, unsigned num, z3::expr const* args) { + if (f.arity() == num) { + for (unsigned i = 0; i < num; ++i) { + if (!eq(args[i].get_sort(), f.domain(i))) { + return false; + } + } + return true; + } + else { + return true; + } + } + + void mk_args(TreeNode* args, z3::expr_vector& result) { + z3::expr t(m_context); + while (args) { + term(args->child(0), m_univ, t); + result.push_back(t); + args = args->child(2); + } + } + + + bool find_bound(char const* v, z3::expr& b) { + for (unsigned l = m_bound.size(); l > 0; ) { + --l; + if (v == m_bound[l].decl().name().str()) { + b = m_bound[l]; + return true; + } + } + return false; + } + + void mk_id(TreeNode* f, char const*& sym) { + char const* name = f->symbol(); + if (!strcmp(name, "tff_untyped_atom") || + !strcmp(name, "functor") || + !strcmp(name, "system_functor")) { + mk_id(f->child(0), sym); + } + else if (!strcmp(name, "atomic_word") || + !strcmp(name, "atomic_system_word")) { + sym = f->child(0)->symbol(); + } + else { + mk_error(f, "atom"); + } + } + + void mk_let(TreeNode* let_vars, TreeNode* f, z3::expr& fml) { + mk_error(f, "let construct is not handled"); + } + + FILE* open_file(char const* filename) { + FILE* fp = 0; +#ifdef _WINDOWS + if (0 > fopen_s(&fp, filename, "r") || fp == 0) { + fp = 0; + } +#else + fp = fopen(filename, "r"); +#endif + return fp; + } + + bool is_sep(char s) { + return s == '/' || s == '\\'; + } + + void add_separator(const char* rel_name, std::string& inc_name) { + size_t sz = inc_name.size(); + if (sz == 0) return; + if (sz > 0 && is_sep(inc_name[sz-1])) return; + if (is_sep(rel_name[0])) return; + inc_name += "/"; + } + + void append_rel_name(const char * rel_name, std::string& inc_name) { + if (rel_name[0] == '\'') { + add_separator(rel_name+1, inc_name); + inc_name.append(rel_name+1); + inc_name.resize(inc_name.size()-1); + } + else { + add_separator(rel_name, inc_name); + inc_name.append(rel_name); + } + } + + bool mk_filename(const char *rel_name, unsigned num_sep, std::string& inc_name) { + unsigned sep1 = 0, sep2 = 0, sep3 = 0; + size_t len = strlen(m_filename); + for (unsigned i = 0; i < len; ++i) { + if (is_sep(m_filename[i])) { + sep3 = sep2; + sep2 = sep1; + sep1 = i; + } + } + if ((num_sep == 3) && sep3 > 0) { + inc_name.append(m_filename,sep3+1); + } + if ((num_sep == 2) && sep2 > 0) { + inc_name.append(m_filename,sep2+1); + } + if ((num_sep == 1) && sep1 > 0) { + inc_name.append(m_filename,sep1+1); + } + append_rel_name(rel_name, inc_name); + return file_exists(inc_name.c_str()); + } + + bool file_exists(char const* filename) { + FILE* fp = open_file(filename); + if (!fp) { + return false; + } + fclose(fp); + return true; + } + + bool mk_env_filename(const char* rel_name, std::string& inc_name) { +#ifdef _WINDOWS + char buffer[1024]; + size_t sz; + errno_t err = getenv_s( + &sz, + buffer, + "$TPTP"); + if (err != 0) { + return false; + } +#else + char const* buffer = getenv("$TPTP"); + if (!buffer) { + return false; + } +#endif + inc_name = buffer; + append_rel_name(rel_name, inc_name); + return file_exists(inc_name.c_str()); + } + + void get_cnf_variables(TreeNode* t, symbol_set& symbols) { + std::vector todo; + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + if (!t) continue; + if (!strcmp(t->symbol(),"variable")) { + z3::symbol sym = symbol(t->child(0)->symbol()); + symbols.insert(sym); + } + else { + for (unsigned i = 0; i < 10; ++i) { + todo.push_back(t->child(i)); + } + } + } + } + + z3::symbol symbol(char const* s) { + return m_context.str_symbol(s); + } + + z3::sort mk_sort(char const* s) { + z3::symbol sym = symbol(s); + return mk_sort(sym); + } + + z3::sort mk_sort(z3::symbol& s) { + return z3::sort(m_context, Z3_mk_uninterpreted_sort(m_context, s)); + } + +public: + env(z3::context& ctx): + m_context(ctx), + m_bound(ctx), + m_univ(mk_sort("$i")), + m_filename(0) { + m_nodes = 0; + m_region = new alloc_region(); + m_defined_sorts.insert(symbol("$i"), m_univ); + m_defined_sorts.insert(symbol("$o"), m_context.bool_sort()); + m_defined_sorts.insert(symbol("$real"), m_context.real_sort()); + m_defined_sorts.insert(symbol("$int"), m_context.int_sort()); + + } + + ~env() { + delete m_region; + m_region = 0; + } + void parse(const char* filename, named_formulas& fmls); + static void register_node(TreeNode* t) { m_nodes->push_back(t); } + static alloc_region& r() { return *m_region; } +}; + +std::vector* env::m_nodes = 0; +alloc_region* env::m_region = 0; + +# define P_USERPROC +# define P_ACT(ss) if(verbose)printf("%7d %s\n",yylineno,ss); +# define P_BUILD(sym,A,B,C,D,E,F,G,H,I,J) new (env::r()) TreeNode(env::r(), sym,A,B,C,D,E,F,G,H,I,J) +# define P_TOKEN(tok,symbolIndex) MkToken(env::r(), tok,symbolIndex) +# define P_PRINT(ss) env::register_node(ss) + + +// ------------------------------------------------------ +// created by YACC. +#include "tptp5.tab.c" + +extern FILE* yyin; + + +void env::parse(const char* filename, named_formulas& fmls) { + std::vector nodes; + flet fn(m_filename, filename); + flet*> fnds(m_nodes, &nodes); + + FILE* fp = open_file(filename); + if (!fp) { + std::stringstream strm; + strm << "Could not open file " << filename << "\n"; + throw failure_ex(strm.str().c_str()); + } + yyin = fp; + int result = yyparse(); + fclose(fp); + + if (result != 0) { + throw failure_ex("could not parse input"); + } + + for (unsigned i = 0; i < nodes.size(); ++i) { + TreeNode* cl = nodes[i]; + if (cl) { + mk_input(cl, fmls); + } + } + +} + +class pp_tptp { + z3::context& ctx; + std::vector names; + std::vector sorts; + std::vector funs; + std::vector todo; + std::set seen_ids; + unsigned m_formula_id; + unsigned m_node_number; + std::map m_proof_ids; + std::map > m_proof_hypotheses; + std::map m_axiom_ids; + named_formulas* m_named_formulas; + +public: + pp_tptp(z3::context& ctx): ctx(ctx), m_formula_id(0) {} + + + void display_func_decl(std::ostream& out, z3::func_decl& f) { + std::string name = lower_case_fun(f.name()); + out << "tff(" << name << "_type, type, (\n " << name << ": "; + unsigned na = f.arity(); + switch(na) { + case 0: + break; + case 1: { + z3::sort s(f.domain(0)); + display_sort(out, s); + out << " > "; + break; + } + default: + out << "( "; + for (unsigned j = 0; j < na; ++j) { + z3::sort s(f.domain(j)); + display_sort(out, s); + if (j + 1 < na) { + out << " * "; + } + } + out << " ) > "; + } + z3::sort srt(f.range()); + display_sort(out, srt); + out << ")).\n"; + } + + void display_axiom(std::ostream& out, z3::expr e) { + out << "tff(formula" << (++m_formula_id) << ", axiom,\n "; + display(out, e); + out << ").\n"; + } + + void display(std::ostream& out, z3::expr e) { + if (e.is_numeral()) { + __int64 num, den; + if (Z3_get_numeral_small(ctx, e, &num, &den)) { + if (num < 0 && den == 1 && num != std::numeric_limits<__int64>::min()) { + out << "-" << (-num); + return; + } + } + // potential incompatibility: prints negative numbers with a space. + out << e; + } + else if (e.is_var()) { + unsigned idx = Z3_get_index_value(ctx, e); + out << names[names.size()-1-idx]; + } + else if (e.is_app()) { + switch(e.decl().decl_kind()) { + case Z3_OP_TRUE: + out << "$true"; + break; + case Z3_OP_FALSE: + out << "$false"; + break; + case Z3_OP_AND: + display_infix(out, "&", e); + break; + case Z3_OP_OR: + display_infix(out, "|", e); + break; + case Z3_OP_IMPLIES: + display_infix(out, "=>", e); + break; + case Z3_OP_NOT: + out << "(~"; + display(out, e.arg(0)); + out << ")"; + break; + case Z3_OP_EQ: + if (e.arg(0).is_bool()) { + display_infix(out, "<=>", e); + } + else { + display_infix(out, "=", e); + } + break; + case Z3_OP_IFF: + display_infix(out, "<=>", e); + break; + case Z3_OP_XOR: + display_infix(out, "<~>", e); + break; + case Z3_OP_MUL: + display_binary(out, "$product", e); + break; + case Z3_OP_ADD: + display_binary(out, "$sum", e); + break; + case Z3_OP_SUB: + display_prefix(out, "$difference", e); + break; + case Z3_OP_LE: + display_prefix(out, "$lesseq", e); + break; + case Z3_OP_GE: + display_prefix(out, "$greatereq", e); + break; + case Z3_OP_LT: + display_prefix(out, "$less", e); + break; + case Z3_OP_GT: + display_prefix(out, "$greater", e); + break; + case Z3_OP_UMINUS: + display_prefix(out, "$uminus", e); + break; + case Z3_OP_DIV: + display_prefix(out, "$quotient", e); + break; + case Z3_OP_IS_INT: + display_prefix(out, "$is_int", e); + break; + case Z3_OP_TO_REAL: + display_prefix(out, "$to_real", e); + break; + case Z3_OP_TO_INT: + display_prefix(out, "$to_int", e); + break; + case Z3_OP_IDIV: + display_prefix(out, "$quotient_e", e); + break; + case Z3_OP_MOD: + display_prefix(out, "$remainder_e", e); + break; + case Z3_OP_ITE: + display_prefix(out, e.is_bool()?"ite_f":"ite_t", e); + break; + case Z3_OP_DISTINCT: + display_prefix(out, "$distinct", e); + break; + case Z3_OP_REM: + throw failure_ex("rem is not handled"); + break; + case Z3_OP_OEQ: + display_prefix(out, "$oeq", e); + break; + default: + display_app(out, e); + break; + } + } + else if (e.is_quantifier()) { + Z3_bool is_forall = Z3_is_quantifier_forall(ctx, e); + unsigned nb = Z3_get_quantifier_num_bound(ctx, e); + + out << (is_forall?"!":"?") << "["; + for (unsigned i = 0; i < nb; ++i) { + Z3_symbol n = Z3_get_quantifier_bound_name(ctx, e, i); + names.push_back(upper_case_var(z3::symbol(ctx, n))); + z3::sort srt(ctx, Z3_get_quantifier_bound_sort(ctx, e, i)); + out << names.back() << ": "; + display_sort(out, srt); + if (i + 1 < nb) { + out << ", "; + } + } + out << "] : "; + display(out, e.body()); + for (unsigned i = 0; i < nb; ++i) { + names.pop_back(); + } + } + } + + void display_app(std::ostream& out, z3::expr e) { + if (e.is_const()) { + out << e; + return; + } + out << lower_case_fun(e.decl().name()) << "("; + unsigned n = e.num_args(); + for(unsigned i = 0; i < n; ++i) { + display(out, e.arg(i)); + if (i + 1 < n) { + out << ", "; + } + } + out << ")"; + } + + void display_sort(std::ostream& out, z3::sort const& s) { + if (s.is_int()) { + out << "$int"; + } + else if (s.is_real()) { + out << "$real"; + } + else if (s.is_bool()) { + out << "$o"; + } + else { + out << s; + } + } + + void display_infix(std::ostream& out, char const* conn, z3::expr& e) { + out << "("; + unsigned sz = e.num_args(); + for (unsigned i = 0; i < sz; ++i) { + display(out, e.arg(i)); + if (i + 1 < sz) { + out << " " << conn << " "; + } + } + out << ")"; + } + + void display_prefix(std::ostream& out, char const* conn, z3::expr& e) { + out << conn << "("; + unsigned sz = e.num_args(); + for (unsigned i = 0; i < sz; ++i) { + display(out, e.arg(i)); + if (i + 1 < sz) { + out << ", "; + } + } + out << ")"; + } + + void display_binary(std::ostream& out, char const* conn, z3::expr& e) { + out << conn << "("; + unsigned sz = e.num_args(); + unsigned np = 1; + for (unsigned i = 0; i < sz; ++i) { + display(out, e.arg(i)); + if (i + 1 < sz) { + out << ", "; + } + if (i + 2 < sz) { + out << conn << "("; + ++np; + } + } + for (unsigned i = 0; i < np; ++i) { + out << ")"; + } + } + + void collect_axiom_ids(named_formulas& axioms) { + m_named_formulas = &axioms; + m_axiom_ids.clear(); + for (unsigned i = 0; i < axioms.m_formulas.size(); ++i) { + z3::expr& e = axioms.m_formulas[i]; + unsigned id = Z3_get_ast_id(ctx, e); + m_axiom_ids.insert(std::make_pair(id, i)); + } + } + + void display_proof(std::ostream& out, named_formulas& fmls, z3::solver& solver) { + m_node_number = 0; + m_proof_ids.clear(); + m_proof_hypotheses.clear(); + z3::expr proof = solver.proof(); + collect_axiom_ids(fmls); + collect_decls(proof); + collect_hypotheses(proof); + display_sort_decls(out); + display_func_decls(out); + display_proof_rec(out, proof); + } + + /** + \brief collect hypotheses for each proof node. + */ + void collect_hypotheses(z3::expr& proof) { + Z3_sort proof_sort = proof.get_sort(); + size_t todo_size = todo.size(); + todo.push_back(proof); + while (todo_size != todo.size()) { + z3::expr p = todo.back(); + unsigned id = Z3_get_ast_id(ctx, p); + if (m_proof_hypotheses.find(id) != m_proof_hypotheses.end()) { + todo.pop_back(); + continue; + } + bool all_visited = true; + for (unsigned i = 0; i < p.num_args(); ++i) { + z3::expr arg = p.arg(i); + if (arg.get_sort() == proof_sort) { + if (m_proof_hypotheses.find(Z3_get_ast_id(ctx,arg)) == m_proof_hypotheses.end()) { + all_visited = false; + todo.push_back(arg); + } + } + } + if (!all_visited) { + continue; + } + todo.pop_back(); + std::set hyps; + if (p.decl().decl_kind() == Z3_OP_PR_LEMMA) { + // we assume here that all hypotheses get consumed in lemmas. + } + else { + for (unsigned i = 0; i < p.num_args(); ++i) { + z3::expr arg = p.arg(i); + if (arg.get_sort() == proof_sort) { + unsigned arg_id = Z3_get_ast_id(ctx,arg); + std::set const& arg_hyps = m_proof_hypotheses.find(arg_id)->second; + std::set::iterator it = arg_hyps.begin(), end = arg_hyps.end(); + for (; it != end; ++it) { + hyps.insert(*it); + } + } + } + } + m_proof_hypotheses.insert(std::make_pair(id, hyps)); + } + + } + + unsigned display_proof_rec(std::ostream& out, z3::expr proof) { + Z3_sort proof_sort = proof.get_sort(); + size_t todo_size = todo.size(); + todo.push_back(proof); + while (todo_size != todo.size()) { + z3::expr p = todo.back(); + unsigned id = Z3_get_ast_id(ctx, p); + if (m_proof_ids.find(id) != m_proof_ids.end()) { + todo.pop_back(); + continue; + } + + switch (p.decl().decl_kind()) { + case Z3_OP_PR_MODUS_PONENS_OEQ: { + unsigned hyp = display_proof_rec(out, p.arg(0)); + unsigned num = display_proof_hyp(out, hyp, p.arg(1)); + m_proof_ids.insert(std::make_pair(id, num)); + todo.pop_back(); + continue; + } + default: + break; + } + bool all_visited = true; + for (unsigned i = 0; i < p.num_args(); ++i) { + z3::expr arg = p.arg(i); + if (arg.get_sort() == proof_sort) { + if (m_proof_ids.find(Z3_get_ast_id(ctx,arg)) == m_proof_ids.end()) { + all_visited = false; + todo.push_back(arg); + } + } + } + if (!all_visited) { + continue; + } + todo.pop_back(); + unsigned num = ++m_node_number; + m_proof_ids.insert(std::make_pair(id, num)); + + switch (p.decl().decl_kind()) { + case Z3_OP_PR_ASSERTED: { + std::string formula_name; + std::string formula_file; + unsigned id = Z3_get_ast_id(ctx, p.arg(0)); + std::map::iterator it = m_axiom_ids.find(id); + if (it != m_axiom_ids.end()) { + formula_name = m_named_formulas->m_names[it->second]; + formula_file = m_named_formulas->m_files[it->second]; + } + else { + std::ostringstream str; + str << "axiom_" << id; + formula_name = str.str(); + formula_file = "unknown"; + } + out << "tff(" << m_node_number << ",axiom,("; + display(out, get_proof_formula(p)); + out << "), file('" << formula_file << "','"; + out << formula_name << "')).\n"; + break; + } + case Z3_OP_PR_UNDEF: + throw failure_ex("undef rule not handled"); + case Z3_OP_PR_TRUE: + display_inference(out, "true", "thm", p); + break; + case Z3_OP_PR_GOAL: + display_inference(out, "goal", "thm", p); + break; + case Z3_OP_PR_MODUS_PONENS: + display_inference(out, "modus_ponens", "thm", p); + break; + case Z3_OP_PR_REFLEXIVITY: + display_inference(out, "reflexivity", "thm", p); + break; + case Z3_OP_PR_SYMMETRY: + display_inference(out, "symmetry", "thm", p); + break; + case Z3_OP_PR_TRANSITIVITY: + case Z3_OP_PR_TRANSITIVITY_STAR: + display_inference(out, "transitivity", "thm", p); + break; + case Z3_OP_PR_MONOTONICITY: + display_inference(out, "monotonicity", "thm", p); + break; + case Z3_OP_PR_QUANT_INTRO: + display_inference(out, "quant_intro", "thm", p); + break; + case Z3_OP_PR_DISTRIBUTIVITY: + display_inference(out, "distributivity", "thm", p); + break; + case Z3_OP_PR_AND_ELIM: + display_inference(out, "and_elim", "thm", p); + break; + case Z3_OP_PR_NOT_OR_ELIM: + display_inference(out, "or_elim", "thm", p); + break; + case Z3_OP_PR_REWRITE: + case Z3_OP_PR_REWRITE_STAR: + display_inference(out, "rewrite", "thm", p); + break; + case Z3_OP_PR_PULL_QUANT: + case Z3_OP_PR_PULL_QUANT_STAR: + display_inference(out, "pull_quant", "thm", p); + break; + case Z3_OP_PR_PUSH_QUANT: + display_inference(out, "push_quant", "thm", p); + break; + case Z3_OP_PR_ELIM_UNUSED_VARS: + display_inference(out, "elim_unused_vars", "thm", p); + break; + case Z3_OP_PR_DER: + display_inference(out, "destructive_equality_resolution", "thm", p); + break; + case Z3_OP_PR_QUANT_INST: + display_inference(out, "quant_inst", "thm", p); + break; + case Z3_OP_PR_HYPOTHESIS: + out << "tff(" << m_node_number << ",assumption,("; + display(out, get_proof_formula(p)); + out << "), introduced(assumption)).\n"; + break; + case Z3_OP_PR_LEMMA: { + out << "tff(" << m_node_number << ",plain,("; + display(out, get_proof_formula(p)); + out << "), inference(lemma,lemma(discharge,"; + unsigned parent_id = Z3_get_ast_id(ctx, p.arg(0)); + std::set const& hyps = m_proof_hypotheses.find(parent_id)->second; + print_hypotheses(out, hyps); + out << ").\n"; + break; + display_inference(out, "lemma", "thm", p); + break; + } + case Z3_OP_PR_UNIT_RESOLUTION: + display_inference(out, "unit_resolution", "thm", p); + break; + case Z3_OP_PR_IFF_TRUE: + display_inference(out, "iff_true", "thm", p); + break; + case Z3_OP_PR_IFF_FALSE: + display_inference(out, "iff_false", "thm", p); + break; + case Z3_OP_PR_COMMUTATIVITY: + display_inference(out, "commutativity", "thm", p); + break; + case Z3_OP_PR_DEF_AXIOM: + display_inference(out, "tautology", "thm", p); + break; + case Z3_OP_PR_DEF_INTRO: + display_inference(out, "def_intro", "sab", p); + break; + case Z3_OP_PR_APPLY_DEF: + display_inference(out, "apply_def", "sab", p); + break; + case Z3_OP_PR_IFF_OEQ: + display_inference(out, "iff_oeq", "sab", p); + break; + case Z3_OP_PR_NNF_POS: + display_inference(out, "nnf_pos", "sab", p); + break; + case Z3_OP_PR_NNF_NEG: + display_inference(out, "nnf_neg", "sab", p); + break; + case Z3_OP_PR_NNF_STAR: + display_inference(out, "nnf", "sab", p); + break; + case Z3_OP_PR_CNF_STAR: + display_inference(out, "cnf", "sab", p); + break; + case Z3_OP_PR_SKOLEMIZE: + display_inference(out, "skolemize", "sab", p); + break; + case Z3_OP_PR_MODUS_PONENS_OEQ: + display_inference(out, "modus_ponens_sab", "sab", p); + break; + case Z3_OP_PR_TH_LEMMA: + display_inference(out, "theory_lemma", "thm", p); + break; + case Z3_OP_PR_HYPER_RESOLVE: + display_inference(out, "hyper_resolve", "thm", p); + break; + default: + out << "TBD: " << m_node_number << "\n" << p << "\n"; + throw failure_ex("rule not handled"); + } + } + return m_proof_ids.find(Z3_get_ast_id(ctx, proof))->second; + } + + unsigned display_proof_hyp(std::ostream& out, unsigned hyp, z3::expr p) { + z3::expr fml = p.arg(p.num_args()-1); + z3::expr conclusion = fml.arg(1); + switch (p.decl().decl_kind()) { + case Z3_OP_PR_REFLEXIVITY: + return display_hyp_inference(out, "reflexivity", "sab", conclusion, hyp); + case Z3_OP_PR_IFF_OEQ: { + unsigned hyp2 = display_proof_rec(out, p.arg(0)); + return display_hyp_inference(out, "modus_ponens", "thm", conclusion, hyp, hyp2); + } + case Z3_OP_PR_NNF_POS: + case Z3_OP_PR_NNF_STAR: + return display_hyp_inference(out, "nnf", "sab", conclusion, hyp); + case Z3_OP_PR_CNF_STAR: + return display_hyp_inference(out, "cnf", "sab", conclusion, hyp); + case Z3_OP_PR_SKOLEMIZE: + return display_hyp_inference(out, "skolemize", "sab", conclusion, hyp); + case Z3_OP_PR_TRANSITIVITY: + case Z3_OP_PR_TRANSITIVITY_STAR: { + unsigned na = p.num_args(); + for (unsigned i = 0; i + 1 < na; ++i) { + if (p.arg(i).num_args() != 2) { + // cop-out: Z3 produces transitivity proofs that are not a chain of equivalences/equi-sats. + // the generated proof is (most likely) not going to be checkable. + continue; + } + z3::expr conclusion = p.arg(i).arg(1); + hyp = display_hyp_inference(out, "transitivity", "sab", conclusion, hyp); + } + return hyp; + } + case Z3_OP_PR_MONOTONICITY: + throw failure_ex("monotonicity rule is not handled"); + default: + unsigned hyp2 = 0; + if (p.num_args() == 2) { + hyp2 = display_proof_rec(out, p.arg(0)); + } + if (p.num_args() > 2) { + std::cout << "unexpected number of arguments: " << p << "\n"; + throw failure_ex("unexpected number of arguments"); + } + + return display_hyp_inference(out, p.decl().name().str().c_str(), "sab", conclusion, hyp, hyp2); + } + return 0; + } + + + void display_inference(std::ostream& out, char const* name, char const* status, z3::expr p) { + unsigned id = Z3_get_ast_id(ctx, p); + std::set const& hyps = m_proof_hypotheses.find(id)->second; + out << "tff(" << m_node_number << ",plain,\n ("; + display(out, get_proof_formula(p)); + out << "),\n inference(" << name << ",[status(" << status << ")"; + if (!hyps.empty()) { + out << ", assumptions("; + print_hypotheses(out, hyps); + out << ")"; + } + out << "],"; + display_hypotheses(out, p); + out << ")).\n"; + } + + void print_hypotheses(std::ostream& out, std::set const& hyps) { + std::set::iterator it = hyps.begin(), end = hyps.end(); + bool first = true; + out << "["; + for (; it != end; ++it) { + if (!first) { + out << ", "; + } + first = false; + out << m_proof_ids.find(*it)->second; + } + out << "]"; + } + + unsigned display_hyp_inference(std::ostream& out, char const* name, char const* status, z3::expr conclusion, unsigned hyp1, unsigned hyp2 = 0) { + ++m_node_number; + out << "tff(" << m_node_number << ",plain,(\n "; + display(out, conclusion); + out << "),\n inference(" << name << ",[status(" << status << ")],"; + out << "[" << hyp1; + if (hyp2) { + out << ", " << hyp2; + } + out << "])).\n"; + return m_node_number; + } + + + + void get_free_vars(z3::expr const& e, std::vector& vars) { + std::set seen; + size_t sz = todo.size(); + todo.push_back(e); + while (todo.size() != sz) { + z3::expr e = todo.back(); + todo.pop_back(); + unsigned id = Z3_get_ast_id(e.ctx(), e); + if (seen.find(id) != seen.end()) { + continue; + } + seen.insert(id); + if (e.is_var()) { + unsigned idx = Z3_get_index_value(ctx, e); + while (idx >= vars.size()) { + vars.push_back(e.get_sort()); + } + vars[idx] = e.get_sort(); + } + else if (e.is_app()) { + unsigned sz = e.num_args(); + for (unsigned i = 0; i < sz; ++i) { + todo.push_back(e.arg(i)); + } + } + else { + // e is a quantifier + std::vector fv; + get_free_vars(e.body(), fv); + unsigned nb = Z3_get_quantifier_num_bound(e.ctx(), e); + for (unsigned i = nb; i < fv.size(); ++i) { + if (vars.size() <= i - nb) { + vars.push_back(fv[i]); + } + } + } + } + } + + z3::expr get_proof_formula(z3::expr proof) { + unsigned na = proof.num_args(); + z3::expr result = proof.arg(proof.num_args()-1); + std::vector vars; + get_free_vars(result, vars); + if (vars.empty()) { + return result; + } + Z3_sort* sorts = new Z3_sort[vars.size()]; + Z3_symbol* names = new Z3_symbol[vars.size()]; + for (unsigned i = 0; i < vars.size(); ++i) { + std::ostringstream str; + str << "X" << (i+1); + sorts[vars.size()-i-1] = vars[i]; + names[vars.size()-i-1] = Z3_mk_string_symbol(ctx, str.str().c_str()); + } + result = z3::expr(ctx, Z3_mk_forall(ctx, 1, 0, 0, static_cast(vars.size()), sorts, names, result)); + delete[] sorts; + delete[] names; + return result; + } + + void display_hypotheses(std::ostream& out, z3::expr p) { + unsigned na = p.num_args(); + out << "["; + for (unsigned i = 0; i + 1 < na; ++i) { + out << m_proof_ids.find(Z3_get_ast_id(p.ctx(), p.arg(i)))->second; + if (i + 2 < na) { + out << ", "; + } + } + out << "]"; + } + + void display_sort_decls(std::ostream& out) { + for (unsigned i = 0; i < sorts.size(); ++i) { + display_sort_decl(out, sorts[i]); + } + } + + void display_sort_decl(std::ostream& out, z3::sort& s) { + out << "tff(" << s << "_type, type, (" << s << ": $tType)).\n"; + } + + + void display_func_decls(std::ostream& out) { + for (size_t i = 0; i < funs.size(); ++i) { + display_func_decl(out, funs[i]); + } + } + + bool contains_id(unsigned id) const { + return seen_ids.find(id) != seen_ids.end(); + } + + void collect_decls(z3::expr e) { + todo.push_back(e); + while (!todo.empty()) { + z3::expr e = todo.back(); + todo.pop_back(); + unsigned id = Z3_get_ast_id(ctx, e); + if (contains_id(id)) { + continue; + } + seen_ids.insert(id); + if (e.is_app()) { + collect_fun(e.decl()); + unsigned sz = e.num_args(); + for (unsigned i = 0; i < sz; ++i) { + todo.push_back(e.arg(i)); + } + } + else if (e.is_quantifier()) { + unsigned nb = Z3_get_quantifier_num_bound(e.ctx(), e); + for (unsigned i = 0; i < nb; ++i) { + z3::sort srt(ctx, Z3_get_quantifier_bound_sort(e.ctx(), e, i)); + collect_sort(srt); + } + todo.push_back(e.body()); + } + else if (e.is_var()) { + collect_sort(e.get_sort()); + } + } + } + + void collect_sort(z3::sort s) { + unsigned id = Z3_get_sort_id(ctx, s); + if (s.sort_kind() == Z3_UNINTERPRETED_SORT && + contains_id(id)) { + seen_ids.insert(id); + sorts.push_back(s); + } + } + + void collect_fun(z3::func_decl f) { + unsigned id = Z3_get_func_decl_id(ctx, f); + if (contains_id(id)) { + return; + } + seen_ids.insert(id); + if (f.decl_kind() == Z3_OP_UNINTERPRETED) { + funs.push_back(f); + } + for (unsigned i = 0; i < f.arity(); ++i) { + collect_sort(f.domain(i)); + } + collect_sort(f.range()); + } + + std::string upper_case_var(z3::symbol const& sym) { + std::string result = sanitize(sym); + char ch = result[0]; + if ('A' <= ch && ch <= 'Z') { + return result; + } + return "X" + result; + } + + std::string lower_case_fun(z3::symbol const& sym) { + std::string result = sanitize(sym); + char ch = result[0]; + if ('a' <= ch && ch <= 'z') { + return result; + } + else { + return "tptp_fun_" + result; + } + } + + std::string sanitize(z3::symbol const& sym) { + std::ostringstream str; + if (sym.kind() == Z3_INT_SYMBOL) { + str << sym; + return str.str(); + } + std::string s = sym.str(); + size_t sz = s.size(); + for (size_t i = 0; i < sz; ++i) { + char ch = s[i]; + if ('a' <= ch && ch <= 'z') { + str << ch; + } + else if ('A' <= ch && ch <= 'Z') { + str << ch; + } + else if ('0' <= ch && ch <= '9') { + str << ch; + } + else if ('_' == ch) { + str << ch; + } + else { + str << "_"; + } + } + return str.str(); + } +}; + +static char* g_input_file = 0; +static bool g_display_smt2 = false; +static bool g_generate_model = false; +static bool g_generate_proof = false; +static bool g_generate_core = false; +static bool g_display_statistics = false; +static bool g_first_interrupt = true; +static bool g_smt2status = false; +static bool g_check_status = false; +static int g_timeout = 0; +static double g_start_time = 0; +static z3::solver* g_solver = 0; +static z3::context* g_context = 0; +static std::ostream* g_out = &std::cout; + + + +static void display_usage() { + unsigned major, minor, build_number, revision_number; + Z3_get_version(&major, &minor, &build_number, &revision_number); + std::cout << "Z3tptp [" << major << "." << minor << "." << build_number << "." << revision_number << "] (c) 2006-20**. Microsoft Corp.\n"; + std::cout << "Usage: tptp [options] [-file:]file\n"; + std::cout << " -h, -? prints this message.\n"; + std::cout << " -smt2 print SMT-LIB2 benchmark.\n"; + std::cout << " -m, -model generate model.\n"; + std::cout << " -p, -proof generate proof.\n"; + std::cout << " -c, -core generate unsat core of named formulas.\n"; + std::cout << " -st, -statistics display statistics.\n"; + std::cout << " -t:timeout set timeout (in second).\n"; + std::cout << " -smt2status display status in smt2 format instead of SZS.\n"; + std::cout << " -check_status check the status produced by Z3 against annotation in benchmark.\n"; + std::cout << " -: configuration parameter and value.\n"; + std::cout << " -o: file to place output in.\n"; +} + + +static void display_statistics() { + if (g_solver && g_display_statistics) { + std::cout.flush(); + std::cerr.flush(); + double end_time = static_cast(clock()); + z3::stats stats = g_solver->statistics(); + std::cout << stats << "\n"; + std::cout << "time: " << (end_time - g_start_time)/CLOCKS_PER_SEC << " secs\n"; + } +} + +static void on_ctrl_c(int) { + if (g_context && g_first_interrupt) { + Z3_interrupt(*g_context); + g_first_interrupt = false; + } + else { + signal (SIGINT, SIG_DFL); + display_statistics(); + raise(SIGINT); + } +} + +bool parse_token(char const*& line, char const* token) { + char const* result = line; + while (result[0] == ' ') ++result; + while (token[0] && result[0] == token[0]) { + ++token; + ++result; + } + if (!token[0]) { + line = result; + return true; + } + else { + return false; + } +} + +bool parse_is_sat_line(char const* line, bool& is_sat) { + if (!parse_token(line, "%")) return false; + if (!parse_token(line, "Status")) return false; + if (!parse_token(line, ":")) return false; + + if (parse_token(line, "Unsatisfiable")) { + is_sat = false; + return true; + } + if (parse_token(line, "Theorem")) { + is_sat = false; + return true; + } + if (parse_token(line, "Theorem")) { + is_sat = false; + return true; + } + if (parse_token(line, "CounterSatisfiable")) { + is_sat = true; + return true; + } + if (parse_token(line, "Satisfiable")) { + is_sat = true; + return true; + } + return false; +} + +bool parse_is_sat(char const* filename, bool& is_sat) { + std::ifstream is(filename); + if (is.bad() || is.fail()) { + std::stringstream strm; + strm << "Could not open file " << filename << "\n"; + throw failure_ex(strm.str().c_str()); + } + + for (unsigned i = 0; !is.eof() && i < 200; ++i) { + std::string line; + std::getline(is, line); + if (parse_is_sat_line(line.c_str(), is_sat)) { + return true; + } + } + return false; +} + + +void parse_cmd_line_args(int argc, char ** argv) { + g_input_file = 0; + g_display_smt2 = false; + int i = 1; + while (i < argc) { + char* arg = argv[i]; + char * eq = 0; + char * opt_arg = 0; + if (arg[0] == '-' || arg[0] == '/') { + ++arg; + while (*arg == '-') { + ++arg; + } + char * colon = strchr(arg, ':'); + if (colon) { + opt_arg = colon + 1; + *colon = 0; + } + if (!strcmp(arg,"h") || !strcmp(arg,"help") || !strcmp(arg,"?")) { + display_usage(); + exit(0); + } + if (!strcmp(arg,"p") || !strcmp(arg,"proof")) { + g_generate_proof = true; + } + else if (!strcmp(arg,"m") || !strcmp(arg,"model")) { + g_generate_model = true; + } + else if (!strcmp(arg,"c") || !strcmp(arg,"core")) { + g_generate_core = true; + } + else if (!strcmp(arg,"st") || !strcmp(arg,"statistics")) { + g_display_statistics = true; + } + else if (!strcmp(arg,"check_status")) { + g_check_status = true; + } + else if (!strcmp(arg,"t") || !strcmp(arg,"timeout")) { + if (!opt_arg) { + display_usage(); + exit(0); + } + g_timeout = atoi(opt_arg); + } + else if (!strcmp(arg,"smt2status")) { + g_smt2status = true; + } + else if (!strcmp(arg,"o")) { + if (opt_arg) { + g_out = new std::ofstream(opt_arg); + if (g_out->bad() || g_out->fail()) { + std::cout << "Could not open file of output: " << opt_arg << "\n"; + exit(0); + } + } + else { + display_usage(); + exit(0); + } + } + else if (!strcmp(arg,"smt2")) { + g_display_smt2 = true; + + } + else if (!strcmp(arg, "file")) { + g_input_file = opt_arg; + } + else if (opt_arg && arg[0] != '"') { + Z3_global_param_set(arg, opt_arg); + } + else { + std::cerr << "parameter " << arg << " was not recognized\n"; + display_usage(); + exit(0); + } + } + else { + g_input_file = arg; + } + ++i; + } + + if (!g_input_file) { + display_usage(); + exit(0); + } +} + +static bool is_smt2_file(char const* filename) { + size_t len = strlen(filename); + return (len > 4 && !strcmp(filename + len - 5,".smt2")); +} + +static void check_error(z3::context& ctx) { + Z3_error_code e = Z3_get_error_code(ctx); + if (e != Z3_OK) { + std::cout << Z3_get_error_msg_ex(ctx, e) << "\n"; + exit(1); + } +} + +static void display_tptp(std::ostream& out) { + // run SMT2 parser, pretty print TFA format. + z3::context ctx; + Z3_ast _fml = Z3_parse_smtlib2_file(ctx, g_input_file, 0, 0, 0, 0, 0, 0); + check_error(ctx); + z3::expr fml(ctx, _fml); + + pp_tptp pp(ctx); + pp.collect_decls(fml); + pp.display_sort_decls(out); + pp.display_func_decls(out); + + if (fml.decl().decl_kind() == Z3_OP_AND) { + for (unsigned i = 0; i < fml.num_args(); ++i) { + pp.display_axiom(out, fml.arg(i)); + } + } + else { + pp.display_axiom(out, fml); + } +} + +static void display_proof(z3::context& ctx, named_formulas& fmls, z3::solver& solver) { + pp_tptp pp(ctx); + pp.display_proof(std::cout, fmls, solver); +} + +static void display_model(z3::context& ctx, z3::model model) { + unsigned nc = model.num_consts(); + unsigned nf = model.num_funcs(); + z3::expr_vector fmls(ctx); + for (unsigned i = 0; i < nc; ++i) { + z3::func_decl f = model.get_const_decl(i); + z3::expr e = model.get_const_interp(f); + fmls.push_back(f() == e); + } + + for (unsigned i = 0; i < nf; ++i) { + z3::func_decl f = model.get_func_decl(i); + z3::func_interp fi = model.get_func_interp(f); + unsigned arity = f.arity(); + z3::expr_vector args(ctx); + for (unsigned j = 0; j < arity; ++j) { + std::ostringstream str; + str << "X" << j; + z3::symbol sym(ctx, Z3_mk_string_symbol(ctx, str.str().c_str())); + args.push_back(ctx.constant(sym, f.domain(j))); + } + unsigned ne = fi.num_entries(); + Z3_ast* conds = new Z3_ast[arity]; + Z3_ast* conds_match = new Z3_ast[ne]; + z3::expr_vector conds_matchv(ctx); + z3::expr els = fi.else_value(); + unsigned num_cases = 0; + for (unsigned k = 0; k < ne; ++k) { + z3::func_entry e = fi.entry(k); + z3::expr_vector condv(ctx), args_e(ctx); + if (((Z3_ast)els) && (Z3_get_ast_id(ctx, els) == Z3_get_ast_id(ctx, e.value()))) { + continue; + } + for (unsigned j = 0; j < arity; ++j) { + args_e.push_back(e.arg(j)); + condv.push_back(e.arg(j) == args[j]); + conds[j] = condv.back(); + } + z3::expr cond(ctx, Z3_mk_and(ctx, arity, conds)); + conds_matchv.push_back(cond); + conds_match[num_cases] = cond; + fmls.push_back(f(args_e) == e.value()); + ++num_cases; + } + if (els) { + els = f(args) == els; + switch (num_cases) { + case 0: els = forall(args, els); break; + case 1: els = forall(args, implies(!z3::expr(ctx, conds_match[0]), els)); break; + default: els = forall(args, implies(!z3::expr(ctx, Z3_mk_or(ctx, num_cases, conds_match)), els)); break; + } + fmls.push_back(els); + } + delete[] conds; + delete[] conds_match; + } + + pp_tptp pp(ctx); + for (unsigned i = 0; i < fmls.size(); ++i) { + pp.collect_decls(fmls[i]); + } + pp.display_sort_decls(std::cout); + pp.display_func_decls(std::cout); + for (unsigned i = 0; i < fmls.size(); ++i) { + pp.display_axiom(std::cout, fmls[i]); + } +} + +static void display_smt2(std::ostream& out) { + z3::config config; + z3::context ctx(config); + named_formulas fmls; + env env(ctx); + try { + env.parse(g_input_file, fmls); + } + catch (failure_ex& ex) { + std::cerr << ex.msg << "\n"; + return; + } + + size_t num_assumptions = fmls.m_formulas.size(); + + Z3_ast* assumptions = new Z3_ast[num_assumptions]; + for (size_t i = 0; i < num_assumptions; ++i) { + assumptions[i] = fmls.m_formulas[i]; + } + Z3_string s = + Z3_benchmark_to_smtlib_string( + ctx, + "Benchmark generated from TPTP", // comment + 0, // no logic is set + "unknown", // no status annotation + "", // attributes + static_cast(num_assumptions), + assumptions, + ctx.bool_val(true)); + + out << s << "\n"; + delete[] assumptions; +} + +static void prove_tptp() { + z3::config config; + if (g_generate_proof) { + config.set("proof", true); + z3::set_param("proof", true); + } + z3::context ctx(config); + z3::solver solver(ctx); + g_solver = &solver; + g_context = &ctx; + if (g_timeout) { + // TBD overflow check + z3::set_param("timeout", g_timeout*1000); + z3::params params(ctx); + params.set("timeout", static_cast(g_timeout*1000)); + solver.set(params); + } + + + named_formulas fmls; + env env(ctx); + try { + env.parse(g_input_file, fmls); + } + catch (failure_ex& ex) { + std::cerr << ex.msg << "\n"; + std::cout << "SZS status GaveUp\n"; + return; + } + + size_t num_assumptions = fmls.m_formulas.size(); + + z3::check_result result; + + if (g_generate_core) { + z3::expr_vector assumptions(ctx); + + for (size_t i = 0; i < num_assumptions; ++i) { + z3::expr pred = ctx.constant(fmls.m_names[i].c_str(), ctx.bool_sort()); + z3::expr def = fmls.m_formulas[i] == pred; + solver.add(def); + assumptions.push_back(pred); + } + result = solver.check(assumptions); + } + else { + for (unsigned i = 0; i < num_assumptions; ++i) { + solver.add(fmls.m_formulas[i]); + } + result = solver.check(); + } + + switch(result) { + case z3::unsat: + if (g_smt2status) { + std::cout << result << "\n"; + } + else if (fmls.has_conjecture()) { + std::cout << "SZS status Theorem\n"; + } + else { + std::cout << "SZS status Unsatisfiable\n"; + } + if (g_generate_proof) { + try { + display_proof(ctx, fmls, solver); + } + catch (failure_ex& ex) { + std::cerr << "Proof display could not be completed: " << ex.msg << "\n"; + } + } + if (g_generate_core) { + z3::expr_vector core = solver.unsat_core(); + std::cout << "SZS core "; + for (unsigned i = 0; i < core.size(); ++i) { + std::cout << core[i] << " "; + } + std::cout << "\n"; + } + break; + case z3::sat: + if (g_smt2status) { + std::cout << result << "\n"; + } + else if (fmls.has_conjecture()) { + std::cout << "SZS status CounterSatisfiable\n"; + } + else { + std::cout << "SZS status Satisfiable\n"; + } + if (g_generate_model) { + display_model(ctx, solver.get_model()); + } + break; + case z3::unknown: + if (g_smt2status) { + std::cout << result << "\n"; + } + else if (!g_first_interrupt) { + std::cout << "SZS status Interrupted\n"; + } + else { + std::cout << "SZS status GaveUp\n"; + std::string reason = solver.reason_unknown(); + std::cout << "SZS reason " << reason << "\n"; + } + break; + } + bool is_sat = true; + if (g_check_status && + result != z3::unknown && + parse_is_sat(g_input_file, is_sat)) { + if (is_sat && result == z3::unsat) { + std::cout << "BUG!! expected result is Satisfiable, returned result is Unsat\n"; + } + if (!is_sat && result == z3::sat) { + std::cout << "BUG!! expected result is Unsatisfiable, returned result is Satisfiable\n"; + } + } + display_statistics(); +} + +int main(int argc, char** argv) { + + std::ostream* out = &std::cout; + g_start_time = static_cast(clock()); + signal(SIGINT, on_ctrl_c); + + parse_cmd_line_args(argc, argv); + + if (is_smt2_file(g_input_file)) { + display_tptp(*g_out); + } + else if (g_display_smt2) { + display_smt2(*g_out); + } + else { + prove_tptp(); + } + return 0; +} + diff --git a/examples/tptp/tptp5.h b/examples/tptp/tptp5.h new file mode 100644 index 000000000..69fe79b28 --- /dev/null +++ b/examples/tptp/tptp5.h @@ -0,0 +1,38 @@ +#ifndef TPTP5_H_ +#define TPTP5_H_ + + +class TreeNode; + +#if 0 +class named_formulas { + expr_ref_vector m_fmls; + svector m_names; + bool m_has_conjecture; + unsigned m_conjecture_index; +public: + named_formulas(ast_manager& m) : + m_fmls(m), + m_has_conjecture(false), + m_conjecture_index(0) + {} + void push_back(expr* fml, char const* name) { + m_fmls.push_back(fml); + m_names.push_back(symbol(name)); + } + unsigned size() const { return m_fmls.size(); } + expr*const* c_ptr() const { return m_fmls.c_ptr(); } + expr* operator[](unsigned i) { return m_fmls[i].get(); } + symbol const& name(unsigned i) { return m_names[i]; } + void set_has_conjecture() { + m_has_conjecture = true; + m_conjecture_index = m_fmls.size(); + } + bool has_conjecture() const { return m_has_conjecture; } + unsigned conjecture_index() const { return m_conjecture_index; } +}; + +bool tptp5_parse(ast_manager& m, char const* filename, named_formulas& fmls); +#endif + +#endif diff --git a/examples/tptp/tptp5.lex.cpp b/examples/tptp/tptp5.lex.cpp new file mode 100644 index 000000000..639d92750 --- /dev/null +++ b/examples/tptp/tptp5.lex.cpp @@ -0,0 +1,2672 @@ +#line 2 "tptp5.lex.cpp" + +#line 4 "tptp5.lex.cpp" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires + * access to the local variable yy_act. Since yyless() is a macro, it would break + * existing scanners that call yyless() from OUTSIDE yylex. + * One obvious solution it to make yy_act a global. I tried that, and saw + * a 5% performance hit in a non-yylineno scanner, because yy_act is + * normally declared as a register variable-- so it is not worth it. + */ + #define YY_LESS_LINENO(n) \ + do { \ + int yyl;\ + for ( yyl = n; yyl < yyleng; ++yyl )\ + if ( yytext[yyl] == '\n' )\ + --yylineno;\ + }while(0) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart (FILE *input_file ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); +void yy_delete_buffer (YY_BUFFER_STATE b ); +void yy_flush_buffer (YY_BUFFER_STATE b ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state (void ); + +static void yyensure_buffer_stack (void ); +static void yy_load_buffer_state (void ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + +void *yyalloc (yy_size_t ); +void *yyrealloc (void *,yy_size_t ); +void yyfree (void * ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +typedef unsigned char YY_CHAR; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +typedef int yy_state_type; + +#define YY_FLEX_LEX_COMPAT +extern int yylineno; + +int yylineno = 1; + +extern char yytext[]; + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + if ( yyleng + (yy_more_offset) >= YYLMAX ) \ + YY_FATAL_ERROR( "token too large, exceeds YYLMAX" ); \ + yy_flex_strncpy( &yytext[(yy_more_offset)], (yytext_ptr), yyleng + 1 ); \ + yyleng += (yy_more_offset); \ + (yy_prev_more_offset) = (yy_more_offset); \ + (yy_more_offset) = 0; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 75 +#define YY_END_OF_BUFFER 76 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_acclist[228] = + { 0, + 76, 73, 75, 72, 73, 75, 11, 74, 75, 74, + 75, 74, 75, 74, 75, 71, 74, 75, 1, 74, + 75, 74, 75, 19, 74, 75, 27, 74, 75, 28, + 53, 74, 75, 54, 74, 75, 8, 74, 75, 20, + 74, 75, 22, 74, 75, 74, 75, 63, 65, 66, + 74, 75, 63, 65, 66, 67, 74, 75, 6, 74, + 75, 56, 74, 75, 9, 74, 75, 55, 74, 75, + 23, 74, 75, 2, 74, 75, 50, 74, 75, 15, + 74, 75, 26, 74, 75, 5, 74, 75, 51, 74, + 75, 51, 74, 75, 51, 74, 75, 51, 74, 75, + + 51, 74, 75, 32, 52, 74, 75, 29, 74, 75, + 75, 13, 12, 14, 47, 48, 48, 48, 48, 48, + 71, 63, 64, 63, 64, 70, 63, 65, 66, 67, + 7, 16, 10, 25, 24, 4, 3, 50, 51, 51, + 51, 51, 51, 51, 30, 31, 49, 48, 48, 48, + 48, 48, 48, 46, 63, 64, 21, 70, 57, 59, + 69, 60, 62, 57, 59, 68, 57, 59, 68, 17, + 18, 41, 51, 42, 51, 51, 44, 51, 45, 51, + 49, 33, 48, 34, 48, 35, 48, 48, 39, 48, + 40, 48, 57, 58, 60, 61, 57, 58, 57, 58, + + 71, 57, 59, 69, 60, 62, 57, 59, 68, 51, + 36, 48, 48, 57, 58, 60, 61, 57, 58, 51, + 37, 48, 38, 48, 51, 43, 51 + } ; + +static yyconst flex_int16_t yy_accept[153] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 4, 7, 10, 12, + 14, 16, 19, 22, 24, 27, 30, 34, 37, 40, + 43, 46, 48, 53, 59, 62, 65, 68, 71, 74, + 77, 80, 83, 86, 89, 92, 95, 98, 101, 104, + 108, 111, 112, 113, 114, 115, 115, 116, 116, 116, + 117, 118, 119, 120, 121, 122, 122, 122, 124, 126, + 126, 127, 127, 127, 127, 127, 131, 132, 133, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + + 154, 154, 155, 155, 155, 155, 155, 157, 158, 159, + 159, 159, 162, 164, 167, 170, 171, 172, 174, 176, + 177, 179, 181, 182, 184, 186, 188, 189, 191, 193, + 195, 197, 199, 201, 201, 201, 202, 205, 207, 210, + 211, 213, 214, 216, 218, 220, 221, 223, 225, 226, + 228, 228 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 22, 7, 23, + 24, 25, 26, 27, 28, 28, 28, 28, 29, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 30, 31, 32, 33, 34, 7, 35, 35, 36, 37, + + 38, 39, 35, 40, 41, 35, 35, 42, 35, 43, + 44, 35, 35, 35, 35, 45, 46, 35, 35, 35, + 35, 35, 7, 47, 7, 48, 49, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50 + } ; + +static yyconst flex_int32_t yy_meta[51] = + { 0, + 1, 1, 2, 3, 3, 3, 3, 4, 3, 3, + 5, 3, 3, 3, 3, 3, 3, 3, 3, 6, + 6, 3, 3, 3, 3, 3, 3, 6, 6, 3, + 3, 3, 3, 6, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 3, 3, 1, 1 + } ; + +static yyconst flex_int16_t yy_base[164] = + { 0, + 0, 0, 15, 16, 23, 30, 31, 38, 45, 46, + 53, 60, 61, 68, 302, 303, 303, 90, 47, 303, + 80, 0, 303, 270, 303, 303, 303, 90, 303, 103, + 97, 286, 108, 110, 275, 84, 273, 303, 108, 48, + 0, 303, 303, 303, 0, 254, 252, 252, 96, 303, + 93, 303, 303, 303, 303, 127, 303, 132, 0, 0, + 222, 213, 209, 102, 0, 62, 133, 131, 133, 228, + 135, 231, 145, 223, 147, 154, 303, 218, 217, 303, + 303, 303, 303, 303, 0, 0, 202, 201, 202, 197, + 196, 303, 303, 0, 0, 180, 131, 171, 157, 143, + + 146, 303, 148, 160, 157, 164, 168, 303, 170, 147, + 179, 174, 179, 303, 181, 303, 303, 0, 0, 105, + 0, 0, 0, 0, 0, 0, 165, 0, 0, 187, + 193, 303, 197, 131, 201, 303, 201, 203, 206, 97, + 0, 166, 208, 211, 213, 75, 0, 0, 42, 0, + 303, 244, 248, 255, 260, 262, 264, 51, 266, 271, + 278, 280, 287 + } ; + +static yyconst flex_int16_t yy_def[164] = + { 0, + 151, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 151, 151, 151, 151, 152, 151, + 153, 154, 151, 155, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 156, 151, 151, 151, 157, 157, 157, 157, 157, 151, + 151, 151, 151, 151, 151, 152, 151, 151, 158, 159, + 159, 159, 159, 159, 154, 160, 151, 151, 151, 151, + 151, 161, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 156, 157, 157, 157, 157, 157, + 157, 151, 151, 162, 159, 159, 159, 159, 159, 159, + + 160, 151, 151, 151, 151, 151, 151, 151, 151, 161, + 163, 151, 151, 151, 151, 151, 151, 157, 157, 157, + 157, 157, 162, 159, 159, 159, 159, 159, 159, 151, + 151, 151, 151, 161, 163, 151, 151, 151, 151, 157, + 159, 159, 151, 151, 151, 157, 159, 159, 157, 157, + 0, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151 + } ; + +static yyconst flex_int16_t yy_nxt[354] = + { 0, + 16, 17, 17, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 41, 42, + 20, 43, 44, 20, 45, 46, 45, 45, 47, 45, + 48, 45, 45, 45, 49, 45, 50, 51, 16, 52, + 45, 45, 57, 45, 45, 45, 45, 94, 45, 45, + 45, 45, 83, 45, 84, 45, 45, 45, 45, 45, + 45, 45, 102, 45, 45, 45, 45, 58, 45, 150, + 45, 45, 45, 45, 45, 45, 45, 59, 45, 45, + 45, 45, 103, 45, 53, 45, 45, 45, 45, 45, + + 45, 45, 92, 45, 45, 45, 45, 78, 45, 68, + 69, 149, 45, 54, 55, 61, 71, 71, 62, 70, + 63, 81, 68, 69, 64, 73, 74, 73, 74, 76, + 76, 79, 57, 82, 90, 91, 75, 56, 75, 93, + 99, 100, 146, 66, 111, 75, 140, 75, 104, 105, + 104, 105, 107, 107, 109, 109, 102, 58, 101, 106, + 111, 106, 56, 66, 112, 112, 114, 115, 106, 125, + 106, 73, 74, 76, 76, 126, 103, 131, 101, 130, + 130, 129, 75, 132, 133, 104, 105, 107, 107, 109, + 109, 75, 135, 137, 137, 128, 106, 136, 138, 138, + + 139, 139, 75, 141, 147, 106, 143, 143, 127, 142, + 148, 75, 144, 144, 135, 106, 145, 145, 124, 136, + 137, 137, 138, 138, 106, 139, 139, 143, 143, 75, + 144, 144, 145, 145, 122, 121, 106, 120, 75, 119, + 118, 117, 116, 113, 111, 106, 56, 56, 56, 56, + 56, 60, 108, 98, 60, 65, 97, 65, 65, 65, + 65, 65, 66, 66, 96, 66, 66, 85, 85, 86, + 86, 95, 95, 101, 101, 101, 101, 101, 110, 110, + 110, 110, 110, 110, 110, 123, 123, 134, 134, 134, + 134, 134, 134, 134, 89, 88, 87, 80, 77, 72, + + 67, 151, 15, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151 + } ; + +static yyconst flex_int16_t yy_chk[354] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 4, 19, 3, 4, 3, 4, 158, 5, 3, + 4, 5, 40, 5, 40, 6, 7, 5, 6, 7, + 6, 7, 66, 8, 6, 7, 8, 19, 8, 149, + 9, 10, 8, 9, 10, 9, 10, 21, 11, 9, + 10, 11, 66, 11, 18, 12, 13, 11, 12, 13, + + 12, 13, 51, 14, 12, 13, 14, 36, 14, 28, + 28, 146, 14, 18, 18, 21, 31, 31, 21, 30, + 21, 39, 30, 30, 21, 33, 33, 34, 34, 34, + 34, 36, 56, 39, 49, 49, 33, 58, 34, 51, + 64, 64, 140, 67, 134, 33, 120, 34, 68, 68, + 69, 69, 69, 69, 71, 71, 101, 56, 103, 68, + 110, 69, 58, 67, 73, 73, 75, 75, 68, 97, + 69, 76, 76, 76, 76, 97, 101, 105, 103, 104, + 104, 100, 76, 106, 106, 107, 107, 107, 107, 109, + 109, 76, 111, 112, 112, 99, 107, 111, 113, 113, + + 115, 115, 112, 127, 142, 107, 130, 130, 98, 127, + 142, 112, 131, 131, 135, 130, 133, 133, 96, 135, + 137, 137, 138, 138, 130, 139, 139, 143, 143, 137, + 144, 144, 145, 145, 91, 90, 143, 89, 137, 88, + 87, 79, 78, 74, 72, 143, 152, 152, 152, 152, + 152, 153, 70, 63, 153, 154, 62, 154, 154, 154, + 154, 154, 155, 155, 61, 155, 155, 156, 156, 157, + 157, 159, 159, 160, 160, 160, 160, 160, 161, 161, + 161, 161, 161, 161, 161, 162, 162, 163, 163, 163, + 163, 163, 163, 163, 48, 47, 46, 37, 35, 32, + + 24, 15, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151 + } ; + +/* Table of booleans, true if rule could match eol. */ +static yyconst flex_int32_t yy_rule_can_match_eol[76] = + { 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, }; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +static yy_state_type *yy_state_buf=0, *yy_state_ptr=0; +static char *yy_full_match; +static int yy_lp; +#define REJECT \ +{ \ +*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ \ +yy_cp = (yy_full_match); /* restore poss. backed-over text */ \ +++(yy_lp); \ +goto find_rule; \ +} + +static int yy_more_offset = 0; +static int yy_prev_more_offset = 0; +#define yymore() ((yy_more_offset) = yy_flex_strlen( yytext )) +#define YY_NEED_STRLEN +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET \ + { \ + (yy_more_offset) = (yy_prev_more_offset); \ + yyleng -= (yy_more_offset); \ + } +#ifndef YYLMAX +#define YYLMAX 8192 +#endif + +char yytext[YYLMAX]; +char *yytext_ptr; +#line 1 "tptp5.l" +#line 3 "tptp5.l" +//----------------------------------------------------------------------------- +#include +#include +#include + +#if _WINDOWS + #include + #define isatty _isatty +#else + // Linux + #include + #define _strdup strdup +#endif + +#include "tptp5.h" +#include "tptp5.tab.h" + + +#define YY_NO_UNISTD_H +#define YY_SKIP_YYWRAP +static int yywrap() { return 1; } +//----------------------------------------------------------------------------- +//----Compile with -DP_VERBOSE=2 to list tokens as they are seen. +#ifndef P_VERBOSE +# define P_VERBOSE 0 +# endif +int verbose2 = P_VERBOSE; + +//----If tptp_prev_tok == PERIOD, you are outside any sentence. +//#ifndef PERIOD +//# error "Period not defined" +//# define PERIOD 46 +//# endif + +#define TPTP_STORE_SIZE 32768 + +//----These have to be external as they are references from other code that +//----is generated by lex/yacc. +int tptp_prev_tok = PERIOD; +int tptp_store_size = TPTP_STORE_SIZE; +char* tptp_lval[TPTP_STORE_SIZE]; +//----------------------------------------------------------------------------- +void tptp_print_tok(char* lval) { + + printf("%3d:%s;\n", tptp_prev_tok, lval); + + return; +} +//----------------------------------------------------------------------------- +int tptp_update_lval(char* lval) { + + static int tptp_next_store = 0; + int next = tptp_next_store; + + free(tptp_lval[tptp_next_store]); + tptp_lval[tptp_next_store] = _strdup(lval); + tptp_next_store = (tptp_next_store+1) % TPTP_STORE_SIZE; + if (verbose2 == 2) { + tptp_print_tok(lval); + } + + return next; +} +//----------------------------------------------------------------------------- +//----%Start: INITIAL begin sentence, B before formula. No others. + +#line 710 "tptp5.lex.cpp" + +#define INITIAL 0 +#define B 1 +#define FF 2 +#define SQ1 3 +#define SQ2 4 +#define Q1 5 +#define Q2 6 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (void ); + +int yyget_debug (void ); + +void yyset_debug (int debug_flag ); + +YY_EXTRA_TYPE yyget_extra (void ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in (void ); + +void yyset_in (FILE * in_str ); + +FILE *yyget_out (void ); + +void yyset_out (FILE * out_str ); + +int yyget_leng (void ); + +char *yyget_text (void ); + +int yyget_lineno (void ); + +void yyset_lineno (int line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (void ); +#else +extern int yywrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 110 "tptp5.l" + + +#line 901 "tptp5.lex.cpp" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + /* Create the reject buffer large enough to save one state per allowed character. */ + if ( ! (yy_state_buf) ) + (yy_state_buf) = (yy_state_type *)yyalloc(YY_STATE_BUF_SIZE ); + if ( ! (yy_state_buf) ) + YY_FATAL_ERROR( "out of dynamic memory in yylex()" ); + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); + + (yy_state_ptr) = (yy_state_buf); + *(yy_state_ptr)++ = yy_current_state; + +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 152 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + *(yy_state_ptr)++ = yy_current_state; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 303 ); + +yy_find_action: + yy_current_state = *--(yy_state_ptr); + (yy_lp) = yy_accept[yy_current_state]; +goto find_rule; +find_rule: /* we branch to this label when backing up */ + for ( ; ; ) /* until we find what rule we matched */ + { + if ( (yy_lp) && (yy_lp) < yy_accept[yy_current_state + 1] ) + { + yy_act = yy_acclist[(yy_lp)]; + { + (yy_full_match) = yy_cp; + break; + } + } + --yy_cp; + yy_current_state = *--(yy_state_ptr); + (yy_lp) = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) + { + int yyl; + for ( yyl = (yy_prev_more_offset); yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + + yylineno++; +; + } + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +YY_RULE_SETUP +#line 112 "tptp5.l" +{ + tptp_prev_tok=AMPERSAND; + yylval.ival = tptp_update_lval(yytext); + return(AMPERSAND); +} + YY_BREAK +case 2: +YY_RULE_SETUP +#line 117 "tptp5.l" +{ + tptp_prev_tok=AT_SIGN; + yylval.ival = tptp_update_lval(yytext); + return(AT_SIGN); +} + YY_BREAK +case 3: +YY_RULE_SETUP +#line 122 "tptp5.l" +{ + tptp_prev_tok=AT_SIGN_MINUS; + yylval.ival = tptp_update_lval(yytext); + return(AT_SIGN_MINUS); +} + YY_BREAK +case 4: +YY_RULE_SETUP +#line 127 "tptp5.l" +{ + tptp_prev_tok=AT_SIGN_PLUS; + yylval.ival = tptp_update_lval(yytext); + return(AT_SIGN_PLUS); +} + YY_BREAK +case 5: +YY_RULE_SETUP +#line 132 "tptp5.l" +{ + tptp_prev_tok=CARET; + yylval.ival = tptp_update_lval(yytext); + return(CARET); +} + YY_BREAK +case 6: +YY_RULE_SETUP +#line 137 "tptp5.l" +{ + tptp_prev_tok=COLON; + yylval.ival = tptp_update_lval(yytext); + return(COLON); +} + YY_BREAK +case 7: +YY_RULE_SETUP +#line 142 "tptp5.l" +{ + tptp_prev_tok=COLON_EQUALS; + yylval.ival = tptp_update_lval(yytext); + return(COLON_EQUALS); +} + YY_BREAK +case 8: +YY_RULE_SETUP +#line 147 "tptp5.l" +{ + tptp_prev_tok=COMMA; + yylval.ival = tptp_update_lval(yytext); + return(COMMA); +} + YY_BREAK +case 9: +YY_RULE_SETUP +#line 152 "tptp5.l" +{ + tptp_prev_tok=EQUALS; + yylval.ival = tptp_update_lval(yytext); + return(EQUALS); +} + YY_BREAK +case 10: +YY_RULE_SETUP +#line 157 "tptp5.l" +{ + tptp_prev_tok=EQUALS_GREATER; + yylval.ival = tptp_update_lval(yytext); + return(EQUALS_GREATER); +} + YY_BREAK +case 11: +YY_RULE_SETUP +#line 162 "tptp5.l" +{ + tptp_prev_tok=EXCLAMATION; + yylval.ival = tptp_update_lval(yytext); + return(EXCLAMATION); +} + YY_BREAK +case 12: +YY_RULE_SETUP +#line 167 "tptp5.l" +{ + tptp_prev_tok=EXCLAMATION_EQUALS; + yylval.ival = tptp_update_lval(yytext); + return(EXCLAMATION_EQUALS); +} + YY_BREAK +case 13: +YY_RULE_SETUP +#line 172 "tptp5.l" +{ + tptp_prev_tok=EXCLAMATION_EXCLAMATION; + yylval.ival = tptp_update_lval(yytext); + return(EXCLAMATION_EXCLAMATION); +} + YY_BREAK +case 14: +YY_RULE_SETUP +#line 177 "tptp5.l" +{ + tptp_prev_tok=EXCLAMATION_GREATER; + yylval.ival = tptp_update_lval(yytext); + return(EXCLAMATION_GREATER); +} + YY_BREAK +case 15: +YY_RULE_SETUP +#line 182 "tptp5.l" +{ + tptp_prev_tok=LBRKT; + yylval.ival = tptp_update_lval(yytext); + return(LBRKT); +} + YY_BREAK +case 16: +YY_RULE_SETUP +#line 187 "tptp5.l" +{ + tptp_prev_tok=LESS_EQUALS; + yylval.ival = tptp_update_lval(yytext); + return(LESS_EQUALS); +} + YY_BREAK +case 17: +YY_RULE_SETUP +#line 192 "tptp5.l" +{ + tptp_prev_tok=LESS_EQUALS_GREATER; + yylval.ival = tptp_update_lval(yytext); + return(LESS_EQUALS_GREATER); +} + YY_BREAK +case 18: +YY_RULE_SETUP +#line 197 "tptp5.l" +{ + tptp_prev_tok=LESS_TILDE_GREATER; + yylval.ival = tptp_update_lval(yytext); + return(LESS_TILDE_GREATER); +} + YY_BREAK +case 19: +YY_RULE_SETUP +#line 202 "tptp5.l" +{ + tptp_prev_tok=LPAREN; + yylval.ival = tptp_update_lval(yytext); + return(LPAREN); +} + YY_BREAK +case 20: +YY_RULE_SETUP +#line 207 "tptp5.l" +{ + tptp_prev_tok=MINUS; + yylval.ival = tptp_update_lval(yytext); + return(MINUS); +} + YY_BREAK +case 21: +YY_RULE_SETUP +#line 212 "tptp5.l" +{ + tptp_prev_tok=MINUS_MINUS_GREATER; + yylval.ival = tptp_update_lval(yytext); + return(MINUS_MINUS_GREATER); +} + YY_BREAK +case 22: +YY_RULE_SETUP +#line 217 "tptp5.l" +{ + BEGIN INITIAL; + tptp_prev_tok=PERIOD; + yylval.ival = tptp_update_lval(yytext); + return(PERIOD); +} + YY_BREAK +case 23: +YY_RULE_SETUP +#line 223 "tptp5.l" +{ + tptp_prev_tok=QUESTION; + yylval.ival = tptp_update_lval(yytext); + return(QUESTION); +} + YY_BREAK +case 24: +YY_RULE_SETUP +#line 228 "tptp5.l" +{ + tptp_prev_tok=QUESTION_QUESTION; + yylval.ival = tptp_update_lval(yytext); + return(QUESTION_QUESTION); +} + YY_BREAK +case 25: +YY_RULE_SETUP +#line 233 "tptp5.l" +{ + tptp_prev_tok=QUESTION_STAR; + yylval.ival = tptp_update_lval(yytext); + return(QUESTION_STAR); +} + YY_BREAK +case 26: +YY_RULE_SETUP +#line 238 "tptp5.l" +{ + tptp_prev_tok=RBRKT; + yylval.ival = tptp_update_lval(yytext); + return(RBRKT); +} + YY_BREAK +case 27: +YY_RULE_SETUP +#line 243 "tptp5.l" +{ + tptp_prev_tok=RPAREN; + yylval.ival = tptp_update_lval(yytext); + return(RPAREN); +} + YY_BREAK +case 28: +YY_RULE_SETUP +#line 248 "tptp5.l" +{ + tptp_prev_tok=STAR; + yylval.ival = tptp_update_lval(yytext); + return(STAR); +} + YY_BREAK +case 29: +YY_RULE_SETUP +#line 253 "tptp5.l" +{ + tptp_prev_tok=TILDE; + yylval.ival = tptp_update_lval(yytext); + return(TILDE); +} + YY_BREAK +case 30: +YY_RULE_SETUP +#line 258 "tptp5.l" +{ + tptp_prev_tok=TILDE_AMPERSAND; + yylval.ival = tptp_update_lval(yytext); + return(TILDE_AMPERSAND); +} + YY_BREAK +case 31: +YY_RULE_SETUP +#line 263 "tptp5.l" +{ + tptp_prev_tok=TILDE_VLINE; + yylval.ival = tptp_update_lval(yytext); + return(TILDE_VLINE); +} + YY_BREAK +case 32: +YY_RULE_SETUP +#line 268 "tptp5.l" +{ + tptp_prev_tok=VLINE; + yylval.ival = tptp_update_lval(yytext); + return(VLINE); +} + YY_BREAK +case 33: +YY_RULE_SETUP +#line 273 "tptp5.l" +{ + tptp_prev_tok=_DLR_cnf; + yylval.ival = tptp_update_lval(yytext); + return(_DLR_cnf); +} + YY_BREAK +case 34: +YY_RULE_SETUP +#line 278 "tptp5.l" +{ + tptp_prev_tok=_DLR_fof; + yylval.ival = tptp_update_lval(yytext); + return(_DLR_fof); +} + YY_BREAK +case 35: +YY_RULE_SETUP +#line 283 "tptp5.l" +{ + tptp_prev_tok=_DLR_fot; + yylval.ival = tptp_update_lval(yytext); + return(_DLR_fot); +} + YY_BREAK +case 36: +YY_RULE_SETUP +#line 288 "tptp5.l" +{ + tptp_prev_tok=_DLR_itef; + yylval.ival = tptp_update_lval(yytext); + return(_DLR_itef); +} + YY_BREAK +case 37: +YY_RULE_SETUP +#line 293 "tptp5.l" +{ + tptp_prev_tok=_DLR_itetf; + yylval.ival = tptp_update_lval(yytext); + return(_DLR_itetf); +} + YY_BREAK +case 38: +YY_RULE_SETUP +#line 298 "tptp5.l" +{ + tptp_prev_tok=_DLR_itett; + yylval.ival = tptp_update_lval(yytext); + return(_DLR_itett); +} + YY_BREAK +case 39: +YY_RULE_SETUP +#line 303 "tptp5.l" +{ + tptp_prev_tok=_DLR_tff; + yylval.ival = tptp_update_lval(yytext); + return(_DLR_tff); +} + YY_BREAK +case 40: +YY_RULE_SETUP +#line 308 "tptp5.l" +{ + tptp_prev_tok=_DLR_thf; + yylval.ival = tptp_update_lval(yytext); + return(_DLR_thf); +} + YY_BREAK +case 41: +YY_RULE_SETUP +#line 313 "tptp5.l" +{ + BEGIN B; + tptp_prev_tok=_LIT_cnf; + yylval.ival = tptp_update_lval(yytext); + return(_LIT_cnf); +} + YY_BREAK +case 42: +YY_RULE_SETUP +#line 319 "tptp5.l" +{ + BEGIN B; + tptp_prev_tok=_LIT_fof; + yylval.ival = tptp_update_lval(yytext); + return(_LIT_fof); +} + YY_BREAK +case 43: +YY_RULE_SETUP +#line 325 "tptp5.l" +{ + BEGIN B; + tptp_prev_tok=_LIT_include; + yylval.ival = tptp_update_lval(yytext); + return(_LIT_include); +} + YY_BREAK +case 44: +YY_RULE_SETUP +#line 331 "tptp5.l" +{ + BEGIN B; + tptp_prev_tok=_LIT_tff; + yylval.ival = tptp_update_lval(yytext); + return(_LIT_tff); +} + YY_BREAK +case 45: +YY_RULE_SETUP +#line 337 "tptp5.l" +{ + BEGIN B; + tptp_prev_tok=_LIT_thf; + yylval.ival = tptp_update_lval(yytext); + return(_LIT_thf); +} + YY_BREAK +case 46: +YY_RULE_SETUP +#line 344 "tptp5.l" +{ + tptp_prev_tok=single_quoted; + yylval.ival = tptp_update_lval(yytext); + return(single_quoted); +} + YY_BREAK +case 47: +YY_RULE_SETUP +#line 349 "tptp5.l" +{ + tptp_prev_tok=distinct_object; + yylval.ival = tptp_update_lval(yytext); + return(distinct_object); +} + YY_BREAK +case 48: +YY_RULE_SETUP +#line 354 "tptp5.l" +{ + tptp_prev_tok=dollar_word; + yylval.ival = tptp_update_lval(yytext); + return(dollar_word); +} + YY_BREAK +case 49: +YY_RULE_SETUP +#line 359 "tptp5.l" +{ + tptp_prev_tok=dollar_dollar_word; + yylval.ival = tptp_update_lval(yytext); + return(dollar_dollar_word); +} + YY_BREAK +case 50: +YY_RULE_SETUP +#line 364 "tptp5.l" +{ + tptp_prev_tok=upper_word; + yylval.ival = tptp_update_lval(yytext); + return(upper_word); +} + YY_BREAK +case 51: +YY_RULE_SETUP +#line 369 "tptp5.l" +{ + tptp_prev_tok=lower_word; + yylval.ival = tptp_update_lval(yytext); + return(lower_word); +} + YY_BREAK +case 52: +YY_RULE_SETUP +#line 374 "tptp5.l" +{ + tptp_prev_tok=vline; + yylval.ival = tptp_update_lval(yytext); + return(vline); +} + YY_BREAK +case 53: +YY_RULE_SETUP +#line 379 "tptp5.l" +{ + tptp_prev_tok=star; + yylval.ival = tptp_update_lval(yytext); + return(star); +} + YY_BREAK +case 54: +YY_RULE_SETUP +#line 384 "tptp5.l" +{ + tptp_prev_tok=plus; + yylval.ival = tptp_update_lval(yytext); + return(plus); +} + YY_BREAK +case 55: +YY_RULE_SETUP +#line 389 "tptp5.l" +{ + tptp_prev_tok=arrow; + yylval.ival = tptp_update_lval(yytext); + return(arrow); +} + YY_BREAK +case 56: +YY_RULE_SETUP +#line 394 "tptp5.l" +{ + tptp_prev_tok=less_sign; + yylval.ival = tptp_update_lval(yytext); + return(less_sign); +} + YY_BREAK +case 57: +YY_RULE_SETUP +#line 399 "tptp5.l" +{ + tptp_prev_tok=real; + yylval.ival = tptp_update_lval(yytext); + return(real); +} + YY_BREAK +case 58: +YY_RULE_SETUP +#line 404 "tptp5.l" +{ + tptp_prev_tok=signed_real; + yylval.ival = tptp_update_lval(yytext); + return(signed_real); +} + YY_BREAK +case 59: +YY_RULE_SETUP +#line 409 "tptp5.l" +{ + tptp_prev_tok=unsigned_real; + yylval.ival = tptp_update_lval(yytext); + return(unsigned_real); +} + YY_BREAK +case 60: +YY_RULE_SETUP +#line 414 "tptp5.l" +{ + tptp_prev_tok=rational; + yylval.ival = tptp_update_lval(yytext); + return(rational); +} + YY_BREAK +case 61: +YY_RULE_SETUP +#line 419 "tptp5.l" +{ + tptp_prev_tok=signed_rational; + yylval.ival = tptp_update_lval(yytext); + return(signed_rational); +} + YY_BREAK +case 62: +YY_RULE_SETUP +#line 424 "tptp5.l" +{ + tptp_prev_tok=unsigned_rational; + yylval.ival = tptp_update_lval(yytext); + return(unsigned_rational); +} + YY_BREAK +case 63: +YY_RULE_SETUP +#line 429 "tptp5.l" +{ + tptp_prev_tok=integer; + yylval.ival = tptp_update_lval(yytext); + return(integer); +} + YY_BREAK +case 64: +YY_RULE_SETUP +#line 434 "tptp5.l" +{ + tptp_prev_tok=signed_integer; + yylval.ival = tptp_update_lval(yytext); + return(signed_integer); +} + YY_BREAK +case 65: +YY_RULE_SETUP +#line 439 "tptp5.l" +{ + tptp_prev_tok=unsigned_integer; + yylval.ival = tptp_update_lval(yytext); + return(unsigned_integer); +} + YY_BREAK +case 66: +YY_RULE_SETUP +#line 444 "tptp5.l" +{ + tptp_prev_tok=decimal; + yylval.ival = tptp_update_lval(yytext); + return(decimal); +} + YY_BREAK +case 67: +YY_RULE_SETUP +#line 449 "tptp5.l" +{ + tptp_prev_tok=positive_decimal; + yylval.ival = tptp_update_lval(yytext); + return(positive_decimal); +} + YY_BREAK +case 68: +YY_RULE_SETUP +#line 454 "tptp5.l" +{ + tptp_prev_tok=decimal_exponent; + yylval.ival = tptp_update_lval(yytext); + return(decimal_exponent); +} + YY_BREAK +case 69: +YY_RULE_SETUP +#line 459 "tptp5.l" +{ + tptp_prev_tok=decimal_fraction; + yylval.ival = tptp_update_lval(yytext); + return(decimal_fraction); +} + YY_BREAK +case 70: +YY_RULE_SETUP +#line 464 "tptp5.l" +{ + tptp_prev_tok=dot_decimal; + yylval.ival = tptp_update_lval(yytext); + return(dot_decimal); +} + YY_BREAK +case 71: +/* rule 71 can match eol */ +YY_RULE_SETUP +#line 469 "tptp5.l" +tptp_update_lval(yytext); + YY_BREAK +case 72: +/* rule 72 can match eol */ +YY_RULE_SETUP +#line 470 "tptp5.l" +; + YY_BREAK +case 73: +/* rule 73 can match eol */ +YY_RULE_SETUP +#line 471 "tptp5.l" +; + YY_BREAK +case 74: +YY_RULE_SETUP +#line 472 "tptp5.l" +return(unrecognized); + YY_BREAK +case 75: +YY_RULE_SETUP +#line 473 "tptp5.l" +ECHO; + YY_BREAK +#line 1667 "tptp5.lex.cpp" + case YY_STATE_EOF(INITIAL): + case YY_STATE_EOF(B): + case YY_STATE_EOF(FF): + case YY_STATE_EOF(SQ1): + case YY_STATE_EOF(SQ2): + case YY_STATE_EOF(Q1): + case YY_STATE_EOF(Q2): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (int)num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + (yy_state_ptr) = (yy_state_buf); + *(yy_state_ptr)++ = yy_current_state; + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 152 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + *(yy_state_ptr)++ = yy_current_state; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + + register YY_CHAR yy_c = 1; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 152 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 151); + if ( ! yy_is_jam ) + *(yy_state_ptr)++ = yy_current_state; + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up yytext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = (int)YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + if ( c == '\n' ){ + --yylineno; + } + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + size_t offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + if ( c == '\n' ) + + yylineno++; +; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ); + + yyfree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + +#ifdef _WINDOWS + b->yy_is_interactive = file ? (isatty( _fileno(file) ) > 0) : 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + size_t num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = (int)b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) +{ + + return yy_scan_bytes(yystr,(int)strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) yyalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param line_number + * + */ +void yyset_lineno (int line_number ) +{ + + yylineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str ) +{ + yyin = in_str ; +} + +void yyset_out (FILE * out_str ) +{ + yyout = out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int bdebug ) +{ + yy_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + /* We do not touch yylineno unless the option is enabled. */ + yylineno = 1; + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + + (yy_state_buf) = 0; + (yy_state_ptr) = 0; + (yy_full_match) = 0; + (yy_lp) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + yyfree ( (yy_state_buf) ); + (yy_state_buf) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 473 "tptp5.l" + + diff --git a/examples/tptp/tptp5.tab.c b/examples/tptp/tptp5.tab.c new file mode 100644 index 000000000..7fcf7915b --- /dev/null +++ b/examples/tptp/tptp5.tab.c @@ -0,0 +1,4475 @@ +/* A Bison parser, made by GNU Bison 2.4.2. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.4.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Copy the first part of user declarations. */ + +/* Line 189 of yacc.c */ +#line 2 "tptp5.y" + +//----------------------------------------------------------------------------- +#include +#include +#include +//----------------------------------------------------------------------------- +//----Compile with -DP_VERBOSE=1 for verbose output. +#ifndef P_VERBOSE +# define P_VERBOSE 0 +#endif +int verbose = P_VERBOSE; + +//----Compile with -DP_USERPROC=1 to #include p_user_proc.c. p_user_proc.c +//----should #define P_ACT, P_BUILD, P_TOKEN, P_PRINT to different procedures +//----from those below, and supply code. +#ifdef P_USERPROC + +#else +# define P_ACT(ss) if(verbose)printf("%7d %s\n",yylineno,ss); +# define P_BUILD(sym,A,B,C,D,E,F,G,H,I,J) pBuildTree(sym,A,B,C,D,E,F,G,H,I,J) +# define P_TOKEN(tok,symbolIndex) pToken(tok,symbolIndex) +# define P_PRINT(ss) if(verbose){printf("\n\n");pPrintTree(ss,0);} +#endif + +extern int yylineno; +extern int yychar; +extern char yytext[]; + +extern int tptp_store_size; +extern char* tptp_lval[]; + +#define MAX_CHILDREN 12 +typedef struct pTreeNode * pTree; +struct pTreeNode { + char* symbol; + int symbolIndex; + pTree children[MAX_CHILDREN+1]; +}; +//----------------------------------------------------------------------------- +int yyerror( char *s ) { + + fprintf( stderr, "%s in line %d at item \"%s\".\n", s, yylineno, yytext); + return 0; +} +//----------------------------------------------------------------------------- +pTree pBuildTree(char* symbol,pTree A,pTree B,pTree C,pTree D,pTree E,pTree F, +pTree G, pTree H, pTree I, pTree J) { + + pTree ss = (pTree)calloc(1,sizeof(struct pTreeNode)); + + ss->symbol = symbol; + ss->symbolIndex = -1; + ss->children[0] = A; + ss->children[1] = B; + ss->children[2] = C; + ss->children[3] = D; + ss->children[4] = E; + ss->children[5] = F; + ss->children[6] = G; + ss->children[7] = H; + ss->children[8] = I; + ss->children[9] = J; + ss->children[10] = NULL; + + return ss; +} +//----------------------------------------------------------------------------- +pTree pToken(char* token, int symbolIndex) { + + //char pTokenBuf[8240]; + pTree ss; + char* symbol = tptp_lval[symbolIndex]; + char* safeSym = 0; + + //strncpy(pTokenBuf, token, 39); + //strncat(pTokenBuf, symbol, 8193); + //safeSym = strdup(pTokenBuf); + ss = pBuildTree(safeSym,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); + ss->symbolIndex = symbolIndex; + + return ss; +} +//----------------------------------------------------------------------------- +void pPrintComments(int start, int depth) { + + int d, j; + char c1[4] = "%", c2[4] = "/*"; + + j = start; + while (tptp_lval[j] != NULL && (tptp_lval[j][0]==c1[0] || +(tptp_lval[j][0]==c2[0] && tptp_lval[j][1]==c2[1]))) { + for (d=0; d= 0) { + pPrintComments(pPrintIdx, 0); + pPrintIdx = -1; + } + if (ss == NULL) { + return; + } + for (d = 0; d < depth-1; d++) { + printf("| "); + } + printf("%1d ",depth % 10); + if (ss->children[0] == NULL) { + printf("%s\n", ss->symbol); + } else { + printf("<%s>\n", ss->symbol); + } + if (strcmp(ss->symbol, "PERIOD .") == 0) { + pPrintIdx = (ss->symbolIndex+1) % tptp_store_size; + } + if (ss->symbolIndex >= 0) { + pPrintComments((ss->symbolIndex+1) % tptp_store_size, depth); + } + i = 0; + while(ss->children[i] != NULL) { + pPrintTree(ss->children[i],depth+1); + i++; + } + return; +} +//----------------------------------------------------------------------------- +int yywrap(void) { + + P_PRINT(NULL); + return 1; +} +//----------------------------------------------------------------------------- + + +/* Line 189 of yacc.c */ +#line 219 "tptp5.tab.c" + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + AMPERSAND = 258, + AT_SIGN = 259, + AT_SIGN_MINUS = 260, + AT_SIGN_PLUS = 261, + CARET = 262, + COLON = 263, + COLON_EQUALS = 264, + COMMA = 265, + EQUALS = 266, + EQUALS_GREATER = 267, + EXCLAMATION = 268, + EXCLAMATION_EQUALS = 269, + EXCLAMATION_EXCLAMATION = 270, + EXCLAMATION_GREATER = 271, + LBRKT = 272, + LESS_EQUALS = 273, + LESS_EQUALS_GREATER = 274, + LESS_TILDE_GREATER = 275, + LPAREN = 276, + MINUS = 277, + MINUS_MINUS_GREATER = 278, + PERIOD = 279, + QUESTION = 280, + QUESTION_QUESTION = 281, + QUESTION_STAR = 282, + RBRKT = 283, + RPAREN = 284, + STAR = 285, + TILDE = 286, + TILDE_AMPERSAND = 287, + TILDE_VLINE = 288, + VLINE = 289, + _DLR_cnf = 290, + _DLR_fof = 291, + _DLR_fot = 292, + _DLR_itef = 293, + _DLR_itetf = 294, + _DLR_itett = 295, + _DLR_tff = 296, + _DLR_thf = 297, + _LIT_cnf = 298, + _LIT_fof = 299, + _LIT_include = 300, + _LIT_tff = 301, + _LIT_thf = 302, + arrow = 303, + comment = 304, + comment_line = 305, + decimal = 306, + decimal_exponent = 307, + decimal_fraction = 308, + distinct_object = 309, + dollar_dollar_word = 310, + dollar_word = 311, + dot_decimal = 312, + integer = 313, + less_sign = 314, + lower_word = 315, + plus = 316, + positive_decimal = 317, + rational = 318, + real = 319, + signed_integer = 320, + signed_rational = 321, + signed_real = 322, + single_quoted = 323, + star = 324, + unrecognized = 325, + unsigned_integer = 326, + unsigned_rational = 327, + unsigned_real = 328, + upper_word = 329, + vline = 330 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 214 of yacc.c */ +#line 148 "tptp5.y" +int ival; double dval; char* sval; TreeNode* pval; + + +/* Line 214 of yacc.c */ +#line 334 "tptp5.tab.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 264 of yacc.c */ +#line 346 "tptp5.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 3 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 1612 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 76 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 141 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 281 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 523 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 330 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint16 yyprhs[] = +{ + 0, 0, 3, 5, 8, 10, 12, 14, 16, 18, + 20, 31, 42, 53, 64, 68, 70, 72, 74, 76, + 78, 80, 82, 84, 86, 88, 90, 94, 96, 98, + 100, 104, 108, 112, 116, 120, 124, 126, 128, 130, + 132, 134, 136, 140, 147, 149, 153, 155, 157, 161, + 166, 170, 172, 174, 178, 182, 184, 186, 188, 190, + 192, 196, 200, 204, 208, 212, 216, 218, 220, 223, + 227, 229, 233, 240, 242, 246, 250, 254, 263, 267, + 271, 273, 275, 277, 279, 281, 283, 285, 289, 291, + 293, 297, 301, 305, 309, 311, 313, 315, 317, 319, + 321, 325, 332, 334, 338, 340, 342, 346, 349, 351, + 355, 359, 361, 363, 365, 367, 369, 373, 375, 377, + 381, 385, 389, 393, 397, 404, 406, 410, 414, 419, + 423, 432, 436, 440, 442, 444, 446, 448, 450, 452, + 456, 458, 460, 464, 468, 472, 476, 478, 480, 482, + 484, 486, 488, 492, 499, 501, 505, 508, 510, 517, + 519, 523, 527, 532, 536, 545, 549, 553, 557, 559, + 561, 565, 567, 570, 572, 574, 576, 578, 582, 584, + 586, 588, 590, 592, 594, 596, 598, 600, 602, 604, + 606, 609, 611, 613, 615, 617, 619, 621, 623, 625, + 627, 629, 631, 633, 635, 637, 639, 641, 643, 645, + 647, 649, 653, 655, 657, 659, 661, 663, 665, 667, + 669, 671, 673, 675, 680, 682, 684, 686, 688, 690, + 692, 694, 696, 701, 703, 705, 707, 712, 714, 716, + 718, 720, 724, 733, 742, 744, 747, 749, 751, 758, + 763, 765, 767, 771, 773, 777, 779, 781, 786, 788, + 790, 792, 794, 799, 804, 809, 814, 819, 822, 826, + 828, 832, 834, 836, 838, 840, 842, 844, 846, 848, + 850, 852 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int16 yyrhs[] = +{ + 77, 0, -1, 216, -1, 77, 78, -1, 79, -1, + 202, -1, 80, -1, 81, -1, 82, -1, 83, -1, + 47, 21, 210, 10, 85, 10, 86, 84, 29, 24, + -1, 46, 21, 210, 10, 85, 10, 117, 84, 29, + 24, -1, 44, 21, 210, 10, 85, 10, 142, 84, + 29, 24, -1, 43, 21, 210, 10, 85, 10, 158, + 84, 29, 24, -1, 10, 199, 200, -1, 216, -1, + 60, -1, 87, -1, 116, -1, 88, -1, 94, -1, + 100, -1, 102, -1, 89, -1, 90, -1, 105, -1, + 94, 164, 94, -1, 91, -1, 92, -1, 93, -1, + 94, 34, 94, -1, 91, 34, 94, -1, 94, 3, + 94, -1, 92, 3, 94, -1, 94, 4, 94, -1, + 93, 4, 94, -1, 95, -1, 99, -1, 109, -1, + 110, -1, 112, -1, 115, -1, 21, 87, 29, -1, + 163, 17, 96, 28, 8, 94, -1, 97, -1, 97, + 10, 96, -1, 98, -1, 196, -1, 196, 8, 103, + -1, 165, 21, 87, 29, -1, 101, 8, 103, -1, + 109, -1, 110, -1, 21, 87, 29, -1, 185, 166, + 185, -1, 87, -1, 94, -1, 106, -1, 107, -1, + 108, -1, 104, 48, 104, -1, 104, 48, 106, -1, + 104, 30, 104, -1, 107, 30, 104, -1, 104, 61, + 104, -1, 108, 61, 104, -1, 182, -1, 161, -1, + 17, 28, -1, 17, 111, 28, -1, 94, -1, 94, + 10, 111, -1, 9, 17, 113, 28, 8, 94, -1, + 114, -1, 114, 10, 113, -1, 97, 9, 87, -1, + 21, 114, 29, -1, 38, 21, 87, 10, 87, 10, + 87, 29, -1, 110, 171, 110, -1, 21, 116, 29, + -1, 118, -1, 130, -1, 141, -1, 119, -1, 124, + -1, 120, -1, 121, -1, 124, 168, 124, -1, 122, + -1, 123, -1, 124, 34, 124, -1, 122, 34, 124, + -1, 124, 3, 124, -1, 123, 3, 124, -1, 125, + -1, 129, -1, 173, -1, 137, -1, 196, -1, 140, + -1, 21, 118, 29, -1, 167, 17, 126, 28, 8, + 124, -1, 127, -1, 127, 10, 126, -1, 128, -1, + 196, -1, 196, 8, 134, -1, 170, 124, -1, 162, + -1, 131, 8, 132, -1, 21, 130, 29, -1, 186, + -1, 195, -1, 134, -1, 135, -1, 134, -1, 21, + 136, 29, -1, 211, -1, 172, -1, 133, 48, 134, + -1, 21, 135, 29, -1, 134, 30, 134, -1, 136, + 30, 134, -1, 21, 136, 29, -1, 9, 17, 138, + 28, 8, 124, -1, 139, -1, 139, 10, 138, -1, + 196, 9, 118, -1, 196, 8, 22, 182, -1, 21, + 139, 29, -1, 38, 21, 118, 10, 118, 10, 118, + 29, -1, 118, 171, 118, -1, 21, 141, 29, -1, + 143, -1, 157, -1, 144, -1, 149, -1, 145, -1, + 146, -1, 149, 168, 149, -1, 147, -1, 148, -1, + 149, 34, 149, -1, 147, 34, 149, -1, 149, 3, + 149, -1, 148, 3, 149, -1, 150, -1, 152, -1, + 173, -1, 153, -1, 196, -1, 156, -1, 21, 143, + 29, -1, 167, 17, 151, 28, 8, 149, -1, 196, + -1, 196, 10, 151, -1, 170, 149, -1, 162, -1, + 9, 17, 154, 28, 8, 149, -1, 155, -1, 155, + 10, 154, -1, 196, 9, 143, -1, 196, 8, 22, + 182, -1, 21, 155, 29, -1, 38, 21, 143, 10, + 143, 10, 143, 29, -1, 143, 171, 143, -1, 21, + 157, 29, -1, 21, 159, 29, -1, 159, -1, 160, + -1, 159, 34, 160, -1, 173, -1, 31, 173, -1, + 162, -1, 164, -1, 169, -1, 165, -1, 182, 180, + 182, -1, 167, -1, 7, -1, 16, -1, 27, -1, + 6, -1, 5, -1, 179, -1, 180, -1, 168, -1, + 170, -1, 15, -1, 26, -1, 59, 59, -1, 13, + -1, 25, -1, 19, -1, 12, -1, 18, -1, 20, + -1, 33, -1, 32, -1, 34, -1, 3, -1, 31, + -1, 23, -1, 212, -1, 174, -1, 175, -1, 181, + -1, 184, -1, 176, -1, 177, -1, 190, -1, 182, + 178, 182, -1, 179, -1, 11, -1, 14, -1, 193, + -1, 183, -1, 196, -1, 198, -1, 184, -1, 187, + -1, 193, -1, 185, -1, 186, 21, 197, 29, -1, + 186, -1, 211, -1, 188, -1, 189, -1, 214, -1, + 54, -1, 190, -1, 191, -1, 192, 21, 197, 29, + -1, 192, -1, 212, -1, 194, -1, 195, 21, 197, + 29, -1, 195, -1, 213, -1, 74, -1, 182, -1, + 182, 10, 197, -1, 40, 21, 118, 10, 182, 10, + 182, 29, -1, 39, 21, 143, 10, 182, 10, 182, + 29, -1, 205, -1, 10, 201, -1, 216, -1, 208, + -1, 45, 21, 215, 203, 29, 24, -1, 10, 17, + 204, 28, -1, 216, -1, 210, -1, 210, 10, 204, + -1, 206, -1, 206, 8, 205, -1, 208, -1, 211, + -1, 211, 21, 209, 29, -1, 196, -1, 214, -1, + 54, -1, 207, -1, 42, 21, 86, 29, -1, 41, + 21, 117, 29, -1, 36, 21, 142, 29, -1, 35, + 21, 158, 29, -1, 37, 21, 182, 29, -1, 17, + 28, -1, 17, 209, 28, -1, 205, -1, 205, 10, + 209, -1, 211, -1, 58, -1, 60, -1, 68, -1, + 56, -1, 55, -1, 58, -1, 63, -1, 64, -1, + 68, -1, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 225, 225, 226, 229, 230, 233, 234, 235, 236, + 239, 242, 245, 248, 251, 252, 255, 258, 259, 262, + 263, 264, 265, 268, 269, 270, 273, 276, 277, 278, + 281, 282, 285, 286, 289, 290, 293, 294, 295, 296, + 297, 298, 299, 302, 305, 306, 309, 310, 313, 316, + 319, 322, 323, 324, 327, 330, 333, 336, 337, 338, + 341, 342, 345, 346, 349, 350, 353, 354, 357, 358, + 361, 362, 365, 368, 369, 372, 373, 376, 379, 380, + 383, 384, 385, 388, 389, 392, 393, 396, 399, 400, + 403, 404, 407, 408, 411, 412, 413, 414, 415, 416, + 417, 420, 423, 424, 427, 428, 431, 434, 435, 438, + 439, 442, 443, 446, 447, 450, 451, 454, 455, 458, + 459, 462, 463, 464, 467, 470, 471, 474, 475, 476, + 479, 482, 483, 486, 487, 490, 491, 494, 495, 498, + 501, 502, 505, 506, 509, 510, 513, 514, 515, 516, + 517, 518, 519, 522, 525, 526, 529, 530, 533, 536, + 537, 540, 541, 542, 545, 548, 549, 552, 553, 556, + 557, 560, 561, 562, 565, 566, 567, 570, 573, 574, + 575, 576, 577, 578, 581, 582, 583, 586, 587, 588, + 591, 594, 595, 598, 599, 600, 601, 602, 603, 606, + 607, 610, 613, 616, 619, 620, 621, 624, 627, 628, + 631, 634, 637, 640, 643, 646, 649, 650, 651, 654, + 655, 656, 659, 660, 663, 666, 669, 670, 673, 674, + 677, 680, 681, 684, 687, 690, 691, 694, 697, 700, + 703, 704, 707, 708, 711, 714, 715, 718, 721, 724, + 725, 728, 729, 732, 733, 734, 737, 738, 739, 740, + 741, 742, 745, 746, 747, 748, 749, 752, 753, 756, + 757, 760, 761, 764, 765, 768, 771, 774, 775, 776, + 779, 782 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "AMPERSAND", "AT_SIGN", "AT_SIGN_MINUS", + "AT_SIGN_PLUS", "CARET", "COLON", "COLON_EQUALS", "COMMA", "EQUALS", + "EQUALS_GREATER", "EXCLAMATION", "EXCLAMATION_EQUALS", + "EXCLAMATION_EXCLAMATION", "EXCLAMATION_GREATER", "LBRKT", "LESS_EQUALS", + "LESS_EQUALS_GREATER", "LESS_TILDE_GREATER", "LPAREN", "MINUS", + "MINUS_MINUS_GREATER", "PERIOD", "QUESTION", "QUESTION_QUESTION", + "QUESTION_STAR", "RBRKT", "RPAREN", "STAR", "TILDE", "TILDE_AMPERSAND", + "TILDE_VLINE", "VLINE", "_DLR_cnf", "_DLR_fof", "_DLR_fot", "_DLR_itef", + "_DLR_itetf", "_DLR_itett", "_DLR_tff", "_DLR_thf", "_LIT_cnf", + "_LIT_fof", "_LIT_include", "_LIT_tff", "_LIT_thf", "arrow", "comment", + "comment_line", "decimal", "decimal_exponent", "decimal_fraction", + "distinct_object", "dollar_dollar_word", "dollar_word", "dot_decimal", + "integer", "less_sign", "lower_word", "plus", "positive_decimal", + "rational", "real", "signed_integer", "signed_rational", "signed_real", + "single_quoted", "star", "unrecognized", "unsigned_integer", + "unsigned_rational", "unsigned_real", "upper_word", "vline", "$accept", + "TPTP_file", "TPTP_input", "annotated_formula", "thf_annotated", + "tff_annotated", "fof_annotated", "cnf_annotated", "annotations", + "formula_role", "thf_formula", "thf_logic_formula", "thf_binary_formula", + "thf_binary_pair", "thf_binary_tuple", "thf_or_formula", + "thf_and_formula", "thf_apply_formula", "thf_unitary_formula", + "thf_quantified_formula", "thf_variable_list", "thf_variable", + "thf_typed_variable", "thf_unary_formula", "thf_type_formula", + "thf_typeable_formula", "thf_subtype", "thf_top_level_type", + "thf_unitary_type", "thf_binary_type", "thf_mapping_type", + "thf_xprod_type", "thf_union_type", "thf_atom", "thf_tuple", + "thf_tuple_list", "thf_let", "thf_let_list", "thf_defined_var", + "thf_conditional", "thf_sequent", "tff_formula", "tff_logic_formula", + "tff_binary_formula", "tff_binary_nonassoc", "tff_binary_assoc", + "tff_or_formula", "tff_and_formula", "tff_unitary_formula", + "tff_quantified_formula", "tff_variable_list", "tff_variable", + "tff_typed_variable", "tff_unary_formula", "tff_typed_atom", + "tff_untyped_atom", "tff_top_level_type", "tff_unitary_type", + "tff_atomic_type", "tff_mapping_type", "tff_xprod_type", "tff_let", + "tff_let_list", "tff_defined_var", "tff_conditional", "tff_sequent", + "fof_formula", "fof_logic_formula", "fof_binary_formula", + "fof_binary_nonassoc", "fof_binary_assoc", "fof_or_formula", + "fof_and_formula", "fof_unitary_formula", "fof_quantified_formula", + "fof_variable_list", "fof_unary_formula", "fof_let", "fof_let_list", + "fof_defined_var", "fof_conditional", "fof_sequent", "cnf_formula", + "disjunction", "literal", "thf_conn_term", "fol_infix_unary", + "thf_quantifier", "thf_pair_connective", "thf_unary_connective", + "subtype_sign", "fol_quantifier", "binary_connective", + "assoc_connective", "unary_connective", "gentzen_arrow", "defined_type", + "atomic_formula", "plain_atomic_formula", "defined_atomic_formula", + "defined_plain_formula", "defined_infix_formula", "defined_infix_pred", + "infix_equality", "infix_inequality", "system_atomic_formula", "term", + "function_term", "plain_term", "constant", "functor", "defined_term", + "defined_atom", "defined_atomic_term", "defined_plain_term", + "defined_constant", "defined_functor", "system_term", "system_constant", + "system_functor", "variable", "arguments", "conditional_term", "source", + "optional_info", "useful_info", "include", "formula_selection", + "name_list", "general_term", "general_data", "formula_data", + "general_list", "general_terms", "name", "atomic_word", + "atomic_defined_word", "atomic_system_word", "number", "file_name", + "null", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 76, 77, 77, 78, 78, 79, 79, 79, 79, + 80, 81, 82, 83, 84, 84, 85, 86, 86, 87, + 87, 87, 87, 88, 88, 88, 89, 90, 90, 90, + 91, 91, 92, 92, 93, 93, 94, 94, 94, 94, + 94, 94, 94, 95, 96, 96, 97, 97, 98, 99, + 100, 101, 101, 101, 102, 103, 104, 105, 105, 105, + 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, + 111, 111, 112, 113, 113, 114, 114, 115, 116, 116, + 117, 117, 117, 118, 118, 119, 119, 120, 121, 121, + 122, 122, 123, 123, 124, 124, 124, 124, 124, 124, + 124, 125, 126, 126, 127, 127, 128, 129, 129, 130, + 130, 131, 131, 132, 132, 133, 133, 134, 134, 135, + 135, 136, 136, 136, 137, 138, 138, 139, 139, 139, + 140, 141, 141, 142, 142, 143, 143, 144, 144, 145, + 146, 146, 147, 147, 148, 148, 149, 149, 149, 149, + 149, 149, 149, 150, 151, 151, 152, 152, 153, 154, + 154, 155, 155, 155, 156, 157, 157, 158, 158, 159, + 159, 160, 160, 160, 161, 161, 161, 162, 163, 163, + 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, + 166, 167, 167, 168, 168, 168, 168, 168, 168, 169, + 169, 170, 171, 172, 173, 173, 173, 174, 175, 175, + 176, 177, 178, 179, 180, 181, 182, 182, 182, 183, + 183, 183, 184, 184, 185, 186, 187, 187, 188, 188, + 189, 190, 190, 191, 192, 193, 193, 194, 195, 196, + 197, 197, 198, 198, 199, 200, 200, 201, 202, 203, + 203, 204, 204, 205, 205, 205, 206, 206, 206, 206, + 206, 206, 207, 207, 207, 207, 207, 208, 208, 209, + 209, 210, 210, 211, 211, 212, 213, 214, 214, 214, + 215, 216 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 2, 1, 1, 1, 1, 1, 1, + 10, 10, 10, 10, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, + 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, + 1, 1, 3, 6, 1, 3, 1, 1, 3, 4, + 3, 1, 1, 3, 3, 1, 1, 1, 1, 1, + 3, 3, 3, 3, 3, 3, 1, 1, 2, 3, + 1, 3, 6, 1, 3, 3, 3, 8, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, + 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, + 3, 6, 1, 3, 1, 1, 3, 2, 1, 3, + 3, 1, 1, 1, 1, 1, 3, 1, 1, 3, + 3, 3, 3, 3, 6, 1, 3, 3, 4, 3, + 8, 3, 3, 1, 1, 1, 1, 1, 1, 3, + 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, + 1, 1, 3, 6, 1, 3, 2, 1, 6, 1, + 3, 3, 4, 3, 8, 3, 3, 3, 1, 1, + 3, 1, 2, 1, 1, 1, 1, 3, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, + 1, 1, 4, 1, 1, 1, 4, 1, 1, 1, + 1, 3, 8, 8, 1, 2, 1, 1, 6, 4, + 1, 1, 3, 1, 3, 1, 1, 4, 1, 1, + 1, 1, 4, 4, 4, 4, 4, 2, 3, 1, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint16 yydefact[] = +{ + 281, 0, 2, 1, 0, 0, 0, 0, 0, 3, + 4, 6, 7, 8, 9, 5, 0, 0, 0, 0, + 0, 272, 273, 274, 0, 271, 0, 280, 281, 0, + 0, 0, 0, 0, 0, 250, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 251, 248, + 0, 0, 0, 0, 0, 0, 229, 276, 275, 277, + 278, 279, 239, 281, 168, 169, 173, 171, 204, 205, + 208, 209, 206, 0, 216, 207, 222, 224, 220, 226, + 227, 210, 231, 233, 215, 235, 237, 217, 218, 225, + 234, 238, 228, 0, 191, 0, 192, 201, 0, 281, + 133, 135, 137, 138, 140, 141, 136, 146, 147, 149, + 151, 134, 157, 0, 0, 148, 150, 249, 0, 0, + 0, 0, 281, 80, 83, 85, 86, 88, 89, 84, + 94, 95, 81, 0, 97, 99, 82, 108, 0, 0, + 96, 224, 237, 98, 200, 183, 182, 179, 0, 213, + 194, 214, 188, 180, 0, 195, 193, 196, 0, 189, + 181, 198, 197, 199, 0, 281, 17, 19, 23, 24, + 27, 28, 29, 20, 36, 37, 21, 0, 22, 0, + 25, 57, 58, 59, 38, 39, 40, 41, 18, 67, + 0, 174, 176, 178, 186, 175, 187, 184, 185, 66, + 219, 222, 230, 221, 0, 172, 0, 0, 0, 0, + 0, 15, 0, 0, 212, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 202, 0, 0, 0, 0, 0, + 0, 0, 0, 156, 252, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 107, 0, 0, 68, 70, 38, 39, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 167, 0, 0, 0, 0, 0, 0, 0, 0, 260, + 258, 281, 244, 253, 261, 255, 256, 259, 0, 170, + 211, 177, 240, 0, 0, 0, 0, 0, 159, 0, + 152, 166, 0, 0, 165, 143, 145, 144, 142, 139, + 0, 154, 0, 0, 0, 125, 0, 100, 110, 132, + 0, 0, 131, 91, 93, 92, 90, 87, 0, 109, + 0, 113, 114, 118, 117, 203, 0, 102, 104, 105, + 0, 0, 0, 46, 0, 73, 47, 0, 0, 39, + 0, 69, 42, 79, 0, 0, 31, 33, 35, 32, + 34, 30, 26, 55, 50, 56, 62, 60, 61, 64, + 63, 65, 78, 0, 44, 0, 190, 54, 224, 0, + 0, 267, 269, 0, 0, 0, 0, 0, 0, 0, + 14, 246, 0, 0, 13, 0, 223, 232, 236, 0, + 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 11, 0, 115, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 42, 71, + 0, 10, 0, 0, 49, 0, 0, 0, 268, 0, + 0, 0, 0, 0, 245, 247, 254, 0, 241, 163, + 0, 160, 0, 161, 0, 0, 155, 129, 0, 126, + 0, 127, 0, 0, 0, 120, 116, 0, 119, 0, + 103, 106, 76, 75, 0, 74, 48, 0, 0, 45, + 0, 0, 270, 265, 264, 266, 263, 262, 257, 158, + 162, 0, 153, 124, 128, 0, 123, 121, 122, 101, + 72, 0, 43, 0, 0, 0, 0, 0, 243, 242, + 164, 130, 77 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 1, 9, 10, 11, 12, 13, 14, 210, 39, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 383, 352, 353, 175, 176, 177, 178, 374, 179, 180, + 181, 182, 183, 255, 256, 257, 186, 354, 355, 187, + 188, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 346, 347, 348, 131, 132, 133, 339, 340, 426, 427, + 428, 134, 324, 325, 135, 136, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 320, 108, 109, 307, 308, + 110, 111, 63, 64, 65, 189, 112, 190, 191, 192, + 279, 193, 194, 195, 196, 225, 343, 115, 68, 69, + 70, 71, 213, 197, 198, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 303, 88, 291, 400, 454, 15, 34, 47, 392, + 293, 294, 295, 393, 48, 89, 90, 91, 92, 28, + 211 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -388 +static const yytype_int16 yypact[] = +{ + -388, 110, -388, -388, 12, 14, 26, 41, 46, -388, + -388, -388, -388, -388, -388, -388, -12, -12, 10, -12, + -12, -388, -388, -388, 72, -388, 74, -388, 84, 89, + 91, 52, 52, 104, 101, -388, 52, 52, -388, 132, + 141, -12, 145, 155, 164, 750, 32, 148, 174, -388, + 790, 1403, 594, 471, 177, 179, -388, -388, -388, -388, + -388, -388, -388, 201, 178, -388, -388, -388, -388, -388, + -388, -388, -388, 50, -388, 111, -388, 192, -388, -388, + -388, 127, -388, 193, 152, -388, 195, -388, -388, -388, + -388, -388, -388, 216, -388, 32, -388, -388, 214, 201, + 217, -388, -388, -388, 205, 238, 298, -388, -388, -388, + -388, -388, -388, 227, 1158, -388, 153, -388, -12, 228, + 790, 226, 201, 217, -388, -388, -388, 231, 259, 316, + -388, -388, -388, 258, -388, -388, -388, -388, 251, 1241, + -388, 19, 22, 153, -388, -388, -388, -388, 254, -388, + -388, -388, -388, -388, 1339, -388, -388, -388, 1403, -388, + -388, -388, -388, -388, 252, 201, -388, -388, -388, -388, + 240, 269, 272, 566, -388, -388, -388, 270, -388, -6, + -388, -388, 250, 220, 275, 21, -388, -388, -388, -388, + 267, -388, 268, -388, -388, -388, -388, -388, -388, -388, + -388, 234, -388, -388, 2, -388, 279, 1158, 1241, 1058, + 266, -388, 594, 471, -388, 471, 471, 471, 471, -1, + 3, 271, 1158, 274, -388, 1158, 1158, 1158, 1158, 1158, + 1158, 222, 1158, -388, -388, 0, 36, 276, 282, 1241, + 284, 1241, 1241, 1241, 1241, 1241, 1241, 29, 222, 1241, + -388, 1, 1467, -388, 287, -388, -388, 295, 285, 296, + 1467, 297, 1531, 1531, 1531, 1531, 1531, 1531, 1531, 1467, + 1531, 1531, 1531, 1531, 1531, 281, 222, 1467, 245, 23, + -388, 289, 314, 1538, 306, 308, 312, 317, 318, -388, + -388, 330, -388, 334, -388, -388, 326, -388, 331, -388, + -388, -388, 346, 329, 332, 335, -1, 337, 349, 183, + -388, -388, 353, 342, -388, -388, -388, -388, -388, -388, + 339, 358, 340, 0, 343, 360, 186, -388, -388, -388, + 362, 354, -388, -388, -388, -388, -388, -388, 77, -388, + 333, 345, -388, -388, -388, -388, 355, 369, -388, 380, + 365, 1, 387, -388, 370, 390, 389, 1467, 372, 396, + 1531, -388, 397, -388, 400, 382, -388, -388, -388, -388, + -388, -388, -388, -388, -388, -388, -388, 359, -388, -388, + -388, -388, -388, 383, 402, 385, -388, -388, -388, 471, + 471, -388, 408, 391, 750, 32, 471, 790, 1403, 404, + -388, -388, 1058, 1058, -388, 471, -388, -388, -388, 393, + 416, -1, 405, 1158, 1158, -388, 420, 222, 401, 421, + 0, 411, 1241, 1241, -388, 77, 412, 409, 167, -2, + 433, 222, -2, 418, 1467, 441, 1, 1467, -388, -388, + 1467, -388, 442, 222, -388, 443, 445, 1058, -388, 423, + 430, 431, 434, 435, -388, -388, -388, 436, -388, -388, + 1158, -388, 471, -388, 452, 1158, -388, -388, 1241, -388, + 471, -388, 457, 180, -2, -388, -388, -2, -388, 1241, + -388, -388, -388, -388, 1531, -388, -388, 458, 1531, -388, + 471, 471, -388, -388, -388, -388, -388, -388, -388, -388, + -388, 1158, -388, -388, -388, 1241, 424, -388, -388, -388, + -388, 1467, -388, 440, 444, 446, 449, 451, -388, -388, + -388, -388, -388 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -388, -388, -388, -388, -388, -388, -388, -388, -84, 99, + 96, -149, -388, -388, -388, -388, -388, -388, -85, -388, + 55, -253, -388, -388, -388, -388, -388, 58, -112, -388, + 235, -388, -388, 465, 17, 154, -388, 76, 162, -388, + 357, 120, -115, -388, -388, -388, -388, -388, -127, -388, + 87, -388, -388, -388, 399, -388, -388, -388, -168, 273, + 97, -388, 103, 198, -388, 410, 129, -93, -388, -388, + -388, -388, -388, -80, -388, 115, -388, -388, 122, 230, + -388, 447, 143, 488, 350, -388, 516, -388, 368, -388, + -388, 781, -78, -388, 842, -109, -388, 455, -388, -388, + -388, -388, -388, -54, 470, -388, -45, -388, -14, 351, + -43, -388, -388, -388, 219, -388, -388, 286, -388, -40, + 722, -200, -388, -388, -388, -388, -388, -388, 426, -196, + -388, -388, 165, -387, 88, -16, -195, -388, -160, -388, + 11 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -231 +static const yytype_int16 yytable[] = +{ + 25, 25, 220, 25, 25, 236, 199, 141, 206, 258, + 142, 2, 250, 292, 241, 223, 457, 304, 305, 214, + 306, 323, 351, 384, 270, 25, 224, -111, 230, -52, + -112, 280, 310, 16, 233, 17, 212, 200, 240, 35, + 216, 93, 271, 218, 224, 94, 21, 18, 22, 297, + 338, 246, 345, 95, 58, 272, 23, 96, 22, 224, + 492, 149, 19, 97, 151, 327, 23, 20, 185, 254, + 98, 54, 55, 62, 62, 62, 275, 141, 27, 341, + 142, 261, 31, 22, 32, 58, 56, 57, 58, 22, + 59, 23, 22, 282, 33, 60, 61, 23, 425, 36, + 23, 37, 25, 358, 24, 26, 62, 29, 30, 199, + 3, 364, 38, 199, 281, 333, 334, 335, 336, 337, + 373, 41, -219, 297, 330, -219, 332, 241, 385, 312, + 42, 40, 314, 58, 350, 43, 44, 22, -230, 322, + 200, -230, 45, 345, 200, 23, 315, 316, 317, 318, + 319, 46, 214, 4, 5, 6, 7, 8, 376, 377, + 379, 380, 381, -221, -217, 50, -221, -217, 300, 49, + 301, 302, 302, 302, 51, 185, 117, 366, 367, 368, + 369, 370, 371, 372, 118, 375, 375, 375, 375, 375, + 384, 412, 413, 296, 421, 422, 476, 477, 207, 200, + 208, 200, 200, 200, 200, 458, 456, 199, 258, 506, + 477, 209, 212, 216, 217, 199, 218, 199, 199, 199, + 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, + 345, 344, 199, 219, 345, 222, 388, 345, 200, 226, + 224, 227, 297, 297, 231, 235, 200, 239, 200, 200, + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 200, 478, 243, 200, 481, 242, 247, 296, 248, 359, + 202, 251, 263, 260, 262, 254, 264, 359, 269, 345, + 273, 274, 345, -51, 276, 483, 359, 297, 373, 277, + 149, 487, 382, 278, 359, 298, 62, 360, 154, 389, + 311, 228, 401, 313, 386, 328, 507, 471, 472, 508, + 150, 329, 199, 331, 362, 199, 155, 156, 157, 244, + 463, 464, 344, 361, 390, 363, 365, 394, 150, 395, + 161, 162, 229, 396, 155, 156, 157, 203, 397, 398, + 399, 503, 402, 200, 445, 446, 200, 403, 161, 162, + 245, 451, 509, 199, 141, 404, 405, 142, 406, 411, + 302, 407, 517, 414, 408, 410, 415, 416, 417, 310, + 420, 419, 423, 202, 359, 200, 200, 202, 424, 431, + 499, 429, 200, 430, 200, 502, 296, 296, 432, 199, + 516, 200, 199, -115, 327, 199, 434, 437, 435, 510, + 436, 438, 201, 512, -52, -53, 441, 271, 515, 344, + 440, 442, 443, 344, 444, 185, 344, 500, 447, 448, + 200, 283, 459, 200, 460, 504, 200, 462, 465, 468, + 467, 296, 202, 470, 202, 202, 202, 202, 475, 199, + 203, 479, 474, 199, 203, 513, 514, 482, 200, 484, + 488, 359, 493, 490, 359, 491, 200, 359, 344, 494, + 495, 344, 501, 496, 497, 498, 199, 505, 511, 518, + 200, 202, -116, 519, 200, 520, 200, 200, 521, 202, + 522, 202, 202, 202, 202, 202, 202, 202, 202, 202, + 202, 202, 202, 202, 453, 486, 202, 200, 489, 203, + 67, 203, 203, 203, 203, 140, 378, 67, 205, 201, + 54, 55, 485, 433, 439, 259, 184, 452, 480, 237, + 342, 418, 473, 469, 450, 56, 57, 58, 359, 59, + 238, 22, 466, 461, 60, 61, 409, 449, 203, 23, + 204, 268, 221, 215, 234, 62, 203, 0, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, + 203, 66, 299, 203, 455, 0, 137, 0, 66, 265, + 266, 0, 0, 0, 0, 140, 202, 149, 150, 202, + 151, 0, 0, 0, 155, 156, 157, 0, 0, 0, + 0, 0, 0, 0, 140, 0, -56, 0, 161, 162, + 267, 0, 0, 201, 0, 0, 0, 0, 202, 202, + 0, 201, 0, 0, -56, 202, 0, 202, 0, 0, + 201, 0, 0, 184, 202, 53, 0, -56, 201, 0, + 387, 0, 0, 54, 55, 0, 137, 0, 0, 0, + 0, 0, 0, 203, 0, 0, 203, 0, 56, 57, + 58, 0, 59, 202, 22, 137, 202, 60, 61, 202, + 0, 0, 23, 140, 0, 0, 0, 67, 62, 0, + 0, 0, 0, 0, 0, 203, 203, 0, 0, 0, + 0, 202, 203, 0, 203, 0, 0, 0, 0, 202, + 0, 203, 0, 0, 140, 0, 140, 140, 140, 140, + 140, 140, 0, 202, 140, 0, 0, 202, 201, 202, + 202, 0, 0, 0, 0, 0, 0, 184, 0, 0, + 203, 0, 0, 203, 137, 184, 203, 0, 66, 0, + 202, 0, 0, 0, 184, 0, 0, 0, 0, 0, + 0, 0, 184, 0, 0, 0, 0, 0, 203, 201, + 0, 0, 0, 0, 0, 137, 203, 137, 137, 137, + 137, 137, 137, 0, 0, 137, 0, 0, 116, 0, + 203, 52, 143, 0, 203, 0, 203, 203, 0, 0, + 0, 53, 0, 0, 0, 201, 0, 0, 201, 54, + 55, 201, 0, 0, 0, 0, 0, 203, 0, 119, + 0, 0, 0, 94, 56, 57, 58, 0, 59, 0, + 22, 120, 0, 60, 61, 96, 0, 116, 23, 0, + 0, 97, 184, 0, 62, 0, 0, 113, 121, 54, + 55, 138, 0, 0, 0, 0, 116, 0, 0, 0, + 0, 0, 143, 0, 56, 57, 58, 0, 59, 67, + 22, 0, 140, 60, 61, 0, 0, 0, 23, 0, + 0, 143, 201, 184, 62, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 113, 140, 140, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, + 0, 0, 139, 0, 0, 113, 0, 0, 0, 184, + 0, 138, 184, 0, 0, 184, 0, 0, 0, 0, + 66, 0, 0, 137, 0, 0, 0, 0, 0, 0, + 138, 0, 0, 140, 0, 0, 0, 0, 0, 116, + 143, 290, 0, 0, 140, 0, 0, 114, 137, 137, + 0, 309, 0, 0, 116, 0, 0, 116, 116, 116, + 116, 116, 116, 321, 116, 0, 114, 326, 0, 0, + 140, 143, 139, 143, 143, 143, 143, 143, 143, 0, + 349, 143, 0, 356, 0, 0, 184, 0, 0, 0, + 0, 139, 0, 0, 137, 0, 0, 0, 113, 138, + 0, 0, 0, 0, 0, 137, 0, 0, 356, 0, + 0, 0, 0, 113, 0, 290, 113, 113, 113, 113, + 113, 113, 0, 113, 0, 0, 0, 0, 0, 0, + 138, 137, 138, 138, 138, 138, 138, 138, 309, 0, + 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 326, 0, 0, 0, 114, + 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 114, 0, 0, 114, 114, 114, + 114, 114, 114, 356, 114, 283, 0, 0, 0, 0, + 0, 139, 0, 139, 139, 139, 139, 139, 139, 0, + 0, 139, 0, 284, 285, 286, 0, 0, 0, 287, + 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 289, 0, 0, 0, 59, 116, 22, 143, + 0, 60, 61, 0, 290, 290, 23, 0, 0, 0, + 0, 0, 62, 309, 0, 116, 116, 0, 0, 321, + 0, 0, 326, 0, 143, 143, 0, 0, 0, 0, + 0, 0, 0, 349, 0, 0, 0, 0, 356, 0, + 0, 0, 0, 0, 0, 356, 0, 93, 0, 290, + 0, 94, 0, 0, 0, 0, 113, 0, 138, 232, + 0, 0, 116, 96, 0, 0, 0, 116, 0, 97, + 143, 0, 0, 0, 113, 113, 98, 54, 55, 0, + 0, 143, 0, 138, 138, 0, 0, 0, 0, 0, + 0, 0, 56, 57, 58, 0, 59, 0, 22, 0, + 0, 60, 61, 116, 0, 0, 23, 143, 0, 0, + 0, 0, 62, 0, 0, 0, 0, 114, 0, 139, + 0, 113, 0, 0, 0, 0, 113, 0, 0, 138, + 119, 0, 0, 0, 94, 114, 114, 0, 0, 0, + 138, 0, 249, 0, 139, 139, 96, 0, 0, 0, + 0, 0, 97, 0, 0, 0, 0, 0, 0, 121, + 54, 55, 113, 0, 0, 0, 138, 0, 0, 0, + 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, + 0, 22, 114, 0, 60, 61, 0, 114, 0, 23, + 139, 0, 0, 0, 0, 62, 0, 0, 0, 0, + 0, 139, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 144, 114, 145, 146, 147, 139, 148, 0, + 149, 150, 94, 151, 152, 153, 154, 155, 156, 157, + 252, 0, 0, 0, 96, 159, 160, 253, 0, 0, + 97, 161, 162, 163, 0, 0, 0, 164, 54, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, + 0, 0, 60, 61, 0, 0, 144, 23, 145, 146, + 147, 0, 148, 62, 149, 150, 94, 151, 152, 153, + 154, 155, 156, 157, 158, 0, 0, 0, 96, 159, + 160, 0, 0, 0, 97, 161, 162, 163, 0, 0, + 0, 164, 54, 55, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, + 0, 59, 0, 22, 0, 0, 60, 61, 0, 0, + 144, 23, 145, 146, 147, 0, 148, 62, 149, 150, + 94, 151, 152, 153, 154, 155, 156, 157, 357, 0, + 0, 0, 96, 159, 160, 0, 0, 0, 97, 161, + 162, 163, 0, 0, 0, 164, 54, 55, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 56, 57, 58, 0, 59, 0, 22, 0, 0, + 60, 61, 0, 0, 144, 23, 145, 146, 147, 0, + 148, 62, 149, 150, 94, 151, 152, 153, 154, 155, + 156, 157, 252, 0, 0, 283, 96, 159, 160, 0, + 0, 0, 97, 161, 162, 163, 391, 0, 0, 164, + 54, 55, 0, 284, 285, 286, 0, 0, 0, 287, + 288, 0, 0, 0, 0, 56, 57, 58, 0, 59, + 0, 22, 289, 0, 60, 61, 59, 0, 22, 23, + 0, 60, 61, 0, 0, 62, 23, 0, 0, 0, + 0, 0, 62 +}; + +static const yytype_int16 yycheck[] = +{ + 16, 17, 95, 19, 20, 120, 51, 50, 53, 158, + 50, 0, 139, 209, 123, 99, 403, 217, 218, 73, + 21, 21, 21, 276, 30, 41, 23, 8, 106, 8, + 8, 29, 29, 21, 114, 21, 34, 51, 122, 28, + 21, 9, 48, 21, 23, 13, 58, 21, 60, 209, + 21, 129, 247, 21, 56, 61, 68, 25, 60, 23, + 447, 11, 21, 31, 14, 29, 68, 21, 51, 154, + 38, 39, 40, 74, 74, 74, 185, 120, 68, 247, + 120, 165, 10, 60, 10, 56, 54, 55, 56, 60, + 58, 68, 60, 208, 10, 63, 64, 68, 21, 10, + 68, 10, 118, 252, 16, 17, 74, 19, 20, 154, + 0, 260, 60, 158, 207, 242, 243, 244, 245, 246, + 269, 17, 11, 283, 239, 14, 241, 236, 277, 222, + 29, 32, 225, 56, 249, 36, 37, 60, 11, 232, + 154, 14, 10, 338, 158, 68, 226, 227, 228, 229, + 230, 10, 206, 43, 44, 45, 46, 47, 270, 271, + 272, 273, 274, 11, 11, 10, 14, 14, 213, 24, + 215, 216, 217, 218, 10, 158, 28, 262, 263, 264, + 265, 266, 267, 268, 10, 270, 271, 272, 273, 274, + 443, 8, 9, 209, 8, 9, 29, 30, 21, 213, + 21, 215, 216, 217, 218, 405, 402, 252, 357, 29, + 30, 10, 34, 21, 21, 260, 21, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 425, 247, 277, 17, 429, 21, 279, 432, 252, 34, + 23, 3, 402, 403, 17, 17, 260, 21, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 429, 3, 277, 432, 34, 8, 283, 17, 252, + 51, 17, 3, 21, 34, 360, 4, 260, 8, 474, + 30, 61, 477, 8, 17, 434, 269, 447, 437, 21, + 11, 440, 275, 59, 277, 29, 74, 10, 17, 10, + 29, 3, 291, 29, 59, 29, 474, 422, 423, 477, + 12, 29, 357, 29, 29, 360, 18, 19, 20, 3, + 413, 414, 338, 28, 10, 29, 29, 21, 12, 21, + 32, 33, 34, 21, 18, 19, 20, 51, 21, 21, + 10, 468, 8, 357, 389, 390, 360, 21, 32, 33, + 34, 396, 479, 398, 397, 24, 10, 397, 29, 10, + 405, 29, 511, 10, 29, 28, 24, 28, 10, 29, + 10, 28, 10, 154, 357, 389, 390, 158, 24, 10, + 460, 48, 396, 28, 398, 465, 402, 403, 8, 434, + 505, 405, 437, 48, 29, 440, 9, 8, 28, 484, + 10, 29, 51, 488, 8, 8, 24, 48, 501, 425, + 10, 28, 10, 429, 29, 398, 432, 462, 10, 28, + 434, 17, 29, 437, 8, 470, 440, 22, 8, 8, + 29, 447, 213, 22, 215, 216, 217, 218, 29, 484, + 154, 8, 30, 488, 158, 490, 491, 29, 462, 8, + 8, 434, 29, 10, 437, 10, 470, 440, 474, 29, + 29, 477, 10, 29, 29, 29, 511, 10, 10, 29, + 484, 252, 48, 29, 488, 29, 490, 491, 29, 260, + 29, 262, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 398, 437, 277, 511, 443, 213, + 45, 215, 216, 217, 218, 50, 271, 52, 53, 158, + 39, 40, 436, 351, 360, 158, 51, 397, 431, 120, + 247, 323, 425, 420, 395, 54, 55, 56, 511, 58, + 120, 60, 417, 411, 63, 64, 306, 394, 252, 68, + 52, 173, 95, 73, 118, 74, 260, -1, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 45, 212, 277, 399, -1, 50, -1, 52, 3, + 4, -1, -1, -1, -1, 120, 357, 11, 12, 360, + 14, -1, -1, -1, 18, 19, 20, -1, -1, -1, + -1, -1, -1, -1, 139, -1, 30, -1, 32, 33, + 34, -1, -1, 252, -1, -1, -1, -1, 389, 390, + -1, 260, -1, -1, 48, 396, -1, 398, -1, -1, + 269, -1, -1, 158, 405, 31, -1, 61, 277, -1, + 279, -1, -1, 39, 40, -1, 120, -1, -1, -1, + -1, -1, -1, 357, -1, -1, 360, -1, 54, 55, + 56, -1, 58, 434, 60, 139, 437, 63, 64, 440, + -1, -1, 68, 208, -1, -1, -1, 212, 74, -1, + -1, -1, -1, -1, -1, 389, 390, -1, -1, -1, + -1, 462, 396, -1, 398, -1, -1, -1, -1, 470, + -1, 405, -1, -1, 239, -1, 241, 242, 243, 244, + 245, 246, -1, 484, 249, -1, -1, 488, 357, 490, + 491, -1, -1, -1, -1, -1, -1, 252, -1, -1, + 434, -1, -1, 437, 208, 260, 440, -1, 212, -1, + 511, -1, -1, -1, 269, -1, -1, -1, -1, -1, + -1, -1, 277, -1, -1, -1, -1, -1, 462, 398, + -1, -1, -1, -1, -1, 239, 470, 241, 242, 243, + 244, 245, 246, -1, -1, 249, -1, -1, 46, -1, + 484, 21, 50, -1, 488, -1, 490, 491, -1, -1, + -1, 31, -1, -1, -1, 434, -1, -1, 437, 39, + 40, 440, -1, -1, -1, -1, -1, 511, -1, 9, + -1, -1, -1, 13, 54, 55, 56, -1, 58, -1, + 60, 21, -1, 63, 64, 25, -1, 95, 68, -1, + -1, 31, 357, -1, 74, -1, -1, 46, 38, 39, + 40, 50, -1, -1, -1, -1, 114, -1, -1, -1, + -1, -1, 120, -1, 54, 55, 56, -1, 58, 394, + 60, -1, 397, 63, 64, -1, -1, -1, 68, -1, + -1, 139, 511, 398, 74, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 95, 422, 423, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 46, -1, + -1, -1, 50, -1, -1, 114, -1, -1, -1, 434, + -1, 120, 437, -1, -1, 440, -1, -1, -1, -1, + 394, -1, -1, 397, -1, -1, -1, -1, -1, -1, + 139, -1, -1, 468, -1, -1, -1, -1, -1, 207, + 208, 209, -1, -1, 479, -1, -1, 95, 422, 423, + -1, 219, -1, -1, 222, -1, -1, 225, 226, 227, + 228, 229, 230, 231, 232, -1, 114, 235, -1, -1, + 505, 239, 120, 241, 242, 243, 244, 245, 246, -1, + 248, 249, -1, 251, -1, -1, 511, -1, -1, -1, + -1, 139, -1, -1, 468, -1, -1, -1, 207, 208, + -1, -1, -1, -1, -1, 479, -1, -1, 276, -1, + -1, -1, -1, 222, -1, 283, 225, 226, 227, 228, + 229, 230, -1, 232, -1, -1, -1, -1, -1, -1, + 239, 505, 241, 242, 243, 244, 245, 246, 306, -1, + 249, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 323, -1, -1, -1, 207, + 208, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 222, -1, -1, 225, 226, 227, + 228, 229, 230, 351, 232, 17, -1, -1, -1, -1, + -1, 239, -1, 241, 242, 243, 244, 245, 246, -1, + -1, 249, -1, 35, 36, 37, -1, -1, -1, 41, + 42, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 54, -1, -1, -1, 58, 395, 60, 397, + -1, 63, 64, -1, 402, 403, 68, -1, -1, -1, + -1, -1, 74, 411, -1, 413, 414, -1, -1, 417, + -1, -1, 420, -1, 422, 423, -1, -1, -1, -1, + -1, -1, -1, 431, -1, -1, -1, -1, 436, -1, + -1, -1, -1, -1, -1, 443, -1, 9, -1, 447, + -1, 13, -1, -1, -1, -1, 395, -1, 397, 21, + -1, -1, 460, 25, -1, -1, -1, 465, -1, 31, + 468, -1, -1, -1, 413, 414, 38, 39, 40, -1, + -1, 479, -1, 422, 423, -1, -1, -1, -1, -1, + -1, -1, 54, 55, 56, -1, 58, -1, 60, -1, + -1, 63, 64, 501, -1, -1, 68, 505, -1, -1, + -1, -1, 74, -1, -1, -1, -1, 395, -1, 397, + -1, 460, -1, -1, -1, -1, 465, -1, -1, 468, + 9, -1, -1, -1, 13, 413, 414, -1, -1, -1, + 479, -1, 21, -1, 422, 423, 25, -1, -1, -1, + -1, -1, 31, -1, -1, -1, -1, -1, -1, 38, + 39, 40, 501, -1, -1, -1, 505, -1, -1, -1, + -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, + -1, 60, 460, -1, 63, 64, -1, 465, -1, 68, + 468, -1, -1, -1, -1, 74, -1, -1, -1, -1, + -1, 479, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 3, 501, 5, 6, 7, 505, 9, -1, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, -1, -1, -1, 25, 26, 27, 28, -1, -1, + 31, 32, 33, 34, -1, -1, -1, 38, 39, 40, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, + -1, -1, 63, 64, -1, -1, 3, 68, 5, 6, + 7, -1, 9, 74, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, -1, -1, -1, 25, 26, + 27, -1, -1, -1, 31, 32, 33, 34, -1, -1, + -1, 38, 39, 40, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, + -1, 58, -1, 60, -1, -1, 63, 64, -1, -1, + 3, 68, 5, 6, 7, -1, 9, 74, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, -1, + -1, -1, 25, 26, 27, -1, -1, -1, 31, 32, + 33, 34, -1, -1, -1, 38, 39, 40, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 54, 55, 56, -1, 58, -1, 60, -1, -1, + 63, 64, -1, -1, 3, 68, 5, 6, 7, -1, + 9, 74, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, -1, -1, 17, 25, 26, 27, -1, + -1, -1, 31, 32, 33, 34, 28, -1, -1, 38, + 39, 40, -1, 35, 36, 37, -1, -1, -1, 41, + 42, -1, -1, -1, -1, 54, 55, 56, -1, 58, + -1, 60, 54, -1, 63, 64, 58, -1, 60, 68, + -1, 63, 64, -1, -1, 74, 68, -1, -1, -1, + -1, -1, 74 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 77, 216, 0, 43, 44, 45, 46, 47, 78, + 79, 80, 81, 82, 83, 202, 21, 21, 21, 21, + 21, 58, 60, 68, 210, 211, 210, 68, 215, 210, + 210, 10, 10, 10, 203, 216, 10, 10, 60, 85, + 85, 17, 29, 85, 85, 10, 10, 204, 210, 24, + 10, 10, 21, 31, 39, 40, 54, 55, 56, 58, + 63, 64, 74, 158, 159, 160, 162, 173, 174, 175, + 176, 177, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 198, 211, + 212, 213, 214, 9, 13, 21, 25, 31, 38, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 152, 153, + 156, 157, 162, 167, 170, 173, 196, 28, 10, 9, + 21, 38, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 129, 130, 131, 137, 140, 141, 162, 167, 170, + 173, 186, 195, 196, 3, 5, 6, 7, 9, 11, + 12, 14, 15, 16, 17, 18, 19, 20, 21, 26, + 27, 32, 33, 34, 38, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 99, 100, 101, 102, 104, + 105, 106, 107, 108, 109, 110, 112, 115, 116, 161, + 163, 164, 165, 167, 168, 169, 170, 179, 180, 182, + 184, 185, 190, 193, 159, 173, 182, 21, 21, 10, + 84, 216, 34, 178, 179, 180, 21, 21, 21, 17, + 143, 157, 21, 84, 23, 171, 34, 3, 3, 34, + 168, 17, 21, 149, 204, 17, 118, 130, 141, 21, + 84, 171, 34, 3, 3, 34, 168, 8, 17, 21, + 124, 17, 21, 28, 94, 109, 110, 111, 87, 116, + 21, 84, 34, 3, 4, 3, 4, 34, 164, 8, + 30, 48, 61, 30, 61, 171, 17, 21, 59, 166, + 29, 143, 118, 17, 35, 36, 37, 41, 42, 54, + 196, 199, 205, 206, 207, 208, 211, 214, 29, 160, + 182, 182, 182, 197, 197, 197, 21, 154, 155, 196, + 29, 29, 143, 29, 143, 149, 149, 149, 149, 149, + 151, 196, 143, 21, 138, 139, 196, 29, 29, 29, + 118, 29, 118, 124, 124, 124, 124, 124, 21, 132, + 133, 134, 135, 172, 211, 212, 126, 127, 128, 196, + 118, 21, 97, 98, 113, 114, 196, 21, 87, 110, + 10, 28, 29, 29, 87, 29, 94, 94, 94, 94, + 94, 94, 94, 87, 103, 94, 104, 104, 106, 104, + 104, 104, 110, 96, 97, 87, 59, 185, 186, 10, + 10, 28, 205, 209, 21, 21, 21, 21, 21, 10, + 200, 216, 8, 21, 24, 10, 29, 29, 29, 155, + 28, 10, 8, 9, 10, 24, 28, 10, 139, 28, + 10, 8, 9, 10, 24, 21, 134, 135, 136, 48, + 28, 10, 8, 114, 9, 28, 10, 8, 29, 111, + 10, 24, 28, 10, 29, 182, 182, 10, 28, 158, + 142, 182, 117, 86, 201, 208, 205, 209, 197, 29, + 8, 154, 22, 143, 143, 8, 151, 29, 8, 138, + 22, 118, 118, 136, 30, 29, 29, 30, 134, 8, + 126, 134, 29, 87, 8, 113, 103, 87, 8, 96, + 10, 10, 209, 29, 29, 29, 29, 29, 29, 149, + 182, 10, 149, 124, 182, 10, 29, 134, 134, 124, + 94, 10, 94, 182, 182, 143, 118, 87, 29, 29, + 29, 29, 29 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ + +#define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + +#if 0 + switch (yytype) + { + + default: + break; + } +#endif +} + +/* Prevent warnings from -Wmissing-prototypes. */ +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*-------------------------. +| yyparse or yypush_parse. | +`-------------------------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: + +/* Line 1464 of yacc.c */ +#line 225 "tptp5.y" + {;} + break; + + case 3: + +/* Line 1464 of yacc.c */ +#line 226 "tptp5.y" + {;} + break; + + case 4: + +/* Line 1464 of yacc.c */ +#line 229 "tptp5.y" + {P_PRINT((yyval.pval));;} + break; + + case 5: + +/* Line 1464 of yacc.c */ +#line 230 "tptp5.y" + {P_PRINT((yyval.pval));;} + break; + + case 6: + +/* Line 1464 of yacc.c */ +#line 233 "tptp5.y" + {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 7: + +/* Line 1464 of yacc.c */ +#line 234 "tptp5.y" + {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 8: + +/* Line 1464 of yacc.c */ +#line 235 "tptp5.y" + {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 9: + +/* Line 1464 of yacc.c */ +#line 236 "tptp5.y" + {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 10: + +/* Line 1464 of yacc.c */ +#line 239 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_annotated", P_TOKEN("_LIT_thf ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} + break; + + case 11: + +/* Line 1464 of yacc.c */ +#line 242 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_annotated", P_TOKEN("_LIT_tff ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} + break; + + case 12: + +/* Line 1464 of yacc.c */ +#line 245 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_annotated", P_TOKEN("_LIT_fof ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} + break; + + case 13: + +/* Line 1464 of yacc.c */ +#line 248 "tptp5.y" + {(yyval.pval) = P_BUILD("cnf_annotated", P_TOKEN("_LIT_cnf ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} + break; + + case 14: + +/* Line 1464 of yacc.c */ +#line 251 "tptp5.y" + {(yyval.pval) = P_BUILD("annotations", P_TOKEN("COMMA ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 15: + +/* Line 1464 of yacc.c */ +#line 252 "tptp5.y" + {(yyval.pval) = P_BUILD("annotations", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 16: + +/* Line 1464 of yacc.c */ +#line 255 "tptp5.y" + {(yyval.pval) = P_BUILD("formula_role", P_TOKEN("lower_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 17: + +/* Line 1464 of yacc.c */ +#line 258 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 18: + +/* Line 1464 of yacc.c */ +#line 259 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 19: + +/* Line 1464 of yacc.c */ +#line 262 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 20: + +/* Line 1464 of yacc.c */ +#line 263 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 21: + +/* Line 1464 of yacc.c */ +#line 264 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 22: + +/* Line 1464 of yacc.c */ +#line 265 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 23: + +/* Line 1464 of yacc.c */ +#line 268 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 24: + +/* Line 1464 of yacc.c */ +#line 269 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 25: + +/* Line 1464 of yacc.c */ +#line 270 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 26: + +/* Line 1464 of yacc.c */ +#line 273 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_binary_pair", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 27: + +/* Line 1464 of yacc.c */ +#line 276 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_binary_tuple", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 28: + +/* Line 1464 of yacc.c */ +#line 277 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_binary_tuple", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 29: + +/* Line 1464 of yacc.c */ +#line 278 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_binary_tuple", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 30: + +/* Line 1464 of yacc.c */ +#line 281 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 31: + +/* Line 1464 of yacc.c */ +#line 282 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 32: + +/* Line 1464 of yacc.c */ +#line 285 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 33: + +/* Line 1464 of yacc.c */ +#line 286 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 34: + +/* Line 1464 of yacc.c */ +#line 289 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_apply_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AT_SIGN ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 35: + +/* Line 1464 of yacc.c */ +#line 290 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_apply_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AT_SIGN ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 36: + +/* Line 1464 of yacc.c */ +#line 293 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 37: + +/* Line 1464 of yacc.c */ +#line 294 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 38: + +/* Line 1464 of yacc.c */ +#line 295 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 39: + +/* Line 1464 of yacc.c */ +#line 296 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 40: + +/* Line 1464 of yacc.c */ +#line 297 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 41: + +/* Line 1464 of yacc.c */ +#line 298 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 42: + +/* Line 1464 of yacc.c */ +#line 299 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unitary_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 43: + +/* Line 1464 of yacc.c */ +#line 302 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_quantified_formula", (yyvsp[(1) - (6)].pval), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} + break; + + case 44: + +/* Line 1464 of yacc.c */ +#line 305 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_variable_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 45: + +/* Line 1464 of yacc.c */ +#line 306 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_variable_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 46: + +/* Line 1464 of yacc.c */ +#line 309 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 47: + +/* Line 1464 of yacc.c */ +#line 310 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 48: + +/* Line 1464 of yacc.c */ +#line 313 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_typed_variable", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 49: + +/* Line 1464 of yacc.c */ +#line 316 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unary_formula", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 50: + +/* Line 1464 of yacc.c */ +#line 319 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_type_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 51: + +/* Line 1464 of yacc.c */ +#line 322 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_typeable_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 52: + +/* Line 1464 of yacc.c */ +#line 323 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_typeable_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 53: + +/* Line 1464 of yacc.c */ +#line 324 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_typeable_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 54: + +/* Line 1464 of yacc.c */ +#line 327 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_subtype", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 55: + +/* Line 1464 of yacc.c */ +#line 330 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_top_level_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 56: + +/* Line 1464 of yacc.c */ +#line 333 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unitary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 57: + +/* Line 1464 of yacc.c */ +#line 336 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_binary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 58: + +/* Line 1464 of yacc.c */ +#line 337 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_binary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 59: + +/* Line 1464 of yacc.c */ +#line 338 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_binary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 60: + +/* Line 1464 of yacc.c */ +#line 341 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_mapping_type", (yyvsp[(1) - (3)].pval), P_TOKEN("arrow ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 61: + +/* Line 1464 of yacc.c */ +#line 342 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_mapping_type", (yyvsp[(1) - (3)].pval), P_TOKEN("arrow ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 62: + +/* Line 1464 of yacc.c */ +#line 345 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 63: + +/* Line 1464 of yacc.c */ +#line 346 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 64: + +/* Line 1464 of yacc.c */ +#line 349 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_union_type", (yyvsp[(1) - (3)].pval), P_TOKEN("plus ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 65: + +/* Line 1464 of yacc.c */ +#line 350 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_union_type", (yyvsp[(1) - (3)].pval), P_TOKEN("plus ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 66: + +/* Line 1464 of yacc.c */ +#line 353 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 67: + +/* Line 1464 of yacc.c */ +#line 354 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 68: + +/* Line 1464 of yacc.c */ +#line 357 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_tuple", P_TOKEN("LBRKT ", (yyvsp[(1) - (2)].ival)), P_TOKEN("RBRKT ", (yyvsp[(2) - (2)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 69: + +/* Line 1464 of yacc.c */ +#line 358 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_tuple", P_TOKEN("LBRKT ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RBRKT ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 70: + +/* Line 1464 of yacc.c */ +#line 361 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_tuple_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 71: + +/* Line 1464 of yacc.c */ +#line 362 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_tuple_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 72: + +/* Line 1464 of yacc.c */ +#line 365 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_let", P_TOKEN("COLON_EQUALS ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} + break; + + case 73: + +/* Line 1464 of yacc.c */ +#line 368 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_let_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 74: + +/* Line 1464 of yacc.c */ +#line 369 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_let_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 75: + +/* Line 1464 of yacc.c */ +#line 372 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_defined_var", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON_EQUALS ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 76: + +/* Line 1464 of yacc.c */ +#line 373 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_defined_var", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 77: + +/* Line 1464 of yacc.c */ +#line 376 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_conditional", P_TOKEN("_DLR_itef ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} + break; + + case 78: + +/* Line 1464 of yacc.c */ +#line 379 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_sequent", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 79: + +/* Line 1464 of yacc.c */ +#line 380 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_sequent", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 80: + +/* Line 1464 of yacc.c */ +#line 383 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 81: + +/* Line 1464 of yacc.c */ +#line 384 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 82: + +/* Line 1464 of yacc.c */ +#line 385 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 83: + +/* Line 1464 of yacc.c */ +#line 388 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 84: + +/* Line 1464 of yacc.c */ +#line 389 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 85: + +/* Line 1464 of yacc.c */ +#line 392 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 86: + +/* Line 1464 of yacc.c */ +#line 393 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 87: + +/* Line 1464 of yacc.c */ +#line 396 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_binary_nonassoc", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 88: + +/* Line 1464 of yacc.c */ +#line 399 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 89: + +/* Line 1464 of yacc.c */ +#line 400 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 90: + +/* Line 1464 of yacc.c */ +#line 403 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 91: + +/* Line 1464 of yacc.c */ +#line 404 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 92: + +/* Line 1464 of yacc.c */ +#line 407 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 93: + +/* Line 1464 of yacc.c */ +#line 408 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 94: + +/* Line 1464 of yacc.c */ +#line 411 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 95: + +/* Line 1464 of yacc.c */ +#line 412 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 96: + +/* Line 1464 of yacc.c */ +#line 413 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 97: + +/* Line 1464 of yacc.c */ +#line 414 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 98: + +/* Line 1464 of yacc.c */ +#line 415 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 99: + +/* Line 1464 of yacc.c */ +#line 416 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 100: + +/* Line 1464 of yacc.c */ +#line 417 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unitary_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 101: + +/* Line 1464 of yacc.c */ +#line 420 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_quantified_formula", (yyvsp[(1) - (6)].pval), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} + break; + + case 102: + +/* Line 1464 of yacc.c */ +#line 423 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_variable_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 103: + +/* Line 1464 of yacc.c */ +#line 424 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_variable_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 104: + +/* Line 1464 of yacc.c */ +#line 427 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 105: + +/* Line 1464 of yacc.c */ +#line 428 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 106: + +/* Line 1464 of yacc.c */ +#line 431 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_typed_variable", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 107: + +/* Line 1464 of yacc.c */ +#line 434 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unary_formula", (yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 108: + +/* Line 1464 of yacc.c */ +#line 435 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 109: + +/* Line 1464 of yacc.c */ +#line 438 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_typed_atom", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 110: + +/* Line 1464 of yacc.c */ +#line 439 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_typed_atom", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 111: + +/* Line 1464 of yacc.c */ +#line 442 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_untyped_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 112: + +/* Line 1464 of yacc.c */ +#line 443 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_untyped_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 113: + +/* Line 1464 of yacc.c */ +#line 446 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_top_level_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 114: + +/* Line 1464 of yacc.c */ +#line 447 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_top_level_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 115: + +/* Line 1464 of yacc.c */ +#line 450 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unitary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 116: + +/* Line 1464 of yacc.c */ +#line 451 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_unitary_type", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 117: + +/* Line 1464 of yacc.c */ +#line 454 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_atomic_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 118: + +/* Line 1464 of yacc.c */ +#line 455 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_atomic_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 119: + +/* Line 1464 of yacc.c */ +#line 458 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_mapping_type", (yyvsp[(1) - (3)].pval), P_TOKEN("arrow ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 120: + +/* Line 1464 of yacc.c */ +#line 459 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_mapping_type", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 121: + +/* Line 1464 of yacc.c */ +#line 462 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 122: + +/* Line 1464 of yacc.c */ +#line 463 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 123: + +/* Line 1464 of yacc.c */ +#line 464 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_xprod_type", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 124: + +/* Line 1464 of yacc.c */ +#line 467 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_let", P_TOKEN("COLON_EQUALS ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} + break; + + case 125: + +/* Line 1464 of yacc.c */ +#line 470 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_let_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 126: + +/* Line 1464 of yacc.c */ +#line 471 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_let_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 127: + +/* Line 1464 of yacc.c */ +#line 474 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_defined_var", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON_EQUALS ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 128: + +/* Line 1464 of yacc.c */ +#line 475 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_defined_var", (yyvsp[(1) - (4)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (4)].ival)), P_TOKEN("MINUS ", (yyvsp[(3) - (4)].ival)), (yyvsp[(4) - (4)].pval),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 129: + +/* Line 1464 of yacc.c */ +#line 476 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_defined_var", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 130: + +/* Line 1464 of yacc.c */ +#line 479 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_conditional", P_TOKEN("_DLR_itef ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} + break; + + case 131: + +/* Line 1464 of yacc.c */ +#line 482 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_sequent", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 132: + +/* Line 1464 of yacc.c */ +#line 483 "tptp5.y" + {(yyval.pval) = P_BUILD("tff_sequent", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 133: + +/* Line 1464 of yacc.c */ +#line 486 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 134: + +/* Line 1464 of yacc.c */ +#line 487 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 135: + +/* Line 1464 of yacc.c */ +#line 490 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 136: + +/* Line 1464 of yacc.c */ +#line 491 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 137: + +/* Line 1464 of yacc.c */ +#line 494 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 138: + +/* Line 1464 of yacc.c */ +#line 495 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 139: + +/* Line 1464 of yacc.c */ +#line 498 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_binary_nonassoc", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 140: + +/* Line 1464 of yacc.c */ +#line 501 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 141: + +/* Line 1464 of yacc.c */ +#line 502 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 142: + +/* Line 1464 of yacc.c */ +#line 505 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 143: + +/* Line 1464 of yacc.c */ +#line 506 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 144: + +/* Line 1464 of yacc.c */ +#line 509 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 145: + +/* Line 1464 of yacc.c */ +#line 510 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 146: + +/* Line 1464 of yacc.c */ +#line 513 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 147: + +/* Line 1464 of yacc.c */ +#line 514 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 148: + +/* Line 1464 of yacc.c */ +#line 515 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 149: + +/* Line 1464 of yacc.c */ +#line 516 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 150: + +/* Line 1464 of yacc.c */ +#line 517 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 151: + +/* Line 1464 of yacc.c */ +#line 518 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 152: + +/* Line 1464 of yacc.c */ +#line 519 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_unitary_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 153: + +/* Line 1464 of yacc.c */ +#line 522 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_quantified_formula", (yyvsp[(1) - (6)].pval), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} + break; + + case 154: + +/* Line 1464 of yacc.c */ +#line 525 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_variable_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 155: + +/* Line 1464 of yacc.c */ +#line 526 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_variable_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 156: + +/* Line 1464 of yacc.c */ +#line 529 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_unary_formula", (yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 157: + +/* Line 1464 of yacc.c */ +#line 530 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_unary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 158: + +/* Line 1464 of yacc.c */ +#line 533 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_let", P_TOKEN("COLON_EQUALS ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} + break; + + case 159: + +/* Line 1464 of yacc.c */ +#line 536 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_let_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 160: + +/* Line 1464 of yacc.c */ +#line 537 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_let_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 161: + +/* Line 1464 of yacc.c */ +#line 540 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_defined_var", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON_EQUALS ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 162: + +/* Line 1464 of yacc.c */ +#line 541 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_defined_var", (yyvsp[(1) - (4)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (4)].ival)), P_TOKEN("MINUS ", (yyvsp[(3) - (4)].ival)), (yyvsp[(4) - (4)].pval),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 163: + +/* Line 1464 of yacc.c */ +#line 542 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_defined_var", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 164: + +/* Line 1464 of yacc.c */ +#line 545 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_conditional", P_TOKEN("_DLR_itef ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} + break; + + case 165: + +/* Line 1464 of yacc.c */ +#line 548 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_sequent", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 166: + +/* Line 1464 of yacc.c */ +#line 549 "tptp5.y" + {(yyval.pval) = P_BUILD("fof_sequent", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 167: + +/* Line 1464 of yacc.c */ +#line 552 "tptp5.y" + {(yyval.pval) = P_BUILD("cnf_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 168: + +/* Line 1464 of yacc.c */ +#line 553 "tptp5.y" + {(yyval.pval) = P_BUILD("cnf_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 169: + +/* Line 1464 of yacc.c */ +#line 556 "tptp5.y" + {(yyval.pval) = P_BUILD("disjunction", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 170: + +/* Line 1464 of yacc.c */ +#line 557 "tptp5.y" + {(yyval.pval) = P_BUILD("disjunction", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 171: + +/* Line 1464 of yacc.c */ +#line 560 "tptp5.y" + {(yyval.pval) = P_BUILD("literal", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 172: + +/* Line 1464 of yacc.c */ +#line 561 "tptp5.y" + {(yyval.pval) = P_BUILD("literal", P_TOKEN("TILDE ", (yyvsp[(1) - (2)].ival)), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 173: + +/* Line 1464 of yacc.c */ +#line 562 "tptp5.y" + {(yyval.pval) = P_BUILD("literal", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 174: + +/* Line 1464 of yacc.c */ +#line 565 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_conn_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 175: + +/* Line 1464 of yacc.c */ +#line 566 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_conn_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 176: + +/* Line 1464 of yacc.c */ +#line 567 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_conn_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 177: + +/* Line 1464 of yacc.c */ +#line 570 "tptp5.y" + {(yyval.pval) = P_BUILD("fol_infix_unary", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 178: + +/* Line 1464 of yacc.c */ +#line 573 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_quantifier", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 179: + +/* Line 1464 of yacc.c */ +#line 574 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("CARET ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 180: + +/* Line 1464 of yacc.c */ +#line 575 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("EXCLAMATION_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 181: + +/* Line 1464 of yacc.c */ +#line 576 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("QUESTION_STAR ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 182: + +/* Line 1464 of yacc.c */ +#line 577 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("AT_SIGN_PLUS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 183: + +/* Line 1464 of yacc.c */ +#line 578 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("AT_SIGN_MINUS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 184: + +/* Line 1464 of yacc.c */ +#line 581 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_pair_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 185: + +/* Line 1464 of yacc.c */ +#line 582 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_pair_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 186: + +/* Line 1464 of yacc.c */ +#line 583 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_pair_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 187: + +/* Line 1464 of yacc.c */ +#line 586 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unary_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 188: + +/* Line 1464 of yacc.c */ +#line 587 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unary_connective", P_TOKEN("EXCLAMATION_EXCLAMATION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 189: + +/* Line 1464 of yacc.c */ +#line 588 "tptp5.y" + {(yyval.pval) = P_BUILD("thf_unary_connective", P_TOKEN("QUESTION_QUESTION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 190: + +/* Line 1464 of yacc.c */ +#line 591 "tptp5.y" + {(yyval.pval) = P_BUILD("subtype_sign", P_TOKEN("less_sign ", (yyvsp[(1) - (2)].ival)), P_TOKEN("less_sign ", (yyvsp[(2) - (2)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 191: + +/* Line 1464 of yacc.c */ +#line 594 "tptp5.y" + {(yyval.pval) = P_BUILD("fol_quantifier", P_TOKEN("EXCLAMATION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 192: + +/* Line 1464 of yacc.c */ +#line 595 "tptp5.y" + {(yyval.pval) = P_BUILD("fol_quantifier", P_TOKEN("QUESTION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 193: + +/* Line 1464 of yacc.c */ +#line 598 "tptp5.y" + {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("LESS_EQUALS_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 194: + +/* Line 1464 of yacc.c */ +#line 599 "tptp5.y" + {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("EQUALS_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 195: + +/* Line 1464 of yacc.c */ +#line 600 "tptp5.y" + {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("LESS_EQUALS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 196: + +/* Line 1464 of yacc.c */ +#line 601 "tptp5.y" + {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("LESS_TILDE_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 197: + +/* Line 1464 of yacc.c */ +#line 602 "tptp5.y" + {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("TILDE_VLINE ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 198: + +/* Line 1464 of yacc.c */ +#line 603 "tptp5.y" + {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("TILDE_AMPERSAND ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 199: + +/* Line 1464 of yacc.c */ +#line 606 "tptp5.y" + {(yyval.pval) = P_BUILD("assoc_connective", P_TOKEN("VLINE ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 200: + +/* Line 1464 of yacc.c */ +#line 607 "tptp5.y" + {(yyval.pval) = P_BUILD("assoc_connective", P_TOKEN("AMPERSAND ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 201: + +/* Line 1464 of yacc.c */ +#line 610 "tptp5.y" + {(yyval.pval) = P_BUILD("unary_connective", P_TOKEN("TILDE ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 202: + +/* Line 1464 of yacc.c */ +#line 613 "tptp5.y" + {(yyval.pval) = P_BUILD("gentzen_arrow", P_TOKEN("MINUS_MINUS_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 203: + +/* Line 1464 of yacc.c */ +#line 616 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 204: + +/* Line 1464 of yacc.c */ +#line 619 "tptp5.y" + {(yyval.pval) = P_BUILD("atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 205: + +/* Line 1464 of yacc.c */ +#line 620 "tptp5.y" + {(yyval.pval) = P_BUILD("atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 206: + +/* Line 1464 of yacc.c */ +#line 621 "tptp5.y" + {(yyval.pval) = P_BUILD("atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 207: + +/* Line 1464 of yacc.c */ +#line 624 "tptp5.y" + {(yyval.pval) = P_BUILD("plain_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 208: + +/* Line 1464 of yacc.c */ +#line 627 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 209: + +/* Line 1464 of yacc.c */ +#line 628 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 210: + +/* Line 1464 of yacc.c */ +#line 631 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_plain_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 211: + +/* Line 1464 of yacc.c */ +#line 634 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_infix_formula", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 212: + +/* Line 1464 of yacc.c */ +#line 637 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_infix_pred", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 213: + +/* Line 1464 of yacc.c */ +#line 640 "tptp5.y" + {(yyval.pval) = P_BUILD("infix_equality", P_TOKEN("EQUALS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 214: + +/* Line 1464 of yacc.c */ +#line 643 "tptp5.y" + {(yyval.pval) = P_BUILD("infix_inequality", P_TOKEN("EXCLAMATION_EQUALS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 215: + +/* Line 1464 of yacc.c */ +#line 646 "tptp5.y" + {(yyval.pval) = P_BUILD("system_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 216: + +/* Line 1464 of yacc.c */ +#line 649 "tptp5.y" + {(yyval.pval) = P_BUILD("term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 217: + +/* Line 1464 of yacc.c */ +#line 650 "tptp5.y" + {(yyval.pval) = P_BUILD("term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 218: + +/* Line 1464 of yacc.c */ +#line 651 "tptp5.y" + {(yyval.pval) = P_BUILD("term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 219: + +/* Line 1464 of yacc.c */ +#line 654 "tptp5.y" + {(yyval.pval) = P_BUILD("function_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 220: + +/* Line 1464 of yacc.c */ +#line 655 "tptp5.y" + {(yyval.pval) = P_BUILD("function_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 221: + +/* Line 1464 of yacc.c */ +#line 656 "tptp5.y" + {(yyval.pval) = P_BUILD("function_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 222: + +/* Line 1464 of yacc.c */ +#line 659 "tptp5.y" + {(yyval.pval) = P_BUILD("plain_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 223: + +/* Line 1464 of yacc.c */ +#line 660 "tptp5.y" + {(yyval.pval) = P_BUILD("plain_term", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 224: + +/* Line 1464 of yacc.c */ +#line 663 "tptp5.y" + {(yyval.pval) = P_BUILD("constant", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 225: + +/* Line 1464 of yacc.c */ +#line 666 "tptp5.y" + {(yyval.pval) = P_BUILD("functor", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 226: + +/* Line 1464 of yacc.c */ +#line 669 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 227: + +/* Line 1464 of yacc.c */ +#line 670 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 228: + +/* Line 1464 of yacc.c */ +#line 673 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 229: + +/* Line 1464 of yacc.c */ +#line 674 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_atom", P_TOKEN("distinct_object ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 230: + +/* Line 1464 of yacc.c */ +#line 677 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_atomic_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 231: + +/* Line 1464 of yacc.c */ +#line 680 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_plain_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 232: + +/* Line 1464 of yacc.c */ +#line 681 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_plain_term", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 233: + +/* Line 1464 of yacc.c */ +#line 684 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_constant", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 234: + +/* Line 1464 of yacc.c */ +#line 687 "tptp5.y" + {(yyval.pval) = P_BUILD("defined_functor", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 235: + +/* Line 1464 of yacc.c */ +#line 690 "tptp5.y" + {(yyval.pval) = P_BUILD("system_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 236: + +/* Line 1464 of yacc.c */ +#line 691 "tptp5.y" + {(yyval.pval) = P_BUILD("system_term", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 237: + +/* Line 1464 of yacc.c */ +#line 694 "tptp5.y" + {(yyval.pval) = P_BUILD("system_constant", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 238: + +/* Line 1464 of yacc.c */ +#line 697 "tptp5.y" + {(yyval.pval) = P_BUILD("system_functor", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 239: + +/* Line 1464 of yacc.c */ +#line 700 "tptp5.y" + {(yyval.pval) = P_BUILD("variable", P_TOKEN("upper_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 240: + +/* Line 1464 of yacc.c */ +#line 703 "tptp5.y" + {(yyval.pval) = P_BUILD("arguments", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 241: + +/* Line 1464 of yacc.c */ +#line 704 "tptp5.y" + {(yyval.pval) = P_BUILD("arguments", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 242: + +/* Line 1464 of yacc.c */ +#line 707 "tptp5.y" + {(yyval.pval) = P_BUILD("conditional_term", P_TOKEN("_DLR_itett ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} + break; + + case 243: + +/* Line 1464 of yacc.c */ +#line 708 "tptp5.y" + {(yyval.pval) = P_BUILD("conditional_term", P_TOKEN("_DLR_itetf ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} + break; + + case 244: + +/* Line 1464 of yacc.c */ +#line 711 "tptp5.y" + {(yyval.pval) = P_BUILD("source", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 245: + +/* Line 1464 of yacc.c */ +#line 714 "tptp5.y" + {(yyval.pval) = P_BUILD("optional_info", P_TOKEN("COMMA ", (yyvsp[(1) - (2)].ival)), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 246: + +/* Line 1464 of yacc.c */ +#line 715 "tptp5.y" + {(yyval.pval) = P_BUILD("optional_info", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 247: + +/* Line 1464 of yacc.c */ +#line 718 "tptp5.y" + {(yyval.pval) = P_BUILD("useful_info", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 248: + +/* Line 1464 of yacc.c */ +#line 721 "tptp5.y" + {(yyval.pval) = P_BUILD("include", P_TOKEN("_LIT_include ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), (yyvsp[(4) - (6)].pval), P_TOKEN("RPAREN ", (yyvsp[(5) - (6)].ival)), P_TOKEN("PERIOD ", (yyvsp[(6) - (6)].ival)),NULL,NULL,NULL,NULL);;} + break; + + case 249: + +/* Line 1464 of yacc.c */ +#line 724 "tptp5.y" + {(yyval.pval) = P_BUILD("formula_selection", P_TOKEN("COMMA ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 250: + +/* Line 1464 of yacc.c */ +#line 725 "tptp5.y" + {(yyval.pval) = P_BUILD("formula_selection", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 251: + +/* Line 1464 of yacc.c */ +#line 728 "tptp5.y" + {(yyval.pval) = P_BUILD("name_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 252: + +/* Line 1464 of yacc.c */ +#line 729 "tptp5.y" + {(yyval.pval) = P_BUILD("name_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 253: + +/* Line 1464 of yacc.c */ +#line 732 "tptp5.y" + {(yyval.pval) = P_BUILD("general_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 254: + +/* Line 1464 of yacc.c */ +#line 733 "tptp5.y" + {(yyval.pval) = P_BUILD("general_term", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 255: + +/* Line 1464 of yacc.c */ +#line 734 "tptp5.y" + {(yyval.pval) = P_BUILD("general_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 256: + +/* Line 1464 of yacc.c */ +#line 737 "tptp5.y" + {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 257: + +/* Line 1464 of yacc.c */ +#line 738 "tptp5.y" + {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 258: + +/* Line 1464 of yacc.c */ +#line 739 "tptp5.y" + {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 259: + +/* Line 1464 of yacc.c */ +#line 740 "tptp5.y" + {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 260: + +/* Line 1464 of yacc.c */ +#line 741 "tptp5.y" + {(yyval.pval) = P_BUILD("general_data", P_TOKEN("distinct_object ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 261: + +/* Line 1464 of yacc.c */ +#line 742 "tptp5.y" + {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 262: + +/* Line 1464 of yacc.c */ +#line 745 "tptp5.y" + {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_thf ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 263: + +/* Line 1464 of yacc.c */ +#line 746 "tptp5.y" + {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_tff ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 264: + +/* Line 1464 of yacc.c */ +#line 747 "tptp5.y" + {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_fof ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 265: + +/* Line 1464 of yacc.c */ +#line 748 "tptp5.y" + {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_cnf ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 266: + +/* Line 1464 of yacc.c */ +#line 749 "tptp5.y" + {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_fot ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 267: + +/* Line 1464 of yacc.c */ +#line 752 "tptp5.y" + {(yyval.pval) = P_BUILD("general_list", P_TOKEN("LBRKT ", (yyvsp[(1) - (2)].ival)), P_TOKEN("RBRKT ", (yyvsp[(2) - (2)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 268: + +/* Line 1464 of yacc.c */ +#line 753 "tptp5.y" + {(yyval.pval) = P_BUILD("general_list", P_TOKEN("LBRKT ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RBRKT ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 269: + +/* Line 1464 of yacc.c */ +#line 756 "tptp5.y" + {(yyval.pval) = P_BUILD("general_terms", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 270: + +/* Line 1464 of yacc.c */ +#line 757 "tptp5.y" + {(yyval.pval) = P_BUILD("general_terms", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 271: + +/* Line 1464 of yacc.c */ +#line 760 "tptp5.y" + {(yyval.pval) = P_BUILD("name", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 272: + +/* Line 1464 of yacc.c */ +#line 761 "tptp5.y" + {(yyval.pval) = P_BUILD("name", P_TOKEN("integer ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 273: + +/* Line 1464 of yacc.c */ +#line 764 "tptp5.y" + {(yyval.pval) = P_BUILD("atomic_word", P_TOKEN("lower_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 274: + +/* Line 1464 of yacc.c */ +#line 765 "tptp5.y" + {(yyval.pval) = P_BUILD("atomic_word", P_TOKEN("single_quoted ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 275: + +/* Line 1464 of yacc.c */ +#line 768 "tptp5.y" + {(yyval.pval) = P_BUILD("atomic_defined_word", P_TOKEN("dollar_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 276: + +/* Line 1464 of yacc.c */ +#line 771 "tptp5.y" + {(yyval.pval) = P_BUILD("atomic_system_word", P_TOKEN("dollar_dollar_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 277: + +/* Line 1464 of yacc.c */ +#line 774 "tptp5.y" + {(yyval.pval) = P_BUILD("number", P_TOKEN("integer ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 278: + +/* Line 1464 of yacc.c */ +#line 775 "tptp5.y" + {(yyval.pval) = P_BUILD("number", P_TOKEN("rational ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 279: + +/* Line 1464 of yacc.c */ +#line 776 "tptp5.y" + {(yyval.pval) = P_BUILD("number", P_TOKEN("real ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 280: + +/* Line 1464 of yacc.c */ +#line 779 "tptp5.y" + {(yyval.pval) = P_BUILD("file_name", P_TOKEN("single_quoted ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + case 281: + +/* Line 1464 of yacc.c */ +#line 782 "tptp5.y" + {(yyval.pval) = P_BUILD("null",NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} + break; + + + +/* Line 1464 of yacc.c */ +#line 4264 "tptp5.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined(yyoverflow) || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + diff --git a/examples/tptp/tptp5.tab.h b/examples/tptp/tptp5.tab.h new file mode 100644 index 000000000..2e03c0d13 --- /dev/null +++ b/examples/tptp/tptp5.tab.h @@ -0,0 +1,138 @@ +/* A Bison parser, made by GNU Bison 2.4.2. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + AMPERSAND = 258, + AT_SIGN = 259, + AT_SIGN_MINUS = 260, + AT_SIGN_PLUS = 261, + CARET = 262, + COLON = 263, + COLON_EQUALS = 264, + COMMA = 265, + EQUALS = 266, + EQUALS_GREATER = 267, + EXCLAMATION = 268, + EXCLAMATION_EQUALS = 269, + EXCLAMATION_EXCLAMATION = 270, + EXCLAMATION_GREATER = 271, + LBRKT = 272, + LESS_EQUALS = 273, + LESS_EQUALS_GREATER = 274, + LESS_TILDE_GREATER = 275, + LPAREN = 276, + MINUS = 277, + MINUS_MINUS_GREATER = 278, + PERIOD = 279, + QUESTION = 280, + QUESTION_QUESTION = 281, + QUESTION_STAR = 282, + RBRKT = 283, + RPAREN = 284, + STAR = 285, + TILDE = 286, + TILDE_AMPERSAND = 287, + TILDE_VLINE = 288, + VLINE = 289, + _DLR_cnf = 290, + _DLR_fof = 291, + _DLR_fot = 292, + _DLR_itef = 293, + _DLR_itetf = 294, + _DLR_itett = 295, + _DLR_tff = 296, + _DLR_thf = 297, + _LIT_cnf = 298, + _LIT_fof = 299, + _LIT_include = 300, + _LIT_tff = 301, + _LIT_thf = 302, + arrow = 303, + comment = 304, + comment_line = 305, + decimal = 306, + decimal_exponent = 307, + decimal_fraction = 308, + distinct_object = 309, + dollar_dollar_word = 310, + dollar_word = 311, + dot_decimal = 312, + integer = 313, + less_sign = 314, + lower_word = 315, + plus = 316, + positive_decimal = 317, + rational = 318, + real = 319, + signed_integer = 320, + signed_rational = 321, + signed_real = 322, + single_quoted = 323, + star = 324, + unrecognized = 325, + unsigned_integer = 326, + unsigned_rational = 327, + unsigned_real = 328, + upper_word = 329, + vline = 330 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 1685 of yacc.c */ +#line 148 "tptp5.y" +int ival; double dval; char* sval; TreeNode* pval; + + +/* Line 1685 of yacc.c */ +#line 130 "tptp5.tab.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +extern YYSTYPE yylval; + + diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 4fec0a9e7..6054f4769 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -83,6 +83,7 @@ def init_project_def(): set_z3py_dir('api/python') # Examples add_cpp_example('cpp_example', 'c++') + add_cpp_example('z3_tptp', 'tptp') add_c_example('c_example', 'c') add_c_example('maxsat') add_dotnet_example('dotnet_example', 'dotnet') diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 59122af32..98f213dd5 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -931,6 +931,9 @@ class ExtraExeComponent(ExeComponent): def main_component(self): return False + def require_mem_initializer(self): + return False + def get_so_ext(): sysname = os.uname()[0] if sysname == 'Darwin': @@ -1227,7 +1230,7 @@ class CppExampleComponent(ExampleComponent): out.write(' ') out.write(os.path.join(self.to_ex_dir, cppfile)) out.write('\n') - out.write('\t%s $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), exefile)) + out.write('\t%s $(OS_DEFINES) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), exefile)) # Add include dir components out.write(' -I%s' % get_component(API_COMPONENT).to_src_dir) out.write(' -I%s' % get_component(CPP_COMPONENT).to_src_dir) @@ -1399,7 +1402,8 @@ def mk_config(): 'LINK_OUT_FLAG=/Fe\n' 'SO_EXT=.dll\n' 'SLINK=cl\n' - 'SLINK_OUT_FLAG=/Fe\n') + 'SLINK_OUT_FLAG=/Fe\n' + 'OS_DEFINES=/D _WINDOWS\n') extra_opt = '' if GIT_HASH: extra_opt = '%s /D Z3GITHASH=%s' % (extra_opt, GIT_HASH) @@ -1447,6 +1451,7 @@ def mk_config(): print('Java Compiler: %s' % JAVAC) else: global CXX, CC, GMP, CPPFLAGS, CXXFLAGS, LDFLAGS + OS_DEFINES = "" ARITH = "internal" check_ar() CXX = find_cxx_compiler() @@ -1488,18 +1493,21 @@ def mk_config(): SLIBFLAGS = '-dynamiclib' elif sysname == 'Linux': CXXFLAGS = '%s -fno-strict-aliasing -D_LINUX_' % CXXFLAGS + OS_DEFINES = '-D_LINUX' SO_EXT = '.so' LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS elif sysname == 'FreeBSD': CXXFLAGS = '%s -fno-strict-aliasing -D_FREEBSD_' % CXXFLAGS + OS_DEFINES = '-D_FREEBSD_' SO_EXT = '.so' LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS elif sysname[:6] == 'CYGWIN': CXXFLAGS = '%s -D_CYGWIN -fno-strict-aliasing' % CXXFLAGS + OS_DEFINES = '-D_CYGWIN' SO_EXT = '.dll' SLIBFLAGS = '-shared' else: @@ -1534,6 +1542,7 @@ def mk_config(): config.write('SLINK_FLAGS=%s\n' % SLIBFLAGS) config.write('SLINK_EXTRA_FLAGS=%s\n' % SLIBEXTRAFLAGS) config.write('SLINK_OUT_FLAG=-o \n') + config.write('OS_DEFINES=%s\n' % OS_DEFINES) if is_verbose(): print('Host platform: %s' % sysname) print('C++ Compiler: %s' % CXX) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 6f745d620..c75acc5e2 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -204,6 +204,8 @@ namespace z3 { func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range); func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range); + func_decl function(symbol const& name, sort_vector const& domain, sort const& range); + func_decl function(char const * name, sort_vector const& domain, sort const& range); func_decl function(char const * name, sort const & domain, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range); @@ -429,6 +431,7 @@ namespace z3 { expr operator()() const; expr operator()(unsigned n, expr const * args) const; + expr operator()(expr_vector const& v) const; expr operator()(expr const & a) const; expr operator()(int a) const; expr operator()(expr const & a1, expr const & a2) const; @@ -1516,6 +1519,22 @@ namespace z3 { inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { return function(range.ctx().str_symbol(name), arity, domain, range); } + + inline func_decl context::function(symbol const& name, sort_vector const& domain, sort const& range) { + array args(domain.size()); + for (unsigned i = 0; i < domain.size(); i++) { + check_context(domain[i], range); + args[i] = domain[i]; + } + Z3_func_decl f = Z3_mk_func_decl(m_ctx, name, domain.size(), args.ptr(), range); + check_error(); + return func_decl(*this, f); + } + + inline func_decl context::function(char const * name, sort_vector const& domain, sort const& range) { + return function(range.ctx().str_symbol(name), domain, range); + } + inline func_decl context::function(char const * name, sort const & domain, sort const & range) { check_context(domain, range); @@ -1602,6 +1621,16 @@ namespace z3 { return expr(ctx(), r); } + inline expr func_decl::operator()(expr_vector const& args) const { + array _args(args.size()); + for (unsigned i = 0; i < args.size(); i++) { + check_context(*this, args[i]); + _args[i] = args[i]; + } + Z3_ast r = Z3_mk_app(ctx(), *this, args.size(), _args.ptr()); + check_error(); + return expr(ctx(), r); + } inline expr func_decl::operator()() const { Z3_ast r = Z3_mk_app(ctx(), *this, 0, 0); ctx().check_error(); diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index a6bd7f7f8..f2c50487f 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -719,7 +719,7 @@ namespace datalog { T& operator*() { return *m_t; } const T& operator*() const { return *m_t; } operator bool() const { return m_t!=0; } - T* get() { return m_t; } + T* get() const { return m_t; } /** \brief Remove object from \c scoped_rel without deleting it. */ diff --git a/src/muz/rel/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp index c9bdec0b6..2cae7771f 100644 --- a/src/muz/rel/dl_sparse_table.cpp +++ b/src/muz/rel/dl_sparse_table.cpp @@ -81,9 +81,9 @@ namespace datalog { } unsigned entry_storage::get_size_estimate_bytes() const { - unsigned sz = m_data.capacity(); + size_t sz = m_data.capacity(); sz += m_data_indexer.capacity()*sizeof(storage_indexer::entry); - return sz; + return static_cast(sz); } // ----------------------------------- @@ -283,7 +283,7 @@ namespace datalog { class sparse_table::general_key_indexer : public key_indexer { typedef svector offset_vector; - typedef u_map index_map; + typedef size_t_map index_map; index_map m_map; mutable entry_storage m_keys; @@ -641,8 +641,8 @@ namespace datalog { unsigned t1_entry_size = t1.m_fact_size; unsigned t2_entry_size = t2.m_fact_size; - unsigned t1idx = 0; - unsigned t1end = t1.m_data.after_last_offset(); + size_t t1idx = 0; + size_t t1end = t1.m_data.after_last_offset(); TRACE("dl_table_relation", tout << "joined_col_cnt: " << joined_col_cnt << "\n"; @@ -654,8 +654,8 @@ namespace datalog { ); if (joined_col_cnt == 0) { - unsigned t2idx = 0; - unsigned t2end = t2.m_data.after_last_offset(); + size_t t2idx = 0; + size_t t2end = t2.m_data.after_last_offset(); for (; t1idx!=t1end; t1idx+=t1_entry_size) { for (t2idx = 0; t2idx != t2end; t2idx += t2_entry_size) { @@ -1064,8 +1064,8 @@ namespace datalog { sparse_table_plugin & plugin = t.get_plugin(); sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); - unsigned res_fact_size = res->m_fact_size; - unsigned res_data_size = res_fact_size*t.row_count(); + size_t res_fact_size = res->m_fact_size; + size_t res_data_size = res_fact_size*t.row_count(); if (res_fact_size != 0 && (res_data_size / res_fact_size) != t.row_count()) { throw default_exception("multiplication overflow"); } @@ -1084,7 +1084,7 @@ namespace datalog { } //and insert them into the hash-map - for (unsigned i=0; i!=res_data_size; i+=res_fact_size) { + for (size_t i = 0; i != res_data_size; i += res_fact_size) { TRUSTME(res->m_data.insert_offset(i)); } @@ -1161,7 +1161,7 @@ namespace datalog { } if (key_modified) { t2_offsets = t2_indexer.get_matching_offsets(t1_key); - key_modified=false; + key_modified = false; } if (t2_offsets.empty()) { @@ -1171,12 +1171,16 @@ namespace datalog { res.push_back(t1_ofs); } else { - key_indexer::offset_iterator it = t2_offsets.begin(); + key_indexer::offset_iterator it = t2_offsets.begin(); key_indexer::offset_iterator end = t2_offsets.end(); for (; it!=end; ++it) { store_offset ofs = *it; - if (!m_intersection_content.contains(ofs)) { - m_intersection_content.insert(ofs); + unsigned offs2 = static_cast(ofs); + if (ofs != offs2) { + throw default_exception("Z3 cannot perform negation with excessively large tables"); + } + if (!m_intersection_content.contains(offs2)) { + m_intersection_content.insert(offs2); res.push_back(ofs); } } diff --git a/src/muz/rel/dl_sparse_table.h b/src/muz/rel/dl_sparse_table.h index 5c5f95a75..0a60c4e10 100644 --- a/src/muz/rel/dl_sparse_table.h +++ b/src/muz/rel/dl_sparse_table.h @@ -97,9 +97,9 @@ namespace datalog { class entry_storage { public: - typedef unsigned store_offset; + typedef size_t store_offset; private: - typedef svector storage; + typedef svector storage; class offset_hash_proc { storage & m_storage; @@ -130,7 +130,7 @@ namespace datalog { unsigned m_entry_size; unsigned m_unique_part_size; - unsigned m_data_size; + size_t m_data_size; /** Invariant: Every or all but one blocks of length \c m_entry_size in the \c m_data vector are unique sequences of bytes and have their offset stored in the \c m_data_indexer hashtable. @@ -214,7 +214,7 @@ namespace datalog { SASSERT(m_reserve==m_data_size-m_entry_size); return; } - m_reserve=m_data_size; + m_reserve = m_data_size; resize_data(m_data_size+m_entry_size); } @@ -273,7 +273,7 @@ namespace datalog { //the following two operations allow breaking of the object invariant! - void resize_data(unsigned sz) { + void resize_data(size_t sz) { m_data_size = sz; if (sz + sizeof(uint64) < sz) { throw default_exception("overflow resizing data section for sparse table"); diff --git a/src/util/vector.h b/src/util/vector.h index 8bfe7c703..9370a4eed 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -37,7 +37,7 @@ Revision History: #pragma warning(disable:4127) #endif -template +template class vector { #define SIZE_IDX -1 #define CAPACITY_IDX -2 @@ -52,13 +52,13 @@ class vector { } void free_memory() { - memory::deallocate(reinterpret_cast(reinterpret_cast(m_data) - 2)); + memory::deallocate(reinterpret_cast(reinterpret_cast(m_data) - 2)); } void expand_vector() { if (m_data == 0) { - unsigned capacity = 2; - unsigned * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(unsigned) * 2)); + SZ capacity = 2; + SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(SZ) * 2)); *mem = capacity; mem++; *mem = 0; @@ -67,15 +67,15 @@ class vector { } else { SASSERT(capacity() > 0); - unsigned old_capacity = reinterpret_cast(m_data)[CAPACITY_IDX]; - unsigned old_capacity_T = sizeof(T) * old_capacity + sizeof(unsigned) * 2; - unsigned new_capacity = (3 * old_capacity + 1) >> 1; - unsigned new_capacity_T = sizeof(T) * new_capacity + sizeof(unsigned) * 2; - unsigned size = reinterpret_cast(m_data)[SIZE_IDX]; + SZ old_capacity = reinterpret_cast(m_data)[CAPACITY_IDX]; + SZ old_capacity_T = sizeof(T) * old_capacity + sizeof(SZ) * 2; + SZ new_capacity = (3 * old_capacity + 1) >> 1; + SZ new_capacity_T = sizeof(T) * new_capacity + sizeof(SZ) * 2; + SZ size = reinterpret_cast(m_data)[SIZE_IDX]; if (new_capacity <= old_capacity || new_capacity_T <= old_capacity_T) { throw default_exception("Overflow encountered when expanding vector"); } - unsigned * mem = reinterpret_cast(memory::allocate(new_capacity_T)); + SZ * mem = reinterpret_cast(memory::allocate(new_capacity_T)); *mem = new_capacity; mem ++; *mem = size; @@ -87,9 +87,9 @@ class vector { } void copy_core(vector const & source) { - unsigned size = source.size(); - unsigned capacity = source.capacity(); - unsigned * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(unsigned) * 2)); + SZ size = source.size(); + SZ capacity = source.capacity(); + SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(SZ) * 2)); *mem = capacity; mem++; *mem = size; @@ -122,8 +122,8 @@ public: m_data(0) { } - vector(unsigned s) { - unsigned * mem = reinterpret_cast(memory::allocate(sizeof(T) * s + sizeof(unsigned) * 2)); + vector(SZ s) { + SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * s + sizeof(SZ) * 2)); *mem = s; mem++; *mem = s; @@ -137,7 +137,7 @@ public: } } - vector(unsigned s, T const & elem): + vector(SZ s, T const & elem): m_data(0) { resize(s, elem); } @@ -150,9 +150,9 @@ public: SASSERT(size() == source.size()); } - vector(unsigned s, T const * data): + vector(SZ s, T const * data): m_data(0) { - for (unsigned i = 0; i < s; i++) { + for (SZ i = 0; i < s; i++) { push_back(data[i]); } } @@ -186,26 +186,26 @@ public: if (CallDestructors) { destroy_elements(); } - reinterpret_cast(m_data)[SIZE_IDX] = 0; + reinterpret_cast(m_data)[SIZE_IDX] = 0; } } bool empty() const { - return m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == 0; + return m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == 0; } - unsigned size() const { + SZ size() const { if (m_data == 0) { return 0; } - return reinterpret_cast(m_data)[SIZE_IDX]; + return reinterpret_cast(m_data)[SIZE_IDX]; } - unsigned capacity() const { + SZ capacity() const { if (m_data == 0) { return 0; } - return reinterpret_cast(m_data)[CAPACITY_IDX]; + return reinterpret_cast(m_data)[CAPACITY_IDX]; } iterator begin() { @@ -226,41 +226,41 @@ public: void set_end(iterator it) { if (m_data) { - unsigned new_sz = static_cast(it - m_data); + SZ new_sz = static_cast(it - m_data); if (CallDestructors) { iterator e = end(); for(; it != e; ++it) { it->~T(); } } - reinterpret_cast(m_data)[SIZE_IDX] = new_sz; + reinterpret_cast(m_data)[SIZE_IDX] = new_sz; } else { SASSERT(it == 0); } } - T & operator[](unsigned idx) { + T & operator[](SZ idx) { SASSERT(idx < size()); return m_data[idx]; } - T const & operator[](unsigned idx) const { + T const & operator[](SZ idx) const { SASSERT(idx < size()); return m_data[idx]; } - T & get(unsigned idx) { + T & get(SZ idx) { SASSERT(idx < size()); return m_data[idx]; } - T const & get(unsigned idx) const { + T const & get(SZ idx) const { SASSERT(idx < size()); return m_data[idx]; } - void set(unsigned idx, T const & val) { + void set(SZ idx, T const & val) { SASSERT(idx < size()); m_data[idx] = val; } @@ -280,15 +280,15 @@ public: if (CallDestructors) { back().~T(); } - reinterpret_cast(m_data)[SIZE_IDX]--; + reinterpret_cast(m_data)[SIZE_IDX]--; } void push_back(T const & elem) { - if (m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { + if (m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { expand_vector(); } - new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(elem); - reinterpret_cast(m_data)[SIZE_IDX]++; + new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(elem); + reinterpret_cast(m_data)[SIZE_IDX]++; } void insert(T const & elem) { @@ -303,7 +303,7 @@ public: for(; pos != e; ++pos, ++prev) { *prev = *pos; } - reinterpret_cast(m_data)[SIZE_IDX]--; + reinterpret_cast(m_data)[SIZE_IDX]--; } void erase(T const & elem) { @@ -313,9 +313,9 @@ public: } } - void shrink(unsigned s) { + void shrink(SZ s) { if (m_data) { - SASSERT(s <= reinterpret_cast(m_data)[SIZE_IDX]); + SASSERT(s <= reinterpret_cast(m_data)[SIZE_IDX]); if (CallDestructors) { iterator it = m_data + s; iterator e = end(); @@ -323,21 +323,21 @@ public: it->~T(); } } - reinterpret_cast(m_data)[SIZE_IDX] = s; + reinterpret_cast(m_data)[SIZE_IDX] = s; } else { SASSERT(s == 0); } } - void resize(unsigned s, T const & elem=T()) { - unsigned sz = size(); + void resize(SZ s, T const & elem=T()) { + SZ sz = size(); if (s <= sz) { shrink(s); return; } while (s > capacity()) { expand_vector(); } SASSERT(m_data != 0); - reinterpret_cast(m_data)[SIZE_IDX] = s; + reinterpret_cast(m_data)[SIZE_IDX] = s; iterator it = m_data + sz; iterator end = m_data + s; for(; it != end; ++it) { @@ -346,13 +346,13 @@ public: } void append(vector const & other) { - for(unsigned i = 0; i < other.size(); ++i) { + for(SZ i = 0; i < other.size(); ++i) { push_back(other[i]); } } - void append(unsigned sz, T const * data) { - for(unsigned i = 0; i < sz; ++i) { + void append(SZ sz, T const * data) { + for(SZ i = 0; i < sz; ++i) { push_back(data[i]); } } @@ -366,8 +366,8 @@ public: } void reverse() { - unsigned sz = size(); - for (unsigned i = 0; i < sz/2; ++i) { + SZ sz = size(); + for (SZ i = 0; i < sz/2; ++i) { std::swap(m_data[i], m_data[sz-i-1]); } } @@ -392,7 +392,7 @@ public: } // set pos idx with elem. If idx >= size, then expand using default. - void setx(unsigned idx, T const & elem, T const & d) { + void setx(SZ idx, T const & elem, T const & d) { if (idx >= size()) { resize(idx+1, d); } @@ -400,14 +400,14 @@ public: } // return element at position idx, if idx >= size, then return default - T const & get(unsigned idx, T const & d) const { + T const & get(SZ idx, T const & d) const { if (idx >= size()) { return d; } return m_data[idx]; } - void reserve(unsigned s, T const & d = T()) { + void reserve(SZ s, T const & d = T()) { if (s > size()) resize(s, d); } @@ -423,14 +423,14 @@ public: ptr_vector(unsigned s, T * const * data):vector(s, const_cast(data)) {} }; -template -class svector : public vector { +template +class svector : public vector { public: - svector():vector() {} - svector(unsigned s):vector(s) {} - svector(unsigned s, T const & elem):vector(s, elem) {} - svector(svector const & source):vector(source) {} - svector(unsigned s, T const * data):vector(s, data) {} + svector():vector() {} + svector(SZ s):vector(s) {} + svector(SZ s, T const & elem):vector(s, elem) {} + svector(svector const & source):vector(source) {} + svector(SZ s, T const * data):vector(s, data) {} }; typedef svector int_vector; From d686448356961b4f03b3191f74b8d852cf312baf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Sep 2013 21:52:01 -0700 Subject: [PATCH 145/509] local changes Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index a6bd7f7f8..f2c50487f 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -719,7 +719,7 @@ namespace datalog { T& operator*() { return *m_t; } const T& operator*() const { return *m_t; } operator bool() const { return m_t!=0; } - T* get() { return m_t; } + T* get() const { return m_t; } /** \brief Remove object from \c scoped_rel without deleting it. */ From 716663b04a894ec7ae6453476491aaf62e6bd66c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Sep 2013 05:52:18 -0700 Subject: [PATCH 146/509] avoid creating full tables when negated variables are unitary, add lazy table infrastructure, fix coi_filter for relations, reduce dependencies on fixedpoing_parameters.hpp header file Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_context.cpp | 36 +- src/muz/base/dl_context.h | 48 +- src/muz/base/dl_util.cpp | 23 + src/muz/base/dl_util.h | 32 +- src/muz/bmc/dl_bmc_engine.cpp | 1 + src/muz/fp/datalog_parser.cpp | 2 +- src/muz/fp/dl_cmds.cpp | 1 + src/muz/fp/horn_tactic.cpp | 1 + src/muz/pdr/pdr_context.cpp | 6 +- src/muz/pdr/pdr_context.h | 1 + src/muz/pdr/pdr_manager.cpp | 5 +- src/muz/pdr/pdr_manager.h | 5 +- src/muz/pdr/pdr_prop_solver.cpp | 5 +- src/muz/pdr/pdr_prop_solver.h | 4 +- src/muz/pdr/pdr_reachable_cache.cpp | 4 +- src/muz/pdr/pdr_reachable_cache.h | 2 +- src/muz/pdr/pdr_smt_context_manager.cpp | 4 +- src/muz/pdr/pdr_smt_context_manager.h | 2 +- src/muz/pdr/pdr_util.cpp | 1 - src/muz/rel/dl_base.h | 44 ++ src/muz/rel/dl_compiler.cpp | 16 +- src/muz/rel/dl_lazy_table.cpp | 468 ++++++++++++++++++ src/muz/rel/dl_lazy_table.h | 305 ++++++++++++ src/muz/rel/dl_relation_manager.cpp | 23 +- src/muz/rel/dl_relation_manager.h | 13 + src/muz/rel/dl_sparse_table.cpp | 221 +++++++-- src/muz/rel/dl_sparse_table.h | 15 + src/muz/rel/dl_table_relation.cpp | 10 +- src/muz/rel/rel_context.cpp | 18 +- src/muz/tab/tab_context.cpp | 1 + src/muz/transforms/dl_mk_bit_blast.cpp | 1 + src/muz/transforms/dl_mk_coi_filter.cpp | 17 +- src/muz/transforms/dl_mk_karr_invariants.cpp | 1 + src/muz/transforms/dl_mk_magic_symbolic.cpp | 1 + .../dl_mk_quantifier_abstraction.cpp | 2 + .../dl_mk_quantifier_instantiation.cpp | 2 + src/muz/transforms/dl_mk_rule_inliner.cpp | 1 + src/muz/transforms/dl_mk_scale.cpp | 1 + src/muz/transforms/dl_transforms.cpp | 1 + src/shell/datalog_frontend.cpp | 3 +- 40 files changed, 1221 insertions(+), 126 deletions(-) create mode 100644 src/muz/rel/dl_lazy_table.cpp create mode 100644 src/muz/rel/dl_lazy_table.h diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 11cb48490..3a62a9b98 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -27,6 +27,7 @@ Revision History: #include"ast_smt2_pp.h" #include"datatype_decl_plugin.h" #include"scoped_proof.h" +#include"fixedpoint_params.hpp" namespace datalog { @@ -199,7 +200,7 @@ namespace datalog { m_register_engine(re), m_fparams(fp), m_params_ref(pa), - m_params(m_params_ref), + m_params(alloc(fixedpoint_params, m_params_ref)), m_decl_util(m), m_rewriter(m), m_var_subst(m), @@ -263,6 +264,31 @@ namespace datalog { return *m_sorts.find(s); } + + bool context::generate_proof_trace() const { return m_params->generate_proof_trace(); } + bool context::output_profile() const { return m_params->output_profile(); } + bool context::output_tuples() const { return m_params->output_tuples(); } + bool context::use_map_names() const { return m_params->use_map_names(); } + bool context::fix_unbound_vars() const { return m_params->fix_unbound_vars(); } + symbol context::default_table() const { return m_params->default_table(); } + symbol context::default_relation() const { return m_params->default_relation(); } // external_relation_plugin::get_name()); + symbol context::default_table_checker() const { return m_params->default_table_checker(); } + bool context::default_table_checked() const { return m_params->default_table_checked(); } + bool context::dbg_fpr_nonempty_relation_signature() const { return m_params->dbg_fpr_nonempty_relation_signature(); } + unsigned context::dl_profile_milliseconds_threshold() const { return m_params->profile_timeout_milliseconds(); } + bool context::all_or_nothing_deltas() const { return m_params->all_or_nothing_deltas(); } + bool context::compile_with_widening() const { return m_params->compile_with_widening(); } + bool context::unbound_compressor() const { return m_params->unbound_compressor(); } + bool context::similarity_compressor() const { return m_params->similarity_compressor(); } + unsigned context::similarity_compressor_threshold() const { return m_params->similarity_compressor_threshold(); } + unsigned context::soft_timeout() const { return m_fparams.m_soft_timeout; } + unsigned context::initial_restart_timeout() const { return m_params->initial_restart_timeout(); } + bool context::generate_explanations() const { return m_params->generate_explanations(); } + bool context::explanations_on_relation_level() const { return m_params->explanations_on_relation_level(); } + bool context::magic_sets_for_queries() const { return m_params->magic_sets_for_queries(); } + bool context::eager_emptiness_checking() const { return m_params->eager_emptiness_checking(); } + + void context::register_finite_sort(sort * s, sort_kind k) { m_pinned.push_back(s); SASSERT(!m_sorts.contains(s)); @@ -861,7 +887,7 @@ namespace datalog { }; void context::configure_engine() { - symbol e = m_params.engine(); + symbol e = m_params->engine(); if (e == symbol("datalog")) { m_engine_type = DATALOG_ENGINE; @@ -1082,9 +1108,9 @@ namespace datalog { expr_ref fml(m); expr_ref_vector rules(m); svector names; - bool use_fixedpoint_extensions = m_params.print_with_fixedpoint_extensions(); - bool print_low_level = m_params.print_low_level_smt2(); - bool do_declare_vars = m_params.print_with_variable_declarations(); + bool use_fixedpoint_extensions = m_params->print_with_fixedpoint_extensions(); + bool print_low_level = m_params->print_low_level_smt2(); + bool do_declare_vars = m_params->print_with_variable_declarations(); #define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env); diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 97a371f5a..2eb9e0652 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -43,6 +43,8 @@ Revision History: #include"expr_functors.h" #include"dl_engine_base.h" +struct fixedpoint_params; + namespace datalog { enum execution_result { @@ -168,7 +170,7 @@ namespace datalog { register_engine_base& m_register_engine; smt_params & m_fparams; params_ref m_params_ref; - fixedpoint_params m_params; + fixedpoint_params* m_params; dl_decl_util m_decl_util; th_rewriter m_rewriter; var_subst m_var_subst; @@ -231,33 +233,35 @@ namespace datalog { ast_manager & get_manager() const { return m; } rule_manager & get_rule_manager() { return m_rule_manager; } smt_params & get_fparams() const { return m_fparams; } - fixedpoint_params const& get_params() const { return m_params; } + fixedpoint_params const& get_params() const { return *m_params; } DL_ENGINE get_engine() { configure_engine(); return m_engine_type; } register_engine_base& get_register_engine() { return m_register_engine; } th_rewriter& get_rewriter() { return m_rewriter; } var_subst & get_var_subst() { return m_var_subst; } dl_decl_util & get_decl_util() { return m_decl_util; } - bool generate_proof_trace() const { return m_params.generate_proof_trace(); } - bool output_profile() const { return m_params.output_profile(); } - bool fix_unbound_vars() const { return m_params.fix_unbound_vars(); } - symbol default_table() const { return m_params.default_table(); } - symbol default_relation() const { return m_params.default_relation(); } // external_relation_plugin::get_name()); - symbol default_table_checker() const { return m_params.default_table_checker(); } - bool default_table_checked() const { return m_params.default_table_checked(); } - bool dbg_fpr_nonempty_relation_signature() const { return m_params.dbg_fpr_nonempty_relation_signature(); } - unsigned dl_profile_milliseconds_threshold() const { return m_params.profile_timeout_milliseconds(); } - bool all_or_nothing_deltas() const { return m_params.all_or_nothing_deltas(); } - bool compile_with_widening() const { return m_params.compile_with_widening(); } - bool unbound_compressor() const { return m_params.unbound_compressor(); } - bool similarity_compressor() const { return m_params.similarity_compressor(); } - unsigned similarity_compressor_threshold() const { return m_params.similarity_compressor_threshold(); } - unsigned soft_timeout() const { return m_fparams.m_soft_timeout; } - unsigned initial_restart_timeout() const { return m_params.initial_restart_timeout(); } - bool generate_explanations() const { return m_params.generate_explanations(); } - bool explanations_on_relation_level() const { return m_params.explanations_on_relation_level(); } - bool magic_sets_for_queries() const { return m_params.magic_sets_for_queries(); } - bool eager_emptiness_checking() const { return m_params.eager_emptiness_checking(); } + bool generate_proof_trace() const; + bool output_profile() const; + bool output_tuples() const; + bool use_map_names() const; + bool fix_unbound_vars() const; + symbol default_table() const; + symbol default_relation() const; + symbol default_table_checker() const; + bool default_table_checked() const; + bool dbg_fpr_nonempty_relation_signature() const; + unsigned dl_profile_milliseconds_threshold() const; + bool all_or_nothing_deltas() const; + bool compile_with_widening() const; + bool unbound_compressor() const; + bool similarity_compressor() const; + unsigned similarity_compressor_threshold() const; + unsigned soft_timeout() const; + unsigned initial_restart_timeout() const; + bool generate_explanations() const; + bool explanations_on_relation_level() const; + bool magic_sets_for_queries() const; + bool eager_emptiness_checking() const; void register_finite_sort(sort * s, sort_kind k); diff --git a/src/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index 2f9eb519c..c23f222e5 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -30,9 +30,32 @@ Revision History: #include"dl_context.h" #include"dl_rule.h" #include"dl_util.h" +#include"stopwatch.h" namespace datalog { + static unsigned verbose_action_inside = 0; + + verbose_action::verbose_action(char const* msg, unsigned lvl): m_lvl(lvl), m_sw(0) { + IF_VERBOSE(m_lvl, + (verbose_stream() << msg << "...").flush(); + m_sw = alloc(stopwatch); + m_sw->start();); + } + + verbose_action::~verbose_action() { + double sec = 0.0; + if (m_sw) m_sw->stop(); + sec = m_sw?m_sw->get_seconds():0.0; + if (sec < 0.001) sec = 0.0; + IF_VERBOSE(m_lvl, + (verbose_stream() << sec << "s\n").flush(); + ); + dealloc(m_sw); + } + + + bool contains_var(expr * trm, unsigned var_idx) { ptr_vector vars; diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index f2c50487f..e13e7b53a 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -26,7 +26,6 @@ Revision History: #include"horn_subsume_model_converter.h" #include"replace_proof_converter.h" #include"substitution.h" -#include"fixedpoint_params.hpp" #include"ast_counter.h" #include"statistics.h" #include"lbool.h" @@ -43,6 +42,14 @@ namespace datalog { class relation_fact; class relation_signature; + class verbose_action { + unsigned m_lvl; + class stopwatch* m_sw; + public: + verbose_action(char const* msg, unsigned lvl = 1); + ~verbose_action(); + }; + enum PDR_CACHE_MODE { NO_CACHE, HASH_CACHE, @@ -706,29 +713,6 @@ namespace datalog { dealloc(ptr); } - template - class scoped_rel { - T* m_t; - public: - scoped_rel(T* t) : m_t(t) {} - ~scoped_rel() { if (m_t) { universal_delete(m_t); } } - scoped_rel() : m_t(0) {} - scoped_rel& operator=(T* t) { if (m_t) { universal_delete(m_t); } m_t = t; return *this; } - T* operator->() { return m_t; } - const T* operator->() const { return m_t; } - T& operator*() { return *m_t; } - const T& operator*() const { return *m_t; } - operator bool() const { return m_t!=0; } - T* get() const { return m_t; } - /** - \brief Remove object from \c scoped_rel without deleting it. - */ - T* release() { - T* res = m_t; - m_t = 0; - return res; - } - }; /** \brief If it is possible to convert the beginning of \c s to uint64, diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 60d285a55..eb4bc72c4 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -32,6 +32,7 @@ Revision History: #include "dl_transforms.h" #include "dl_mk_rule_inliner.h" #include "scoped_proof.h" +#include"fixedpoint_params.hpp" namespace datalog { diff --git a/src/muz/fp/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp index 545f3e14a..830f6d078 100644 --- a/src/muz/fp/datalog_parser.cpp +++ b/src/muz/fp/datalog_parser.cpp @@ -1164,7 +1164,7 @@ public: : dparser(ctx, ctx.get_manager()), m_bool_sort(ctx.get_manager()), m_short_sort(ctx.get_manager()), - m_use_map_names(ctx.get_params().use_map_names()) { + m_use_map_names(ctx.use_map_names()) { } ~wpa_parser_impl() { reset_dealloc_values(m_sort_contents); diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index 7f73b0895..35af38630 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -30,6 +30,7 @@ Notes: #include"scoped_ctrl_c.h" #include"scoped_timer.h" #include"trail.h" +#include"fixedpoint_params.hpp" #include diff --git a/src/muz/fp/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp index ca6cbc2fb..0c094c277 100644 --- a/src/muz/fp/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -27,6 +27,7 @@ Revision History: #include"dl_mk_slice.h" #include"filter_model_converter.h" #include"dl_transforms.h" +#include"fixedpoint_params.hpp" class horn_tactic : public tactic { struct imp { diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index f3e253071..aab7b1388 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -77,9 +77,9 @@ namespace pdr { pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): pm(pm), m(pm.get_manager()), ctx(ctx), m_head(head, m), - m_sig(m), m_solver(pm, head->get_name()), + m_sig(m), m_solver(pm, ctx.get_params(), head->get_name()), m_invariants(m), m_transition(m), m_initial_state(m), - m_reachable(pm, pm.get_params()) {} + m_reachable(pm, (datalog::PDR_CACHE_MODE)ctx.get_params().cache_mode()) {} pred_transformer::~pred_transformer() { rule2inst::iterator it2 = m_rule2inst.begin(), end2 = m_rule2inst.end(); @@ -1239,7 +1239,7 @@ namespace pdr { m_params(params), m(m), m_context(0), - m_pm(m_fparams, params, m), + m_pm(m_fparams, params.max_num_contexts(), m), m_query_pred(m), m_query(0), m_search(m_params.bfs_model_search()), diff --git a/src/muz/pdr/pdr_context.h b/src/muz/pdr/pdr_context.h index 57238abb3..8a4f3e438 100644 --- a/src/muz/pdr/pdr_context.h +++ b/src/muz/pdr/pdr_context.h @@ -28,6 +28,7 @@ Revision History: #include "pdr_manager.h" #include "pdr_prop_solver.h" #include "pdr_reachable_cache.h" +#include "fixedpoint_params.hpp" namespace datalog { diff --git a/src/muz/pdr/pdr_manager.cpp b/src/muz/pdr/pdr_manager.cpp index 9eb10aba9..bda54dbd7 100644 --- a/src/muz/pdr/pdr_manager.cpp +++ b/src/muz/pdr/pdr_manager.cpp @@ -166,14 +166,13 @@ namespace pdr { return res; } - manager::manager(smt_params& fparams, fixedpoint_params const& params, ast_manager& manager) : + manager::manager(smt_params& fparams, unsigned max_num_contexts, ast_manager& manager) : m(manager), m_fparams(fparams), - m_params(params), m_brwr(m), m_mux(m, get_state_suffixes()), m_background(m.mk_true(), m), - m_contexts(fparams, params, m), + m_contexts(fparams, max_num_contexts, m), m_next_unique_num(0) { } diff --git a/src/muz/pdr/pdr_manager.h b/src/muz/pdr/pdr_manager.h index cb2c9b253..0e8e890e8 100644 --- a/src/muz/pdr/pdr_manager.h +++ b/src/muz/pdr/pdr_manager.h @@ -78,7 +78,6 @@ namespace pdr { { ast_manager& m; smt_params& m_fparams; - fixedpoint_params const& m_params; mutable bool_rewriter m_brwr; @@ -99,12 +98,10 @@ namespace pdr { void add_new_state(func_decl * s); public: - manager(smt_params& fparams, fixedpoint_params const& params, - ast_manager & manager); + manager(smt_params& fparams, unsigned max_num_contexts, ast_manager & manager); ast_manager& get_manager() const { return m; } smt_params& get_fparams() const { return m_fparams; } - fixedpoint_params const& get_params() const { return m_params; } bool_rewriter& get_brwr() const { return m_brwr; } expr_ref mk_and(unsigned sz, expr* const* exprs); diff --git a/src/muz/pdr/pdr_prop_solver.cpp b/src/muz/pdr/pdr_prop_solver.cpp index d9b22e04e..8fe8c0e0e 100644 --- a/src/muz/pdr/pdr_prop_solver.cpp +++ b/src/muz/pdr/pdr_prop_solver.cpp @@ -30,6 +30,7 @@ Revision History: #include "pdr_farkas_learner.h" #include "ast_smt2_pp.h" #include "expr_replacer.h" +#include "fixedpoint_params.hpp" // // Auxiliary structure to introduce propositional names for assumptions that are not @@ -225,12 +226,12 @@ namespace pdr { }; - prop_solver::prop_solver(manager& pm, symbol const& name) : + prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& name) : m_fparams(pm.get_fparams()), m(pm.get_manager()), m_pm(pm), m_name(name), - m_try_minimize_core(pm.get_params().try_minimize_core()), + m_try_minimize_core(p.try_minimize_core()), m_ctx(pm.mk_fresh()), m_pos_level_atoms(m), m_neg_level_atoms(m), diff --git a/src/muz/pdr/pdr_prop_solver.h b/src/muz/pdr/pdr_prop_solver.h index 0c60a7124..a63ec2bf4 100644 --- a/src/muz/pdr/pdr_prop_solver.h +++ b/src/muz/pdr/pdr_prop_solver.h @@ -31,6 +31,8 @@ Revision History: #include "pdr_manager.h" #include "pdr_smt_context_manager.h" +struct fixedpoint_params; + namespace pdr { class prop_solver { @@ -73,7 +75,7 @@ namespace pdr { public: - prop_solver(pdr::manager& pm, symbol const& name); + prop_solver(pdr::manager& pm, fixedpoint_params const& p, symbol const& name); /** return true is s is a symbol introduced by prop_solver */ bool is_aux_symbol(func_decl * s) const { diff --git a/src/muz/pdr/pdr_reachable_cache.cpp b/src/muz/pdr/pdr_reachable_cache.cpp index 4f4f620de..85100c19f 100644 --- a/src/muz/pdr/pdr_reachable_cache.cpp +++ b/src/muz/pdr/pdr_reachable_cache.cpp @@ -21,13 +21,13 @@ Revision History: namespace pdr { - reachable_cache::reachable_cache(pdr::manager & pm, fixedpoint_params const& params) + reachable_cache::reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm) : m(pm.get_manager()), m_pm(pm), m_ctx(0), m_ref_holder(m), m_disj_connector(m), - m_cache_mode((datalog::PDR_CACHE_MODE)params.cache_mode()) { + m_cache_mode(cm) { if (m_cache_mode == datalog::CONSTRAINT_CACHE) { m_ctx = pm.mk_fresh(); m_ctx->assert_expr(m_pm.get_background()); diff --git a/src/muz/pdr/pdr_reachable_cache.h b/src/muz/pdr/pdr_reachable_cache.h index 48caa22a5..aef6f7a4f 100644 --- a/src/muz/pdr/pdr_reachable_cache.h +++ b/src/muz/pdr/pdr_reachable_cache.h @@ -47,7 +47,7 @@ namespace pdr { void add_disjuncted_formula(expr * f); public: - reachable_cache(pdr::manager & pm, fixedpoint_params const& params); + reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm); void add_init(app * f) { add_disjuncted_formula(f); } diff --git a/src/muz/pdr/pdr_smt_context_manager.cpp b/src/muz/pdr/pdr_smt_context_manager.cpp index 49ae35423..b6aa8411d 100644 --- a/src/muz/pdr/pdr_smt_context_manager.cpp +++ b/src/muz/pdr/pdr_smt_context_manager.cpp @@ -113,10 +113,10 @@ namespace pdr { return m_context.get_proof(); } - smt_context_manager::smt_context_manager(smt_params& fp, fixedpoint_params const& p, ast_manager& m): + smt_context_manager::smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m): m_fparams(fp), m(m), - m_max_num_contexts(p.max_num_contexts()), + m_max_num_contexts(max_num_contexts), m_num_contexts(0), m_predicate_list(m) { } diff --git a/src/muz/pdr/pdr_smt_context_manager.h b/src/muz/pdr/pdr_smt_context_manager.h index 7d6eebfbd..4775dc58f 100644 --- a/src/muz/pdr/pdr_smt_context_manager.h +++ b/src/muz/pdr/pdr_smt_context_manager.h @@ -97,7 +97,7 @@ namespace pdr { app_ref_vector m_predicate_list; func_decl_set m_predicate_set; public: - smt_context_manager(smt_params& fp, fixedpoint_params const& p, ast_manager& m); + smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m); ~smt_context_manager(); smt_context* mk_fresh(); void collect_statistics(statistics& st) const; diff --git a/src/muz/pdr/pdr_util.cpp b/src/muz/pdr/pdr_util.cpp index 4db835bed..d021d5fb1 100644 --- a/src/muz/pdr/pdr_util.cpp +++ b/src/muz/pdr/pdr_util.cpp @@ -37,7 +37,6 @@ Notes: #include "rewriter_def.h" #include "util.h" #include "pdr_manager.h" -#include "pdr_prop_solver.h" #include "pdr_util.h" #include "arith_decl_plugin.h" #include "expr_replacer.h" diff --git a/src/muz/rel/dl_base.h b/src/muz/rel/dl_base.h index 1556660d8..d03c94154 100644 --- a/src/muz/rel/dl_base.h +++ b/src/muz/rel/dl_base.h @@ -35,6 +35,30 @@ namespace datalog { class context; class relation_manager; + template + class scoped_rel { + T* m_t; + public: + scoped_rel(T* t) : m_t(t) {} + ~scoped_rel() { if (m_t) { universal_delete(m_t); } } + scoped_rel() : m_t(0) {} + scoped_rel& operator=(T* t) { if (m_t && t != m_t) { universal_delete(m_t); } m_t = t; return *this; } + T* operator->() { return m_t; } + const T* operator->() const { return m_t; } + T& operator*() { return *m_t; } + const T& operator*() const { return *m_t; } + operator bool() const { return m_t!=0; } + T* get() const { return m_t; } + /** + \brief Remove object from \c scoped_rel without deleting it. + */ + T* release() { + T* res = m_t; + m_t = 0; + return res; + } + }; + ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm); context & get_context_from_rel_manager(const relation_manager & rm); @@ -208,6 +232,11 @@ namespace datalog { virtual void operator()(base_object & t, const base_object & intersected_obj) = 0; }; + class intersection_join_filter_fn : public base_fn { + public: + virtual void operator()(base_object & t, const base_object & inter1, const base_object& inter2) = 0; + }; + class default_join_project_fn; /** @@ -303,6 +332,7 @@ namespace datalog { protected: //see \c relation_manager for documentation of the operations + virtual join_fn * mk_join_fn(const base_object & t1, const base_object & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { return 0; } @@ -348,10 +378,22 @@ namespace datalog { const unsigned * t_cols, const unsigned * src_cols) { return 0; } + virtual intersection_filter_fn * mk_filter_by_negation_fn(const base_object & t, const base_object & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { return 0; } + + virtual intersection_join_filter_fn * mk_filter_by_negated_join_fn( + const base_object & t, + const base_object & src1, + const base_object & src2, + unsigned_vector const& t_cols, + unsigned_vector const& src_cols, + unsigned_vector const& src1_cols, + unsigned_vector const& src2_cols) + { return 0; } + }; class base_ancestor { @@ -685,6 +727,7 @@ namespace datalog { typedef relation_infrastructure::union_fn relation_union_fn; typedef relation_infrastructure::mutator_fn relation_mutator_fn; typedef relation_infrastructure::intersection_filter_fn relation_intersection_filter_fn; + typedef relation_infrastructure::intersection_join_filter_fn relation_intersection_join_filter_fn; typedef relation_infrastructure::convenient_join_fn convenient_relation_join_fn; typedef relation_infrastructure::convenient_join_project_fn convenient_relation_join_project_fn; @@ -807,6 +850,7 @@ namespace datalog { typedef table_infrastructure::union_fn table_union_fn; typedef table_infrastructure::mutator_fn table_mutator_fn; typedef table_infrastructure::intersection_filter_fn table_intersection_filter_fn; + typedef table_infrastructure::intersection_join_filter_fn table_intersection_join_filter_fn; typedef table_infrastructure::convenient_join_fn convenient_table_join_fn; typedef table_infrastructure::convenient_join_project_fn convenient_table_join_project_fn; diff --git a/src/muz/rel/dl_compiler.cpp b/src/muz/rel/dl_compiler.cpp index 931846c35..1886563c2 100644 --- a/src/muz/rel/dl_compiler.cpp +++ b/src/muz/rel/dl_compiler.cpp @@ -869,6 +869,7 @@ namespace datalog { bool & dealloc, instruction_block & acc) { uint_set pos_vars; u_map neg_vars; + u_map occs; ast_manager& m = m_context.get_manager(); unsigned pt_len = r->get_positive_tail_size(); unsigned ut_len = r->get_uninterpreted_tail_size(); @@ -882,7 +883,14 @@ namespace datalog { for (unsigned j = 0; j < neg_len; ++j) { expr * e = neg_tail->get_arg(j); if (is_var(e)) { - neg_vars.insert(to_var(e)->get_idx(), e); + unsigned idx = to_var(e)->get_idx(); + neg_vars.insert(idx, e); + if (!occs.contains(idx)) { + occs.insert(idx, 1); + } + else { + occs.find(idx)++; + } } } } @@ -893,11 +901,15 @@ namespace datalog { pos_vars.insert(to_var(e)->get_idx()); } } - // add negative variables that are not in positive: + // add negative variables that are not in positive, but only + // for variables that occur more than once. u_map::iterator it = neg_vars.begin(), end = neg_vars.end(); for (; it != end; ++it) { unsigned v = it->m_key; expr* e = it->m_value; + if (occs.find(v) == 1) { + continue; + } if (!pos_vars.contains(v)) { single_res_expr.push_back(e); reg_idx new_single_res; diff --git a/src/muz/rel/dl_lazy_table.cpp b/src/muz/rel/dl_lazy_table.cpp new file mode 100644 index 000000000..16dbc2d21 --- /dev/null +++ b/src/muz/rel/dl_lazy_table.cpp @@ -0,0 +1,468 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_lazy_table.cpp + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2013-09-04 + +Revision History: + +--*/ + +#include "dl_lazy_table.h" +#include "dl_relation_manager.h" +#include + +namespace datalog { + + // ------------------ + // lazy_table_plugin: + + symbol lazy_table_plugin::mk_name(table_plugin& p) { + std::ostringstream strm; + strm << "lazy_" << p.get_name(); + return symbol(strm.str().c_str()); + } + + table_base * lazy_table_plugin::mk_empty(const table_signature & s) { + return alloc(lazy_table, alloc(lazy_table_base, *this, m_plugin.mk_empty(s))); + } + + lazy_table const& lazy_table_plugin::get(table_base const& tb) { return dynamic_cast(tb); } + lazy_table& lazy_table_plugin::get(table_base& tb) { return dynamic_cast(tb); } + lazy_table const* lazy_table_plugin::get(table_base const* tb) { return dynamic_cast(tb); } + lazy_table* lazy_table_plugin::get(table_base* tb) { return dynamic_cast(tb); } + + // -------------------------- + // lazy_table_plugin::join_fn + + class lazy_table_plugin::join_fn : public convenient_table_join_fn { + public: + join_fn(table_signature const& s1, table_signature const& s2, unsigned col_cnt, + unsigned const* cols1, unsigned const* cols2): + convenient_table_join_fn(s1, s2, col_cnt, cols1, cols2) {} + + virtual table_base* operator()(const table_base& _t1, const table_base& _t2) { + lazy_table const& t1 = get(_t1); + lazy_table const& t2 = get(_t2); + lazy_table_plugin& p = t1.get_lplugin(); + lazy_table_ref* tr = alloc(lazy_table_join, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr(), t1, t2, get_result_signature()); + return alloc(lazy_table, tr); + } + }; + + table_join_fn * lazy_table_plugin::mk_join_fn( + const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (check_kind(t1) && check_kind(t2)) { + return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); + } + else { + return 0; + } + } + + // ------------------------ + // lazy_table_plugin::union + + class lazy_table_plugin::union_fn : public table_union_fn { + public: + void operator()(table_base & _tgt, const table_base & _src, + table_base * _delta) { + lazy_table& tgt = get(_tgt); + lazy_table const& src = get(_src); + lazy_table* delta = get(_delta); + table_base const* t_src = src.eval(); + table_base * t_tgt = tgt.eval(); + table_base * t_delta = delta?delta->eval():0; + verbose_action _t("union"); + table_union_fn* m = tgt.get_lplugin().get_manager().mk_union_fn(*t_tgt, *t_src, t_delta); + SASSERT(m); + (*m)(*t_tgt, *t_src, t_delta); + dealloc(m); + } + }; + + + table_union_fn* lazy_table_plugin::mk_union_fn( + const table_base & tgt, const table_base & src, + const table_base * delta) { + if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { + return alloc(union_fn); + } + else { + return 0; + } + } + + // -------------------------- + // lazy_table_plugin::project + + class lazy_table_plugin::project_fn : public convenient_table_project_fn { + public: + project_fn(table_signature const& orig_sig, unsigned cnt, unsigned const* cols): + convenient_table_project_fn(orig_sig, cnt, cols) + {} + + virtual table_base* operator()(table_base const& _t) { + lazy_table const& t = get(_t); + return alloc(lazy_table, alloc(lazy_table_project, m_removed_cols.size(), m_removed_cols.c_ptr(), t, get_result_signature())); + } + }; + + table_transformer_fn * lazy_table_plugin::mk_project_fn( + const table_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + if (check_kind(t)) { + return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); + } + else { + return 0; + } + } + + // ------------------------- + // lazy_table_plugin::rename + + class lazy_table_plugin::rename_fn : public convenient_table_rename_fn { + public: + rename_fn(table_signature const& orig_sig, unsigned cnt, unsigned const* cols): + convenient_table_rename_fn(orig_sig, cnt, cols) + {} + + virtual table_base* operator()(table_base const& _t) { + lazy_table const& t = get(_t); + return alloc(lazy_table, alloc(lazy_table_rename, m_cycle.size(), m_cycle.c_ptr(), t, get_result_signature())); + } + }; + + table_transformer_fn * lazy_table_plugin::mk_rename_fn( + const table_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + if (check_kind(t)) { + return alloc(rename_fn, t.get_signature(), col_cnt, removed_cols); + } + else { + return 0; + } + } + + + // ----------------------------------- + // lazy_table_plugin::filter_identical + + class lazy_table_plugin::filter_identical_fn : public table_mutator_fn { + unsigned_vector m_cols; + public: + filter_identical_fn(unsigned cnt, unsigned const* cols): m_cols(cnt, cols) {} + + virtual void operator()(table_base& _t) { + lazy_table& t = get(_t); + t.set(alloc(lazy_table_filter_identical, m_cols.size(), m_cols.c_ptr(), t)); + } + }; + + table_mutator_fn * lazy_table_plugin::mk_filter_identical_fn( + const table_base & t, unsigned col_cnt, const unsigned * identical_cols) { + if (check_kind(t)) { + return alloc(filter_identical_fn, col_cnt, identical_cols); + } + else { + return 0; + } + } + + + // ------------------------------------- + // lazy_table_plugin::filter_interpreted + + class lazy_table_plugin::filter_interpreted_fn : public table_mutator_fn { + app_ref m_condition; + public: + filter_interpreted_fn(app_ref& p): m_condition(p) {} + + virtual void operator()(table_base& _t) { + lazy_table& t = get(_t); + t.set(alloc(lazy_table_filter_interpreted, t, m_condition)); + } + }; + + table_mutator_fn * lazy_table_plugin::mk_filter_interpreted_fn( + const table_base & t, app* condition) { + if (check_kind(t)) { + app_ref cond(condition, get_ast_manager()); + return alloc(filter_interpreted_fn, cond); + } + else { + return 0; + } + } + + // ------------------------------------- + // lazy_table_plugin::filter_by_negation + + class lazy_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { + unsigned_vector m_cols1; + unsigned_vector m_cols2; + public: + filter_by_negation_fn(unsigned cnt, unsigned const* cols1, unsigned const* cols2): + m_cols1(cnt, cols1), m_cols2(cnt, cols2) {} + virtual void operator()(table_base & _t, const table_base & _intersected_obj) { + lazy_table& t = get(_t); + lazy_table const& it = get(_intersected_obj); + t.set(alloc(lazy_table_filter_by_negation, t, it, m_cols1, m_cols2)); + } + }; + + table_intersection_filter_fn * lazy_table_plugin::mk_filter_by_negation_fn( + const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) { + if (check_kind(t) && check_kind(negated_obj)) { + return alloc(filter_by_negation_fn, joined_col_cnt, t_cols, negated_cols); + } + else { + return 0; + } + } + + + // ------------------------------- + // lazy_table_plugin::filter_equal + + class lazy_table_plugin::filter_equal_fn : public table_mutator_fn { + table_element m_value; + unsigned m_col; + public: + filter_equal_fn(const table_element & value, unsigned col): + m_value(value), + m_col(col) + { } + + virtual void operator()(table_base& _t) { + lazy_table& t = get(_t); + t.set(alloc(lazy_table_filter_equal, m_col, m_value, t)); + } + }; + + table_mutator_fn * lazy_table_plugin::mk_filter_equal_fn( + const table_base & t, const table_element & value, unsigned col) { + if (check_kind(t)) { + return alloc(filter_equal_fn, value, col); + } + else { + return 0; + } + } + + table_plugin* lazy_table_plugin::mk_sparse(relation_manager& rm) { + table_plugin* sp = rm.get_table_plugin(symbol("sparse")); + SASSERT(sp); + if (sp) { + return alloc(lazy_table_plugin, *sp); + } + else { + return 0; + } + } + + + // ---------- + // lazy_table + + table_base * lazy_table::clone() const { + table_base* t = eval(); + verbose_action _t("clone"); + return alloc(lazy_table, alloc(lazy_table_base, get_lplugin(), t->clone())); + } + table_base * lazy_table::complement(func_decl* p, const table_element * func_columns) const { + table_base* t = eval()->complement(p, func_columns); + return alloc(lazy_table, alloc(lazy_table_base, get_lplugin(), t)); + } + bool lazy_table::empty() const { + return m_ref->eval()->empty(); + } + bool lazy_table::contains_fact(const table_fact & f) const { + return m_ref->eval()->contains_fact(f); + } + void lazy_table::remove_fact(table_element const* fact) { + m_ref->eval()->remove_fact(fact); + } + void lazy_table::remove_facts(unsigned fact_cnt, const table_fact * facts) { + m_ref->eval()->remove_facts(fact_cnt, facts); + } + void lazy_table::remove_facts(unsigned fact_cnt, const table_element * facts) { + m_ref->eval()->remove_facts(fact_cnt, facts); + } + void lazy_table::reset() { + m_ref = alloc(lazy_table_base, get_lplugin(), get_lplugin().m_plugin.mk_empty(get_signature())); + } + void lazy_table::add_fact(table_fact const& f) { + m_ref->eval()->add_fact(f); + } + table_base::iterator lazy_table::begin() const { + return eval()->begin(); + } + table_base::iterator lazy_table::end() const { + return eval()->end(); + } + table_base* lazy_table::eval() const { + return m_ref->eval(); + } + + // ------------------------- + // eval + + + table_base* lazy_table_join::force() { + SASSERT(!m_table); + table_base* t1 = m_t1->eval(); + table_base* t2 = m_t2->eval(); + verbose_action _t("join"); + table_join_fn* join = rm().mk_join_fn(*t1, *t2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); + m_table = (*join)(*t1, *t2); + dealloc(join); + return m_table.get(); + } + + table_base* lazy_table_project::force() { + SASSERT(!m_table); + switch(m_src->kind()) { + case LAZY_TABLE_JOIN: { + lazy_table_join& src = dynamic_cast(*m_src); + table_base* t1 = src.t1()->eval(); + table_base* t2 = src.t2()->eval(); + table_join_fn* j_fn = rm().mk_join_project_fn(*t1, *t2, src.cols1(), src.cols2(), m_cols); + if (j_fn) { + verbose_action _t("join_project"); + m_table = (*j_fn)(*t1, *t2); + dealloc(j_fn); + } + break; + } + case LAZY_TABLE_FILTER_INTERPRETED: { + lazy_table_filter_interpreted& src = dynamic_cast(*m_src); + table_transformer_fn* tr = rm().mk_filter_interpreted_and_project_fn(*src.eval(), src.condition(), m_cols.size(), m_cols.c_ptr()); + if (tr) { + verbose_action _t("filter_interpreted_project"); + m_table = (*tr)(*src.eval()); + dealloc(tr); + } + break; + } + case LAZY_TABLE_FILTER_EQUAL: { + lazy_table_filter_equal& src = dynamic_cast(*m_src); + table_base* t = src.eval(); + table_transformer_fn* tr = rm().mk_select_equal_and_project_fn(*t, src.value(), src.col()); + if (tr) { + verbose_action _t("select_equal_project"); + m_table = (*tr)(*t); + dealloc(tr); + } + break; + } + default: + break; + } + if (m_table) { + return m_table.get(); + } + table_base* src = m_src->eval(); + verbose_action _t("project"); + table_transformer_fn* project = rm().mk_project_fn(*src, m_cols.size(), m_cols.c_ptr()); + SASSERT(project); + m_table = (*project)(*src); + dealloc(project); + return m_table.get(); + } + + table_base* lazy_table_rename::force() { + SASSERT(!m_table); + table_base* src = m_src->eval(); + verbose_action _t("rename"); + table_transformer_fn* rename = rm().mk_rename_fn(*src, m_cols.size(), m_cols.c_ptr()); + m_table = (*rename)(*src); + dealloc(rename); + return m_table.get(); + } + + table_base* lazy_table_filter_identical::force() { + SASSERT(!m_table); + m_table = m_src->eval(); + m_src->release_table(); + m_src = 0; + verbose_action _t("filter_identical"); + table_mutator_fn* m = rm().mk_filter_identical_fn(*m_table, m_cols.size(), m_cols.c_ptr()); + SASSERT(m); + (*m)(*m_table); + dealloc(m); + return m_table.get(); + } + + table_base* lazy_table_filter_equal::force() { + SASSERT(!m_table); + m_table = m_src->eval(); + m_src->release_table(); + m_src = 0; + verbose_action _t("filter_equal"); + table_mutator_fn* m = rm().mk_filter_equal_fn(*m_table, m_value, m_col); + SASSERT(m); + (*m)(*m_table); + dealloc(m); + return m_table.get(); + } + + table_base* lazy_table_filter_interpreted::force() { + SASSERT(!m_table); + m_table = m_src->eval(); + m_src->release_table(); + m_src = 0; + verbose_action _t("filter_interpreted"); + table_mutator_fn* m = rm().mk_filter_interpreted_fn(*m_table, m_condition); + SASSERT(m); + (*m)(*m_table); + dealloc(m); + return m_table.get(); + } + + table_base* lazy_table_filter_by_negation::force() { + SASSERT(!m_table); + m_table = m_tgt->eval(); + m_tgt->release_table(); + m_tgt = 0; + + switch(m_src->kind()) { + + case LAZY_TABLE_JOIN: { + lazy_table_join& src = dynamic_cast(*m_src); + table_base* t1 = src.t1()->eval(); + table_base* t2 = src.t2()->eval(); + verbose_action _t("filter_by_negation_join"); + table_intersection_join_filter_fn* jn = rm().mk_filter_by_negated_join_fn(*m_table, *t1, *t2, cols1(), cols2(), src.cols1(), src.cols2()); + if (jn) { + (*jn)(*m_table, *t1, *t2); + dealloc(jn); + return m_table.get(); + } + break; + } + default: + break; + } + table_base* src = m_src->eval(); + verbose_action _t("filter_by_negation"); + table_intersection_filter_fn* m = rm().mk_filter_by_negation_fn(*m_table, *src, m_cols1, m_cols2); + SASSERT(m); + (*m)(*m_table, *src); + dealloc(m); + return m_table.get(); + } +} diff --git a/src/muz/rel/dl_lazy_table.h b/src/muz/rel/dl_lazy_table.h new file mode 100644 index 000000000..34a8b6b92 --- /dev/null +++ b/src/muz/rel/dl_lazy_table.h @@ -0,0 +1,305 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_lazy_table.h + +Abstract: + + Structure for delaying table operations. + + +Author: + + Nikolaj Bjorner (nbjorner) 2013-09-04 + +Revision History: + +--*/ + +#ifndef _DL_LAZY_TABLE_H_ +#define _DL_LAZY_TABLE_H_ + +#include "dl_base.h" +#include "ref.h" + +namespace datalog { + + class lazy_table; + + class lazy_table_plugin : public table_plugin { + friend class lazy_table; + class join_fn; + class project_fn; + class union_fn; + class rename_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + class filter_by_negation_fn; + + table_plugin& m_plugin; + + static symbol mk_name(table_plugin& p); + + public: + lazy_table_plugin(table_plugin& p): + table_plugin(mk_name(p), p.get_manager()), + m_plugin(p) {} + + virtual bool can_handle_signature(const table_signature & s) { + return m_plugin.can_handle_signature(s); + } + + virtual table_base * mk_empty(const table_signature & s); + + virtual void set_cancel(bool f) { m_plugin.set_cancel(f); } + + static table_plugin* mk_sparse(relation_manager& rm); + + protected: + virtual table_join_fn * mk_join_fn( + const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual table_union_fn * mk_union_fn( + const table_base & tgt, const table_base & src, + const table_base * delta); + virtual table_transformer_fn * mk_project_fn( + const table_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual table_transformer_fn * mk_rename_fn( + const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual table_mutator_fn * mk_filter_identical_fn( + const table_base & t, unsigned col_cnt, const unsigned * identical_cols); + virtual table_mutator_fn * mk_filter_equal_fn( + const table_base & t, const table_element & value, unsigned col); + virtual table_mutator_fn * mk_filter_interpreted_fn( + const table_base & t, app * condition); + virtual table_intersection_filter_fn * mk_filter_by_negation_fn( + const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols); + + static lazy_table const& get(table_base const& tb); + static lazy_table& get(table_base& tb); + static lazy_table const* get(table_base const* tb); + static lazy_table* get(table_base* tb); + }; + + enum lazy_table_kind { + LAZY_TABLE_BASE, + LAZY_TABLE_JOIN, + LAZY_TABLE_PROJECT, + LAZY_TABLE_RENAME, + LAZY_TABLE_FILTER_IDENTICAL, + LAZY_TABLE_FILTER_EQUAL, + LAZY_TABLE_FILTER_INTERPRETED, + LAZY_TABLE_FILTER_BY_NEGATION + }; + + class lazy_table_ref { + protected: + lazy_table_plugin& m_plugin; + table_signature m_signature; + unsigned m_ref; + scoped_rel m_table; + relation_manager& rm() { return m_plugin.get_manager(); } + virtual table_base* force() = 0; + public: + lazy_table_ref(lazy_table_plugin& p, table_signature const& sig): + m_plugin(p), m_signature(sig), m_ref(0) {} + virtual ~lazy_table_ref() {} + void inc_ref() { ++m_ref; } + void dec_ref() { --m_ref; if (0 == m_ref) dealloc(this); } + void release_table() { m_table.release(); } + + virtual lazy_table_kind kind() const = 0; + table_signature const& get_signature() const { return m_signature; } + lazy_table_plugin & get_lplugin() const { return m_plugin; } + table_base* eval() { if (!m_table) { m_table = force(); } SASSERT(m_table); return m_table.get(); } + }; + + class lazy_table : public table_base { + protected: + mutable ref m_ref; + + public: + lazy_table(lazy_table_ref* t): + table_base(t->get_lplugin(), t->get_signature()), + m_ref(t) + {} + + virtual ~lazy_table() {} + + lazy_table_plugin& get_lplugin() const { + return dynamic_cast(table_base::get_plugin()); + } + + virtual table_base * clone() const; + virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; + virtual bool empty() const; + virtual bool contains_fact(const table_fact & f) const; + virtual void remove_fact(table_element const* fact); + virtual void remove_facts(unsigned fact_cnt, const table_fact * facts); + virtual void remove_facts(unsigned fact_cnt, const table_element * facts); + virtual void reset(); + virtual void add_fact(table_fact const& f); + + virtual unsigned get_size_estimate_rows() const { return 1; } + virtual unsigned get_size_estimate_bytes() const { return 1; } + virtual bool knows_exact_size() const { return false; } + + table_base* eval() const; + + virtual table_base::iterator begin() const; + virtual table_base::iterator end() const; + + lazy_table_ref* ref() const { return m_ref.get(); } + void set(lazy_table_ref* r) { m_ref = r; } + }; + + class lazy_table_base : public lazy_table_ref { + public: + lazy_table_base(lazy_table_plugin & p, table_base* table) + : lazy_table_ref(p, table->get_signature()) { + m_table = table; + // SASSERT(&p.m_plugin == &table->get_lplugin()); + } + virtual ~lazy_table_base() {} + virtual lazy_table_kind kind() const { return LAZY_TABLE_BASE; } + virtual table_base* force() { return m_table.get(); } + }; + + class lazy_table_join : public lazy_table_ref { + unsigned_vector m_cols1; + unsigned_vector m_cols2; + ref m_t1; + ref m_t2; + public: + lazy_table_join(unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, + lazy_table const& t1, lazy_table const& t2, table_signature const& sig) + : lazy_table_ref(t1.get_lplugin(), sig), + m_cols1(col_cnt, cols1), + m_cols2(col_cnt, cols2), + m_t1(t1.ref()), + m_t2(t2.ref()) { } + virtual ~lazy_table_join() {} + virtual lazy_table_kind kind() const { return LAZY_TABLE_JOIN; } + unsigned_vector const& cols1() const { return m_cols1; } + unsigned_vector const& cols2() const { return m_cols2; } + lazy_table_ref* t1() const { return m_t1.get(); } + lazy_table_ref* t2() const { return m_t2.get(); } + virtual table_base* force(); + }; + + + class lazy_table_project : public lazy_table_ref { + unsigned_vector m_cols; + ref m_src; + public: + lazy_table_project(unsigned col_cnt, const unsigned * cols, lazy_table const& src, table_signature const& sig) + : lazy_table_ref(src.get_lplugin(), sig), + m_cols(col_cnt, cols), + m_src(src.ref()) {} + virtual ~lazy_table_project() {} + + virtual lazy_table_kind kind() const { return LAZY_TABLE_PROJECT; } + unsigned_vector const& cols() const { return m_cols; } + lazy_table_ref* src() const { return m_src.get(); } + virtual table_base* force(); + }; + + class lazy_table_rename : public lazy_table_ref { + unsigned_vector m_cols; + ref m_src; + public: + lazy_table_rename(unsigned col_cnt, const unsigned * cols, lazy_table const& src, table_signature const& sig) + : lazy_table_ref(src.get_lplugin(), sig), + m_cols(col_cnt, cols), + m_src(src.ref()) {} + virtual ~lazy_table_rename() {} + + virtual lazy_table_kind kind() const { return LAZY_TABLE_RENAME; } + unsigned_vector const& cols() const { return m_cols; } + lazy_table_ref* src() const { return m_src.get(); } + virtual table_base* force(); + }; + + class lazy_table_filter_identical : public lazy_table_ref { + unsigned_vector m_cols; + ref m_src; + public: + lazy_table_filter_identical(unsigned col_cnt, const unsigned * cols, lazy_table const& src) + : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_cols(col_cnt, cols), m_src(src.ref()) {} + virtual ~lazy_table_filter_identical() {} + + virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_IDENTICAL; } + unsigned_vector const& cols() const { return m_cols; } + lazy_table_ref* src() const { return m_src.get(); } + virtual table_base* force(); + }; + + class lazy_table_filter_equal : public lazy_table_ref { + unsigned m_col; + table_element m_value; + ref m_src; + public: + lazy_table_filter_equal(unsigned col, table_element value, lazy_table const& src) + : lazy_table_ref(src.get_lplugin(), src.get_signature()), + m_col(col), + m_value(value), + m_src(src.ref()) {} + virtual ~lazy_table_filter_equal() {} + + virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_EQUAL; } + unsigned col() const { return m_col; } + table_element value() const { return m_value; } + lazy_table_ref* src() const { return m_src.get(); } + virtual table_base* force(); + }; + + class lazy_table_filter_interpreted : public lazy_table_ref { + app_ref m_condition; + ref m_src; + public: + lazy_table_filter_interpreted(lazy_table const& src, app* condition) + : lazy_table_ref(src.get_lplugin(), src.get_signature()), + m_condition(condition, src.get_lplugin().get_ast_manager()), m_src(src.ref()) {} + virtual ~lazy_table_filter_interpreted() {} + + virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_INTERPRETED; } + app* condition() const { return m_condition; } + lazy_table_ref* src() const { return m_src.get(); } + virtual table_base* force(); + }; + + + class lazy_table_filter_by_negation : public lazy_table_ref { + ref m_tgt; + ref m_src; + unsigned_vector m_cols1; + unsigned_vector m_cols2; + public: + lazy_table_filter_by_negation(lazy_table const& tgt, lazy_table const& src, + unsigned_vector const& c1, unsigned_vector const& c2) + : lazy_table_ref(tgt.get_lplugin(), tgt.get_signature()), + m_tgt(tgt.ref()), + m_src(src.ref()), + m_cols1(c1), + m_cols2(c2) {} + virtual ~lazy_table_filter_by_negation() {} + virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_BY_NEGATION; } + lazy_table_ref* tgt() const { return m_tgt.get(); } + lazy_table_ref* src() const { return m_src.get(); } + unsigned_vector const& cols1() const { return m_cols1; } + unsigned_vector const& cols2() const { return m_cols2; } + virtual table_base* force(); + }; + + +} + +#endif diff --git a/src/muz/rel/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp index 457ef28c0..9421b26df 100644 --- a/src/muz/rel/dl_relation_manager.cpp +++ b/src/muz/rel/dl_relation_manager.cpp @@ -167,8 +167,13 @@ namespace datalog { register_relation_plugin_impl(tr_plugin); m_table_relation_plugins.insert(plugin, tr_plugin); + if (plugin->get_name()==get_context().default_table()) { + m_favourite_table_plugin = plugin; + m_favourite_relation_plugin = tr_plugin; + } + symbol checker_name = get_context().default_table_checker(); - if(get_context().default_table_checked() && get_table_plugin(checker_name)) { + if (get_context().default_table_checked() && get_table_plugin(checker_name)) { if( m_favourite_table_plugin && (plugin==m_favourite_table_plugin || plugin->get_name()==checker_name) ) { symbol checked_name = get_context().default_table(); @@ -178,7 +183,7 @@ namespace datalog { register_plugin(checking_plugin); m_favourite_table_plugin = checking_plugin; } - if(m_favourite_relation_plugin && m_favourite_relation_plugin->from_table()) { + if (m_favourite_relation_plugin && m_favourite_relation_plugin->from_table()) { table_relation_plugin * fav_rel_plugin = static_cast(m_favourite_relation_plugin); if(&fav_rel_plugin->get_table_plugin()==plugin || plugin->get_name()==checker_name) { @@ -577,6 +582,7 @@ namespace datalog { relation_plugin * p2 = &t2.get_plugin(); relation_join_fn * res = p1->mk_join_fn(t1, t2, col_cnt, cols1, cols2); + if(!res && p1!=p2) { res = p2->mk_join_fn(t1, t2, col_cnt, cols1, cols2); } @@ -1538,6 +1544,19 @@ namespace datalog { return res; } + + table_intersection_join_filter_fn* relation_manager::mk_filter_by_negated_join_fn( + const table_base & t, + const table_base & src1, + const table_base & src2, + unsigned_vector const& t_cols, + unsigned_vector const& src_cols, + unsigned_vector const& src1_cols, + unsigned_vector const& src2_cols) { + return t.get_plugin().mk_filter_by_negated_join_fn(t, src1, src2, t_cols, src_cols, src1_cols, src2_cols); + } + + class relation_manager::default_table_select_equal_and_project_fn : public table_transformer_fn { scoped_ptr m_filter; diff --git a/src/muz/rel/dl_relation_manager.h b/src/muz/rel/dl_relation_manager.h index 9f12b4bb6..2c148c5e6 100644 --- a/src/muz/rel/dl_relation_manager.h +++ b/src/muz/rel/dl_relation_manager.h @@ -564,6 +564,19 @@ namespace datalog { return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); } + /** + combined filter by negation with a join. + */ + table_intersection_join_filter_fn* mk_filter_by_negated_join_fn( + const table_base & t, + const table_base & src1, + const table_base & src2, + unsigned_vector const& t_cols, + unsigned_vector const& src_cols, + unsigned_vector const& src1_cols, + unsigned_vector const& src2_cols); + + /** \c t must contain at least one functional column. diff --git a/src/muz/rel/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp index 2cae7771f..954fcad86 100644 --- a/src/muz/rel/dl_sparse_table.cpp +++ b/src/muz/rel/dl_sparse_table.cpp @@ -24,6 +24,7 @@ Revision History: namespace datalog { + // ----------------------------------- // // entry_storage @@ -415,7 +416,7 @@ namespace datalog { } //We will change the content of the reserve; which does not change the 'high-level' //content of the table. - sparse_table & t = const_cast(m_table); + sparse_table & t = const_cast(m_table); t.write_into_reserve(m_key_fact.c_ptr()); store_offset res; @@ -461,6 +462,8 @@ namespace datalog { sparse_table::key_indexer& sparse_table::get_key_indexer(unsigned key_len, const unsigned * key_cols) const { + verbose_action _va("get_key_indexer"); + #if Z3DEBUG //We allow indexes only on non-functional columns because we want to be able to modify them //without having to worry about updating indexes. @@ -506,6 +509,7 @@ namespace datalog { } bool sparse_table::add_fact(const char * data) { + verbose_action _va("add_fact", 3); m_data.write_into_reserve(data); return add_reserve_content(); } @@ -520,6 +524,7 @@ namespace datalog { } bool sparse_table::contains_fact(const table_fact & f) const { + verbose_action _va("contains_fact", 2); sparse_table & t = const_cast(*this); t.write_into_reserve(f.c_ptr()); unsigned func_col_cnt = get_signature().functional_columns(); @@ -542,6 +547,7 @@ namespace datalog { } bool sparse_table::fetch_fact(table_fact & f) const { + verbose_action _va("fetch_fact", 2); const table_signature & sig = get_signature(); SASSERT(f.size() == sig.size()); if (sig.functional_columns() == 0) { @@ -567,6 +573,7 @@ namespace datalog { This is ok as long as we do not allow indexing on functional columns. */ void sparse_table::ensure_fact(const table_fact & f) { + verbose_action _va("ensure_fact", 2); const table_signature & sig = get_signature(); if (sig.functional_columns() == 0) { add_fact(f); @@ -586,6 +593,7 @@ namespace datalog { } void sparse_table::remove_fact(const table_element* f) { + verbose_action _va("remove_fact", 2); //first insert the fact so that we find it's original location and remove it write_into_reserve(f); if (!m_data.remove_reserve_content()) { @@ -638,6 +646,7 @@ namespace datalog { unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, const unsigned * removed_cols, bool tables_swapped, sparse_table & result) { + verbose_action _va("join_project", 1); unsigned t1_entry_size = t1.m_fact_size; unsigned t2_entry_size = t2.m_fact_size; @@ -737,15 +746,21 @@ namespace datalog { reset(); } + sparse_table const& sparse_table_plugin::get(table_base const& t) { return dynamic_cast(t); } + sparse_table& sparse_table_plugin::get(table_base& t) { return dynamic_cast(t); } + sparse_table const* sparse_table_plugin::get(table_base const* t) { return dynamic_cast(t); } + sparse_table* sparse_table_plugin::get(table_base* t) { return dynamic_cast(t); } + + void sparse_table_plugin::reset() { table_pool::iterator it = m_pool.begin(); table_pool::iterator end = m_pool.end(); for (; it!=end; ++it) { sp_table_vector * vect = it->m_value; - sp_table_vector::iterator it = vect->begin(); - sp_table_vector::iterator end = vect->end(); - for (; it!=end; ++it) { - (*it)->destroy(); //calling deallocate() would only put the table back into the pool + sp_table_vector::iterator vit = vect->begin(); + sp_table_vector::iterator vend = vect->end(); + for (; vit!=vend; ++vit) { + (*vit)->destroy(); //calling deallocate() would only put the table back into the pool } dealloc(vect); } @@ -759,6 +774,7 @@ namespace datalog { } void sparse_table_plugin::recycle(sparse_table * t) { + verbose_action _va("recycle", 2); const table_signature & sig = t->get_signature(); t->reset(); @@ -785,7 +801,7 @@ namespace datalog { } sparse_table * sparse_table_plugin::mk_clone(const sparse_table & t) { - sparse_table * res = static_cast(mk_empty(t.get_signature())); + sparse_table * res = get(mk_empty(t.get_signature())); res->m_data = t.m_data; return res; } @@ -813,12 +829,13 @@ namespace datalog { virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { - const sparse_table & t1 = static_cast(tb1); - const sparse_table & t2 = static_cast(tb2); + verbose_action _va("join_project"); + const sparse_table & t1 = get(tb1); + const sparse_table & t2 = get(tb2); sparse_table_plugin & plugin = t1.get_plugin(); - sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + sparse_table * res = get(plugin.mk_empty(get_result_signature())); //If we join with some intersection, want to iterate over the smaller table and //do indexing into the bigger one. If we simply do a product, we want the bigger @@ -868,10 +885,10 @@ namespace datalog { class sparse_table_plugin::union_fn : public table_union_fn { public: virtual void operator()(table_base & tgt0, const table_base & src0, table_base * delta0) { - - sparse_table & tgt = static_cast(tgt0); - const sparse_table & src = static_cast(src0); - sparse_table * delta = static_cast(delta0); + verbose_action _va("union"); + sparse_table & tgt = get(tgt0); + const sparse_table & src = get(src0); + sparse_table * delta = get(delta0); unsigned fact_size = tgt.m_fact_size; const char* ptr = src.m_data.begin(); @@ -927,12 +944,13 @@ namespace datalog { } virtual table_base * operator()(const table_base & tb) { - const sparse_table & t = static_cast(tb); + verbose_action _va("project"); + const sparse_table & t = get(tb); unsigned t_fact_size = t.m_fact_size; sparse_table_plugin & plugin = t.get_plugin(); - sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + sparse_table * res = get(plugin.mk_empty(get_result_signature())); const sparse_table::column_layout & src_layout = t.m_column_layout; const sparse_table::column_layout & tgt_layout = res->m_column_layout; @@ -970,10 +988,11 @@ namespace datalog { } virtual table_base * operator()(const table_base & tb) { - const sparse_table & t = static_cast(tb); + verbose_action _va("select_equal_and_project"); + const sparse_table & t = get(tb); sparse_table_plugin & plugin = t.get_plugin(); - sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + sparse_table * res = get(plugin.mk_empty(get_result_signature())); const sparse_table::column_layout & t_layout = t.m_column_layout; const sparse_table::column_layout & res_layout = res->m_column_layout; @@ -1056,13 +1075,14 @@ namespace datalog { } virtual table_base * operator()(const table_base & tb) { + verbose_action _va("rename"); - const sparse_table & t = static_cast(tb); + const sparse_table & t = get(tb); unsigned t_fact_size = t.m_fact_size; sparse_table_plugin & plugin = t.get_plugin(); - sparse_table * res = static_cast(plugin.mk_empty(get_result_signature())); + sparse_table * res = get(plugin.mk_empty(get_result_signature())); size_t res_fact_size = res->m_fact_size; size_t res_data_size = res_fact_size*t.row_count(); @@ -1117,12 +1137,12 @@ namespace datalog { negation_filter_fn(const table_base & tgt, const table_base & neg, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : convenient_table_negation_filter_fn(tgt, neg, joined_col_cnt, t_cols, negated_cols) { - unsigned neg_fisrt_func = neg.get_signature().first_functional(); + unsigned neg_first_func = neg.get_signature().first_functional(); counter ctr; ctr.count(m_cols2); m_joining_neg_non_functional = ctr.get_max_counter_value() == 1 - && ctr.get_positive_count() == neg_fisrt_func - && (neg_fisrt_func == 0 || ctr.get_max_positive() == neg_fisrt_func-1); + && ctr.get_positive_count() == neg_first_func + && (neg_first_func == 0 || ctr.get_max_positive() == neg_first_func-1); } /** @@ -1133,9 +1153,7 @@ namespace datalog { bool tgt_is_first, svector & res) { SASSERT(res.empty()); - if (!tgt_is_first) { - m_intersection_content.reset(); - } + m_intersection_content.reset(); unsigned joined_col_cnt = m_cols1.size(); unsigned t1_entry_size = t1.m_data.entry_size(); @@ -1194,8 +1212,10 @@ namespace datalog { } virtual void operator()(table_base & tgt0, const table_base & neg0) { - sparse_table & tgt = static_cast(tgt0); - const sparse_table & neg = static_cast(neg0); + sparse_table & tgt = get(tgt0); + const sparse_table & neg = get(neg0); + + verbose_action _va("filter_by_negation"); if (m_cols1.size() == 0) { if (!neg.empty()) { @@ -1215,12 +1235,9 @@ namespace datalog { collect_intersection_offsets(tgt, neg, true, to_remove); } - if (to_remove.empty()) { - return; - } //the largest offsets are at the end, so we can remove them one by one - while(!to_remove.empty()) { + while (!to_remove.empty()) { store_offset removed_ofs = to_remove.back(); to_remove.pop_back(); tgt.m_data.remove_offset(removed_ofs); @@ -1241,6 +1258,148 @@ namespace datalog { return alloc(negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } + /** + T \ (S1 Join S2) + + t_cols - columns from T + s_cols - columns from (S1 Join S2) that are equated + src1_cols - columns from S1 equated with columns from S2 + src2_cols - columns from S2 equated with columns from S1 + + t1_cols - columns from T that map into S1 + s1_cols - matching columns from s_cols for t1_cols + t2s1_cols - columns from T that map into S2, and columns from src1 that join src2 + s2_cols - matching columns from t2s1_cols + + columns from s2 that are equal to a column from s1 that is in s_cols: + + - ... + + */ + + class sparse_table_plugin::negated_join_fn : public table_intersection_join_filter_fn { + typedef sparse_table::store_offset store_offset; + typedef sparse_table::key_value key_value; + typedef sparse_table::key_indexer key_indexer; + unsigned_vector m_t1_cols; + unsigned_vector m_s1_cols; + unsigned_vector m_t2_cols; + unsigned_vector m_s2_cols; + unsigned_vector m_src1_cols; + public: + negated_join_fn( + table_base const& src1, + unsigned_vector const& t_cols, + unsigned_vector const& src_cols, + unsigned_vector const& src1_cols, + unsigned_vector const& src2_cols): + m_src1_cols(src1_cols) { + + // split t_cols and src_cols according to src1, and src2 + + unsigned src1_size = src1.get_signature().size(); + for (unsigned i = 0; i < t_cols.size(); ++i) { + if (src_cols[i] < src1_size) { + m_t1_cols.push_back(t_cols[i]); + m_s1_cols.push_back(src_cols[i]); + } + else { + m_t2_cols.push_back(t_cols[i]); + m_s2_cols.push_back(src_cols[i]); + } + } + m_s2_cols.append(src2_cols); + } + + virtual void operator()(table_base & _t, const table_base & _s1, const table_base& _s2) { + + verbose_action _va("negated_join"); + sparse_table& t = get(_t); + svector to_remove; + collect_to_remove(t, get(_s1), get(_s2), to_remove); + for (unsigned i = 0; i < to_remove.size(); ++i) { + t.m_data.remove_offset(to_remove[i]); + } + t.reset_indexes(); + } + + private: + void collect_to_remove(sparse_table& t, sparse_table const& s1, sparse_table const& s2, svector& to_remove) { + key_value s1_key, s2_key; + SASSERT(&s1 != &s2); + SASSERT(m_s1_cols.size() == m_t1_cols.size()); + SASSERT(m_s2_cols.size() == m_t2_cols.size() + m_src1_cols.size()); + s1_key.resize(m_s1_cols.size()); + s2_key.resize(m_s2_cols.size()); + key_indexer & s1_indexer = s1.get_key_indexer(m_s1_cols.size(), m_s1_cols.c_ptr()); + key_indexer & s2_indexer = s2.get_key_indexer(m_s2_cols.size(), m_s2_cols.c_ptr()); + + store_offset t_after_last = t.m_data.after_last_offset(); + key_indexer::query_result s1_offsets, s2_offsets; + unsigned t_entry_size = t.m_data.entry_size(); + for (store_offset t_ofs = 0; t_ofs < t_after_last; t_ofs += t_entry_size) { + + if (update_key(s1_key, 0, t, t_ofs, m_t1_cols)) { + s1_offsets = s1_indexer.get_matching_offsets(s1_key); + } + key_indexer::offset_iterator it = s1_offsets.begin(); + key_indexer::offset_iterator end = s1_offsets.end(); + for (; it != end; ++it) { + store_offset s1_ofs = *it; + bool upd1 = update_key(s2_key, 0, t, t_ofs, m_t2_cols); + bool upd2 = update_key(s2_key, m_t2_cols.size(), s1, s1_ofs, m_src1_cols); + if (upd1 || upd2) { + s2_offsets = s2_indexer.get_matching_offsets(s2_key); + } + if (!s2_offsets.empty()) { + to_remove.push_back(t_ofs); + break; + } + } + } + } + + inline bool update_key(key_value& key, unsigned key_offset, sparse_table const& t, store_offset ofs, unsigned_vector const& cols) { + bool modified = false; + unsigned sz = cols.size(); + for (unsigned i = 0; i < sz; ++i) { + table_element val = t.get_cell(ofs, cols[i]); + modified = update_key(key[i+key_offset], val) || modified; + } + return modified; + } + + inline bool update_key(table_element& tgt, table_element src) { + if (tgt == src) { + return false; + } + else { + tgt = src; + return true; + } + } + + }; + + table_intersection_join_filter_fn* sparse_table_plugin::mk_filter_by_negated_join_fn( + const table_base & t, + + + const table_base & src1, + const table_base & src2, + unsigned_vector const& t_cols, + unsigned_vector const& src_cols, + unsigned_vector const& src1_cols, + unsigned_vector const& src2_cols) { + if (check_kind(t) && check_kind(src1) && check_kind(src2)) { + return alloc(negated_join_fn, src1, t_cols, src_cols, src1_cols, src2_cols); + } + else { + return 0; + } + } + + unsigned sparse_table::get_size_estimate_bytes() const { unsigned sz = 0; sz += m_data.get_size_estimate_bytes(); diff --git a/src/muz/rel/dl_sparse_table.h b/src/muz/rel/dl_sparse_table.h index 0a60c4e10..0222aa6e2 100644 --- a/src/muz/rel/dl_sparse_table.h +++ b/src/muz/rel/dl_sparse_table.h @@ -48,6 +48,7 @@ namespace datalog { class project_fn; class negation_filter_fn; class select_equal_and_project_fn; + class negated_join_fn; typedef ptr_vector sp_table_vector; typedef map "; tres->display(tout);); if(&tres->get_plugin()!=&plugin.m_table_plugin) { + IF_VERBOSE(1, verbose_stream() << "new type returned\n";); //Operation returned a table of different type than the one which is associated with //this plugin. We need to get a correct table_relation_plugin and create the relation //using it. diff --git a/src/muz/rel/rel_context.cpp b/src/muz/rel/rel_context.cpp index c57ef2606..b44f67644 100644 --- a/src/muz/rel/rel_context.cpp +++ b/src/muz/rel/rel_context.cpp @@ -31,6 +31,7 @@ Revision History: #include"dl_interval_relation.h" #include"karr_relation.h" #include"dl_finite_product_relation.h" +#include"dl_lazy_table.h" #include"dl_sparse_table.h" #include"dl_table.h" #include"dl_table_relation.h" @@ -45,6 +46,7 @@ Revision History: #include"dl_mk_rule_inliner.h" #include"dl_mk_interp_tail_simplifier.h" #include"dl_mk_bit_blast.h" +#include"fixedpoint_params.hpp" namespace datalog { @@ -96,17 +98,19 @@ namespace datalog { // register plugins for builtin tables - get_rmanager().register_plugin(alloc(sparse_table_plugin, get_rmanager())); - get_rmanager().register_plugin(alloc(hashtable_table_plugin, get_rmanager())); - get_rmanager().register_plugin(alloc(bitvector_table_plugin, get_rmanager())); - get_rmanager().register_plugin(alloc(equivalence_table_plugin, get_rmanager())); + relation_manager& rm = get_rmanager(); + rm.register_plugin(alloc(sparse_table_plugin, rm)); + rm.register_plugin(alloc(hashtable_table_plugin, rm)); + rm.register_plugin(alloc(bitvector_table_plugin, rm)); + rm.register_plugin(alloc(equivalence_table_plugin, rm)); + rm.register_plugin(lazy_table_plugin::mk_sparse(rm)); // register plugins for builtin relations - get_rmanager().register_plugin(alloc(bound_relation_plugin, get_rmanager())); - get_rmanager().register_plugin(alloc(interval_relation_plugin, get_rmanager())); - get_rmanager().register_plugin(alloc(karr_relation_plugin, get_rmanager())); + rm.register_plugin(alloc(bound_relation_plugin, rm)); + rm.register_plugin(alloc(interval_relation_plugin, rm)); + rm.register_plugin(alloc(karr_relation_plugin, rm)); } rel_context::~rel_context() { diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index 2201be73e..83842a68b 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -30,6 +30,7 @@ Revision History: #include "for_each_expr.h" #include "matcher.h" #include "scoped_proof.h" +#include "fixedpoint_params.hpp" namespace tb { diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 271003fff..08f295ff4 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -24,6 +24,7 @@ Revision History: #include "expr_safe_replace.h" #include "filter_model_converter.h" #include "dl_mk_interp_tail_simplifier.h" +#include "fixedpoint_params.hpp" namespace datalog { diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index 8583fbe45..31af7a53f 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -52,14 +52,10 @@ namespace datalog { ptr_vector todo; rule_set::decl2rules body2rules; // initialization for reachability - rel_context_base* rc = m_context.get_rel_context(); for (rule_set::iterator it = source.begin(); it != source.end(); ++it) { rule * r = *it; all.insert(r->get_decl()); - bool non_empty = - (rc && !rc->is_empty_relation(r->get_decl())) || - r->get_uninterpreted_tail_size() == 0; - if (non_empty) { + if (r->get_uninterpreted_tail_size() == 0) { if (!reached.contains(r->get_decl())) { reached.insert(r->get_decl()); todo.insert(r->get_decl()); @@ -77,6 +73,17 @@ namespace datalog { } } } + rel_context_base* rc = m_context.get_rel_context(); + if (rc) { + func_decl_set::iterator fit = all.begin(), fend = all.end(); + for (; fit != fend; ++fit) { + if (!rc->is_empty_relation(*fit) && + !reached.contains(*fit)) { + reached.insert(*fit); + todo.insert(*fit); + } + } + } // reachability computation while (!todo.empty()) { func_decl * d = todo.back(); diff --git a/src/muz/transforms/dl_mk_karr_invariants.cpp b/src/muz/transforms/dl_mk_karr_invariants.cpp index 4c0a24b65..b4ab66bc2 100644 --- a/src/muz/transforms/dl_mk_karr_invariants.cpp +++ b/src/muz/transforms/dl_mk_karr_invariants.cpp @@ -39,6 +39,7 @@ Revision History: #include"dl_mk_karr_invariants.h" #include"dl_mk_backwards.h" #include"dl_mk_loop_counter.h" +#include"fixedpoint_params.hpp" namespace datalog { diff --git a/src/muz/transforms/dl_mk_magic_symbolic.cpp b/src/muz/transforms/dl_mk_magic_symbolic.cpp index a06a573ad..57490466c 100644 --- a/src/muz/transforms/dl_mk_magic_symbolic.cpp +++ b/src/muz/transforms/dl_mk_magic_symbolic.cpp @@ -54,6 +54,7 @@ Revision History: #include"dl_mk_magic_symbolic.h" #include"dl_context.h" +#include"fixedpoint_params.hpp" namespace datalog { diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp index 68f5bc3a3..e686495e9 100644 --- a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp @@ -23,6 +23,8 @@ Revision History: #include "dl_context.h" #include "expr_safe_replace.h" #include "expr_abstract.h" +#include"fixedpoint_params.hpp" + namespace datalog { diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp index b24e3e81c..ebb9310a4 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -26,6 +26,8 @@ Revision History: #include "dl_mk_quantifier_instantiation.h" #include "dl_context.h" #include "pattern_inference.h" +#include "fixedpoint_params.hpp" + namespace datalog { diff --git a/src/muz/transforms/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp index 65ce44b8b..3e933b099 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -52,6 +52,7 @@ Subsumption transformation (remove rule): #include "rewriter.h" #include "rewriter_def.h" #include "dl_mk_rule_inliner.h" +#include "fixedpoint_params.hpp" namespace datalog { diff --git a/src/muz/transforms/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp index d666d0ca8..9618db88e 100644 --- a/src/muz/transforms/dl_mk_scale.cpp +++ b/src/muz/transforms/dl_mk_scale.cpp @@ -18,6 +18,7 @@ Revision History: #include"dl_mk_scale.h" #include"dl_context.h" +#include"fixedpoint_params.hpp" namespace datalog { diff --git a/src/muz/transforms/dl_transforms.cpp b/src/muz/transforms/dl_transforms.cpp index 7c5707ccf..2cf48d46b 100644 --- a/src/muz/transforms/dl_transforms.cpp +++ b/src/muz/transforms/dl_transforms.cpp @@ -33,6 +33,7 @@ Revision History: #include"dl_mk_quantifier_instantiation.h" #include"dl_mk_subsumption_checker.h" #include"dl_mk_scale.h" +#include"fixedpoint_params.hpp" namespace datalog { diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index 15681ea36..a487a99b4 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -232,10 +232,9 @@ unsigned read_datalog(char const * file) { TRACE("dl_compiler", ctx.display(tout); rules_code.display(*ctx.get_rel_context(), tout);); - if (ctx.get_params().output_tuples()) { + if (ctx.output_tuples()) { ctx.get_rel_context()->display_output_facts(ctx.get_rules(), std::cout); } - display_statistics( std::cout, ctx, From 93fd36b5da8c87ccc461bf1de79d100b396f3d36 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Sep 2013 11:52:00 -0700 Subject: [PATCH 147/509] revert wrong optimization for single-occurrence negative columns Signed-off-by: Nikolaj Bjorner --- src/muz/rel/dl_compiler.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/muz/rel/dl_compiler.cpp b/src/muz/rel/dl_compiler.cpp index 1886563c2..276e7b836 100644 --- a/src/muz/rel/dl_compiler.cpp +++ b/src/muz/rel/dl_compiler.cpp @@ -869,7 +869,6 @@ namespace datalog { bool & dealloc, instruction_block & acc) { uint_set pos_vars; u_map neg_vars; - u_map occs; ast_manager& m = m_context.get_manager(); unsigned pt_len = r->get_positive_tail_size(); unsigned ut_len = r->get_uninterpreted_tail_size(); @@ -885,12 +884,6 @@ namespace datalog { if (is_var(e)) { unsigned idx = to_var(e)->get_idx(); neg_vars.insert(idx, e); - if (!occs.contains(idx)) { - occs.insert(idx, 1); - } - else { - occs.find(idx)++; - } } } } @@ -901,15 +894,11 @@ namespace datalog { pos_vars.insert(to_var(e)->get_idx()); } } - // add negative variables that are not in positive, but only - // for variables that occur more than once. + // add negative variables that are not in positive u_map::iterator it = neg_vars.begin(), end = neg_vars.end(); for (; it != end; ++it) { unsigned v = it->m_key; expr* e = it->m_value; - if (occs.find(v) == 1) { - continue; - } if (!pos_vars.contains(v)) { single_res_expr.push_back(e); reg_idx new_single_res; From 861a31f4581a664d297b87cc29d9a57329253d6e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Sep 2013 13:30:03 -0700 Subject: [PATCH 148/509] fix build warning from tptp example Signed-off-by: Nikolaj Bjorner --- examples/tptp/tptp5.cpp | 2 +- src/muz/rel/dl_sparse_table.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/tptp/tptp5.cpp b/examples/tptp/tptp5.cpp index 1f50bdfea..d0e174914 100644 --- a/examples/tptp/tptp5.cpp +++ b/examples/tptp/tptp5.cpp @@ -151,7 +151,7 @@ public: void set_index(int idx) { m_symbol_index = idx; } }; -TreeNode* MkToken(alloc_region& r, char* token, int symbolIndex) { +TreeNode* MkToken(alloc_region& r, char const* token, int symbolIndex) { TreeNode* ss; char* symbol = tptp_lval[symbolIndex]; ss = new (r) TreeNode(r, symbol, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); diff --git a/src/muz/rel/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp index 954fcad86..3b2dc333a 100644 --- a/src/muz/rel/dl_sparse_table.cpp +++ b/src/muz/rel/dl_sparse_table.cpp @@ -1132,6 +1132,7 @@ namespace datalog { If tgt_is_first is false, contains the same items as \c res. */ idx_set m_intersection_content; + public: negation_filter_fn(const table_base & tgt, const table_base & neg, From 1496333e5bafeaec1f6d3afba54a930915b74f0a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Sep 2013 09:22:45 -0700 Subject: [PATCH 149/509] fix mint64 build errors Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_util.cpp | 2 -- src/muz/rel/dl_lazy_table.cpp | 3 +-- src/muz/rel/dl_lazy_table.h | 20 ++++++++++---------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index c23f222e5..218f9906a 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -34,8 +34,6 @@ Revision History: namespace datalog { - static unsigned verbose_action_inside = 0; - verbose_action::verbose_action(char const* msg, unsigned lvl): m_lvl(lvl), m_sw(0) { IF_VERBOSE(m_lvl, (verbose_stream() << msg << "...").flush(); diff --git a/src/muz/rel/dl_lazy_table.cpp b/src/muz/rel/dl_lazy_table.cpp index 16dbc2d21..ec97a4bf5 100644 --- a/src/muz/rel/dl_lazy_table.cpp +++ b/src/muz/rel/dl_lazy_table.cpp @@ -19,7 +19,7 @@ Revision History: #include "dl_lazy_table.h" #include "dl_relation_manager.h" -#include +#include namespace datalog { @@ -53,7 +53,6 @@ namespace datalog { virtual table_base* operator()(const table_base& _t1, const table_base& _t2) { lazy_table const& t1 = get(_t1); lazy_table const& t2 = get(_t2); - lazy_table_plugin& p = t1.get_lplugin(); lazy_table_ref* tr = alloc(lazy_table_join, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr(), t1, t2, get_result_signature()); return alloc(lazy_table, tr); } diff --git a/src/muz/rel/dl_lazy_table.h b/src/muz/rel/dl_lazy_table.h index 34a8b6b92..3c0b47367 100644 --- a/src/muz/rel/dl_lazy_table.h +++ b/src/muz/rel/dl_lazy_table.h @@ -156,7 +156,7 @@ namespace datalog { virtual table_base::iterator begin() const; virtual table_base::iterator end() const; - lazy_table_ref* ref() const { return m_ref.get(); } + lazy_table_ref* get_ref() const { return m_ref.get(); } void set(lazy_table_ref* r) { m_ref = r; } }; @@ -184,8 +184,8 @@ namespace datalog { : lazy_table_ref(t1.get_lplugin(), sig), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2), - m_t1(t1.ref()), - m_t2(t2.ref()) { } + m_t1(t1.get_ref()), + m_t2(t2.get_ref()) { } virtual ~lazy_table_join() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_JOIN; } unsigned_vector const& cols1() const { return m_cols1; } @@ -203,7 +203,7 @@ namespace datalog { lazy_table_project(unsigned col_cnt, const unsigned * cols, lazy_table const& src, table_signature const& sig) : lazy_table_ref(src.get_lplugin(), sig), m_cols(col_cnt, cols), - m_src(src.ref()) {} + m_src(src.get_ref()) {} virtual ~lazy_table_project() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_PROJECT; } @@ -219,7 +219,7 @@ namespace datalog { lazy_table_rename(unsigned col_cnt, const unsigned * cols, lazy_table const& src, table_signature const& sig) : lazy_table_ref(src.get_lplugin(), sig), m_cols(col_cnt, cols), - m_src(src.ref()) {} + m_src(src.get_ref()) {} virtual ~lazy_table_rename() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_RENAME; } @@ -233,7 +233,7 @@ namespace datalog { ref m_src; public: lazy_table_filter_identical(unsigned col_cnt, const unsigned * cols, lazy_table const& src) - : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_cols(col_cnt, cols), m_src(src.ref()) {} + : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_cols(col_cnt, cols), m_src(src.get_ref()) {} virtual ~lazy_table_filter_identical() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_IDENTICAL; } @@ -251,7 +251,7 @@ namespace datalog { : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_col(col), m_value(value), - m_src(src.ref()) {} + m_src(src.get_ref()) {} virtual ~lazy_table_filter_equal() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_EQUAL; } @@ -267,7 +267,7 @@ namespace datalog { public: lazy_table_filter_interpreted(lazy_table const& src, app* condition) : lazy_table_ref(src.get_lplugin(), src.get_signature()), - m_condition(condition, src.get_lplugin().get_ast_manager()), m_src(src.ref()) {} + m_condition(condition, src.get_lplugin().get_ast_manager()), m_src(src.get_ref()) {} virtual ~lazy_table_filter_interpreted() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_INTERPRETED; } @@ -286,8 +286,8 @@ namespace datalog { lazy_table_filter_by_negation(lazy_table const& tgt, lazy_table const& src, unsigned_vector const& c1, unsigned_vector const& c2) : lazy_table_ref(tgt.get_lplugin(), tgt.get_signature()), - m_tgt(tgt.ref()), - m_src(src.ref()), + m_tgt(tgt.get_ref()), + m_src(src.get_ref()), m_cols1(c1), m_cols2(c2) {} virtual ~lazy_table_filter_by_negation() {} From 4ad6660f3585c156e136747da08f4f8c97e45c4a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Sep 2013 09:24:35 -0700 Subject: [PATCH 150/509] add const qualifiers to fix warning messages Signed-off-by: Nikolaj Bjorner --- examples/tptp/tptp5.tab.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tptp/tptp5.tab.c b/examples/tptp/tptp5.tab.c index 7fcf7915b..c97225f4c 100644 --- a/examples/tptp/tptp5.tab.c +++ b/examples/tptp/tptp5.tab.c @@ -107,7 +107,7 @@ struct pTreeNode { pTree children[MAX_CHILDREN+1]; }; //----------------------------------------------------------------------------- -int yyerror( char *s ) { +int yyerror( char const *s ) { fprintf( stderr, "%s in line %d at item \"%s\".\n", s, yylineno, yytext); return 0; From c87ae1e99b64b8ad250076ae3d60fa0c5d6cf623 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Sep 2013 23:05:18 -0700 Subject: [PATCH 151/509] add transformation to reduce overhead of negation for predicates with free variables Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_context.cpp | 1 + src/muz/rel/rel_context.cpp | 2 + .../dl_mk_separate_negated_tails.cpp | 119 ++++++++++++++++++ .../transforms/dl_mk_separate_negated_tails.h | 61 +++++++++ .../transforms/dl_mk_subsumption_checker.h | 2 +- 5 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 src/muz/transforms/dl_mk_separate_negated_tails.cpp create mode 100644 src/muz/transforms/dl_mk_separate_negated_tails.h diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 3a62a9b98..cc2dc02c5 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -232,6 +232,7 @@ namespace datalog { context::~context() { reset(); + dealloc(m_params); } void context::reset() { diff --git a/src/muz/rel/rel_context.cpp b/src/muz/rel/rel_context.cpp index b44f67644..02b00be39 100644 --- a/src/muz/rel/rel_context.cpp +++ b/src/muz/rel/rel_context.cpp @@ -46,6 +46,7 @@ Revision History: #include"dl_mk_rule_inliner.h" #include"dl_mk_interp_tail_simplifier.h" #include"dl_mk_bit_blast.h" +#include"dl_mk_separate_negated_tails.h" #include"fixedpoint_params.hpp" @@ -281,6 +282,7 @@ namespace datalog { transf.register_plugin(alloc(mk_partial_equivalence_transformer, m_context)); transf.register_plugin(alloc(mk_rule_inliner, m_context)); transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context)); + transf.register_plugin(alloc(mk_separate_negated_tails, m_context)); if (m_context.get_params().bit_blast()) { transf.register_plugin(alloc(mk_bit_blast, m_context, 22000)); diff --git a/src/muz/transforms/dl_mk_separate_negated_tails.cpp b/src/muz/transforms/dl_mk_separate_negated_tails.cpp new file mode 100644 index 000000000..18e7feadf --- /dev/null +++ b/src/muz/transforms/dl_mk_separate_negated_tails.cpp @@ -0,0 +1,119 @@ + +#include "dl_mk_separate_negated_tails.h" +#include "dl_context.h" + +namespace datalog { + + mk_separate_negated_tails::mk_separate_negated_tails(context& ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_ctx(ctx) + {} + + bool mk_separate_negated_tails::has_private_vars(rule const& r, unsigned j) { + get_private_vars(r, j); + return !m_vars.empty(); + } + + void mk_separate_negated_tails::get_private_vars(rule const& r, unsigned j) { + m_vars.reset(); + m_fv.reset(); + get_free_vars(r.get_head(), m_fv); + for (unsigned i = 0; i < r.get_tail_size(); ++i) { + if (i != j) { + get_free_vars(r.get_tail(i), m_fv); + } + } + + app* p = r.get_tail(j); + for (unsigned i = 0; i < p->get_num_args(); ++i) { + expr* v = p->get_arg(i); + if (is_var(v)) { + unsigned idx = to_var(v)->get_idx(); + if (idx >= m_fv.size() || !m_fv[idx]) { + m_vars.push_back(v); + } + } + } + } + + void mk_separate_negated_tails::abstract_predicate(app* p, app_ref& q, rule_set& rules) { + expr_ref_vector args(m); + sort_ref_vector sorts(m); + func_decl_ref fn(m); + for (unsigned i = 0; i < p->get_num_args(); ++i) { + expr* arg = p->get_arg(i); + if (!m_vars.contains(arg)) { + args.push_back(arg); + sorts.push_back(m.get_sort(arg)); + } + } + fn = m.mk_fresh_func_decl(p->get_decl()->get_name(), symbol("N"), sorts.size(), sorts.c_ptr(), m.mk_bool_sort()); + m_ctx.register_predicate(fn, false); + q = m.mk_app(fn, args.size(), args.c_ptr()); + bool is_neg = true; + rules.add_rule(rm.mk(q, 1, & p, &is_neg)); + } + + void mk_separate_negated_tails::create_rule(rule const&r, rule_set& rules) { + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned ptsz = r.get_positive_tail_size(); + unsigned tsz = r.get_tail_size(); + app_ref_vector tail(m); + app_ref p(m); + svector neg; + for (unsigned i = 0; i < ptsz; ++i) { + tail.push_back(r.get_tail(i)); + neg.push_back(false); + } + for (unsigned i = ptsz; i < utsz; ++i) { + get_private_vars(r, i); + if (!m_vars.empty()) { + abstract_predicate(r.get_tail(i), p, rules); + tail.push_back(p); + neg.push_back(false); + } + else { + neg.push_back(true); + tail.push_back(r.get_tail(i)); + } + } + for (unsigned i = utsz; i < tsz; ++i) { + tail.push_back(r.get_tail(i)); + neg.push_back(false); + } + rules.add_rule(rm.mk(r.get_head(), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name())); + } + + rule_set * mk_separate_negated_tails::operator()(rule_set const& src) { + scoped_ptr result = alloc(rule_set, m_ctx); + bool has_new_rule = false; + unsigned sz = src.get_num_rules(); + for (unsigned i = 0; i < sz; ++i) { + bool change = false; + rule & r = *src.get_rule(i); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned ptsz = r.get_positive_tail_size(); + for (unsigned j = ptsz; j < utsz; ++j) { + SASSERT(r.is_neg_tail(j)); + if (has_private_vars(r, j)) { + create_rule(r, *result); + has_new_rule = true; + change = true; + break; + } + } + if (!change) { + result->add_rule(&r); + } + } + if (!has_new_rule) { + return 0; + } + else { + result->inherit_predicates(src); + return result.detach(); + } + } +} diff --git a/src/muz/transforms/dl_mk_separate_negated_tails.h b/src/muz/transforms/dl_mk_separate_negated_tails.h new file mode 100644 index 000000000..4b1673307 --- /dev/null +++ b/src/muz/transforms/dl_mk_separate_negated_tails.h @@ -0,0 +1,61 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + mk_separate_negated_tails.h + +Abstract: + + Rule transformer which creates new rules for predicates + in negated tails that use free variables not used + elsewhere. These free variables incur an overhead + on the instructions compiled using dl_compiler. + + Consider the following transformations: + + P(x) :- Exists y, z, u . Q(x,y), !R(y,z), !T(z,u). + => + P(x) :- Exists y, z . Q(x,y), !R(y,z), Exists u . ! T(z,u). + => + P(x) :- Exists y, z . Q(x,y), !R(y,z), TN(z). + TN(z) :- !T(z,u). + + + + +Author: + + Nikolaj Bjorner (nbjorner) 2013-09-09 + +Revision History: + +--*/ + +#ifndef _DL_MK_SEPARAT_NEGATED_TAILS_H_ +#define _DL_MK_SEPARAT_NEGATED_TAILS_H_ + +#include "dl_rule_transformer.h" +#include "dl_context.h" + +namespace datalog { + + class mk_separate_negated_tails : public rule_transformer::plugin { + ast_manager & m; + rule_manager& rm; + context & m_ctx; + ptr_vector m_vars; + ptr_vector m_fv; + + bool has_private_vars(rule const& r, unsigned j); + void get_private_vars(rule const& r, unsigned j); + void abstract_predicate(app* p, app_ref& q, rule_set& rules); + void create_rule(rule const&r, rule_set& rules); + + public: + mk_separate_negated_tails(context& ctx, unsigned priority = 21000); + rule_set * operator()(rule_set const & source); + }; +} + +#endif diff --git a/src/muz/transforms/dl_mk_subsumption_checker.h b/src/muz/transforms/dl_mk_subsumption_checker.h index da42e4202..8d797a9e2 100644 --- a/src/muz/transforms/dl_mk_subsumption_checker.h +++ b/src/muz/transforms/dl_mk_subsumption_checker.h @@ -84,7 +84,7 @@ namespace datalog { reset_dealloc_values(m_ground_unconditional_rule_heads); } - rule_set * operator()(rule_set const & source); + virtual rule_set * operator()(rule_set const & source); }; }; From f4aae5e56adf838c6fe502245170c5b492f62671 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Sep 2013 23:12:55 -0700 Subject: [PATCH 152/509] fix C(R) Signed-off-by: Nikolaj Bjorner --- .../dl_mk_separate_negated_tails.cpp | 18 ++++++++++++++++++ .../transforms/dl_mk_separate_negated_tails.h | 5 +---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/muz/transforms/dl_mk_separate_negated_tails.cpp b/src/muz/transforms/dl_mk_separate_negated_tails.cpp index 18e7feadf..782e1011d 100644 --- a/src/muz/transforms/dl_mk_separate_negated_tails.cpp +++ b/src/muz/transforms/dl_mk_separate_negated_tails.cpp @@ -1,3 +1,21 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + mk_separate_negated_tails.cpp + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2013-09-09 + +Revision History: + +--*/ #include "dl_mk_separate_negated_tails.h" #include "dl_context.h" diff --git a/src/muz/transforms/dl_mk_separate_negated_tails.h b/src/muz/transforms/dl_mk_separate_negated_tails.h index 4b1673307..8cd806f43 100644 --- a/src/muz/transforms/dl_mk_separate_negated_tails.h +++ b/src/muz/transforms/dl_mk_separate_negated_tails.h @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2006 Microsoft Corporation +Copyright (c) 2013 Microsoft Corporation Module Name: @@ -21,9 +21,6 @@ Abstract: P(x) :- Exists y, z . Q(x,y), !R(y,z), TN(z). TN(z) :- !T(z,u). - - - Author: Nikolaj Bjorner (nbjorner) 2013-09-09 From f4e048c1e8ecb72e420f3989984112e185522a50 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Sep 2013 22:23:09 -0700 Subject: [PATCH 153/509] partition inequalities into conjuncts determined by equivalence classes of shared variables Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_farkas_learner.cpp | 138 +++++++++++++++++++++++++++-- 1 file changed, 131 insertions(+), 7 deletions(-) diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index 3cd1932ba..d7ef01aaa 100644 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ b/src/muz/pdr/pdr_farkas_learner.cpp @@ -46,6 +46,9 @@ namespace pdr { app_ref_vector m_ineqs; vector m_coeffs; + unsigned m_time; + unsigned_vector m_roots, m_size, m_his, m_reps, m_ts; + void mk_coerce(expr*& e1, expr*& e2) { if (a.is_int(e1) && a.is_real(e2)) { e1 = a.mk_to_real(e1); @@ -146,7 +149,7 @@ namespace pdr { } public: - constr(ast_manager& m) : m(m), a(m), m_ineqs(m) {} + constr(ast_manager& m) : m(m), a(m), m_ineqs(m), m_time(0) {} /** add a multiple of constraint c to the current constr */ void add(rational const & coef, app * c) { @@ -180,12 +183,133 @@ namespace pdr { tout << m_coeffs[i] << ": " << mk_pp(m_ineqs[i].get(), m) << "\n"; } ); + + res = extract_consequence(0, m_coeffs.size()); + +#if 1 + // partition equalities into variable disjoint sets. + // take the conjunction of these instead of the + // linear combination. + partition_ineqs(); + expr_ref_vector lits(m); + unsigned lo = 0; + for (unsigned i = 0; i < m_his.size(); ++i) { + unsigned hi = m_his[i]; + lits.push_back(extract_consequence(lo, hi)); + lo = hi; + } + res = qe::mk_or(lits); + IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << mk_pp(res, m) << "\n"; } }); +#endif + } + + private: + + // partition inequalities into variable disjoint sets. + void partition_ineqs() { + m_roots.reset(); + m_size.reset(); + m_reps.reset(); + m_his.reset(); + ++m_time; + for (unsigned i = 0; i < m_ineqs.size(); ++i) { + m_reps.push_back(process_term(m_ineqs[i].get())); + } + unsigned head = 0; + while (head < m_ineqs.size()) { + unsigned r = find(m_reps[head]); + unsigned tail = head; + for (unsigned i = head+1; i < m_ineqs.size(); ++i) { + if (find(m_reps[i]) == r) { + ++tail; + if (tail != i) { + SASSERT(tail < i); + std::swap(m_reps[tail], m_reps[i]); + app_ref tmp(m); + tmp = m_ineqs[i].get(); + m_ineqs[i] = m_ineqs[tail].get(); + m_ineqs[tail] = tmp; + std::swap(m_coeffs[tail], m_coeffs[i]); + } + } + } + head = tail + 1; + m_his.push_back(head); + } + } + + unsigned find(unsigned idx) { + if (m_ts.size() <= idx) { + m_roots.resize(idx+1); + m_size.resize(idx+1); + m_ts.resize(idx+1); + m_roots[idx] = idx; + m_ts[idx] = m_time; + m_size[idx] = 1; + return idx; + } + if (m_ts[idx] != m_time) { + m_size[idx] = 1; + m_ts[idx] = m_time; + m_roots[idx] = idx; + return idx; + } + while (true) { + if (m_roots[idx] == idx) { + return idx; + } + idx = m_roots[idx]; + } + } + + void merge(unsigned i, unsigned j) { + i = find(i); + j = find(j); + if (i == j) { + return; + } + if (m_size[i] > m_size[j]) { + std::swap(i, j); + } + m_roots[i] = j; + m_size[j] += m_size[i]; + } + + unsigned process_term(expr* e) { + unsigned r = e->get_id(); + ptr_vector todo; + ast_mark mark; + todo.push_back(e); + while (!todo.empty()) { + e = todo.back(); + todo.pop_back(); + if (mark.is_marked(e)) { + continue; + } + mark.mark(e, true); + if (is_uninterp(e)) { + merge(r, e->get_id()); + } + if (is_app(e)) { + app* a = to_app(e); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + todo.push_back(a->get_arg(i)); + } + } + } + return r; + } + + + expr_ref extract_consequence(unsigned lo, unsigned hi) { + bool is_int = is_int_sort(); app_ref zero(a.mk_numeral(rational::zero(), is_int), m); + expr_ref res(m); res = zero; bool is_strict = false; bool is_eq = true; expr* x, *y; - for (unsigned i = 0; i < m_coeffs.size(); ++i) { + for (unsigned i = lo; i < hi; ++i) { app* c = m_ineqs[i].get(); if (m.is_eq(c, x, y)) { mul(m_coeffs[i], x, res); @@ -220,12 +344,12 @@ namespace pdr { params.set_bool("gcd_rounding", true); rw.updt_params(params); proof_ref pr(m); - expr_ref tmp(m); - rw(res, tmp, pr); - fix_dl(tmp); - res = tmp; + expr_ref result(m); + rw(res, result, pr); + fix_dl(result); + return result; } - + // patch: swap addends to make static // features recognize difference constraint. void fix_dl(expr_ref& r) { From 0aaa67fa7df67bddde88cd75796fedaa8c51aa0e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Sep 2013 22:45:37 -0700 Subject: [PATCH 154/509] check for uninterpreted functions in tail for PDR Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_context.cpp | 2 ++ src/muz/fp/dl_cmds.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index cc2dc02c5..7eed67ff0 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -675,9 +675,11 @@ namespace datalog { case PDR_ENGINE: check_existential_tail(r); check_positive_predicates(r); + check_uninterpreted_free(r); break; case QPDR_ENGINE: check_positive_predicates(r); + check_uninterpreted_free(r); break; case BMC_ENGINE: check_positive_predicates(r); diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index 35af38630..f6965b084 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -223,6 +223,7 @@ public: dlctx.updt_params(m_params); unsigned timeout = m_dl_ctx->get_params().timeout(); cancel_eh eh(dlctx); + bool query_exn = false; lbool status = l_undef; { scoped_ctrl_c ctrlc(eh); @@ -237,6 +238,7 @@ public: } catch (z3_exception& ex) { ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl; + query_exn = true; } } switch (status) { @@ -269,7 +271,7 @@ public: break; case datalog::OK: - UNREACHABLE(); + SASSERT(query_exn); break; case datalog::CANCELED: From 4af4466821c1489da0427c6cb0db5171d84a1185 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Sep 2013 12:19:46 -0700 Subject: [PATCH 155/509] add qe_arith routine for LW projection on monomomes Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_farkas_learner.cpp | 22 +- src/muz/pdr/pdr_farkas_learner.h | 3 + src/muz/pdr/pdr_util.h | 2 - src/qe/qe_arith.cpp | 321 +++++++++++++++++++++++++++++ src/qe/qe_arith.h | 16 ++ src/test/main.cpp | 1 + src/test/qe_arith.cpp | 64 ++++++ 7 files changed, 422 insertions(+), 7 deletions(-) create mode 100644 src/qe/qe_arith.cpp create mode 100644 src/qe/qe_arith.h create mode 100644 src/test/qe_arith.cpp diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index d7ef01aaa..b54eb0117 100644 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ b/src/muz/pdr/pdr_farkas_learner.cpp @@ -151,6 +151,11 @@ namespace pdr { public: constr(ast_manager& m) : m(m), a(m), m_ineqs(m), m_time(0) {} + void reset() { + m_ineqs.reset(); + m_coeffs.reset(); + } + /** add a multiple of constraint c to the current constr */ void add(rational const & coef, app * c) { bool is_pos = true; @@ -300,7 +305,6 @@ namespace pdr { return r; } - expr_ref extract_consequence(unsigned lo, unsigned hi) { bool is_int = is_int_sort(); app_ref zero(a.mk_numeral(rational::zero(), is_int), m); @@ -373,6 +377,7 @@ namespace pdr { farkas_learner::farkas_learner(smt_params& params, ast_manager& outer_mgr) : m_proof_params(get_proof_params(params)), m_pr(PROOF_MODE), + m_constr(0), m_combine_farkas_coefficients(true), p2o(m_pr, outer_mgr), o2p(outer_mgr, m_pr) @@ -381,6 +386,10 @@ namespace pdr { m_ctx = alloc(smt::kernel, m_pr, m_proof_params); } + farkas_learner::~farkas_learner() { + dealloc(m_constr); + } + smt_params farkas_learner::get_proof_params(smt_params& orig_params) { smt_params res(orig_params); res.m_arith_bound_prop = BP_NONE; @@ -538,11 +547,14 @@ namespace pdr { { ast_manager& m = res.get_manager(); if (m_combine_farkas_coefficients) { - constr res_c(m); - for(unsigned i = 0; i < n; ++i) { - res_c.add(coeffs[i], lits[i]); + if (!m_constr) { + m_constr = alloc(constr, m); } - res_c.get(res); + m_constr->reset(); + for (unsigned i = 0; i < n; ++i) { + m_constr->add(coeffs[i], lits[i]); + } + m_constr->get(res); } else { bool_rewriter rw(m); diff --git a/src/muz/pdr/pdr_farkas_learner.h b/src/muz/pdr/pdr_farkas_learner.h index b623c2035..546759a33 100644 --- a/src/muz/pdr/pdr_farkas_learner.h +++ b/src/muz/pdr/pdr_farkas_learner.h @@ -42,6 +42,7 @@ class farkas_learner { smt_params m_proof_params; ast_manager m_pr; scoped_ptr m_ctx; + constr* m_constr; // // true: produce a combined constraint by applying Farkas coefficients. @@ -80,6 +81,8 @@ class farkas_learner { public: farkas_learner(smt_params& params, ast_manager& m); + ~farkas_learner(); + /** All ast objects have the ast_manager which was passed as an argument to the constructor (i.e. m_outer_mgr) diff --git a/src/muz/pdr/pdr_util.h b/src/muz/pdr/pdr_util.h index 67be6751b..446bde8aa 100644 --- a/src/muz/pdr/pdr_util.h +++ b/src/muz/pdr/pdr_util.h @@ -143,8 +143,6 @@ namespace pdr { */ void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml); - - /** \brief hoist non-boolean if expressions. */ diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp new file mode 100644 index 000000000..85229de44 --- /dev/null +++ b/src/qe/qe_arith.cpp @@ -0,0 +1,321 @@ +/*++ +Copyright (c) 2010 Microsoft Corporation + +Module Name: + + qe_arith.cpp + +Abstract: + + Simple projection function for real arithmetic based on Loos-W. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-09-12 + +Revision History: + + +--*/ + +#include "qe_arith.h" +#include "qe_util.h" +#include "arith_decl_plugin.h" +#include "ast_pp.h" +#include "th_rewriter.h" + +namespace qe { + + class arith_project_util { + ast_manager& m; + arith_util a; + th_rewriter m_rw; + expr_ref_vector m_ineq_terms; + vector m_ineq_coeffs; + svector m_ineq_strict; + + struct cant_project {}; + + // TBD: replace by "contains_x" class. + + bool contains(app* var, expr* t) const { + ast_mark mark; + ptr_vector todo; + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + if (mark.is_marked(t)) { + continue; + } + mark.mark(t, true); + if (var == t) { + return true; + } + SASSERT(is_app(t)); + app* ap = to_app(t); + todo.append(ap->get_num_args(), ap->get_args()); + } + return false; + } + + void is_linear(app* var, rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { + expr* t1, *t2; + rational mul1; + if (t == var) { + c += mul; + } + else if (a.is_mul(t, t1, t2) && a.is_numeral(t1, mul1)) { + is_linear(var, mul* mul1, t2, c, ts); + } + else if (a.is_mul(t, t1, t2) && a.is_numeral(t2, mul1)) { + is_linear(var, mul* mul1, t1, c, ts); + } + else if (a.is_add(t)) { + app* ap = to_app(t); + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + is_linear(var, mul, ap->get_arg(i), c, ts); + } + } + else if (a.is_sub(t, t1, t2)) { + is_linear(var, mul, t1, c, ts); + is_linear(var, -mul, t2, c, ts); + } + else if (a.is_uminus(t, t1)) { + is_linear(var, -mul, t1, c, ts); + } + else if (a.is_numeral(t, mul1)) { + ts.push_back(a.mk_numeral(mul*mul1, m.get_sort(t))); + } + else if (contains(var, t)) { + IF_VERBOSE(1, verbose_stream() << mk_pp(t, m) << "\n";); + throw cant_project(); + } + else if (mul.is_one()) { + ts.push_back(t); + } + else { + ts.push_back(a.mk_mul(a.mk_numeral(mul, m.get_sort(t)), t)); + } + } + + bool is_linear(app* var, expr* lit, rational& c, expr_ref& t, bool& is_strict) { + if (!contains(var, lit)) { + return false; + } + expr* e1, *e2; + c.reset(); + sort* s; + expr_ref_vector ts(m); + bool is_not = m.is_not(lit, lit); + rational mul(1); + if (is_not) { + mul.neg(); + } + SASSERT(!m.is_not(lit)); + if (a.is_le(lit, e1, e2) || a.is_ge(lit, e2, e1)) { + is_linear(var, mul, e1, c, ts); + is_linear(var, -mul, e2, c, ts); + s = m.get_sort(e1); + is_strict = is_not; + } + else if (a.is_lt(lit, e1, e2) || a.is_gt(lit, e2, e1)) { + is_linear(var, mul, e1, c, ts); + is_linear(var, -mul, e2, c, ts); + s = m.get_sort(e1); + is_strict = !is_not; + } + else if (m.is_eq(lit, e1, e2) && !is_not) { + is_linear(var, mul, e1, c, ts); + is_linear(var, -mul, e2, c, ts); + s = m.get_sort(e1); + is_strict = false; + } + else { + throw cant_project(); + } + if (ts.empty()) { + t = a.mk_numeral(rational(0), s); + } + else { + t = a.mk_add(ts.size(), ts.c_ptr()); + } + return true; + } + + void project(model& model, app* var, expr_ref_vector& lits) { + unsigned num_pos = 0, num_neg = 0; + expr_ref_vector new_lits(m); + for (unsigned i = 0; i < lits.size(); ++i) { + rational c(0); + expr_ref t(m); + bool is_strict; + if (is_linear(var, lits[i].get(), c, t, is_strict)) { + m_ineq_coeffs.push_back(c); + m_ineq_terms.push_back(t); + m_ineq_strict.push_back(is_strict); + if (c.is_pos()) { + ++num_pos; + } + else { + --num_neg; + } + } + else { + new_lits.push_back(lits[i].get()); + } + } + lits.reset(); + lits.append(new_lits); + if (num_pos == 0 || num_neg == 0) { + return; + } + if (num_pos < num_neg) { + unsigned max_t = find_max(model); + for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { + if (i != max_t) { + if (m_ineq_coeffs[i].is_pos()) { + lits.push_back(mk_le(i, max_t)); + } + else { + lits.push_back(mk_lt(i, max_t)); + } + } + } + } + else { + unsigned min_t = find_min(model); + for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { + if (i != min_t) { + if (m_ineq_coeffs[i].is_neg()) { + lits.push_back(mk_le(min_t, i)); + } + else { + lits.push_back(mk_lt(min_t, i)); + } + } + } + } + } + + unsigned find_max(model& mdl) { + return find_min_max(mdl, true); + } + + unsigned find_min(model& mdl) { + return find_min_max(mdl, false); + } + + unsigned find_min_max(model& mdl, bool do_max) { + unsigned result; + bool found = false; + rational found_val(0), r; + expr_ref val(m); + for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { + rational const& ac = m_ineq_coeffs[i]; + if (ac.is_pos() && do_max) { + VERIFY(mdl.eval(m_ineq_terms[i].get(), val)); + VERIFY(a.is_numeral(val, r)); + r /= ac; + if (!found || r > found_val) { + result = i; + found_val = r; + found = true; + } + } + else if (ac.is_neg() && !do_max) { + VERIFY(mdl.eval(m_ineq_terms[i].get(), val)); + VERIFY(a.is_numeral(val, r)); + r /= abs(ac); //// review. + if (!found || r < found_val) { + result = i; + found_val = r; + found = true; + } + } + } + SASSERT(found); + return result; + } + + // ax + t <= 0 + // bx + s <= 0 + // a and b have different signs. + // Infer: a|b|x + |b|t + |a|bx + |a|s <= 0 + // e.g. |b|t + |a|s <= 0 + expr_ref mk_lt(unsigned i, unsigned j) { + rational const& ac = m_ineq_coeffs[i]; + rational const& bc = m_ineq_coeffs[j]; + SASSERT(ac.is_pos() != bc.is_pos()); + SASSERT(ac.is_neg() != bc.is_neg()); + expr* t = m_ineq_terms[i].get(); + expr* s = m_ineq_terms[j].get(); + expr_ref bt = mk_mul(abs(bc), t); + expr_ref as = mk_mul(abs(ac), s); + expr_ref ts = mk_add(bt, as); + expr* z = a.mk_numeral(rational(0), m.get_sort(t)); + expr_ref result(m); + if (m_ineq_strict[i] || m_ineq_strict[j]) { + result = a.mk_lt(ts, z); + } + else { + result = a.mk_le(ts, z); + } + return result; + } + + // ax + t <= 0 + // bx + s <= 0 + // a and b have same signs. + // encode:// t/|a| <= s/|b| + // e.g. |b|t <= |a|s + expr_ref mk_le(unsigned i, unsigned j) { + rational const& ac = m_ineq_coeffs[i]; + rational const& bc = m_ineq_coeffs[j]; + SASSERT(ac.is_pos() == bc.is_pos()); + SASSERT(ac.is_neg() == bc.is_neg()); + expr* t = m_ineq_terms[i].get(); + expr* s = m_ineq_terms[j].get(); + expr_ref bt = mk_mul(abs(bc), t); + expr_ref as = mk_mul(abs(ac), s); + if (m_ineq_strict[j] && !m_ineq_strict[i]) { + return expr_ref(a.mk_lt(bt, as), m); + } + else { + return expr_ref(a.mk_le(bt, as), m); + } + } + + + expr_ref mk_add(expr* t1, expr* t2) { + return expr_ref(a.mk_add(t1, t2), m); + } + expr_ref mk_mul(rational const& r, expr* t2) { + expr* t1 = a.mk_numeral(r, m.get_sort(t2)); + return expr_ref(a.mk_mul(t1, t2), m); + } + + public: + arith_project_util(ast_manager& m): + m(m), a(m), m_rw(m), m_ineq_terms(m) {} + + expr_ref operator()(model& model, app_ref_vector& vars, expr_ref_vector const& lits) { + expr_ref_vector result(lits); + for (unsigned i = 0; i < vars.size(); ++i) { + project(model, vars[i].get(), result); + } + vars.reset(); + expr_ref res1(m); + expr_ref tmp = qe::mk_and(result); + m_rw(tmp, res1); + return res1; + } + }; + + expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits) { + ast_manager& m = vars.get_manager(); + arith_project_util ap(m); + return ap(model, vars, lits); + } + +} diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h new file mode 100644 index 000000000..230cb36f6 --- /dev/null +++ b/src/qe/qe_arith.h @@ -0,0 +1,16 @@ + +#ifndef __QE_ARITH_H_ +#define __QE_ARITH_H_ + +#include "model.h" + +namespace qe { + /** + Loos-Weispfenning model-based projection for a basic conjunction. + Lits is a vector of literals. + return vector of variables that could not be projected. + */ + expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits); +}; + +#endif diff --git a/src/test/main.cpp b/src/test/main.cpp index b769b20fd..333456369 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -214,6 +214,7 @@ int main(int argc, char ** argv) { TST(quant_solve); TST(rcf); TST(polynorm); + TST(qe_arith); } void initialize_mam() {} diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp new file mode 100644 index 000000000..a2789ee80 --- /dev/null +++ b/src/test/qe_arith.cpp @@ -0,0 +1,64 @@ +#include "qe_arith.h" +#include "th_rewriter.h" +#include "smt2parser.h" +#include "arith_decl_plugin.h" +#include "reg_decl_plugins.h" +#include "arith_rewriter.h" +#include "ast_pp.h" +#include "qe_util.h" +#include "smt_context.h" + + +static expr_ref parse_fml(ast_manager& m, char const* str) { + expr_ref result(m); + cmd_context ctx(false, &m); + ctx.set_ignore_check(true); + std::ostringstream buffer; + buffer << "(declare-const x Real)\n" + << "(declare-const y Real)\n" + << "(declare-const z Real)\n" + << "(declare-const a Real)\n" + << "(declare-const b Real)\n" + << "(assert " << str << ")\n"; + std::istringstream is(buffer.str()); + VERIFY(parse_smt2_commands(ctx, is)); + SASSERT(ctx.begin_assertions() != ctx.end_assertions()); + result = *ctx.begin_assertions(); + return result; +} + +static char const* example1 = "(and (<= x 3.0) (<= (* 3.0 x) y) (<= z y))"; +static char const* example2 = "(and (<= z x) (<= x 3.0) (<= (* 3.0 x) y) (<= z y))"; +static char const* example3 = "(and (<= z x) (<= x 3.0) (< (* 3.0 x) y) (<= z y))"; +static char const* example4 = "(and (<= z x) (<= x 3.0) (not (>= (* 3.0 x) y)) (<= z y))"; + +static void test(char const *ex) { + smt_params params; + params.m_model = true; + ast_manager m; + reg_decl_plugins(m); + arith_util a(m); + expr_ref fml = parse_fml(m, ex); + app_ref_vector vars(m); + expr_ref_vector lits(m); + vars.push_back(m.mk_const(symbol("x"), a.mk_real())); + qe::flatten_and(fml, lits); + + smt::context ctx(m, params); + ctx.assert_expr(fml); + lbool result = ctx.check(); + SASSERT(result == l_true); + ref md; + ctx.get_model(md); + expr_ref pr = qe::arith_project(*md, vars, lits); + + std::cout << mk_pp(fml, m) << "\n"; + std::cout << mk_pp(pr, m) << "\n"; +} + +void tst_qe_arith() { + test(example1); + test(example2); + test(example3); + test(example4); +} From 196aed785e43861064d28461f94f0d97b522c6cc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Sep 2013 13:27:31 -0700 Subject: [PATCH 156/509] fixes for qe_arith Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arith.cpp | 134 +++++++++++++++--------------------------- src/test/qe_arith.cpp | 5 ++ 2 files changed, 51 insertions(+), 88 deletions(-) diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 85229de44..393467cc7 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -23,6 +23,7 @@ Revision History: #include "arith_decl_plugin.h" #include "ast_pp.h" #include "th_rewriter.h" +#include "expr_functors.h" namespace qe { @@ -33,61 +34,39 @@ namespace qe { expr_ref_vector m_ineq_terms; vector m_ineq_coeffs; svector m_ineq_strict; + scoped_ptr m_var; struct cant_project {}; - // TBD: replace by "contains_x" class. - - bool contains(app* var, expr* t) const { - ast_mark mark; - ptr_vector todo; - todo.push_back(t); - while (!todo.empty()) { - t = todo.back(); - todo.pop_back(); - if (mark.is_marked(t)) { - continue; - } - mark.mark(t, true); - if (var == t) { - return true; - } - SASSERT(is_app(t)); - app* ap = to_app(t); - todo.append(ap->get_num_args(), ap->get_args()); - } - return false; - } - - void is_linear(app* var, rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { + void is_linear(rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { expr* t1, *t2; rational mul1; - if (t == var) { + if (t == m_var->x()) { c += mul; } else if (a.is_mul(t, t1, t2) && a.is_numeral(t1, mul1)) { - is_linear(var, mul* mul1, t2, c, ts); + is_linear(mul* mul1, t2, c, ts); } else if (a.is_mul(t, t1, t2) && a.is_numeral(t2, mul1)) { - is_linear(var, mul* mul1, t1, c, ts); + is_linear(mul* mul1, t1, c, ts); } else if (a.is_add(t)) { app* ap = to_app(t); for (unsigned i = 0; i < ap->get_num_args(); ++i) { - is_linear(var, mul, ap->get_arg(i), c, ts); + is_linear(mul, ap->get_arg(i), c, ts); } } else if (a.is_sub(t, t1, t2)) { - is_linear(var, mul, t1, c, ts); - is_linear(var, -mul, t2, c, ts); + is_linear(mul, t1, c, ts); + is_linear(-mul, t2, c, ts); } else if (a.is_uminus(t, t1)) { - is_linear(var, -mul, t1, c, ts); + is_linear(-mul, t1, c, ts); } else if (a.is_numeral(t, mul1)) { ts.push_back(a.mk_numeral(mul*mul1, m.get_sort(t))); } - else if (contains(var, t)) { + else if ((*m_var)(t)) { IF_VERBOSE(1, verbose_stream() << mk_pp(t, m) << "\n";); throw cant_project(); } @@ -99,8 +78,8 @@ namespace qe { } } - bool is_linear(app* var, expr* lit, rational& c, expr_ref& t, bool& is_strict) { - if (!contains(var, lit)) { + bool is_linear(expr* lit, rational& c, expr_ref& t, bool& is_strict) { + if (!(*m_var)(lit)) { return false; } expr* e1, *e2; @@ -114,20 +93,20 @@ namespace qe { } SASSERT(!m.is_not(lit)); if (a.is_le(lit, e1, e2) || a.is_ge(lit, e2, e1)) { - is_linear(var, mul, e1, c, ts); - is_linear(var, -mul, e2, c, ts); + is_linear( mul, e1, c, ts); + is_linear(-mul, e2, c, ts); s = m.get_sort(e1); is_strict = is_not; } else if (a.is_lt(lit, e1, e2) || a.is_gt(lit, e2, e1)) { - is_linear(var, mul, e1, c, ts); - is_linear(var, -mul, e2, c, ts); + is_linear( mul, e1, c, ts); + is_linear(-mul, e2, c, ts); s = m.get_sort(e1); is_strict = !is_not; } else if (m.is_eq(lit, e1, e2) && !is_not) { - is_linear(var, mul, e1, c, ts); - is_linear(var, -mul, e2, c, ts); + is_linear( mul, e1, c, ts); + is_linear(-mul, e2, c, ts); s = m.get_sort(e1); is_strict = false; } @@ -143,14 +122,15 @@ namespace qe { return true; } - void project(model& model, app* var, expr_ref_vector& lits) { - unsigned num_pos = 0, num_neg = 0; + void project(model& model, expr_ref_vector& lits) { + unsigned num_pos = 0; + unsigned num_neg = 0; expr_ref_vector new_lits(m); for (unsigned i = 0; i < lits.size(); ++i) { rational c(0); expr_ref t(m); bool is_strict; - if (is_linear(var, lits[i].get(), c, t, is_strict)) { + if (is_linear(lits[i].get(), c, t, is_strict)) { m_ineq_coeffs.push_back(c); m_ineq_terms.push_back(t); m_ineq_strict.push_back(is_strict); @@ -158,7 +138,7 @@ namespace qe { ++num_pos; } else { - --num_neg; + ++num_neg; } } else { @@ -170,69 +150,39 @@ namespace qe { if (num_pos == 0 || num_neg == 0) { return; } - if (num_pos < num_neg) { - unsigned max_t = find_max(model); - for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { - if (i != max_t) { - if (m_ineq_coeffs[i].is_pos()) { - lits.push_back(mk_le(i, max_t)); - } - else { - lits.push_back(mk_lt(i, max_t)); - } + bool use_pos = num_pos < num_neg; + unsigned max_t = find_max(model, use_pos); + + for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { + if (i != max_t) { + if (m_ineq_coeffs[i].is_pos() == use_pos) { + lits.push_back(mk_le(i, max_t)); } - } - } - else { - unsigned min_t = find_min(model); - for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { - if (i != min_t) { - if (m_ineq_coeffs[i].is_neg()) { - lits.push_back(mk_le(min_t, i)); - } - else { - lits.push_back(mk_lt(min_t, i)); - } + else { + lits.push_back(mk_lt(i, max_t)); } } } } - unsigned find_max(model& mdl) { - return find_min_max(mdl, true); - } - - unsigned find_min(model& mdl) { - return find_min_max(mdl, false); - } - - unsigned find_min_max(model& mdl, bool do_max) { + unsigned find_max(model& mdl, bool do_pos) { unsigned result; bool found = false; rational found_val(0), r; expr_ref val(m); for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { rational const& ac = m_ineq_coeffs[i]; - if (ac.is_pos() && do_max) { + if (ac.is_pos() == do_pos) { VERIFY(mdl.eval(m_ineq_terms[i].get(), val)); VERIFY(a.is_numeral(val, r)); - r /= ac; + r /= abs(ac); + IF_VERBOSE(1, verbose_stream() << "max: " << mk_pp(m_ineq_terms[i].get(), m) << " " << r << " " << (!found || r > found_val) << "\n";); if (!found || r > found_val) { result = i; found_val = r; found = true; } } - else if (ac.is_neg() && !do_max) { - VERIFY(mdl.eval(m_ineq_terms[i].get(), val)); - VERIFY(a.is_numeral(val, r)); - r /= abs(ac); //// review. - if (!found || r < found_val) { - result = i; - found_val = r; - found = true; - } - } } SASSERT(found); return result; @@ -300,11 +250,19 @@ namespace qe { m(m), a(m), m_rw(m), m_ineq_terms(m) {} expr_ref operator()(model& model, app_ref_vector& vars, expr_ref_vector const& lits) { + app_ref_vector new_vars(m); expr_ref_vector result(lits); for (unsigned i = 0; i < vars.size(); ++i) { - project(model, vars[i].get(), result); + m_var = alloc(contains_app, m, vars[i].get()); + try { + project(model, result); + } + catch (cant_project) { + new_vars.push_back(vars[i].get()); + } } vars.reset(); + vars.append(new_vars); expr_ref res1(m); expr_ref tmp = qe::mk_and(result); m_rw(tmp, res1); diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index a2789ee80..8d04e2489 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -17,6 +17,9 @@ static expr_ref parse_fml(ast_manager& m, char const* str) { buffer << "(declare-const x Real)\n" << "(declare-const y Real)\n" << "(declare-const z Real)\n" + << "(declare-const u Real)\n" + << "(declare-const v Real)\n" + << "(declare-const t Real)\n" << "(declare-const a Real)\n" << "(declare-const b Real)\n" << "(assert " << str << ")\n"; @@ -31,6 +34,7 @@ static char const* example1 = "(and (<= x 3.0) (<= (* 3.0 x) y) (<= z y))"; static char const* example2 = "(and (<= z x) (<= x 3.0) (<= (* 3.0 x) y) (<= z y))"; static char const* example3 = "(and (<= z x) (<= x 3.0) (< (* 3.0 x) y) (<= z y))"; static char const* example4 = "(and (<= z x) (<= x 3.0) (not (>= (* 3.0 x) y)) (<= z y))"; +static char const* example5 = "(and (<= y x) (<= z x) (<= x u) (<= x v) (<= x t))"; static void test(char const *ex) { smt_params params; @@ -61,4 +65,5 @@ void tst_qe_arith() { test(example2); test(example3); test(example4); + test(example5); } From 1741671a9cacf7ccc195e5f4232bc6899f9f3c4e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Sep 2013 13:32:35 -0700 Subject: [PATCH 157/509] update test in qe_arith Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arith.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 393467cc7..b4f1a781a 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -168,7 +168,7 @@ namespace qe { unsigned find_max(model& mdl, bool do_pos) { unsigned result; bool found = false; - rational found_val(0), r; + rational found_val(0), r, found_c; expr_ref val(m); for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { rational const& ac = m_ineq_coeffs[i]; @@ -180,11 +180,15 @@ namespace qe { if (!found || r > found_val) { result = i; found_val = r; + found_c = ac; found = true; } } } SASSERT(found); + if (a.is_int(m_var->x()) && !found_c.is_one()) { + throw cant_project(); + } return result; } From 8ab04fb05b85fbd35ddd75be6ec6bd616cf40c21 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Sep 2013 15:27:09 -0700 Subject: [PATCH 158/509] testing qe_arith Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arith.cpp | 53 +++++++++++++++++++++++---------- src/qe/qe_arith.h | 2 ++ src/test/qe_arith.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 16 deletions(-) diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index b4f1a781a..360c2ac8c 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -67,7 +67,7 @@ namespace qe { ts.push_back(a.mk_numeral(mul*mul1, m.get_sort(t))); } else if ((*m_var)(t)) { - IF_VERBOSE(1, verbose_stream() << mk_pp(t, m) << "\n";); + IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(t, m) << "\n";); throw cant_project(); } else if (mul.is_one()) { @@ -111,6 +111,7 @@ namespace qe { is_strict = false; } else { + IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(lit, m) << "\n";); throw cant_project(); } if (ts.empty()) { @@ -125,6 +126,9 @@ namespace qe { void project(model& model, expr_ref_vector& lits) { unsigned num_pos = 0; unsigned num_neg = 0; + m_ineq_terms.reset(); + m_ineq_coeffs.reset(); + m_ineq_strict.reset(); expr_ref_vector new_lits(m); for (unsigned i = 0; i < lits.size(); ++i) { rational c(0); @@ -134,12 +138,16 @@ namespace qe { m_ineq_coeffs.push_back(c); m_ineq_terms.push_back(t); m_ineq_strict.push_back(is_strict); - if (c.is_pos()) { + if (c.is_zero()) { + m_rw(lits[i].get(), t); + new_lits.push_back(t); + } + else if (c.is_pos()) { ++num_pos; } else { ++num_neg; - } + } } else { new_lits.push_back(lits[i].get()); @@ -208,14 +216,15 @@ namespace qe { expr_ref as = mk_mul(abs(ac), s); expr_ref ts = mk_add(bt, as); expr* z = a.mk_numeral(rational(0), m.get_sort(t)); - expr_ref result(m); + expr_ref result1(m), result2(m); if (m_ineq_strict[i] || m_ineq_strict[j]) { - result = a.mk_lt(ts, z); + result1 = a.mk_lt(ts, z); } else { - result = a.mk_le(ts, z); + result1 = a.mk_le(ts, z); } - return result; + m_rw(result1, result2); + return result2; } // ax + t <= 0 @@ -232,12 +241,15 @@ namespace qe { expr* s = m_ineq_terms[j].get(); expr_ref bt = mk_mul(abs(bc), t); expr_ref as = mk_mul(abs(ac), s); + expr_ref result1(m), result2(m); if (m_ineq_strict[j] && !m_ineq_strict[i]) { - return expr_ref(a.mk_lt(bt, as), m); + result1 = a.mk_lt(bt, as); } else { - return expr_ref(a.mk_le(bt, as), m); + result1 = a.mk_le(bt, as); } + m_rw(result1, result2); + return result2; } @@ -257,20 +269,23 @@ namespace qe { app_ref_vector new_vars(m); expr_ref_vector result(lits); for (unsigned i = 0; i < vars.size(); ++i) { - m_var = alloc(contains_app, m, vars[i].get()); + app* v = vars[i].get(); + m_var = alloc(contains_app, m, v); try { project(model, result); + TRACE("qe", tout << "projected: " << mk_pp(v, m) << " "; + for (unsigned i = 0; i < result.size(); ++i) { + tout << mk_pp(result[i].get(), m) << "\n"; + }); } catch (cant_project) { - new_vars.push_back(vars[i].get()); + IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(v, m) << "\n";); + new_vars.push_back(v); } } vars.reset(); vars.append(new_vars); - expr_ref res1(m); - expr_ref tmp = qe::mk_and(result); - m_rw(tmp, res1); - return res1; + return qe::mk_and(result); } }; @@ -280,4 +295,12 @@ namespace qe { return ap(model, vars, lits); } + expr_ref arith_project(model& model, app_ref_vector& vars, expr* fml) { + ast_manager& m = vars.get_manager(); + arith_project_util ap(m); + expr_ref_vector lits(m); + qe::flatten_and(fml, lits); + return ap(model, vars, lits); + } + } diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index 230cb36f6..c8f7e8b8d 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -11,6 +11,8 @@ namespace qe { return vector of variables that could not be projected. */ expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits); + + expr_ref arith_project(model& model, app_ref_vector& vars, expr* fml); }; #endif diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index 8d04e2489..d54ac1ccf 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -1,4 +1,5 @@ #include "qe_arith.h" +#include "qe.h" #include "th_rewriter.h" #include "smt2parser.h" #include "arith_decl_plugin.h" @@ -7,7 +8,7 @@ #include "ast_pp.h" #include "qe_util.h" #include "smt_context.h" - +#include "expr_abstract.h" static expr_ref parse_fml(ast_manager& m, char const* str) { expr_ref result(m); @@ -36,6 +37,15 @@ static char const* example3 = "(and (<= z x) (<= x 3.0) (< (* 3.0 x) y) (<= z y) static char const* example4 = "(and (<= z x) (<= x 3.0) (not (>= (* 3.0 x) y)) (<= z y))"; static char const* example5 = "(and (<= y x) (<= z x) (<= x u) (<= x v) (<= x t))"; +static char const* example6 = "(and (<= 0 (+ x z))\ + (>= y x) \ + (<= y x)\ + (<= (- u y) 0.0)\ + (>= x (+ v z))\ + (>= x 0.0)\ + (<= x 1.0))"; + + static void test(char const *ex) { smt_params params; params.m_model = true; @@ -58,9 +68,65 @@ static void test(char const *ex) { std::cout << mk_pp(fml, m) << "\n"; std::cout << mk_pp(pr, m) << "\n"; + +} + +static void test2(char const *ex) { + smt_params params; + params.m_model = true; + ast_manager m; + reg_decl_plugins(m); + arith_util a(m); + expr_ref fml = parse_fml(m, ex); + app_ref_vector vars(m); + expr_ref_vector lits(m); + vars.push_back(m.mk_const(symbol("x"), a.mk_real())); + vars.push_back(m.mk_const(symbol("y"), a.mk_real())); + vars.push_back(m.mk_const(symbol("z"), a.mk_real())); + qe::flatten_and(fml, lits); + + smt::context ctx(m, params); + ctx.push(); + ctx.assert_expr(fml); + lbool result = ctx.check(); + SASSERT(result == l_true); + ref md; + ctx.get_model(md); + ctx.pop(1); + + std::cout << mk_pp(fml, m) << "\n"; + + expr_ref pr2(m), fml2(m); + expr_ref_vector bound(m); + ptr_vector sorts; + svector names; + for (unsigned i = 0; i < vars.size(); ++i) { + bound.push_back(vars[i].get()); + names.push_back(vars[i]->get_decl()->get_name()); + sorts.push_back(m.get_sort(vars[i].get())); + } + expr_abstract(m, 0, 3, bound.c_ptr(), fml, fml2); + fml2 = m.mk_exists(3, sorts.c_ptr(), names.c_ptr(), fml2); + qe::expr_quant_elim qe(m, params); + expr_ref pr1 = qe::arith_project(*md, vars, lits); + qe(m.mk_true(), fml2, pr2); + std::cout << mk_pp(pr1, m) << "\n"; + std::cout << mk_pp(pr2, m) << "\n"; + + expr_ref npr2(m); + npr2 = m.mk_not(pr2); + ctx.push(); + ctx.assert_expr(pr1); + ctx.assert_expr(npr2); + VERIFY(l_false == ctx.check()); + ctx.pop(1); + + } void tst_qe_arith() { + test2(example6); + return; test(example1); test(example2); test(example3); From 10e203da43ba94426e5448c2e1bb9cb90dffa678 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Sep 2013 20:22:26 -0700 Subject: [PATCH 159/509] remove some dependencies on parameter file Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_context.cpp | 7 +++++++ src/muz/base/dl_context.h | 6 ++++++ src/muz/transforms/dl_mk_bit_blast.cpp | 2 +- src/muz/transforms/dl_mk_karr_invariants.cpp | 3 +-- src/muz/transforms/dl_mk_magic_symbolic.cpp | 3 +-- src/muz/transforms/dl_mk_quantifier_abstraction.cpp | 3 +-- src/muz/transforms/dl_mk_quantifier_instantiation.cpp | 4 +--- src/muz/transforms/dl_mk_scale.cpp | 2 +- 8 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 7eed67ff0..f08efb00a 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -289,6 +289,13 @@ namespace datalog { bool context::magic_sets_for_queries() const { return m_params->magic_sets_for_queries(); } bool context::eager_emptiness_checking() const { return m_params->eager_emptiness_checking(); } + bool context::bit_blast() const { return m_params->bit_blast(); } + bool context::karr() const { return m_params->karr(); } + bool context::scale() const { return m_params->scale(); } + bool context::magic() const { return m_params->magic(); } + bool context::quantify_arrays() const { return m_params->quantify_arrays(); } + bool context::instantiate_quantifiers() const { return m_params->instantiate_quantifiers(); } + void context::register_finite_sort(sort * s, sort_kind k) { m_pinned.push_back(s); diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 2eb9e0652..5eef04202 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -262,6 +262,12 @@ namespace datalog { bool explanations_on_relation_level() const; bool magic_sets_for_queries() const; bool eager_emptiness_checking() const; + bool bit_blast() const; + bool karr() const; + bool scale() const; + bool magic() const; + bool quantify_arrays() const; + bool instantiate_quantifiers() const; void register_finite_sort(sort * s, sort_kind k); diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 08f295ff4..91481ed5a 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -254,7 +254,7 @@ namespace datalog { rule_set * operator()(rule_set const & source) { // TODO pc - if (!m_context.get_params().bit_blast()) { + if (!m_context.bit_blast()) { return 0; } rule_manager& rm = m_context.get_rule_manager(); diff --git a/src/muz/transforms/dl_mk_karr_invariants.cpp b/src/muz/transforms/dl_mk_karr_invariants.cpp index b4ab66bc2..ced0d39f4 100644 --- a/src/muz/transforms/dl_mk_karr_invariants.cpp +++ b/src/muz/transforms/dl_mk_karr_invariants.cpp @@ -39,7 +39,6 @@ Revision History: #include"dl_mk_karr_invariants.h" #include"dl_mk_backwards.h" #include"dl_mk_loop_counter.h" -#include"fixedpoint_params.hpp" namespace datalog { @@ -197,7 +196,7 @@ namespace datalog { } rule_set * mk_karr_invariants::operator()(rule_set const & source) { - if (!m_ctx.get_params().karr()) { + if (!m_ctx.karr()) { return 0; } rule_set::iterator it = source.begin(), end = source.end(); diff --git a/src/muz/transforms/dl_mk_magic_symbolic.cpp b/src/muz/transforms/dl_mk_magic_symbolic.cpp index 57490466c..1d269cff0 100644 --- a/src/muz/transforms/dl_mk_magic_symbolic.cpp +++ b/src/muz/transforms/dl_mk_magic_symbolic.cpp @@ -54,7 +54,6 @@ Revision History: #include"dl_mk_magic_symbolic.h" #include"dl_context.h" -#include"fixedpoint_params.hpp" namespace datalog { @@ -68,7 +67,7 @@ namespace datalog { mk_magic_symbolic::~mk_magic_symbolic() { } rule_set * mk_magic_symbolic::operator()(rule_set const & source) { - if (!m_ctx.get_params().magic()) { + if (!m_ctx.magic()) { return 0; } context& ctx = source.get_context(); diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp index e686495e9..0ad4d61ab 100644 --- a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp @@ -300,8 +300,7 @@ namespace datalog { } rule_set * mk_quantifier_abstraction::operator()(rule_set const & source) { - TRACE("dl", tout << "quantify " << source.get_num_rules() << " " << m_ctx.get_params().quantify_arrays() << "\n";); - if (!m_ctx.get_params().quantify_arrays()) { + if (!m_ctx.quantify_arrays()) { return 0; } unsigned sz = source.get_num_rules(); diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp index ebb9310a4..9c55ca658 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -26,7 +26,6 @@ Revision History: #include "dl_mk_quantifier_instantiation.h" #include "dl_context.h" #include "pattern_inference.h" -#include "fixedpoint_params.hpp" namespace datalog { @@ -251,8 +250,7 @@ namespace datalog { } rule_set * mk_quantifier_instantiation::operator()(rule_set const & source) { - TRACE("dl", tout << m_ctx.get_params().instantiate_quantifiers() << "\n";); - if (!m_ctx.get_params().instantiate_quantifiers()) { + if (!m_ctx.instantiate_quantifiers()) { return 0; } bool has_quantifiers = false; diff --git a/src/muz/transforms/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp index 9618db88e..9ada7be20 100644 --- a/src/muz/transforms/dl_mk_scale.cpp +++ b/src/muz/transforms/dl_mk_scale.cpp @@ -115,7 +115,7 @@ namespace datalog { } rule_set * mk_scale::operator()(rule_set const & source) { - if (!m_ctx.get_params().scale()) { + if (!m_ctx.scale()) { return 0; } rule_manager& rm = source.get_rule_manager(); From 419f99c329dfcc628b05e53c6f5d463fe4701c26 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Sep 2013 15:30:56 -0700 Subject: [PATCH 160/509] fix bug found by Ethan: fresh values for bit-vectors loops if the domain of bit-vectors is truly small Signed-off-by: Nikolaj Bjorner --- src/ast/datatype_decl_plugin.cpp | 3 ++- src/muz/pdr/pdr_farkas_learner.cpp | 2 -- src/smt/proto_model/value_factory.h | 14 ++++++++++++++ src/test/qe_arith.cpp | 4 ++-- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index 0714af28b..ee3271b9b 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -84,7 +84,8 @@ class datatype_decl { ptr_vector m_constructors; public: datatype_decl(const symbol & n, unsigned num_constructors, constructor_decl * const * constructors): - m_name(n), m_constructors(num_constructors, constructors) {} + m_name(n), m_constructors(num_constructors, constructors) { + } ~datatype_decl() { std::for_each(m_constructors.begin(), m_constructors.end(), delete_proc()); } diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index b54eb0117..7c38bf86f 100644 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ b/src/muz/pdr/pdr_farkas_learner.cpp @@ -212,8 +212,6 @@ namespace pdr { // partition inequalities into variable disjoint sets. void partition_ineqs() { - m_roots.reset(); - m_size.reset(); m_reps.reset(); m_his.reset(); ++m_time; diff --git a/src/smt/proto_model/value_factory.h b/src/smt/proto_model/value_factory.h index b7df55f43..115a01345 100644 --- a/src/smt/proto_model/value_factory.h +++ b/src/smt/proto_model/value_factory.h @@ -180,10 +180,24 @@ public: value_set * set = get_value_set(s); bool is_new = false; expr * result = 0; + sort_info* s_info = s->get_info(); + sort_size const* sz = s_info?&s_info->get_num_elements():0; + bool has_max = false; + Number max_size; + if (sz && sz->is_finite()) { + if (sz->size() < UINT_MAX) { + unsigned usz = static_cast(sz->size()); + max_size = Number(usz); + has_max = true; + } + } Number & next = set->m_next; while (!is_new) { result = mk_value(next, s, is_new); next++; + if (has_max && next >= max_size) { + return 0; + } } SASSERT(result != 0); return result; diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index d54ac1ccf..e25abc48c 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -105,8 +105,8 @@ static void test2(char const *ex) { names.push_back(vars[i]->get_decl()->get_name()); sorts.push_back(m.get_sort(vars[i].get())); } - expr_abstract(m, 0, 3, bound.c_ptr(), fml, fml2); - fml2 = m.mk_exists(3, sorts.c_ptr(), names.c_ptr(), fml2); + expr_abstract(m, 0, bound.size(), bound.c_ptr(), fml, fml2); + fml2 = m.mk_exists(boud.size(), sorts.c_ptr(), names.c_ptr(), fml2); qe::expr_quant_elim qe(m, params); expr_ref pr1 = qe::arith_project(*md, vars, lits); qe(m.mk_true(), fml2, pr2); From be044f42c3e15614a809a1ac3064fe1a6ad70a4e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Sep 2013 04:24:20 -0700 Subject: [PATCH 161/509] Fix build of test Signed-off-by: Nikolaj Bjorner --- src/test/qe_arith.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index e25abc48c..8b6577b3f 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -106,7 +106,7 @@ static void test2(char const *ex) { sorts.push_back(m.get_sort(vars[i].get())); } expr_abstract(m, 0, bound.size(), bound.c_ptr(), fml, fml2); - fml2 = m.mk_exists(boud.size(), sorts.c_ptr(), names.c_ptr(), fml2); + fml2 = m.mk_exists(bound.size(), sorts.c_ptr(), names.c_ptr(), fml2); qe::expr_quant_elim qe(m, params); expr_ref pr1 = qe::arith_project(*md, vars, lits); qe(m.mk_true(), fml2, pr2); From 21b27cd2d1322005292a055927e085e3d2deba92 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Sep 2013 04:37:05 -0700 Subject: [PATCH 162/509] patching crash in data-type factory when fresh values are not produced Signed-off-by: Nikolaj Bjorner --- src/smt/proto_model/datatype_factory.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/smt/proto_model/datatype_factory.cpp b/src/smt/proto_model/datatype_factory.cpp index 5be802714..a45b53155 100644 --- a/src/smt/proto_model/datatype_factory.cpp +++ b/src/smt/proto_model/datatype_factory.cpp @@ -67,6 +67,7 @@ expr * datatype_factory::get_almost_fresh_value(sort * s) { value_set * set = get_value_set(s); if (set->empty()) { expr * val = get_some_value(s); + SASSERT(val); if (m_util.is_recursive(s)) m_last_fresh_value.insert(s, val); return val; @@ -185,10 +186,16 @@ expr * datatype_factory::get_fresh_value(sort * s) { if (!found_sibling && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) { found_sibling = true; expr * maybe_new_arg = get_almost_fresh_value(s_arg); + if (!maybe_new_arg) { + maybe_new_arg = m_model.get_some_value(s_arg); + found_sibling = false; + } + SASSERT(maybe_new_arg); args.push_back(maybe_new_arg); } else { expr * some_arg = m_model.get_some_value(s_arg); + SASSERT(some_arg); args.push_back(some_arg); } } From c54929e59f7cbeaaa25f64c47cb78e5f4a6d6906 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Sep 2013 04:52:21 -0700 Subject: [PATCH 163/509] cycle through domain size before giving up Signed-off-by: Nikolaj Bjorner --- src/smt/proto_model/value_factory.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/smt/proto_model/value_factory.h b/src/smt/proto_model/value_factory.h index 115a01345..5bd0a1f75 100644 --- a/src/smt/proto_model/value_factory.h +++ b/src/smt/proto_model/value_factory.h @@ -184,18 +184,16 @@ public: sort_size const* sz = s_info?&s_info->get_num_elements():0; bool has_max = false; Number max_size; - if (sz && sz->is_finite()) { - if (sz->size() < UINT_MAX) { - unsigned usz = static_cast(sz->size()); - max_size = Number(usz); - has_max = true; - } + if (sz && sz->is_finite() && sz->size() < UINT_MAX) { + unsigned usz = static_cast(sz->size()); + max_size = Number(usz); + has_max = true; } Number & next = set->m_next; while (!is_new) { result = mk_value(next, s, is_new); next++; - if (has_max && next >= max_size) { + if (has_max && next > max_size + set->m_next) { return 0; } } From 2c9c5ba1f0f44e0b07efbe58042d77b1d2276443 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 15 Sep 2013 13:33:20 -0700 Subject: [PATCH 164/509] still working on interpolation of full z3 proofs --- scripts/mk_util.py | 1 + src/interp/iz3foci.cpp | 4 + src/interp/iz3mgr.cpp | 9 +- src/interp/iz3mgr.h | 29 +- src/interp/iz3proof_itp.cpp | 610 +++++++++++++++++++++++++++++------- src/interp/iz3proof_itp.h | 6 +- src/interp/iz3translate.cpp | 18 +- src/interp/iz3translate.h | 5 +- 8 files changed, 547 insertions(+), 135 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 4a042d37f..79c1877ff 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1496,6 +1496,7 @@ def mk_config(): if test_foci2(CXX,FOCI2LIB): LDFLAGS = '%s %s' % (LDFLAGS,FOCI2LIB) SLIBEXTRAFLAGS = '%s %s' % (SLIBEXTRAFLAGS,FOCI2LIB) + CPPFLAGS = '%s -D_FOCI2' % CPPFLAGS else: print "FAILED\n" FOCI2 = False diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp index 251599041..85e090c5b 100755 --- a/src/interp/iz3foci.cpp +++ b/src/interp/iz3foci.cpp @@ -308,7 +308,11 @@ public: int interpolate(const std::vector &cnsts, std::vector &itps){ assert((int)cnsts.size() == frames); std::string lia("lia"); +#ifdef _FOCI2 foci = foci2::create(lia); +#else + foci = 0; +#endif if(!foci){ std::cerr << "iZ3: cannot find foci lia solver.\n"; assert(0); diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 7a787fdea..0cff3b970 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -494,6 +494,12 @@ void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ } rats[i-2] = r; } + if(rats.size() != 0 && rats[0].is_neg()){ + for(unsigned i = 0; i < rats.size(); i++){ + assert(rats[i].is_neg()); + rats[i] = -rats[i]; + } + } extract_lcd(rats); } @@ -519,11 +525,10 @@ void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& r { ast con = arg(conc(proof),i-1); ast temp = make_real(r); // for debugging -#if 0 opr o = is_not(con) ? op(arg(con,0)) : op(con); if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) -#endif r = -r; + r = -r; } rats[i-1] = r; } diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 83cd573f3..e49182073 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -460,6 +460,17 @@ class iz3mgr { return make(Or,x,y); } + ast mk_implies(ast x, ast y){ + opr ox = op(x); + opr oy = op(y); + if(ox == True) return y; + if(oy == False) return mk_not(x); + if(ox == False) return mk_true(); + if(oy == True) return y; + if(x == y) return mk_true(); + return make(Implies,x,y); + } + ast mk_or(const std::vector &x){ ast res = mk_false(); for(unsigned i = 0; i < x.size(); i++) @@ -468,10 +479,20 @@ class iz3mgr { } ast mk_and(const std::vector &x){ - ast res = mk_true(); - for(unsigned i = 0; i < x.size(); i++) - res = mk_and(res,x[i]); - return res; + std::vector conjs; + for(unsigned i = 0; i < x.size(); i++){ + const ast &e = x[i]; + opr o = op(e); + if(o == False) + return mk_false(); + if(o != True) + conjs.push_back(e); + } + if(conjs.size() == 0) + return mk_true(); + if(conjs.size() == 1) + return conjs[0]; + return make(And,conjs); } ast mk_equal(ast x, ast y){ diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 5cd75bc26..a4b742d7d 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -33,7 +33,60 @@ class iz3proof_itp_impl : public iz3proof_itp { enum LitType {LitA,LitB,LitMixed}; hash_map placeholders; - symb farkas; + + // These symbols represent deduction rules + + /* This symbol represents a proof by contradiction. That is, + contra(p,l1 /\ ... /\ lk) takes a proof p of + + l1,...,lk |- false + + and returns a proof of + + |- ~l1,...,~l2 + */ + symb contra; + + /* The summation rule. The term sum(p,c,i) takes a proof p of an + inequality i', an integer coefficient c and an inequality i, and + yieds a proof of i' + ci. */ + symb sum; + + /* Proof rotation. The proof term rotate(q,p) takes a + proof p of: + + Gamma, q |- false + + and yields a proof of: + + Gamma |- ~q + */ + symb rotate_sum; + + /* Inequalities to equality. leq2eq(p, q, r) takes a proof + p of ~x=y, a proof q of x <= y and a proof r of y <= x + and yields a proof of false. */ + symb leq2eq; + + /* Proof term cong(p,q) takes a proof p of x=y and a proof + q of t != t and returns a proof of false. */ + symb cong; + + + /* Excluded middle. exmid(phi,p,q) takes a proof p of phi and a + proof q of ~\phi and returns a proof of false. */ + symb exmid; + + /* Symmetry. symm(p) takes a proof p of x=y and produces + a proof of y=x. */ + symb symm; + + /* Modus ponens. modpon(p,e,q) takes proofs p of P, e of P=Q + and q of ~Q and returns a proof of false. */ + symb modpon; + + // This is used to represent an infinitessimal value + ast epsilon; ast get_placeholder(ast t){ hash_map::iterator it = placeholders.find(t); @@ -49,8 +102,17 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } - ast make_farkas(ast &coeff, ast &ineq){ - return make(farkas,coeff,ineq); + ast make_contra_node(const ast &pf, const std::vector &lits){ + if(lits.size() == 0) + return pf; + std::vector reslits; + reslits.push_back(make(contra,pf,mk_false())); + for(unsigned i = 0; i < lits.size(); i++){ + ast bar = make(rotate_sum,lits[i],pf); + ast foo = make(contra,bar,lits[i]); + reslits.push_back(foo); + } + return make(And,reslits); } LitType get_term_type(const ast &lit){ @@ -81,24 +143,10 @@ class iz3proof_itp_impl : public iz3proof_itp { /* the mixed case is a bit complicated */ - ast atom = get_lit_atom(pivot); - switch(op(atom)){ - case Equal: - return resolve_equality(pivot,conc,premise1,premise2); - case Leq: - case Geq: - case Lt: - case Gt: - return resolve_arith(pivot,conc,premise1,premise2); - break; - default:; - } - - /* we can resolve on mixed equality and inequality literals, - but nothing else. */ - throw proof_error(); + return resolve_arith(pivot,conc,premise1,premise2); } +#if 0 /* Handles the case of resolution on a mixed equality atom. */ ast resolve_equality(const ast &pivot, const std::vector &conc, node premise1, node premise2){ @@ -134,7 +182,8 @@ class iz3proof_itp_impl : public iz3proof_itp { else { switch(op(premise1)){ case Or: - case And: { + case And: + case Implies: { unsigned nargs = num_args(premise1); std::vector args; args.resize(nargs); for(unsigned i = 0; i < nargs; i++) @@ -149,6 +198,7 @@ class iz3proof_itp_impl : public iz3proof_itp { } return res; } +#endif /* Handles the case of resolution on a mixed arith atom. */ @@ -161,15 +211,21 @@ class iz3proof_itp_impl : public iz3proof_itp { return resolve_arith_rec1(memo, neg_pivot_lit, premise1, premise2); } - void collect_farkas_resolvents(const ast &pivot, const ast &coeff, const ast &conj, std::vector &res){ - int nargs = num_args(conj); - for(int i = 1; i < nargs; i++){ - ast f = arg(conj,i); - if(!(f == pivot)){ - ast newf = make(farkas,make(Times,arg(f,0),coeff),arg(f,1)); - res.push_back(newf); - } + + ast apply_coeff(const ast &coeff, const ast &t){ +#if 0 + rational r; + if(!is_integer(coeff,r)) + throw "ack!"; + ast n = make_int(r.numerator()); + ast res = make(Times,n,t); + if(!r.is_int()) { + ast d = make_int(r.numerator()); + res = mk_idiv(res,d); } + return res; +#endif + return make(Times,coeff,t); } ast sum_ineq(const ast &coeff1, const ast &ineq1, const ast &coeff2, const ast &ineq2){ @@ -178,29 +234,63 @@ class iz3proof_itp_impl : public iz3proof_itp { sum_op = Lt; ast sum_sides[2]; for(int i = 0; i < 2; i++){ - sum_sides[i] = make(Plus,make(Times,coeff1,arg(ineq1,i)),make(Times,coeff2,arg(ineq2,i))); + sum_sides[i] = make(Plus,apply_coeff(coeff1,arg(ineq1,i)),apply_coeff(coeff2,arg(ineq2,i))); sum_sides[i] = z3_simplify(sum_sides[i]); } return make(sum_op,sum_sides[0],sum_sides[1]); } - - ast resolve_farkas(const ast &pivot1, const ast &conj1, const ast &pivot2, const ast &conj2){ +#if 0 + ast resolve_farkas(const ast &pivot1, const ast &conj1, const ast &implicant1, + const ast &pivot2, const ast &conj2, const ast &implicant2){ std::vector resolvent; ast coeff1 = get_farkas_coeff(pivot1); ast coeff2 = get_farkas_coeff(pivot2); - resolvent.push_back(sum_ineq(coeff2,arg(conj1,0),coeff1,arg(conj2,0))); + ast s1 = resolve_arith_placeholders(pivot2,conj2,arg(conj1,0)); + ast s2 = resolve_arith_placeholders(pivot1,conj1,arg(conj2,0)); + resolvent.push_back(sum_ineq(coeff2,s1,coeff1,s2)); collect_farkas_resolvents(pivot1,coeff2,conj1,resolvent); collect_farkas_resolvents(pivot2,coeff1,conj2,resolvent); + ast res = make(And,resolvent); + if(implicant1.null() && implicant2.null()) + return res; + ast i1 = implicant1.null() ? mk_false() : resolve_arith_placeholders(pivot2,conj2,implicant1); + ast i2 = implicant2.null() ? mk_false() : resolve_arith_placeholders(pivot1,conj1,implicant2); + return make(Implies,res,my_or(i1,i2)); + } +#endif + + void collect_contra_resolvents(int from, const ast &pivot1, const ast &pivot, const ast &conj, std::vector &res){ + int nargs = num_args(conj); + for(int i = from; i < nargs; i++){ + ast f = arg(conj,i); + if(!(f == pivot)){ + ast ph = get_placeholder(mk_not(arg(pivot1,1))); + ast pf = arg(pivot1,0); + ast thing = subst_term_and_simp(ph,pf,arg(f,0)); + ast newf = make(contra,thing,arg(f,1)); + res.push_back(newf); + } + } + } + + ast resolve_contra(const ast &pivot1, const ast &conj1, + const ast &pivot2, const ast &conj2){ + std::vector resolvent; + collect_contra_resolvents(0,pivot1,pivot2,conj2,resolvent); + collect_contra_resolvents(1,pivot2,pivot1,conj1,resolvent); + if(resolvent.size() == 1) // we have proved a contradiction + return simplify(arg(resolvent[0],0)); // this is the proof -- get interpolant return make(And,resolvent); } + - bool is_farkas_itp(const ast &pivot1, const ast &itp2, ast &pivot2){ + bool is_contra_itp(const ast &pivot1, ast itp2, ast &pivot2){ if(op(itp2) == And){ int nargs = num_args(itp2); for(int i = 1; i < nargs; i++){ ast foo = arg(itp2,i); - if(op(foo) == Uninterpreted && sym(foo) == farkas){ + if(op(foo) == Uninterpreted && sym(foo) == contra){ if(arg(foo,1) == pivot1){ pivot2 = foo; return true; @@ -218,12 +308,13 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; ast pivot2; - if(is_farkas_itp(mk_not(arg(pivot1,1)),itp2,pivot2)) - res = resolve_farkas(pivot1,conj1,pivot2,itp2); + if(is_contra_itp(mk_not(arg(pivot1,1)),itp2,pivot2)) + res = resolve_contra(pivot1,conj1,pivot2,itp2); else { switch(op(itp2)){ case Or: - case And: { + case And: + case Implies: { unsigned nargs = num_args(itp2); std::vector args; args.resize(nargs); for(unsigned i = 0; i < nargs; i++) @@ -244,15 +335,15 @@ class iz3proof_itp_impl : public iz3proof_itp { if(!res.null()) return res; ast pivot1; - if(is_farkas_itp(neg_pivot_lit,itp1,pivot1)){ + if(is_contra_itp(neg_pivot_lit,itp1,pivot1)){ hash_map memo2; res = resolve_arith_rec2(memo2,pivot1,itp1,itp2); - res = resolve_arith_placeholders(pivot1,itp1,res); } else { switch(op(itp1)){ case Or: - case And: { + case And: + case Implies: { unsigned nargs = num_args(itp1); std::vector args; args.resize(nargs); for(unsigned i = 0; i < nargs; i++) @@ -268,21 +359,26 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } +#if 0 ast resolve_arith_placeholders(const ast &pivot1, const ast &conj1, const ast &itp2){ ast coeff = arg(pivot1,0); + ast val = arg(arg(conj1,0),1); + if(op(conj1)==Lt) + val = make(Sub,val,epsilon); // represent x < c by x <= c - epsilon coeff = z3_simplify(coeff); - ast soln = mk_idiv(arg(arg(conj1,0),1),coeff); + ast soln = mk_idiv(val,coeff); int nargs = num_args(conj1); for(int i = 1; i < nargs; i++){ ast c = arg(conj1,i); if(!(c == pivot1)){ - soln = make(Plus,soln,get_placeholder(mk_not(c))); + soln = make(Plus,soln,get_placeholder(arg(c,1))); } } ast pl = get_placeholder(mk_not(arg(pivot1,1))); ast res = subst_term_and_simp(pl,soln,itp2); return res; } +#endif hash_map subst_memo; // memo of subst function @@ -311,6 +407,197 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } + /* This is where the real work happens. Here, we simplify the + proof obtained by cut elimination, obtaining a interpolant. */ + + struct cannot_simplify {}; + hash_map simplify_memo; + + ast simplify(const ast &t){ + return simplify_rec(t); + } + + ast simplify_rec(const ast &e){ + std::pair foo(e,ast()); + std::pair::iterator,bool> bar = simplify_memo.insert(foo); + ast &res = bar.first->second; + if(bar.second){ + int nargs = num_args(e); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = simplify_rec(arg(e,i)); + try { + opr f = op(e); + if(f == Equal && args[0] == args[1]) res = mk_true(); + else if(f == And) res = my_and(args); + else if(f == Or) res = my_or(args); + else if(f == Idiv) res = mk_idiv(args[0],args[1]); + else if(f == Uninterpreted){ + symb g = sym(e); + if(g == rotate_sum) res = simplify_rotate(args); + else if(g == symm) res = simplify_symm(args); + else if(g == modpon) res = simplify_modpon(args); +#if 0 + else if(g == cong) res = simplify_cong(args); + else if(g == modpon) res = simplify_modpon(args); + else if(g == leq2eq) res = simplify_leq2eq(args); + else if(g == eq2leq) res = simplify_eq2leq(args); +#endif + else res = clone(e,args); + } + else res = clone(e,args); + } + catch (const cannot_simplify &){ + res = clone(e,args); + } + } + return res; + } + + + ast simplify_rotate(const std::vector &args){ + const ast &pf = args[1]; + ast pl = get_placeholder(args[0]); + if(op(pf) == Uninterpreted){ + symb g = sym(pf); + if(g == sum) return simplify_rotate_sum(pl,pf); + if(g == leq2eq) return simplify_rotate_leq2eq(pl,args[0],pf); + if(g == cong) return simplify_rotate_cong(pl,args[0],pf); + // if(g == symm) return simplify_rotate_symm(pl,args[0],pf); + } + throw cannot_simplify(); + } + + ast simplify_rotate_sum(const ast &pl, const ast &pf){ + ast cond = mk_true(); + ast ineq = make(Leq,make_int("0"),make_int("0")); + rotate_sum_rec(pl,pf,cond,ineq); + return ineq; + } + + void sum_cond_ineq(ast &ineq, ast &cond, const ast &coeff2, const ast &ineq2){ + opr o = op(ineq2); + if(o == Implies){ + sum_cond_ineq(ineq,cond,coeff2,arg(ineq2,1)); + cond = my_and(cond,arg(ineq2,0)); + } + else if(o == Leq || o == Lt) + ineq = sum_ineq(make_int("1"),ineq,coeff2,ineq2); + else + throw cannot_simplify(); + } + + // divide both sides of inequality by a non-negative integer divisor + ast idiv_ineq(const ast &ineq, const ast &divisor){ + return make(op(ineq),mk_idiv(arg(ineq,0),divisor),mk_idiv(arg(ineq,1),divisor)); + } + + ast rotate_sum_rec(const ast &pl, const ast &pf, ast &cond, ast &ineq){ + if(op(pf) == Uninterpreted && sym(pf) == sum){ + if(arg(pf,2) == pl){ + sum_cond_ineq(ineq,cond,make_int("1"),arg(pf,0)); + ineq = idiv_ineq(ineq,arg(pf,1)); + return my_implies(cond,ineq); + } + sum_cond_ineq(ineq,cond,arg(pf,1),arg(pf,2)); + return rotate_sum_rec(pl,arg(pf,0),cond,ineq); + } + throw cannot_simplify(); + } + + ast simplify_rotate_leq2eq(const ast &pl, const ast &neg_equality, const ast &pf){ + if(pl == arg(pf,0)){ + ast equality = arg(neg_equality,0); + ast x = arg(equality,0); + ast y = arg(equality,1); + ast xleqy = arg(pf,1); + ast yleqx = arg(pf,2); + ast itpeq; + if(get_term_type(x) == LitA) + itpeq = make(Equal,x,make(Plus,x,get_ineq_rhs(xleqy))); + else if(get_term_type(y) == LitA) + itpeq = make(Equal,make(Plus,y,get_ineq_rhs(yleqx)),y); + else + throw cannot_simplify(); + ast cond = mk_true(); + ast ineq = make(Leq,make_int("0"),make_int("0")); + sum_cond_ineq(ineq,cond,make_int("-1"),xleqy); + sum_cond_ineq(ineq,cond,make_int("-1"),yleqx); + cond = my_and(cond,ineq); + return my_implies(cond,itpeq); + } + throw cannot_simplify(); + } + + ast get_ineq_rhs(const ast &ineq2){ + opr o = op(ineq2); + if(o == Implies) + return get_ineq_rhs(arg(ineq2,1)); + else if(o == Leq || o == Lt) + return arg(ineq2,1); + throw cannot_simplify(); + } + + ast simplify_rotate_cong(const ast &pl, const ast &neg_equality, const ast &pf){ + if(pl == arg(pf,2)){ + if(op(arg(pf,0)) == True) + return mk_true(); + rational pos; + if(is_numeral(arg(pf,1),pos)){ + int ipos = pos.get_unsigned(); + ast cond = mk_true(); + ast equa = sep_cond(arg(pf,0),cond); + if(op(equa) == Equal){ + ast pe = mk_not(neg_equality); + ast lhs = subst_in_arg_pos(ipos,arg(equa,0),arg(pe,0)); + ast rhs = subst_in_arg_pos(ipos,arg(equa,1),arg(pe,1)); + ast res = make(Equal,lhs,rhs); + return my_implies(cond,res); + } + } + } + throw cannot_simplify(); + } + + ast simplify_symm(const std::vector &args){ + if(op(args[0]) == True) + return mk_true(); + ast cond = mk_true(); + ast equa = sep_cond(args[0],cond); + if(op(equa) == Equal) + return my_implies(cond,make(Equal,arg(equa,1),arg(equa,0))); + throw cannot_simplify(); + } + + ast simplify_modpon(const std::vector &args){ + if(op(args[1]) == True){ + ast cond = mk_true(); + ast P = sep_cond(args[0],cond); + ast notQ = sep_cond(args[2],cond); + ast Q = mk_not(notQ); + ast d = mk_not(delta(P,Q)); + return my_implies(cond,d); + } + throw cannot_simplify(); + } + + ast delta(const ast &x, const ast &y){ + if(op(x) != op(y) || (op(x) == Uninterpreted && sym(x) != sym(y)) || num_args(x) != num_args(y)) + return make(Equal,x,y); + ast res = mk_true(); + int nargs = num_args(x); + for(int i = 0; i < nargs; i++) + res = my_and(res,delta(arg(x,i),arg(y,i))); + return res; + } + + ast sep_cond(const ast &t, ast &cond){ + if(op(t) == Implies){ + cond = my_and(cond,arg(t,0)); + return arg(t,1); + } + return t; + } /** Make an assumption node. The given clause is assumed in the given frame. */ virtual node make_assumption(int frame, const std::vector &assumption){ @@ -327,39 +614,76 @@ class iz3proof_itp_impl : public iz3proof_itp { } } - /** Make a modus-ponens node. This takes derivations of |- x - and |- x = y and produces |- y */ - - virtual node make_mp(const ast &p, const ast &q, const ast &prem1, const ast &prem2){ - - /* Interpolate the axiom p, p=q -> q */ - ast itp; - if(get_term_type(p) == LitA){ - if(get_term_type(q) == LitA) - itp = mk_false(); - else { - if(get_term_type(make(Equal,p,q)) == LitA) - itp = q; - else - itp = get_placeholder(make(Equal,p,q)); + ast triv_interp(const symb &rule, const std::vector &premises){ + std::vector ps; ps.resize(premises.size()); + std::vector conjs; + for(unsigned i = 0; i < ps.size(); i++){ + ast p = premises[i]; + switch(get_term_type(p)){ + case LitA: + ps[i] = p; + break; + case LitB: + ps[i] = mk_true(); + break; + default: + ps[i] = get_placeholder(p); + conjs.push_back(p); } } + ast ref = make(rule,ps); + ast res = make_contra_node(ref,conjs); + return res; + } + + ast triv_interp(const symb &rule, const ast &p0, const ast &p1, const ast &p2){ + std::vector ps; ps.resize(3); + ps[0] = p0; + ps[1] = p1; + ps[2] = p2; + return triv_interp(rule,ps); + } + + /** Make a modus-ponens node. This takes derivations of |- x + and |- x = y and produces |- y */ + + virtual node make_mp(const ast &p_eq_q, const ast &prem1, const ast &prem2){ + + /* Interpolate the axiom p, p=q -> q */ + ast p = arg(p_eq_q,0); + ast q = arg(p_eq_q,1); + ast itp; + if(get_term_type(p_eq_q) == LitMixed){ + itp = triv_interp(modpon,p,p_eq_q,mk_not(q)); + } else { - if(get_term_type(q) == LitA){ - if(get_term_type(make(Equal,p,q)) == LitA) - itp = mk_not(p); - else - itp = mk_not(get_placeholder(make(Equal,p,q))); + if(get_term_type(p) == LitA){ + if(get_term_type(q) == LitA) + itp = mk_false(); + else { + if(get_term_type(p_eq_q) == LitA) + itp = q; + else + throw proof_error(); + } + } + else { + if(get_term_type(q) == LitA){ + if(get_term_type(make(Equal,p,q)) == LitA) + itp = mk_not(p); + else + throw proof_error(); } else itp = mk_true(); + } } /* Resolve it with the premises */ std::vector conc; conc.push_back(q); conc.push_back(mk_not(make(Equal,p,q))); itp = make_resolution(p,conc,itp,prem1); conc.pop_back(); - itp = make_resolution(make(Equal,p,q),conc,itp,prem2); + itp = make_resolution(p_eq_q,conc,itp,prem2); return itp; } @@ -387,26 +711,25 @@ class iz3proof_itp_impl : public iz3proof_itp { return mk_true(); default: // mixed hypothesis switch(op(P)){ - case Equal: - { - ast x = arg(P,0); - ast y = arg(P,1); - ast A_term = (get_term_type(y) == LitA) ? y : x; - ast res = make(And,make(Equal,A_term,get_placeholder(P)),mk_not(P)); - return res; - } case Geq: case Leq: case Gt: case Lt: { ast zleqz = make(Leq,make_int("0"),make_int("0")); - ast fark1 = make(farkas,make_int("1"),P); - ast fark2 = make(farkas,make_int("1"),mk_not(P)); - ast res = make(And,zleqz,fark1,fark2); + ast fark1 = make(sum,zleqz,make_int("1"),get_placeholder(P)); + ast fark2 = make(sum,fark1,make_int("1"),get_placeholder(mk_not(P))); + ast res = make(And,make(contra,fark2,mk_false()), + make(contra,get_placeholder(mk_not(P)),P), + make(contra,get_placeholder(P),mk_not(P))); + return res; + } + default: { + ast em = make(exmid,P,get_placeholder(P),get_placeholder(mk_not(P))); + ast res = make(And,make(contra,em,mk_false()), + make(contra,get_placeholder(mk_not(P)),P), + make(contra,get_placeholder(P),mk_not(P))); return res; } - default: - throw proof_error(); } } } @@ -420,20 +743,31 @@ class iz3proof_itp_impl : public iz3proof_itp { /** Make a Symmetry node. This takes a derivation of |- x = y and produces | y = x */ - virtual node make_symmetry(ast con, node prem){ + virtual node make_symmetry(ast con, const ast &premcon, node prem){ ast x = arg(con,0); ast y = arg(con,1); - ast p = make(Equal,y,x); + ast p = make(op(con),y,x); + if(p == premcon) + std::cout << "ok\n"; + if(get_term_type(con) != LitMixed) + return prem; // symmetry shmymmetry... +#if 0 LitType xt = get_term_type(x); LitType yt = get_term_type(y); ast A_term; + if(xt == LitA && yt == LitB) A_term = x; else if(yt == LitA && xt == LitB) A_term = y; else - return prem; // symmetry shmymmetry... ast itp = make(And,make(Equal,A_term,get_placeholder(p)),mk_not(con)); +#endif + ast em = make(exmid,con,make(symm,get_placeholder(p)),get_placeholder(mk_not(con))); + ast itp = make(And,make(contra,em,mk_false()), + make(contra,make(symm,get_placeholder(mk_not(con))),p), + make(contra,make(symm,get_placeholder(p)),mk_not(con))); + std::vector conc; conc.push_back(con); itp = make_resolution(p,conc,itp,prem); return itp; @@ -448,7 +782,13 @@ class iz3proof_itp_impl : public iz3proof_itp { ast p = make(Equal,x,y); ast q = make(Equal,y,z); ast r = make(Equal,x,z); + ast equiv = make(Iff,p,r); ast itp; + + itp = make_congruence(q,equiv,prem2); + itp = make_mp(equiv,prem1,itp); + +#if 0 if(get_term_type(p) == LitA){ if(get_term_type(q) == LitA){ if(get_term_type(r) == LitA) @@ -506,21 +846,24 @@ class iz3proof_itp_impl : public iz3proof_itp { itp = make_resolution(p,conc,itp,prem1); conc.pop_back(); itp = make_resolution(q,conc,itp,prem2); +#endif + + return itp; - + } /** Make a congruence node. This takes derivations of |- x_i = y_i and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ - virtual node make_congruence(const ast &x, const ast &y, const ast &con, const std::vector &hyps, const ast &prem1){ - ast p = make(Equal,x,y); + virtual node make_congruence(const ast &p, const ast &con, const ast &prem1){ + ast x = arg(p,0), y = arg(p,1); ast itp; if(get_term_type(p) == LitA){ if(get_term_type(con) == LitA) itp = mk_false(); else - throw proof_error(); // itp = p; + itp = make_mixed_congruence(x, y, p, con, prem1); } else { if(get_term_type(con) == LitA) @@ -529,7 +872,7 @@ class iz3proof_itp_impl : public iz3proof_itp { if(get_term_type(con) == LitB) itp = mk_true(); else - itp = make_mixed_congruence(x, y, con, hyps, prem1); + itp = make_mixed_congruence(x, y, p, con, prem1); } } std::vector conc; conc.push_back(con); @@ -539,14 +882,19 @@ class iz3proof_itp_impl : public iz3proof_itp { /* Interpolate a mixed congruence axiom. */ - virtual ast make_mixed_congruence(const ast &x, const ast &y, const ast &con, const std::vector &hyps, const ast &prem1){ - ast A_term = x; - ast f_A_term = arg(con,0); - if(get_term_type(y) == LitA){ - A_term = y; - f_A_term = arg(con,1); + virtual ast make_mixed_congruence(const ast &x, const ast &y, const ast &p, const ast &con, const ast &prem1){ + ast foo = p; + std::vector conjs; + switch(get_term_type(foo)){ + case LitA: + break; + case LitB: + foo = mk_true(); + break; + case LitMixed: + conjs.push_back(foo); + foo = get_placeholder(foo); } - // find the argument position of x and y int pos = -1; int nargs = num_args(arg(con,0)); @@ -555,10 +903,22 @@ class iz3proof_itp_impl : public iz3proof_itp { pos = i; if(pos == -1) throw proof_error(); + ast bar = make(cong,foo,make_int(rational(pos)),get_placeholder(mk_not(con))); + conjs.push_back(mk_not(con)); + return make_contra_node(bar,conjs); +#if 0 + ast A_term = x; + ast f_A_term = arg(con,0); + if(get_term_type(y) == LitA){ + A_term = y; + f_A_term = arg(con,1); + } + ast res = make(Equal,f_A_term,subst_in_arg_pos(pos,get_placeholder(make(Equal,x,y)),f_A_term)); res = make(And,res,mk_not(con)); return res; +#endif } ast subst_in_arg_pos(int pos, ast term, ast app){ @@ -576,20 +936,22 @@ class iz3proof_itp_impl : public iz3proof_itp { /* Compute the interpolant for the clause */ ast zero = make_int("0"); - std::vector conjs; conjs.resize(1); + std::vector conjs; ast thing = make(Leq,zero,zero); for(unsigned i = 0; i < prem_cons.size(); i++){ const ast &lit = prem_cons[i]; if(get_term_type(lit) == LitA) linear_comb(thing,coeffs[i],lit); - else if(get_term_type(lit) != LitB) - conjs.push_back(make(farkas,coeffs[i],prem_cons[i])); } thing = simplify_ineq(thing); - if(conjs.size() > 1){ - conjs[0] = thing; - thing = make(And,conjs); + for(unsigned i = 0; i < prem_cons.size(); i++){ + const ast &lit = prem_cons[i]; + if(get_term_type(lit) == LitMixed){ + thing = make(sum,thing,coeffs[i],get_placeholder(lit)); + conjs.push_back(lit); + } } + thing = make_contra_node(thing,conjs); /* Resolve it with the premises */ std::vector conc; conc.resize(prem_cons.size()); @@ -667,24 +1029,15 @@ class iz3proof_itp_impl : public iz3proof_itp { itp = mk_true(); break; default: { // mixed equality - ast mid,ante; - if(get_term_type(y) == LitA){ - std::swap(x,y); - mid = make(Plus,x,get_placeholder(yleqx)); - } - else { - mid = make(Plus,x,get_placeholder(xleqy)); - } - ante = make(Uminus,make(Plus,get_placeholder(xleqy),get_placeholder(yleqx))); - ante = mk_not(make(Leq,make_int("0"),ante)); -#if 0 - ast zleqz = make(Leq,make_int("0"),make_int("0")); - ast fark1 = make(farkas,make_int("1"),xleqy); - ast fark2 = make(farkas,make_int("1"),yleqx); - ast ante = make(And,zleqz,fark1,fark2); -#endif - ast conc = make(And,make(Equal,x,mid),mk_not(con)); - itp = my_or(ante,conc); + std::vector conjs; conjs.resize(3); + conjs[0] = mk_not(con); + conjs[1] = xleqy; + conjs[2] = yleqx; + itp = make_contra_node(make(leq2eq, + get_placeholder(mk_not(con)), + get_placeholder(xleqy), + get_placeholder(yleqx)), + conjs); } } return itp; @@ -710,7 +1063,7 @@ class iz3proof_itp_impl : public iz3proof_itp { mid = make(Sub,mid,x); } ast zleqmid = make(Leq,make_int("0"),mid); - ast fark = make(farkas,make_int("1"),mk_not(xleqy)); + ast fark = make(contra,make_int("1"),mk_not(xleqy)); itp = make(And,zleqmid,fark); } } @@ -732,7 +1085,7 @@ class iz3proof_itp_impl : public iz3proof_itp { default: { ast t = arg(tleqc,0); ast c = arg(tleqc,1); - ast thing = make(farkas,make_int("1"),mk_not(con)); + ast thing = make(contra,make_int("1"),mk_not(con)); itp = make(And,make(Leq,make_int("0"),make(Idiv,get_placeholder(tleqc),d)),thing); } } @@ -741,7 +1094,9 @@ class iz3proof_itp_impl : public iz3proof_itp { return itp; } - ast get_farkas_coeff(const ast &f){ + + + ast get_contra_coeff(const ast &f){ ast c = arg(f,0); // if(!is_not(arg(f,1))) // c = make(Uminus,c); @@ -755,6 +1110,10 @@ class iz3proof_itp_impl : public iz3proof_itp { ast my_and(const ast &a, const ast &b){ return mk_and(a,b); } + + ast my_implies(const ast &a, const ast &b){ + return mk_implies(a,b); + } ast my_or(const std::vector &a){ return mk_or(a); @@ -777,8 +1136,19 @@ public: pv = p; rng = r; weak = w; - type domain[2] = {int_type(),bool_type()}; - farkas = function("@farkas",2,domain,bool_type()); + type boolintbooldom[3] = {bool_type(),int_type(),bool_type()}; + type booldom[1] = {bool_type()}; + type boolbooldom[2] = {bool_type(),bool_type()}; + type boolboolbooldom[3] = {bool_type(),bool_type(),bool_type()}; + contra = function("@contra",2,boolbooldom,bool_type()); + sum = function("@sum",3,boolintbooldom,bool_type()); + rotate_sum = function("@rotsum",2,boolbooldom,bool_type()); + leq2eq = function("@leq2eq",3,boolboolbooldom,bool_type()); + cong = function("@cong",3,boolintbooldom,bool_type()); + exmid = function("@exmid",3,boolboolbooldom,bool_type()); + symm = function("@symm",1,booldom,bool_type()); + epsilon = make_var("@eps",int_type()); + modpon = function("@mp",3,boolboolbooldom,bool_type()); } }; diff --git a/src/interp/iz3proof_itp.h b/src/interp/iz3proof_itp.h index 692fb7772..a50fa184a 100644 --- a/src/interp/iz3proof_itp.h +++ b/src/interp/iz3proof_itp.h @@ -82,7 +82,7 @@ class iz3proof_itp : public iz3mgr { /** Make a Symmetry node. This takes a derivation of |- x = y and produces | y = x */ - virtual node make_symmetry(ast con, node prem) = 0; + virtual node make_symmetry(ast con, const ast &premcon, node prem) = 0; /** Make a transitivity node. This takes derivations of |- x = y and |- y = z produces | x = z */ @@ -92,12 +92,12 @@ class iz3proof_itp : public iz3mgr { /** Make a congruence node. This takes a derivation of |- x_i = y_i and produces |- f(...x_i,...) = f(...,y_i,...) */ - virtual node make_congruence(const ast &x, const ast &y, const ast &con, const std::vector &hyps, const ast &prem1) = 0; + virtual node make_congruence(const ast &xi_eq_yi, const ast &con, const ast &prem1) = 0; /** Make a modus-ponens node. This takes derivations of |- x and |- x = y and produces |- y */ - virtual node make_mp(const ast &x, const ast &y, const ast &prem1, const ast &prem2) = 0; + virtual node make_mp(const ast &x_eq_y, const ast &prem1, const ast &prem2) = 0; /** Make a farkas proof node. */ diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 8220fb214..ceacb18bf 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -888,15 +888,21 @@ public: for(unsigned i = 0; i < farkas_coeffs.size(); i++) my_coeffs.push_back(farkas_coeffs[i]); #else - std::vector &my_coeffs = farkas_coeffs; + std::vector my_coeffs; #endif std::vector my_cons; - for(int i = 0; i < nargs; i++) + for(int i = 1; i < nargs; i++){ my_cons.push_back(mk_not(arg(con,i))); + my_coeffs.push_back(farkas_coeffs[i]); + } + ast farkas_con = normalize_inequality(sum_inequalities(farkas_coeffs,my_cons)); + my_cons.push_back(mk_not(farkas_con)); + my_coeffs.push_back(make_int("1")); std::vector my_hyps; for(int i = 0; i < nargs; i++) my_hyps.push_back(iproof->make_hypothesis(my_cons[i])); ast res = iproof->make_farkas(mk_false(),my_hyps,my_cons,my_coeffs); + res = iproof->make_cut_rule(farkas_con,farkas_coeffs[0],arg(con,0),res); return res; } @@ -973,10 +979,12 @@ public: // assume the premise is x = y ast x = arg(conc(prem(proof,0)),0); ast y = arg(conc(prem(proof,0)),1); +#if 0 AstSet &hyps = get_hyps(proof); std::vector hyps_vec; hyps_vec.resize(hyps.size()); std::copy(hyps.begin(),hyps.end(),hyps_vec.begin()); - res = iproof->make_congruence(x,y,con,hyps_vec,args[0]); +#endif + res = iproof->make_congruence(conc(prem(proof,0)),con,args[0]); break; } case PR_REFLEXIVITY: { @@ -984,11 +992,11 @@ public: break; } case PR_SYMMETRY: { - res = iproof->make_symmetry(con,args[0]); + res = iproof->make_symmetry(con,conc(prem(proof,0)),args[0]); break; } case PR_MODUS_PONENS: { - res = iproof->make_mp(conc(prem(proof,0)),arg(conc(prem(proof,1)),1),args[0],args[1]); + res = iproof->make_mp(conc(prem(proof,1)),args[0],args[1]); break; } case PR_TH_LEMMA: { diff --git a/src/interp/iz3translate.h b/src/interp/iz3translate.h index 6663f406b..afb9df9bc 100755 --- a/src/interp/iz3translate.h +++ b/src/interp/iz3translate.h @@ -53,8 +53,11 @@ public: }; //#define IZ3_TRANSLATE_DIRECT2 +#ifndef _FOCI2 #define IZ3_TRANSLATE_DIRECT -// #define IZ3_TRANSLATE_FULL +#else +#define IZ3_TRANSLATE_FULL +#endif #endif From 4ce39087dbcc088d77d3a01c8994958e692fbe18 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 15 Sep 2013 14:00:45 -0700 Subject: [PATCH 165/509] something cl was complaining about --- src/interp/iz3translate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index ceacb18bf..f6f72d920 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -65,7 +65,7 @@ public: typedef hash_set AstHashSet; AstHashSet equivs_visited; // proofs already checked for equivalences - typedef pair, hash_map > AstToIpf; + typedef std::pair, hash_map > AstToIpf; AstToIpf translation; // Z3 proof nodes to Iproof nodes AstToInt frame_map; // map assertions to frames From 5be4365b47fe518ddbe3583ce8c3449567c4ab7d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Sep 2013 16:53:52 -0700 Subject: [PATCH 166/509] redo edit Signed-off-by: Nikolaj Bjorner --- src/smt/proto_model/value_factory.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/smt/proto_model/value_factory.h b/src/smt/proto_model/value_factory.h index 5bd0a1f75..92ad2373b 100644 --- a/src/smt/proto_model/value_factory.h +++ b/src/smt/proto_model/value_factory.h @@ -189,11 +189,12 @@ public: max_size = Number(usz); has_max = true; } + Number start = set->m_next; Number & next = set->m_next; while (!is_new) { result = mk_value(next, s, is_new); next++; - if (has_max && next > max_size + set->m_next) { + if (has_max && next > max_size + start) { return 0; } } From 8a44766382df18b1a8610d7dd54b47f9b85b56e6 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 18 Sep 2013 13:47:20 +0100 Subject: [PATCH 167/509] qfbv-sls tactic bugfix Signed-off-by: Christoph M. Wintersteiger --- src/tactic/sls/sls_tactic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index 69e1c8bc3..eb806e23c 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -1343,6 +1343,7 @@ class sls_tactic : public tactic { m_zero(m_mpz_manager.mk_z(0)), m_one(m_mpz_manager.mk_z(1)), m_two(m_mpz_manager.mk_z(2)), + m_cancel(false), m_bv_util(m), m_tracker(m, m_bv_util, m_mpz_manager, m_powers) { From 4be468d312aa166fe15d7579639ee6d2dc31cf50 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 19 Sep 2013 16:18:23 +0100 Subject: [PATCH 168/509] Reorganized the SLS code. Signed-off-by: Christoph M. Wintersteiger --- src/tactic/sls/sls_evaluator.h | 620 +++++++++++++++ src/tactic/sls/sls_params.pyg | 8 + src/tactic/sls/sls_powers.h | 49 ++ src/tactic/sls/sls_tactic.cpp | 1303 +------------------------------- src/tactic/sls/sls_tracker.h | 675 +++++++++++++++++ 5 files changed, 1374 insertions(+), 1281 deletions(-) create mode 100644 src/tactic/sls/sls_evaluator.h create mode 100644 src/tactic/sls/sls_params.pyg create mode 100644 src/tactic/sls/sls_powers.h create mode 100644 src/tactic/sls/sls_tracker.h diff --git a/src/tactic/sls/sls_evaluator.h b/src/tactic/sls/sls_evaluator.h new file mode 100644 index 000000000..77ff50454 --- /dev/null +++ b/src/tactic/sls/sls_evaluator.h @@ -0,0 +1,620 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + sls_evaluator.h + +Abstract: + + SLS Evaluator + +Author: + + Christoph (cwinter) 2012-02-29 + +Notes: + +--*/ + +#ifndef _SLS_EVALUATOR_H_ +#define _SLS_EVALUATOR_H_ + +#include"sls_powers.h" +#include"sls_tracker.h" + +class sls_evaluator { + ast_manager & m_manager; + bv_util & m_bv_util; + family_id m_basic_fid; + family_id m_bv_fid; + sls_tracker & m_tracker; + unsynch_mpz_manager & m_mpz_manager; + mpz m_zero, m_one, m_two; + powers & m_powers; + expr_ref_buffer m_temp_exprs; + vector > m_traversal_stack; + +public: + sls_evaluator(ast_manager & m, bv_util & bvu, sls_tracker & t, unsynch_mpz_manager & mm, powers & p) : + m_manager(m), + m_bv_util(bvu), + m_tracker(t), + m_mpz_manager(mm), + m_zero(m_mpz_manager.mk_z(0)), + m_one(m_mpz_manager.mk_z(1)), + m_two(m_mpz_manager.mk_z(2)), + m_powers(p), + m_temp_exprs(m) { + m_bv_fid = m_bv_util.get_family_id(); + m_basic_fid = m_manager.get_basic_family_id(); + } + + ~sls_evaluator() { + m_mpz_manager.del(m_zero); + m_mpz_manager.del(m_one); + m_mpz_manager.del(m_two); + } + + void operator()(app * n, mpz & result) { + family_id nfid = n->get_family_id(); + func_decl * fd = n->get_decl(); + unsigned n_args = n->get_num_args(); + + if (n_args == 0) { + m_mpz_manager.set(result, m_tracker.get_value(n)); + return; + } + + expr * const * args = n->get_args(); + + m_mpz_manager.set(result, m_zero); + + if (nfid == m_basic_fid) { + switch (n->get_decl_kind()) { + case OP_AND: { + m_mpz_manager.set(result, m_one); + for (unsigned i = 0; i < n_args; i++) + if (m_mpz_manager.neq(m_tracker.get_value(args[i]), result)) { + m_mpz_manager.set(result, m_zero); + break; + } + break; + } + case OP_OR: { + for (unsigned i = 0; i < n_args; i++) + if (m_mpz_manager.neq(m_tracker.get_value(args[i]), result)) { + m_mpz_manager.set(result, m_one); + break; + } + break; + } + case OP_NOT: { + SASSERT(n_args == 1); + const mpz & child = m_tracker.get_value(args[0]); + SASSERT(m_mpz_manager.is_one(child) || m_mpz_manager.is_zero(child)); + m_mpz_manager.set(result, (m_mpz_manager.is_zero(child)) ? m_one : m_zero); + break; + } + case OP_EQ: { + SASSERT(n_args >= 2); + m_mpz_manager.set(result, m_one); + const mpz & first = m_tracker.get_value(args[0]); + for (unsigned i = 1; i < n_args; i++) + if (m_mpz_manager.neq(m_tracker.get_value(args[i]), first)) { + m_mpz_manager.set(result, m_zero); + break; + } + break; + } + case OP_DISTINCT: { + m_mpz_manager.set(result, m_one); + for (unsigned i = 0; i < n_args && m_mpz_manager.is_one(result); i++) { + for (unsigned j = i+1; j < n_args && m_mpz_manager.is_one(result); j++) { + if (m_mpz_manager.eq(m_tracker.get_value(args[i]), m_tracker.get_value(args[j]))) + m_mpz_manager.set(result, m_zero); + } + } + break; + } + default: + NOT_IMPLEMENTED_YET(); + } + } + else if (nfid == m_bv_fid) { + bv_op_kind k = static_cast(fd->get_decl_kind()); + switch(k) { + case OP_CONCAT: { + SASSERT(n_args >= 2); + for (unsigned i = 0; i < n_args; i++) { + if (i != 0) { + const mpz & p = m_powers(m_bv_util.get_bv_size(args[i])); + m_mpz_manager.mul(result, p, result); + } + m_mpz_manager.add(result, m_tracker.get_value(args[i]), result); + } + break; + } + case OP_EXTRACT: { + SASSERT(n_args == 1); + const mpz & child = m_tracker.get_value(args[0]); + unsigned h = m_bv_util.get_extract_high(n); + unsigned l = m_bv_util.get_extract_low(n); + + m_mpz_manager.rem(child, m_powers(h+1), result); // result = [h:0] of child + m_mpz_manager.machine_div2k(result, l, result); + break; + } + case OP_BADD: { + SASSERT(n_args >= 2); + for (unsigned i = 0; i < n_args; i++) { + const mpz & next = m_tracker.get_value(args[i]); + m_mpz_manager.add(result, next, result); + } + const mpz & p = m_powers(m_bv_util.get_bv_size(n)); + m_mpz_manager.rem(result, p, result); + break; + } + case OP_BSUB: { + SASSERT(n_args == 2); + const mpz & p = m_powers(m_bv_util.get_bv_size(n)); + mpz temp; + m_mpz_manager.sub(m_tracker.get_value(args[0]), m_tracker.get_value(args[1]), temp); + m_mpz_manager.mod(temp, p, result); + m_mpz_manager.del(temp); + break; + } + case OP_BMUL: { + SASSERT(n_args >= 2); + m_mpz_manager.set(result, m_tracker.get_value(args[0])); + for (unsigned i = 1; i < n_args; i++) { + const mpz & next = m_tracker.get_value(args[i]); + m_mpz_manager.mul(result, next, result); + } + const mpz & p = m_powers(m_bv_util.get_bv_size(n)); + m_mpz_manager.rem(result, p, result); + break; + } + case OP_BNEG: { // 2's complement unary minus + SASSERT(n_args == 1); + const mpz & child = m_tracker.get_value(args[0]); + if (m_mpz_manager.is_zero(child)) { + m_mpz_manager.set(result, m_zero); + } + else { + unsigned bv_sz = m_bv_util.get_bv_size(n); + m_mpz_manager.bitwise_not(bv_sz, child, result); + m_mpz_manager.inc(result); // can't overflow + } + break; + } + case OP_BSDIV: + case OP_BSDIV0: + case OP_BSDIV_I: { + SASSERT(n_args == 2); + mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); + mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); + SASSERT(m_mpz_manager.is_nonneg(x) && m_mpz_manager.is_nonneg(y)); + unsigned bv_sz = m_bv_util.get_bv_size(args[0]); + const mpz & p = m_powers(bv_sz); + const mpz & p_half = m_powers(bv_sz-1); + if (x >= p_half) { m_mpz_manager.sub(x, p, x); } + if (y >= p_half) { m_mpz_manager.sub(y, p, y); } + + if (m_mpz_manager.is_zero(y)) { + if (m_mpz_manager.is_neg(x)) + m_mpz_manager.set(result, m_one); + else { + m_mpz_manager.set(result, m_powers(m_bv_util.get_bv_size(n))); + m_mpz_manager.dec(result); + } + } + else { + m_mpz_manager.machine_div(x, y, result); + } + if (m_mpz_manager.is_neg(result)) + m_mpz_manager.add(result, p, result); + m_mpz_manager.del(x); + m_mpz_manager.del(y); + break; + } + case OP_BUDIV: + case OP_BUDIV0: + case OP_BUDIV_I: { + SASSERT(n_args == 2); + mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); + mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); + + if (m_mpz_manager.is_zero(y)) { + m_mpz_manager.set(result, m_powers(m_bv_util.get_bv_size(n))); + m_mpz_manager.dec(result); + } + else { + m_mpz_manager.machine_div(x, y, result); + } + m_mpz_manager.del(x); + m_mpz_manager.del(y); + break; + } + case OP_BSREM: + case OP_BSREM0: + case OP_BSREM_I: { + SASSERT(n_args == 2); + mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); + mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); + unsigned bv_sz = m_bv_util.get_bv_size(args[0]); + const mpz & p = m_powers(bv_sz); + const mpz & p_half = m_powers(bv_sz-1); + if (x >= p_half) { m_mpz_manager.sub(x, p, x); } + if (y >= p_half) { m_mpz_manager.sub(y, p, y); } + + if (m_mpz_manager.is_zero(y)) { + m_mpz_manager.set(result, x); + } + else { + m_mpz_manager.rem(x, y, result); + } + if (m_mpz_manager.is_neg(result)) + m_mpz_manager.add(result, p, result); + m_mpz_manager.del(x); + m_mpz_manager.del(y); + break; + } + case OP_BUREM: + case OP_BUREM0: + case OP_BUREM_I: { + SASSERT(n_args == 2); + mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); + mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); + + if (m_mpz_manager.is_zero(y)) { + m_mpz_manager.set(result, x); + } + else { + m_mpz_manager.mod(x, y, result); + } + m_mpz_manager.del(x); + m_mpz_manager.del(y); + break; + } + case OP_BSMOD: + case OP_BSMOD0: + case OP_BSMOD_I:{ + SASSERT(n_args == 2); + mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); + mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); + unsigned bv_sz = m_bv_util.get_bv_size(args[0]); + const mpz & p = m_powers(bv_sz); + const mpz & p_half = m_powers(bv_sz-1); + if (x >= p_half) { m_mpz_manager.sub(x, p, x); } + if (y >= p_half) { m_mpz_manager.sub(y, p, y); } + + if (m_mpz_manager.is_zero(y)) + m_mpz_manager.set(result, x); + else { + bool neg_x = m_mpz_manager.is_neg(x); + bool neg_y = m_mpz_manager.is_neg(y); + mpz abs_x, abs_y; + m_mpz_manager.set(abs_x, x); + m_mpz_manager.set(abs_y, y); + if (neg_x) m_mpz_manager.neg(abs_x); + if (neg_y) m_mpz_manager.neg(abs_y); + SASSERT(m_mpz_manager.is_nonneg(abs_x) && m_mpz_manager.is_nonneg(abs_y)); + + m_mpz_manager.mod(abs_x, abs_y, result); + + if (m_mpz_manager.is_zero(result) || (!neg_x && !neg_y)) { + /* Nothing */ + } + else if (neg_x && !neg_y) { + m_mpz_manager.neg(result); + m_mpz_manager.add(result, y, result); + } + else if (!neg_x && neg_y) { + m_mpz_manager.add(result, y, result); + } + else { + m_mpz_manager.neg(result); + } + + m_mpz_manager.del(abs_x); + m_mpz_manager.del(abs_y); + } + + if (m_mpz_manager.is_neg(result)) + m_mpz_manager.add(result, p, result); + + m_mpz_manager.del(x); + m_mpz_manager.del(y); + break; + } + case OP_BAND: { + SASSERT(n_args >= 2); + m_mpz_manager.set(result, m_tracker.get_value(args[0])); + for (unsigned i = 1; i < n_args; i++) + m_mpz_manager.bitwise_and(result, m_tracker.get_value(args[i]), result); + break; + } + case OP_BOR: { + SASSERT(n_args >= 2); + m_mpz_manager.set(result, m_tracker.get_value(args[0])); + for (unsigned i = 1; i < n_args; i++) { + m_mpz_manager.bitwise_or(result, m_tracker.get_value(args[i]), result); + } + break; + } + case OP_BXOR: { + SASSERT(n_args >= 2); + m_mpz_manager.set(result, m_tracker.get_value(args[0])); + for (unsigned i = 1; i < n_args; i++) + m_mpz_manager.bitwise_xor(result, m_tracker.get_value(args[i]), result); + break; + } + case OP_BNAND: { + SASSERT(n_args >= 2); + mpz temp; + unsigned bv_sz = m_bv_util.get_bv_size(n); + m_mpz_manager.set(result, m_tracker.get_value(args[0])); + for (unsigned i = 1; i < n_args; i++) { + m_mpz_manager.bitwise_and(result, m_tracker.get_value(args[i]), temp); + m_mpz_manager.bitwise_not(bv_sz, temp, result); + } + m_mpz_manager.del(temp); + break; + } + case OP_BNOR: { + SASSERT(n_args >= 2); + mpz temp; + unsigned bv_sz = m_bv_util.get_bv_size(n); + m_mpz_manager.set(result, m_tracker.get_value(args[0])); + for (unsigned i = 1; i < n_args; i++) { + m_mpz_manager.bitwise_or(result, m_tracker.get_value(args[i]), temp); + m_mpz_manager.bitwise_not(bv_sz, temp, result); + } + m_mpz_manager.del(temp); + break; + } + case OP_BNOT: { + SASSERT(n_args == 1); + m_mpz_manager.bitwise_not(m_bv_util.get_bv_size(args[0]), m_tracker.get_value(args[0]), result); + break; + } + case OP_ULT: + case OP_ULEQ: + case OP_UGT: + case OP_UGEQ: { + SASSERT(n_args == 2); + const mpz & x = m_tracker.get_value(args[0]); + const mpz & y = m_tracker.get_value(args[1]); + if ((k == OP_ULT && m_mpz_manager.lt(x, y)) || + (k == OP_ULEQ && m_mpz_manager.le(x, y)) || + (k == OP_UGT && m_mpz_manager.gt(x, y)) || + (k == OP_UGEQ && m_mpz_manager.ge(x, y))) + m_mpz_manager.set(result, m_one); + break; + } + case OP_SLT: + case OP_SLEQ: + case OP_SGT: + case OP_SGEQ: { + SASSERT(n_args == 2); + mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); + mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); + unsigned bv_sz = m_bv_util.get_bv_size(args[0]); + const mpz & p = m_powers(bv_sz); + const mpz & p_half = m_powers(bv_sz-1); + if (x >= p_half) { m_mpz_manager.sub(x, p, x); } + if (y >= p_half) { m_mpz_manager.sub(y, p, y); } + if ((k == OP_SLT && m_mpz_manager.lt(x, y)) || + (k == OP_SLEQ && m_mpz_manager.le(x, y)) || + (k == OP_SGT && m_mpz_manager.gt(x, y)) || + (k == OP_SGEQ && m_mpz_manager.ge(x, y))) + m_mpz_manager.set(result, m_one); + m_mpz_manager.del(x); + m_mpz_manager.del(y); + break; + } + case OP_BIT2BOOL: { + SASSERT(n_args == 1); + const mpz & child = m_tracker.get_value(args[0]); + m_mpz_manager.set(result, child); + break; + } + case OP_BASHR: { + SASSERT(n_args == 2); + m_mpz_manager.set(result, m_tracker.get_value(args[0])); + mpz first; + const mpz & p = m_powers(m_bv_util.get_bv_size(args[0])-1); + m_mpz_manager.bitwise_and(result, p, first); + mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); + mpz temp; + while (!m_mpz_manager.is_zero(shift)) { + m_mpz_manager.machine_div(result, m_two, temp); + m_mpz_manager.add(temp, first, result); + m_mpz_manager.dec(shift); + } + m_mpz_manager.del(first); + m_mpz_manager.del(shift); + m_mpz_manager.del(temp); + break; + } + case OP_BLSHR: { + SASSERT(n_args == 2); + m_mpz_manager.set(result, m_tracker.get_value(args[0])); + mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); + while (!m_mpz_manager.is_zero(shift)) { + m_mpz_manager.machine_div(result, m_two, result); + m_mpz_manager.dec(shift); + } + m_mpz_manager.del(shift); + break; + } + case OP_BSHL: { + SASSERT(n_args == 2); + m_mpz_manager.set(result, m_tracker.get_value(args[0])); + mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); + while (!m_mpz_manager.is_zero(shift)) { + m_mpz_manager.mul(result, m_two, result); + m_mpz_manager.dec(shift); + } + const mpz & p = m_powers(m_bv_util.get_bv_size(n)); + m_mpz_manager.rem(result, p, result); + m_mpz_manager.del(shift); + break; + } + case OP_SIGN_EXT: { + SASSERT(n_args == 1); + m_mpz_manager.set(result, m_tracker.get_value(args[0])); + break; + } + default: + NOT_IMPLEMENTED_YET(); + } + } + else { + NOT_IMPLEMENTED_YET(); + } + + TRACE("sls_eval", tout << "(" << fd->get_name(); + for (unsigned i = 0; i < n_args; i++) + tout << " " << m_mpz_manager.to_string(m_tracker.get_value(args[i])); + tout << ") ---> " << m_mpz_manager.to_string(result); + if (m_manager.is_bool(fd->get_range())) tout << " [Boolean]"; + else tout << " [vector size: " << m_bv_util.get_bv_size(fd->get_range()) << "]"; + tout << std::endl; ); + + SASSERT(m_mpz_manager.is_nonneg(result)); + } + + void eval_checked(expr * n, mpz & result) { + switch(n->get_kind()) { + case AST_APP: { + app * a = to_app(n); + (*this)(a, result); + + unsigned n_args = a->get_num_args(); + m_temp_exprs.reset(); + for (unsigned i = 0; i < n_args; i++) { + expr * arg = a->get_arg(i); + const mpz & v = m_tracker.get_value(arg); + m_temp_exprs.push_back(m_tracker.mpz2value(m_manager.get_sort(arg), v)); + } + expr_ref q(m_manager), temp(m_manager); + q = m_manager.mk_app(a->get_decl(), m_temp_exprs.size(), m_temp_exprs.c_ptr()); + model dummy_model(m_manager); + model_evaluator evaluator(dummy_model); + evaluator(q, temp); + mpz check_res; + m_tracker.value2mpz(temp, check_res); + CTRACE("sls", !m_mpz_manager.eq(check_res, result), + tout << "EVAL BUG: IS " << m_mpz_manager.to_string(result) << + " SHOULD BE " << m_mpz_manager.to_string(check_res) << std::endl; ); + SASSERT(m_mpz_manager.eq(check_res, result)); + m_mpz_manager.del(check_res); + + break; + } + default: + NOT_IMPLEMENTED_YET(); + } + } + + void run_update(unsigned cur_depth) { + // precondition: m_traversal_stack contains the entry point(s) + expr_fast_mark1 visited; + mpz new_value; + + SASSERT(cur_depth < m_traversal_stack.size()); + while (cur_depth != static_cast(-1)) { + ptr_vector & cur_depth_exprs = m_traversal_stack[cur_depth]; + + for (unsigned i = 0; i < cur_depth_exprs.size(); i++) { + expr * cur = cur_depth_exprs[i]; + + (*this)(to_app(cur), new_value); + m_tracker.set_value(cur, new_value); + m_tracker.set_score(cur, m_tracker.score(cur)); + + if (m_tracker.has_uplinks(cur)) { + ptr_vector & ups = m_tracker.get_uplinks(cur); + for (unsigned j = 0; j < ups.size(); j++) { + expr * next = ups[j]; + unsigned next_d = m_tracker.get_distance(next); + SASSERT(next_d < cur_depth); + if (!visited.is_marked(next)) { + m_traversal_stack[next_d].push_back(next); + visited.mark(next); + } + } + } + } + + cur_depth_exprs.reset(); + cur_depth--; + } + + m_mpz_manager.del(new_value); + } + + void update_all() { + unsigned max_depth = 0; + + sls_tracker::entry_point_type::iterator start = m_tracker.get_entry_points().begin(); + sls_tracker::entry_point_type::iterator end = m_tracker.get_entry_points().end(); + for (sls_tracker::entry_point_type::iterator it = start; it != end; it++) { + expr * ep = m_tracker.get_entry_point(it->m_key); + unsigned cur_depth = m_tracker.get_distance(ep); + if (m_traversal_stack.size() <= cur_depth) + m_traversal_stack.resize(cur_depth+1); + m_traversal_stack[cur_depth].push_back(ep); + if (cur_depth > max_depth) max_depth = cur_depth; + } + + run_update(max_depth); + } + + void update(func_decl * fd, const mpz & new_value) { + m_tracker.set_value(fd, new_value); + expr * ep = m_tracker.get_entry_point(fd); + unsigned cur_depth = m_tracker.get_distance(ep); + if (m_traversal_stack.size() <= cur_depth) + m_traversal_stack.resize(cur_depth+1); + m_traversal_stack[cur_depth].push_back(ep); + + run_update(cur_depth); + } + + void randomize_local(goal_ref const & g) { + ptr_vector & unsat_constants = m_tracker.get_unsat_constants(g); + + // Randomize _all_ candidates: + + //// bool did_something = false; + //for (unsigned i = 0; i < unsat_constants.size(); i++) { + // func_decl * fd = unsat_constants[i]; + // mpz temp = m_tracker.get_random(fd->get_range()); + // // if (m_mpz_manager.neq(temp, m_tracker.get_value(fd))) { + // // did_something = true; + // // } + // update(fd, temp); + // m_mpz_manager.del(temp); + //} + + // Randomize _one_ candidate: + unsigned r = m_tracker.get_random_uint(16) % unsat_constants.size(); + func_decl * fd = unsat_constants[r]; + mpz temp = m_tracker.get_random(fd->get_range()); + update(fd, temp); + m_mpz_manager.del(temp); + + TRACE("sls", /*tout << "Randomization candidates: "; + for (unsigned i = 0; i < unsat_constants.size(); i++) + tout << unsat_constants[i]->get_name() << ", "; + tout << std::endl;*/ + tout << "Randomization candidate: " << unsat_constants[r]->get_name() << std::endl; + tout << "Locally randomized model: " << std::endl; + m_tracker.show_model(tout); ); + } +}; + +#endif \ No newline at end of file diff --git a/src/tactic/sls/sls_params.pyg b/src/tactic/sls/sls_params.pyg new file mode 100644 index 000000000..cc3e05966 --- /dev/null +++ b/src/tactic/sls/sls_params.pyg @@ -0,0 +1,8 @@ +def_module_params('sls', + export=True, + description='Experimental Stochastic Local Search Solver (for QFBV only).', + params=(max_memory_param(), + ('restarts', UINT, UINT_MAX, '(max) number of restarts'), + ('plateau_limit', UINT, 10, 'pleateau limit'), + ('random_seed', UINT, 0, 'random seed') + )) diff --git a/src/tactic/sls/sls_powers.h b/src/tactic/sls/sls_powers.h new file mode 100644 index 000000000..d0cc0815e --- /dev/null +++ b/src/tactic/sls/sls_powers.h @@ -0,0 +1,49 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + sls_powers.h + +Abstract: + + Power-of-2 module for SLS + +Author: + + Christoph (cwinter) 2012-02-29 + +Notes: + +--*/ + +#ifndef _SLS_POWERS_H_ +#define _SLS_POWERS_H_ + +#include"mpz.h" + +class powers : public u_map { + unsynch_mpz_manager & m; +public: + powers(unsynch_mpz_manager & m) : m(m) {} + ~powers() { + for (iterator it = begin(); it != end(); it++) { + m.del(*it->m_value); + dealloc(it->m_value); + } + } + + const mpz & operator()(unsigned n) { + u_map::iterator it = find_iterator(n); + if (it != end()) + return *it->m_value; + else { + mpz * new_obj = alloc(mpz); + m.mul2k(m.mk_z(1), n, *new_obj); + insert(n, new_obj); + return *new_obj; + } + } +}; + +#endif \ No newline at end of file diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index eb806e23c..fff082bd8 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -35,6 +35,10 @@ Notes: #include"sls_tactic.h" #include"nnf_tactic.h" +#include"sls_params.hpp" +#include"sls_evaluator.h" +#include"sls_tracker.h" + class sls_tactic : public tactic { class stats { public: @@ -62,1265 +66,7 @@ class sls_tactic : public tactic { } }; - struct imp { - class score_tracker; - - class powers : public u_map { - unsynch_mpz_manager & m; - public: - powers(unsynch_mpz_manager & m) : m(m) {} - ~powers() { - for (iterator it = begin(); it != end(); it++) { - m.del(*it->m_value); - dealloc(it->m_value); - } - } - - const mpz & operator()(unsigned n) { - u_map::iterator it = find_iterator(n); - if (it != end()) - return *it->m_value; - else { - mpz * new_obj = alloc(mpz); - insert(n, new_obj); - m.power(unsynch_mpz_manager::mk_z(2), n, *new_obj); - return *new_obj; - } - } - }; - - class evaluator { - ast_manager & m_manager; - bv_util & m_bv_util; - family_id m_bv_fid; - score_tracker & m_tracker; - unsynch_mpz_manager & m_mpz_manager; - mpz m_zero, m_one, m_two; - powers & m_powers; - - - public: - evaluator(ast_manager & m, bv_util & bvu, score_tracker & t, unsynch_mpz_manager & mm, powers & p) : - m_manager(m), - m_bv_util(bvu), - m_tracker(t), - m_mpz_manager(mm), - m_zero(m_mpz_manager.mk_z(0)), - m_one(m_mpz_manager.mk_z(1)), - m_two(m_mpz_manager.mk_z(2)), - m_powers(p) { - m_bv_fid = m_bv_util.get_family_id(); - } - - ~evaluator() { - m_mpz_manager.del(m_zero); - m_mpz_manager.del(m_one); - m_mpz_manager.del(m_two); - } - - void operator()(app * n, mpz & result) { - func_decl * fd = n->get_decl(); - unsigned n_args = n->get_num_args(); - expr * const * args = n->get_args(); - - m_mpz_manager.set(result, m_zero); - - if (m_manager.is_and(n)) { - m_mpz_manager.set(result, m_one); - for (unsigned i = 0; i < n_args; i++) { - if (m_mpz_manager.neq(m_tracker.get_value(args[i]), result)) { - m_mpz_manager.set(result, m_zero); - break; - } - } - } - else if (m_manager.is_or(n)) { - for (unsigned i = 0; i < n_args; i++) { - if (m_mpz_manager.neq(m_tracker.get_value(args[i]), result)) { - m_mpz_manager.set(result, m_one); - break; - } - } - } - else if (m_manager.is_not(n)) { - SASSERT(n_args == 1); - const mpz & child = m_tracker.get_value(args[0]); - SASSERT(m_mpz_manager.is_one(child) || m_mpz_manager.is_zero(child)); - m_mpz_manager.set(result, (m_mpz_manager.is_zero(child)) ? m_one : m_zero); - } - else if (m_manager.is_eq(n)) { - SASSERT(n_args >= 2); - m_mpz_manager.set(result, m_one); - const mpz & first = m_tracker.get_value(args[0]); - for (unsigned i = 1; i < n_args; i++) { - if (m_mpz_manager.neq(m_tracker.get_value(args[i]), first)) { - m_mpz_manager.set(result, m_zero); - break; - } - } - } - else if (m_manager.is_distinct(n)) { - m_mpz_manager.set(result, m_one); - for (unsigned i = 0; i < n_args && m_mpz_manager.is_one(result); i++) { - for (unsigned j = i+1; j < n_args && m_mpz_manager.is_one(result); j++) { - if (m_mpz_manager.eq(m_tracker.get_value(args[i]), m_tracker.get_value(args[j]))) - m_mpz_manager.set(result, m_zero); - } - } - } - else if (fd->get_family_id() == m_bv_fid) { - bv_op_kind k = static_cast(fd->get_decl_kind()); - switch(k) { - case OP_CONCAT: { - SASSERT(n_args >= 2); - for (unsigned i = 0; i < n_args; i++) { - if (i != 0) { - const mpz & p = m_powers(m_bv_util.get_bv_size(args[i])); - m_mpz_manager.mul(result, p, result); - } - m_mpz_manager.add(result, m_tracker.get_value(args[i]), result); - } - break; - } - case OP_EXTRACT: { - SASSERT(n_args == 1); - const mpz & child = m_tracker.get_value(args[0]); - unsigned h = m_bv_util.get_extract_high(n); - unsigned l = m_bv_util.get_extract_low(n); - - mpz mask; - m_mpz_manager.set(mask, m_powers(h+1)); - m_mpz_manager.dec(mask); - m_mpz_manager.bitwise_and(child, mask, result); // result = [h:0] of child - - // shift result by l - for (; l != 0 ; l--) - m_mpz_manager.machine_div(result, m_two, result); - - m_mpz_manager.del(mask); - break; - } - case OP_BADD: { - SASSERT(n_args >= 2); - for (unsigned i = 0; i < n_args; i++) { - const mpz & next = m_tracker.get_value(args[i]); - m_mpz_manager.add(result, next, result); - } - const mpz & p = m_powers(m_bv_util.get_bv_size(n)); - m_mpz_manager.rem(result, p, result); - break; - } - case OP_BSUB: { - SASSERT(n_args == 2); - const mpz & p = m_powers(m_bv_util.get_bv_size(n)); - mpz temp; - m_mpz_manager.sub(m_tracker.get_value(args[0]), m_tracker.get_value(args[1]), temp); - m_mpz_manager.mod(temp, p, result); - m_mpz_manager.del(temp); - break; - } - case OP_BMUL: { - SASSERT(n_args >= 2); - m_mpz_manager.set(result, m_tracker.get_value(args[0])); - for (unsigned i = 1; i < n_args; i++) { - const mpz & next = m_tracker.get_value(args[i]); - m_mpz_manager.mul(result, next, result); - } - const mpz & p = m_powers(m_bv_util.get_bv_size(n)); - m_mpz_manager.rem(result, p, result); - break; - } - case OP_BNEG: { // 2's complement unary minus - SASSERT(n_args == 1); - const mpz & child = m_tracker.get_value(args[0]); - if (m_mpz_manager.is_zero(child)) { - m_mpz_manager.set(result, m_zero); - } - else { - unsigned bv_sz = m_bv_util.get_bv_size(n); - m_mpz_manager.bitwise_not(bv_sz, child, result); - m_mpz_manager.inc(result); // can't overflow - } - break; - } - case OP_BSDIV: - case OP_BSDIV0: - case OP_BSDIV_I: { - SASSERT(n_args == 2); - mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); - mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); - SASSERT(m_mpz_manager.is_nonneg(x) && m_mpz_manager.is_nonneg(y)); - unsigned bv_sz = m_bv_util.get_bv_size(args[0]); - const mpz & p = m_powers(bv_sz); - const mpz & p_half = m_powers(bv_sz-1); - if (x >= p_half) { m_mpz_manager.sub(x, p, x); } - if (y >= p_half) { m_mpz_manager.sub(y, p, y); } - - if (m_mpz_manager.is_zero(y)) { - if (m_mpz_manager.is_neg(x)) - m_mpz_manager.set(result, m_one); - else { - m_mpz_manager.set(result, m_powers(m_bv_util.get_bv_size(n))); - m_mpz_manager.dec(result); - } - } - else { - m_mpz_manager.machine_div(x, y, result); - } - if (m_mpz_manager.is_neg(result)) - m_mpz_manager.add(result, p, result); - m_mpz_manager.del(x); - m_mpz_manager.del(y); - break; - } - case OP_BUDIV: - case OP_BUDIV0: - case OP_BUDIV_I: { - SASSERT(n_args == 2); - mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); - mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); - - if (m_mpz_manager.is_zero(y)) { - m_mpz_manager.set(result, m_powers(m_bv_util.get_bv_size(n))); - m_mpz_manager.dec(result); - } - else { - m_mpz_manager.machine_div(x, y, result); - } - m_mpz_manager.del(x); - m_mpz_manager.del(y); - break; - } - case OP_BSREM: - case OP_BSREM0: - case OP_BSREM_I: { - SASSERT(n_args == 2); - mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); - mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); - unsigned bv_sz = m_bv_util.get_bv_size(args[0]); - const mpz & p = m_powers(bv_sz); - const mpz & p_half = m_powers(bv_sz-1); - if (x >= p_half) { m_mpz_manager.sub(x, p, x); } - if (y >= p_half) { m_mpz_manager.sub(y, p, y); } - - if (m_mpz_manager.is_zero(y)) { - m_mpz_manager.set(result, x); - } - else { - m_mpz_manager.rem(x, y, result); - } - if (m_mpz_manager.is_neg(result)) - m_mpz_manager.add(result, p, result); - m_mpz_manager.del(x); - m_mpz_manager.del(y); - break; - } - case OP_BUREM: - case OP_BUREM0: - case OP_BUREM_I: { - SASSERT(n_args == 2); - mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); - mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); - - if (m_mpz_manager.is_zero(y)) { - m_mpz_manager.set(result, x); - } - else { - m_mpz_manager.mod(x, y, result); - } - m_mpz_manager.del(x); - m_mpz_manager.del(y); - break; - } - case OP_BSMOD: - case OP_BSMOD0: - case OP_BSMOD_I:{ - SASSERT(n_args == 2); - mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); - mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); - unsigned bv_sz = m_bv_util.get_bv_size(args[0]); - const mpz & p = m_powers(bv_sz); - const mpz & p_half = m_powers(bv_sz-1); - if (x >= p_half) { m_mpz_manager.sub(x, p, x); } - if (y >= p_half) { m_mpz_manager.sub(y, p, y); } - - if (m_mpz_manager.is_zero(y)) - m_mpz_manager.set(result, x); - else { - bool neg_x = m_mpz_manager.is_neg(x); - bool neg_y = m_mpz_manager.is_neg(y); - mpz abs_x, abs_y; - m_mpz_manager.set(abs_x, x); - m_mpz_manager.set(abs_y, y); - if (neg_x) m_mpz_manager.neg(abs_x); - if (neg_y) m_mpz_manager.neg(abs_y); - SASSERT(m_mpz_manager.is_nonneg(abs_x) && m_mpz_manager.is_nonneg(abs_y)); - - m_mpz_manager.mod(abs_x, abs_y, result); - - if (m_mpz_manager.is_zero(result) || (!neg_x && !neg_y)) { - /* Nothing */ - } - else if (neg_x && !neg_y) { - m_mpz_manager.neg(result); - m_mpz_manager.add(result, y, result); - } - else if (!neg_x && neg_y) { - m_mpz_manager.add(result, y, result); - } - else { - m_mpz_manager.neg(result); - } - - m_mpz_manager.del(abs_x); - m_mpz_manager.del(abs_y); - } - - if (m_mpz_manager.is_neg(result)) - m_mpz_manager.add(result, p, result); - - m_mpz_manager.del(x); - m_mpz_manager.del(y); - break; - } - case OP_BAND: { - SASSERT(n_args >= 2); - m_mpz_manager.set(result, m_tracker.get_value(args[0])); - for (unsigned i = 1; i < n_args; i++) - m_mpz_manager.bitwise_and(result, m_tracker.get_value(args[i]), result); - break; - } - case OP_BOR: { - SASSERT(n_args >= 2); - m_mpz_manager.set(result, m_tracker.get_value(args[0])); - for (unsigned i = 1; i < n_args; i++) { - m_mpz_manager.bitwise_or(result, m_tracker.get_value(args[i]), result); - } - break; - } - case OP_BXOR: { - SASSERT(n_args >= 2); - m_mpz_manager.set(result, m_tracker.get_value(args[0])); - for (unsigned i = 1; i < n_args; i++) - m_mpz_manager.bitwise_xor(result, m_tracker.get_value(args[i]), result); - break; - } - case OP_BNAND: { - SASSERT(n_args >= 2); - mpz temp; - unsigned bv_sz = m_bv_util.get_bv_size(n); - m_mpz_manager.set(result, m_tracker.get_value(args[0])); - for (unsigned i = 1; i < n_args; i++) { - m_mpz_manager.bitwise_and(result, m_tracker.get_value(args[i]), temp); - m_mpz_manager.bitwise_not(bv_sz, temp, result); - } - m_mpz_manager.del(temp); - break; - } - case OP_BNOR: { - SASSERT(n_args >= 2); - mpz temp; - unsigned bv_sz = m_bv_util.get_bv_size(n); - m_mpz_manager.set(result, m_tracker.get_value(args[0])); - for (unsigned i = 1; i < n_args; i++) { - m_mpz_manager.bitwise_or(result, m_tracker.get_value(args[i]), temp); - m_mpz_manager.bitwise_not(bv_sz, temp, result); - } - m_mpz_manager.del(temp); - break; - } - case OP_BNOT: { - SASSERT(n_args == 1); - m_mpz_manager.bitwise_not(m_bv_util.get_bv_size(args[0]), m_tracker.get_value(args[0]), result); - break; - } - case OP_ULT: - case OP_ULEQ: - case OP_UGT: - case OP_UGEQ: { - SASSERT(n_args == 2); - const mpz & x = m_tracker.get_value(args[0]); - const mpz & y = m_tracker.get_value(args[1]); - if ((k == OP_ULT && m_mpz_manager.lt(x, y)) || - (k == OP_ULEQ && m_mpz_manager.le(x, y)) || - (k == OP_UGT && m_mpz_manager.gt(x, y)) || - (k == OP_UGEQ && m_mpz_manager.ge(x, y))) - m_mpz_manager.set(result, m_one); - break; - } - case OP_SLT: - case OP_SLEQ: - case OP_SGT: - case OP_SGEQ: { - SASSERT(n_args == 2); - mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); - mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); - unsigned bv_sz = m_bv_util.get_bv_size(args[0]); - const mpz & p = m_powers(bv_sz); - const mpz & p_half = m_powers(bv_sz-1); - if (x >= p_half) { m_mpz_manager.sub(x, p, x); } - if (y >= p_half) { m_mpz_manager.sub(y, p, y); } - if ((k == OP_SLT && m_mpz_manager.lt(x, y)) || - (k == OP_SLEQ && m_mpz_manager.le(x, y)) || - (k == OP_SGT && m_mpz_manager.gt(x, y)) || - (k == OP_SGEQ && m_mpz_manager.ge(x, y))) - m_mpz_manager.set(result, m_one); - m_mpz_manager.del(x); - m_mpz_manager.del(y); - break; - } - case OP_BIT2BOOL: { - SASSERT(n_args == 1); - const mpz & child = m_tracker.get_value(args[0]); - m_mpz_manager.set(result, child); - break; - } - case OP_BASHR: { - SASSERT(n_args == 2); - m_mpz_manager.set(result, m_tracker.get_value(args[0])); - mpz first; - const mpz & p = m_powers(m_bv_util.get_bv_size(args[0])-1); - m_mpz_manager.bitwise_and(result, p, first); - mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); - mpz temp; - while (!m_mpz_manager.is_zero(shift)) { - m_mpz_manager.machine_div(result, m_two, temp); - m_mpz_manager.add(temp, first, result); - m_mpz_manager.dec(shift); - } - m_mpz_manager.del(first); - m_mpz_manager.del(shift); - m_mpz_manager.del(temp); - break; - } - case OP_BLSHR: { - SASSERT(n_args == 2); - m_mpz_manager.set(result, m_tracker.get_value(args[0])); - mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); - while (!m_mpz_manager.is_zero(shift)) { - m_mpz_manager.machine_div(result, m_two, result); - m_mpz_manager.dec(shift); - } - m_mpz_manager.del(shift); - break; - } - case OP_BSHL: { - SASSERT(n_args == 2); - m_mpz_manager.set(result, m_tracker.get_value(args[0])); - mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); - while (!m_mpz_manager.is_zero(shift)) { - m_mpz_manager.mul(result, m_two, result); - m_mpz_manager.dec(shift); - } - const mpz & p = m_powers(m_bv_util.get_bv_size(n)); - m_mpz_manager.rem(result, p, result); - m_mpz_manager.del(shift); - break; - } - case OP_SIGN_EXT: { - SASSERT(n_args == 1); - m_mpz_manager.set(result, m_tracker.get_value(args[0])); - break; - } - default: - NOT_IMPLEMENTED_YET(); - } - } - else { - NOT_IMPLEMENTED_YET(); - } - - TRACE("sls_eval", tout << "(" << fd->get_name(); - for (unsigned i = 0; i < n_args; i++) - tout << " " << m_mpz_manager.to_string(m_tracker.get_value(args[i])); - tout << ") ---> " << m_mpz_manager.to_string(result); - if (m_manager.is_bool(fd->get_range())) tout << " [Boolean]"; - else tout << " [vector size: " << m_bv_util.get_bv_size(fd->get_range()) << "]"; - tout << std::endl; ); - - SASSERT(m_mpz_manager.is_nonneg(result)); - } - }; - - class score_tracker { - ast_manager & m_manager; - unsynch_mpz_manager & m_mpz_manager; - bv_util & m_bv_util; - powers & m_powers; - random_gen m_rng; - unsigned m_random_bits; - unsigned m_random_bits_cnt; - vector > m_traversal_stack; - evaluator m_sls_evaluator; - mpz m_zero, m_one, m_two; - - model m_dummy_model; - model_evaluator m_evaluator; - expr_ref_buffer m_temp_exprs; - - struct value_score { - value_score() : m(0), value(unsynch_mpz_manager::mk_z(0)), score(0.0), distance(0) { }; - ~value_score() { if (m) m->del(value); } - unsynch_mpz_manager * m; - mpz value; - double score; - unsigned distance; // max distance from any root - value_score & operator=(const value_score & other) { - SASSERT(m == 0 || m == other.m); - if (m) m->set(value, 0); else m = other.m; - m->set(value, other.value); - score = other.score; - distance = other.distance; - return *this; - } - }; - - typedef obj_map scores_type; - typedef obj_map > uplinks_type; - typedef obj_map entry_point_type; - typedef obj_map > occ_type; - scores_type m_scores; - uplinks_type m_uplinks; - entry_point_type m_entry_points; - ptr_vector m_constants; - ptr_vector m_temp_constants; - occ_type m_constants_occ; - - public: - score_tracker(ast_manager & m, bv_util & bvu, unsynch_mpz_manager & mm, powers & p) : - m_manager(m), - m_mpz_manager(mm), - m_bv_util(bvu), - m_powers(p), - m_random_bits_cnt(0), - m_sls_evaluator(m, m_bv_util, *this, m_mpz_manager, p), - m_zero(m_mpz_manager.mk_z(0)), - m_one(m_mpz_manager.mk_z(1)), - m_two(m_mpz_manager.mk_z(2)), - m_dummy_model(m), - m_evaluator(m_dummy_model), - m_temp_exprs(m) { - } - - ~score_tracker() { - m_mpz_manager.del(m_zero); - m_mpz_manager.del(m_one); - m_mpz_manager.del(m_two); - } - - void set_value(expr * n, const mpz & r) { - SASSERT(m_scores.contains(n)); - m_mpz_manager.set(m_scores.find(n).value, r); - } - - void set_value(func_decl * fd, const mpz & r) { - SASSERT(m_entry_points.contains(fd)); - expr * ep = get_entry_point(fd); - set_value(ep, r); - } - - mpz & get_value(expr * n) { - SASSERT(m_scores.contains(n)); - return m_scores.find(n).value; - } - - mpz & get_value(func_decl * fd) { - SASSERT(m_entry_points.contains(fd)); - expr * ep = get_entry_point(fd); - return get_value(ep); - } - - void set_score(expr * n, double score) { - SASSERT(m_scores.contains(n)); - m_scores.find(n).score = score; - } - - void set_score(func_decl * fd, double score) { - SASSERT(m_entry_points.contains(fd)); - expr * ep = get_entry_point(fd); - set_score(ep, score); - } - - double & get_score(expr * n) { - SASSERT(m_scores.contains(n)); - return m_scores.find(n).score; - } - - double & get_score(func_decl * fd) { - SASSERT(m_entry_points.contains(fd)); - expr * ep = get_entry_point(fd); - return get_score(ep); - } - - unsigned get_distance(expr * n) { - SASSERT(m_scores.contains(n)); - return m_scores.find(n).distance; - } - - void set_distance(expr * n, unsigned d) { - SASSERT(m_scores.contains(n)); - m_scores.find(n).distance = d; - } - - expr * get_entry_point(func_decl * fd) { - SASSERT(m_entry_points.contains(fd)); - return m_entry_points.find(fd); - } - - bool has_uplinks(expr * n) { - return m_uplinks.contains(n); - } - - ptr_vector & get_uplinks(expr * n) { - SASSERT(m_uplinks.contains(n)); - return m_uplinks.find(n); - } - - void initialize(app * n) { - // Build score table - if (!m_scores.contains(n)) { - value_score vs; - vs.m = & m_mpz_manager; - m_scores.insert(n, vs); - } - - // Update uplinks - unsigned na = n->get_num_args(); - for (unsigned i = 0; i < na; i++) { - expr * c = n->get_arg(i); - uplinks_type::obj_map_entry * entry = m_uplinks.insert_if_not_there2(c, ptr_vector()); - entry->get_data().m_value.push_back(n); - } - - func_decl * d = n->get_decl(); - - if (n->get_num_args() == 0) { - if (d->get_family_id() != null_family_id) { - // Interpreted constant - mpz t; - value2mpz(n, t); - set_value(n, t); - m_mpz_manager.del(t); - } - else { - // Uninterpreted constant - m_entry_points.insert_if_not_there(d, n); - m_constants.push_back(d); - } - } - } - - struct init_proc { - ast_manager & m_manager; - score_tracker & m_tracker; - - init_proc(ast_manager & m, score_tracker & tracker): - m_manager(m), - m_tracker(tracker) { - } - - void operator()(var * n) {} - - void operator()(quantifier * n) {} - - void operator()(app * n) { - m_tracker.initialize(n); - } - }; - - struct find_func_decls_proc { - ast_manager & m_manager; - ptr_vector & m_occs; - - find_func_decls_proc (ast_manager & m, ptr_vector & occs): - m_manager(m), - m_occs(occs) { - } - - void operator()(var * n) {} - - void operator()(quantifier * n) {} - - void operator()(app * n) { - if (n->get_num_args() != 0) - return; - func_decl * d = n->get_decl(); - if (d->get_family_id() != null_family_id) - return; - m_occs.push_back(d); - } - }; - - void calculate_expr_distances(goal_ref const & g) { - // precondition: m_scores is set up. - unsigned sz = g->size(); - ptr_vector stack; - for (unsigned i = 0; i < sz; i++) - stack.push_back(to_app(g->form(i))); - while (!stack.empty()) { - app * cur = stack.back(); - stack.pop_back(); - - unsigned d = get_distance(cur); - - for (unsigned i = 0; i < cur->get_num_args(); i++) { - app * child = to_app(cur->get_arg(i)); - unsigned d_child = get_distance(child); - if (d >= d_child) { - set_distance(child, d+1); - stack.push_back(child); - } - } - } - } - - void initialize(goal_ref const & g) { - init_proc proc(m_manager, *this); - expr_mark visited; - unsigned sz = g->size(); - for (unsigned i = 0; i < sz; i++) { - expr * e = g->form(i); - for_each_expr(proc, visited, e); - } - - visited.reset(); - - for (unsigned i = 0; i < sz; i++) { - expr * e = g->form(i); - ptr_vector t; - m_constants_occ.insert_if_not_there(e, t); - find_func_decls_proc ffd_proc(m_manager, m_constants_occ.find(e)); - expr_fast_mark1 visited; - quick_for_each_expr(ffd_proc, visited, e); - } - - calculate_expr_distances(g); - - TRACE("sls", tout << "Initial model:" << std::endl; show_model(tout); ); - } - - void show_model(std::ostream & out) { - unsigned sz = get_num_constants(); - for (unsigned i = 0; i < sz; i++) { - func_decl * fd = get_constant(i); - out << fd->get_name() << " = " << m_mpz_manager.to_string(get_value(fd)) << std::endl; - } - } - - model_ref get_model() { - model_ref res = alloc(model, m_manager); - unsigned sz = get_num_constants(); - for (unsigned i = 0; i < sz; i++) { - func_decl * fd = get_constant(i); - res->register_decl(fd, mpz2value(fd->get_range(), get_value(fd))); - } - return res; - } - - unsigned get_num_constants() { - return m_constants.size(); - } - - ptr_vector & get_constants() { - return m_constants; - } - - func_decl * get_constant(unsigned i) { - return m_constants[i]; - } - - void set_random_seed(unsigned s) { - m_rng.set_seed(s); - } - - mpz get_random_bv(sort * s) { - SASSERT(m_bv_util.is_bv_sort(s)); - unsigned bv_size = m_bv_util.get_bv_size(s); - mpz r; m_mpz_manager.set(r, 0); - - mpz temp; - do - { - m_mpz_manager.mul(r, m_two, temp); - m_mpz_manager.add(temp, get_random_bool(), r); - } while (--bv_size > 0); - m_mpz_manager.del(temp); - - return r; - } - - mpz & get_random_bool() { - if (m_random_bits_cnt == 0) { - m_random_bits = m_rng(); - m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. - } - - bool val = (m_random_bits & 0x01) != 0; - m_random_bits = m_random_bits >> 1; - m_random_bits_cnt--; - - return (val) ? m_one : m_zero; - } - - unsigned get_random_uint(unsigned bits) { - if (m_random_bits_cnt == 0) { - m_random_bits = m_rng(); - m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. - } - - unsigned val = 0; - while (bits-- > 0) { - if ((m_random_bits & 0x01) != 0) val++; - val <<= 1; - m_random_bits >>= 1; - m_random_bits_cnt--; - - if (m_random_bits_cnt == 0) { - m_random_bits = m_rng(); - m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. - } - } - - return val; - } - - mpz get_random(sort * s) { - if (m_bv_util.is_bv_sort(s)) - return get_random_bv(s); - else if (m_manager.is_bool(s)) - return get_random_bool(); - else - NOT_IMPLEMENTED_YET(); // This only works for bit-vectors for now. - } - - void randomize() { - TRACE("sls", tout << "Abandoned model:" << std::endl; show_model(tout); ); - - for (entry_point_type::iterator it = m_entry_points.begin(); it != m_entry_points.end(); it++) { - func_decl * fd = it->m_key; - sort * s = fd->get_range(); - mpz temp = get_random(s); - set_value(it->m_value, temp); - m_mpz_manager.del(temp); - } - - TRACE("sls", tout << "Randomized model:" << std::endl; show_model(tout); ); - } - - void randomize_local(goal_ref const & g) { - ptr_vector & unsat_constants = get_unsat_constants(g); - // bool did_something = false; - for (unsigned i = 0; i < unsat_constants.size(); i++) { - func_decl * fd = unsat_constants[i]; - mpz temp = get_random(fd->get_range()); - if (m_mpz_manager.neq(temp, get_value(fd))) { - // did_something = true; - } - update(fd, temp); - m_mpz_manager.del(temp); - } - TRACE("sls", tout << "Randomization candidates: "; - for (unsigned i = 0; i < unsat_constants.size(); i++) - tout << unsat_constants[i]->get_name() << ", "; - tout << std::endl; - tout << "Locally randomized model: " << std::endl; show_model(tout); ); - } - - #define _SCORE_AND_MIN - - double score_bool(expr * n, bool negated = false) { - TRACE("sls_score", tout << ((negated)?"NEG ":"") << "BOOL: " << mk_ismt2_pp(n, m_manager) << std::endl; ); - - double res = 0.0; - - if (is_uninterp_const(n)) { - const mpz & r = get_value(n); - if (negated) - res = (m_mpz_manager.is_one(r)) ? 0.0 : 1.0; - else - res = (m_mpz_manager.is_one(r)) ? 1.0 : 0.0; - } - else if (m_manager.is_and(n)) { - SASSERT(!negated); - app * a = to_app(n); - expr * const * args = a->get_args(); - #ifdef _SCORE_AND_MIN - double min = 1.0; - for (unsigned i = 0; i < a->get_num_args(); i++) { - double cur = get_score(args[i]); - if (cur < min) min = cur; - } - res = min; - #else - double sum = 0.0; - for (unsigned i = 0; i < a->get_num_args(); i++) - sum += get_score(args[i]); - res = sum / (double) a->get_num_args(); - #endif - } - else if (m_manager.is_or(n)) { - SASSERT(!negated); - app * a = to_app(n); - expr * const * args = a->get_args(); - double max = 0.0; - for (unsigned i = 0; i < a->get_num_args(); i++) { - double cur = get_score(args[i]); - if (cur > max) max = cur; - } - res = max; - } - else if (m_manager.is_ite(n)) { - SASSERT(!negated); - app * a = to_app(n); - SASSERT(a->get_num_args() == 3); - const mpz & cond = get_value(a->get_arg(0)); - double s_t = get_score(a->get_arg(1)); - double s_f = get_score(a->get_arg(2)); - res = (m_mpz_manager.is_one(cond)) ? s_t : s_f; - } - else if (m_manager.is_eq(n) || m_manager.is_iff(n)) { - app * a = to_app(n); - SASSERT(a->get_num_args() == 2); - expr * arg0 = a->get_arg(0); - expr * arg1 = a->get_arg(1); - const mpz & v0 = get_value(arg0); - const mpz & v1 = get_value(arg1); - - if (negated) { - res = (m_mpz_manager.eq(v0, v1)) ? 0.0 : 1.0; - TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << - m_mpz_manager.to_string(v1) << std::endl; ); - } - else if (m_manager.is_bool(arg0)) { - res = m_mpz_manager.eq(v0, v1) ? 1.0 : 0.0; - TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << - m_mpz_manager.to_string(v1) << std::endl; ); - } - else if (m_bv_util.is_bv(arg0)) { - mpz diff, diff_m1; - m_mpz_manager.bitwise_xor(v0, v1, diff); - unsigned hamming_distance = 0; - unsigned bv_sz = m_bv_util.get_bv_size(arg0); - #if 1 // unweighted hamming distance - while (!m_mpz_manager.is_zero(diff)) { - //m_mpz_manager.set(diff_m1, diff); - //m_mpz_manager.dec(diff_m1); - //m_mpz_manager.bitwise_and(diff, diff_m1, diff); - //hamming_distance++; - if (!m_mpz_manager.is_even(diff)) { - hamming_distance++; - } - m_mpz_manager.machine_div(diff, m_two, diff); - } - res = 1.0 - (hamming_distance / (double) bv_sz); - #else - rational r(diff); - r /= m_powers(bv_sz); - double dbl = r.get_double(); - res = (dbl < 0.0) ? 1.0 : (dbl > 1.0) ? 0.0 : 1.0 - dbl; - #endif - TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << - m_mpz_manager.to_string(v1) << " ; HD = " << hamming_distance << - " ; SZ = " << bv_sz << std::endl; ); - m_mpz_manager.del(diff); - m_mpz_manager.del(diff_m1); - } - else - NOT_IMPLEMENTED_YET(); - } - else if (m_bv_util.is_bv_ule(n)) { // x <= y - app * a = to_app(n); - SASSERT(a->get_num_args() == 2); - const mpz & x = get_value(a->get_arg(0)); - const mpz & y = get_value(a->get_arg(1)); - unsigned bv_sz = m_bv_util.get_bv_size(a->get_decl()->get_domain()[0]); - - if (negated) { - if (m_mpz_manager.gt(x, y)) - res = 1.0; - else { - mpz diff; - m_mpz_manager.sub(y, x, diff); - m_mpz_manager.inc(diff); - rational n(diff); - n /= rational(m_powers(bv_sz)); - double dbl = n.get_double(); - // In extreme cases, n is 0.9999 but to_double returns something > 1.0 - res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; - m_mpz_manager.del(diff); - } - } - else { - if (m_mpz_manager.le(x, y)) - res = 1.0; - else { - mpz diff; - m_mpz_manager.sub(x, y, diff); - rational n(diff); - n /= rational(m_powers(bv_sz)); - double dbl = n.get_double(); - res = (dbl > 1.0) ? 1.0 : (dbl < 0.0) ? 0.0 : dbl; - m_mpz_manager.del(diff); - } - } - TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << - m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); - } - else if (m_bv_util.is_bv_sle(n)) { // x <= y - app * a = to_app(n); - SASSERT(a->get_num_args() == 2); - mpz x; m_mpz_manager.set(x, get_value(a->get_arg(0))); - mpz y; m_mpz_manager.set(y, get_value(a->get_arg(1))); - unsigned bv_sz = m_bv_util.get_bv_size(a->get_decl()->get_domain()[0]); - const mpz & p = m_powers(bv_sz); - const mpz & p_half = m_powers(bv_sz-1); - if (x >= p_half) { m_mpz_manager.sub(x, p, x); } - if (y >= p_half) { m_mpz_manager.sub(y, p, y); } - - if (negated) { - if (x > y) - res = 1.0; - else { - mpz diff; - m_mpz_manager.sub(y, x, diff); - m_mpz_manager.inc(diff); - rational n(diff); - n /= p; - double dbl = n.get_double(); - res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; - m_mpz_manager.del(diff); - } - TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << - m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); - } - else { - if (x <= y) - res = 1.0; - else { - mpz diff; - m_mpz_manager.sub(x, y, diff); - rational n(diff); - n /= p; - double dbl = n.get_double(); - res = (dbl > 1.0) ? 1.0 : (dbl < 0.0) ? 0.0 : dbl; - m_mpz_manager.del(diff); - } - TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << - m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); - } - m_mpz_manager.del(x); - m_mpz_manager.del(y); - } - else if (m_manager.is_not(n)) { - SASSERT(!negated); - app * a = to_app(n); - SASSERT(a->get_num_args() == 1); - expr * child = a->get_arg(0); - if (m_manager.is_and(child) || m_manager.is_or(child)) // Precondition: Assertion set is in NNF. - NOT_IMPLEMENTED_YET(); - res = score_bool(child, true); - } - else if (m_manager.is_distinct(n)) { - app * a = to_app(n); - unsigned pairs = 0, distinct_pairs = 0; - unsigned sz = a->get_num_args(); - for (unsigned i = 0; i < sz; i++) { - for (unsigned j = i+1; j < sz; j++) { - // pair i/j - const mpz & v0 = get_value(a->get_arg(0)); - const mpz & v1 = get_value(a->get_arg(1)); - pairs++; - if (v0 != v1) - distinct_pairs++; - } - } - res = (distinct_pairs/(double)pairs); - if (negated) res = 1.0 - res; - } - else - NOT_IMPLEMENTED_YET(); - - SASSERT(res >= 0.0 && res <= 1.0); - - TRACE("sls_score", tout << "SCORE = " << res << std::endl; ); - return res; - } - - double score_bv(expr * n) { - return 0.0; // a bv-expr is always scored as 0.0; we won't use those scores. - } - - void value2mpz(expr * n, mpz & result) { - m_mpz_manager.set(result, m_zero); - - if (m_manager.is_bool(n)) { - m_mpz_manager.set(result, m_manager.is_true(n) ? m_one : m_zero); - } - else if (m_bv_util.is_bv(n)) { - unsigned bv_sz = m_bv_util.get_bv_size(n); - rational q; - if (!m_bv_util.is_numeral(n, q, bv_sz)) - NOT_IMPLEMENTED_YET(); - mpq temp = q.to_mpq(); - SASSERT(m_mpz_manager.is_one(temp.denominator())); - m_mpz_manager.set(result, temp.numerator()); - } - else - NOT_IMPLEMENTED_YET(); - } - - expr_ref mpz2value(sort * s, const mpz & r) { - expr_ref res(m_manager); - if (m_manager.is_bool(s)) - res = (m_mpz_manager.is_zero(r)) ? m_manager.mk_false() : m_manager.mk_true(); - else if (m_bv_util.is_bv_sort(s)) { - rational rat(r); - res = m_bv_util.mk_numeral(rat, s); - } - else - NOT_IMPLEMENTED_YET(); - return res; - } - - void eval(expr * n, mpz & result) { - switch(n->get_kind()) { - case AST_APP: { - app * a = to_app(n); - unsigned n_args = a->get_num_args(); - - if (n_args == 0) { - m_mpz_manager.set(result, get_value(n)); - } - else { - m_sls_evaluator(a, result); - - //#define _EVAL_CHECKED - #ifdef _EVAL_CHECKED - m_temp_exprs.reset(); - for (unsigned i = 0; i < n_args; i++) { - expr * arg = a->get_arg(i); - const mpz & v = get_value(arg); - m_temp_exprs.push_back(mpz2value(m_manager.get_sort(arg), v)); - } - expr_ref q(m_manager), temp(m_manager); - q = m_manager.mk_app(fd, m_temp_exprs.size(), m_temp_exprs.c_ptr()); - m_evaluator(q, temp); - mpz check_res; - value2mpz(temp, check_res); - if (!m_mpz_manager.eq(check_res, result)) - TRACE("sls", tout << "EVAL BUG: IS " << m_mpz_manager.to_string(result) << - " SHOULD BE " << m_mpz_manager.to_string(check_res) << std::endl; ); - SASSERT(m_mpz_manager.eq(check_res, result)); - m_mpz_manager.del(check_res); - #endif - } - break; - } - default: - NOT_IMPLEMENTED_YET(); - } - // TRACE("sls", tout << "EVAL: " << mk_ismt2_pp(n, m_manager) << " IS " << res << std::endl;); - } - - double score(expr * n) { - if (m_manager.is_bool(n)) - return score_bool(n); - else if (m_bv_util.is_bv(n)) - return score_bv(n); - else - NOT_IMPLEMENTED_YET(); - } - - void run_update(unsigned cur_depth) { - // precondition: m_traversal_stack contains the entry point(s) - expr_fast_mark1 visited; - mpz new_value; - - SASSERT(cur_depth < m_traversal_stack.size()); - while (cur_depth != static_cast(-1)) { - ptr_vector & cur_depth_exprs = m_traversal_stack[cur_depth]; - - for (unsigned i = 0; i < cur_depth_exprs.size(); i++) { - expr * cur = cur_depth_exprs[i]; - - eval(cur, new_value); - set_value(cur, new_value); - set_score(cur, score(cur)); - - if (has_uplinks(cur)) { - ptr_vector & ups = get_uplinks(cur); - for (unsigned j = 0; j < ups.size(); j++) { - expr * next = ups[j]; - unsigned next_d = get_distance(next); - SASSERT(next_d < cur_depth); - if (!visited.is_marked(next)) { - m_traversal_stack[next_d].push_back(next); - visited.mark(next); - } - } - } - } - - cur_depth_exprs.reset(); - cur_depth--; - } - - m_mpz_manager.del(new_value); - } - - void update_all() { - unsigned max_depth = 0; - - for (entry_point_type::iterator it = m_entry_points.begin(); - it != m_entry_points.end(); - it++) { - expr * ep = get_entry_point(it->m_key); - unsigned cur_depth = get_distance(ep); - if (m_traversal_stack.size() <= cur_depth) - m_traversal_stack.resize(cur_depth+1); - m_traversal_stack[cur_depth].push_back(ep); - if (cur_depth > max_depth) max_depth = cur_depth; - } - - run_update(max_depth); - } - - void update(func_decl * fd, const mpz & new_value) { - set_value(fd, new_value); - expr * ep = get_entry_point(fd); - unsigned cur_depth = get_distance(ep); - if (m_traversal_stack.size() <= cur_depth) - m_traversal_stack.resize(cur_depth+1); - m_traversal_stack[cur_depth].push_back(ep); - - run_update(cur_depth); - } - - ptr_vector & get_unsat_constants(goal_ref const & g) { - unsigned sz = g->size(); - - if (sz == 1) { - return get_constants(); - } - else { - m_temp_constants.reset(); - for (unsigned i = 0; i < sz; i++) { - expr * q = g->form(i); - if (m_mpz_manager.eq(get_value(q), m_one)) - continue; - ptr_vector const & this_decls = m_constants_occ.find(q); - unsigned sz2 = this_decls.size(); - for (unsigned j = 0; j < sz2; j++) { - func_decl * fd = this_decls[j]; - if (!m_temp_constants.contains(fd)) - m_temp_constants.push_back(fd); - } - } - return m_temp_constants; - } - } - }; - + struct imp { ast_manager & m_manager; stats & m_stats; unsynch_mpz_manager m_mpz_manager; @@ -1329,7 +75,8 @@ class sls_tactic : public tactic { bool m_produce_models; volatile bool m_cancel; bv_util m_bv_util; - score_tracker m_tracker; + sls_tracker m_tracker; + sls_evaluator m_evaluator; unsigned m_max_restarts; unsigned m_plateau_limit; @@ -1345,7 +92,8 @@ class sls_tactic : public tactic { m_two(m_mpz_manager.mk_z(2)), m_cancel(false), m_bv_util(m), - m_tracker(m, m_bv_util, m_mpz_manager, m_powers) + m_tracker(m, m_bv_util, m_mpz_manager, m_powers), + m_evaluator(m, m_bv_util, m_tracker, m_mpz_manager, m_powers) { updt_params(p); } @@ -1363,17 +111,15 @@ class sls_tactic : public tactic { void reset_cancel() { set_cancel(false); } static void collect_param_descrs(param_descrs & r) { - insert_produce_models(r); - r.insert("sls_restarts", CPK_UINT, "(default: infty) # of SLS restarts."); - r.insert("random_seed", CPK_UINT, "(default: 0) random seed."); - r.insert("plateau_limit", CPK_UINT, "(default: 100) SLS plateau limit."); + sls_params::collect_param_descrs(r); } - void updt_params(params_ref const & p) { - m_produce_models = p.get_bool("produce_models", false); - m_max_restarts = p.get_uint("sls_restarts", (unsigned)-1); - m_tracker.set_random_seed(p.get_uint("random_seed", 0)); - m_plateau_limit = p.get_uint("plateau_limit", 100); + void updt_params(params_ref const & _p) { + sls_params p(_p); + m_produce_models = _p.get_bool("model", false); + m_max_restarts = p.restarts(); + m_tracker.set_random_seed(p.random_seed()); + m_plateau_limit = p.plateau_limit(); } void checkpoint() { @@ -1429,13 +175,13 @@ class sls_tactic : public tactic { } double rescore(goal_ref const & g) { - m_tracker.update_all(); + m_evaluator.update_all(); m_stats.m_full_evals++; return top_score(g); } double incremental_score(goal_ref const & g, func_decl * fd, const mpz & new_value) { - m_tracker.update(fd, new_value); + m_evaluator.update(fd, new_value); m_stats.m_incr_evals++; return top_score(g); } @@ -1539,7 +285,7 @@ class sls_tactic : public tactic { NOT_IMPLEMENTED_YET(); } - m_tracker.update(fd, new_value); + m_evaluator.update(fd, new_value); TRACE("sls", tout << "Randomization candidates: "; for (unsigned i = 0; i < unsat_constants.size(); i++) @@ -1713,7 +459,8 @@ class sls_tactic : public tactic { else { plateau_cnt++; if (plateau_cnt < m_plateau_limit) { - m_tracker.randomize_local(g); + TRACE("sls", tout << "In a plateau (" << plateau_cnt << "/" << m_plateau_limit << "); randomizing locally." << std::endl; ); + m_evaluator.randomize_local(g); //mk_random_move(g); score = top_score(g); } @@ -1890,13 +637,7 @@ tactic * mk_preamble(ast_manager & m, params_ref const & p) { } tactic * mk_qfbv_sls_tactic(ast_manager & m, params_ref const & p) { - params_ref sls_p(p); - sls_p.set_uint("sls_restarts", 10000); - sls_p.set_uint("plateau_limit", 100); - - tactic * t = and_then(mk_preamble(m, p), - using_params(mk_sls_tactic(m, p), sls_p)); - + tactic * t = and_then(mk_preamble(m, p), mk_sls_tactic(m)); t->updt_params(p); return t; } diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h new file mode 100644 index 000000000..7fbafec60 --- /dev/null +++ b/src/tactic/sls/sls_tracker.h @@ -0,0 +1,675 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + sls_score_tracker.h + +Abstract: + + Score and value tracking module for SLS + +Author: + + Christoph (cwinter) 2012-02-29 + +Notes: + +--*/ + +#ifndef _SLS_TRACKER_H_ +#define _SLS_TRACKER_H_ + +class sls_tracker { + ast_manager & m_manager; + unsynch_mpz_manager & m_mpz_manager; + bv_util & m_bv_util; + powers & m_powers; + random_gen m_rng; + unsigned m_random_bits; + unsigned m_random_bits_cnt; + mpz m_zero, m_one, m_two; + + struct value_score { + value_score() : m(0), value(unsynch_mpz_manager::mk_z(0)), score(0.0), distance(0) { }; + ~value_score() { if (m) m->del(value); } + unsynch_mpz_manager * m; + mpz value; + double score; + unsigned distance; // max distance from any root + value_score & operator=(const value_score & other) { + SASSERT(m == 0 || m == other.m); + if (m) m->set(value, 0); else m = other.m; + m->set(value, other.value); + score = other.score; + distance = other.distance; + return *this; + } + }; + +public: + typedef obj_map entry_point_type; + +private: + typedef obj_map scores_type; + typedef obj_map > uplinks_type; + typedef obj_map > occ_type; + scores_type m_scores; + uplinks_type m_uplinks; + entry_point_type m_entry_points; + ptr_vector m_constants; + ptr_vector m_temp_constants; + occ_type m_constants_occ; + +public: + sls_tracker(ast_manager & m, bv_util & bvu, unsynch_mpz_manager & mm, powers & p) : + m_manager(m), + m_mpz_manager(mm), + m_bv_util(bvu), + m_powers(p), + m_random_bits_cnt(0), + m_zero(m_mpz_manager.mk_z(0)), + m_one(m_mpz_manager.mk_z(1)), + m_two(m_mpz_manager.mk_z(2)) { + } + + ~sls_tracker() { + m_mpz_manager.del(m_zero); + m_mpz_manager.del(m_one); + m_mpz_manager.del(m_two); + } + + inline void set_value(expr * n, const mpz & r) { + SASSERT(m_scores.contains(n)); + m_mpz_manager.set(m_scores.find(n).value, r); + } + + inline void set_value(func_decl * fd, const mpz & r) { + SASSERT(m_entry_points.contains(fd)); + expr * ep = get_entry_point(fd); + set_value(ep, r); + } + + inline mpz & get_value(expr * n) { + SASSERT(m_scores.contains(n)); + return m_scores.find(n).value; + } + + inline mpz & get_value(func_decl * fd) { + SASSERT(m_entry_points.contains(fd)); + expr * ep = get_entry_point(fd); + return get_value(ep); + } + + inline void set_score(expr * n, double score) { + SASSERT(m_scores.contains(n)); + m_scores.find(n).score = score; + } + + inline void set_score(func_decl * fd, double score) { + SASSERT(m_entry_points.contains(fd)); + expr * ep = get_entry_point(fd); + set_score(ep, score); + } + + inline double & get_score(expr * n) { + SASSERT(m_scores.contains(n)); + return m_scores.find(n).score; + } + + inline double & get_score(func_decl * fd) { + SASSERT(m_entry_points.contains(fd)); + expr * ep = get_entry_point(fd); + return get_score(ep); + } + + inline unsigned get_distance(expr * n) { + SASSERT(m_scores.contains(n)); + return m_scores.find(n).distance; + } + + inline void set_distance(expr * n, unsigned d) { + SASSERT(m_scores.contains(n)); + m_scores.find(n).distance = d; + } + + inline expr * get_entry_point(func_decl * fd) { + SASSERT(m_entry_points.contains(fd)); + return m_entry_points.find(fd); + } + + inline entry_point_type const & get_entry_points() { + return m_entry_points; + } + + inline bool has_uplinks(expr * n) { + return m_uplinks.contains(n); + } + + inline ptr_vector & get_uplinks(expr * n) { + SASSERT(m_uplinks.contains(n)); + return m_uplinks.find(n); + } + + void initialize(app * n) { + // Build score table + if (!m_scores.contains(n)) { + value_score vs; + vs.m = & m_mpz_manager; + m_scores.insert(n, vs); + } + + // Update uplinks + unsigned na = n->get_num_args(); + for (unsigned i = 0; i < na; i++) { + expr * c = n->get_arg(i); + uplinks_type::obj_map_entry * entry = m_uplinks.insert_if_not_there2(c, ptr_vector()); + entry->get_data().m_value.push_back(n); + } + + func_decl * d = n->get_decl(); + + if (n->get_num_args() == 0) { + if (d->get_family_id() != null_family_id) { + // Interpreted constant + mpz t; + value2mpz(n, t); + set_value(n, t); + m_mpz_manager.del(t); + } + else { + // Uninterpreted constant + m_entry_points.insert_if_not_there(d, n); + m_constants.push_back(d); + } + } + } + + struct init_proc { + ast_manager & m_manager; + sls_tracker & m_tracker; + + init_proc(ast_manager & m, sls_tracker & tracker): + m_manager(m), + m_tracker(tracker) { + } + + void operator()(var * n) {} + + void operator()(quantifier * n) {} + + void operator()(app * n) { + m_tracker.initialize(n); + } + }; + + struct find_func_decls_proc { + ast_manager & m_manager; + ptr_vector & m_occs; + + find_func_decls_proc (ast_manager & m, ptr_vector & occs): + m_manager(m), + m_occs(occs) { + } + + void operator()(var * n) {} + + void operator()(quantifier * n) {} + + void operator()(app * n) { + if (n->get_num_args() != 0) + return; + func_decl * d = n->get_decl(); + if (d->get_family_id() != null_family_id) + return; + m_occs.push_back(d); + } + }; + + void calculate_expr_distances(goal_ref const & g) { + // precondition: m_scores is set up. + unsigned sz = g->size(); + ptr_vector stack; + for (unsigned i = 0; i < sz; i++) + stack.push_back(to_app(g->form(i))); + while (!stack.empty()) { + app * cur = stack.back(); + stack.pop_back(); + + unsigned d = get_distance(cur); + + for (unsigned i = 0; i < cur->get_num_args(); i++) { + app * child = to_app(cur->get_arg(i)); + unsigned d_child = get_distance(child); + if (d >= d_child) { + set_distance(child, d+1); + stack.push_back(child); + } + } + } + } + + void initialize(goal_ref const & g) { + init_proc proc(m_manager, *this); + expr_mark visited; + unsigned sz = g->size(); + for (unsigned i = 0; i < sz; i++) { + expr * e = g->form(i); + for_each_expr(proc, visited, e); + } + + visited.reset(); + + for (unsigned i = 0; i < sz; i++) { + expr * e = g->form(i); + ptr_vector t; + m_constants_occ.insert_if_not_there(e, t); + find_func_decls_proc ffd_proc(m_manager, m_constants_occ.find(e)); + expr_fast_mark1 visited; + quick_for_each_expr(ffd_proc, visited, e); + } + + calculate_expr_distances(g); + + TRACE("sls", tout << "Initial model:" << std::endl; show_model(tout); ); + } + + void show_model(std::ostream & out) { + unsigned sz = get_num_constants(); + for (unsigned i = 0; i < sz; i++) { + func_decl * fd = get_constant(i); + out << fd->get_name() << " = " << m_mpz_manager.to_string(get_value(fd)) << std::endl; + } + } + + model_ref get_model() { + model_ref res = alloc(model, m_manager); + unsigned sz = get_num_constants(); + for (unsigned i = 0; i < sz; i++) { + func_decl * fd = get_constant(i); + res->register_decl(fd, mpz2value(fd->get_range(), get_value(fd))); + } + return res; + } + + unsigned get_num_constants() { + return m_constants.size(); + } + + ptr_vector & get_constants() { + return m_constants; + } + + func_decl * get_constant(unsigned i) { + return m_constants[i]; + } + + void set_random_seed(unsigned s) { + m_rng.set_seed(s); + } + + mpz get_random_bv(sort * s) { + SASSERT(m_bv_util.is_bv_sort(s)); + unsigned bv_size = m_bv_util.get_bv_size(s); + mpz r; m_mpz_manager.set(r, 0); + + mpz temp; + do + { + m_mpz_manager.mul(r, m_two, temp); + m_mpz_manager.add(temp, get_random_bool(), r); + } while (--bv_size > 0); + m_mpz_manager.del(temp); + + return r; + } + + mpz & get_random_bool() { + if (m_random_bits_cnt == 0) { + m_random_bits = m_rng(); + m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. + } + + bool val = (m_random_bits & 0x01) != 0; + m_random_bits = m_random_bits >> 1; + m_random_bits_cnt--; + + return (val) ? m_one : m_zero; + } + + unsigned get_random_uint(unsigned bits) { + if (m_random_bits_cnt == 0) { + m_random_bits = m_rng(); + m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. + } + + unsigned val = 0; + while (bits-- > 0) { + if ((m_random_bits & 0x01) != 0) val++; + val <<= 1; + m_random_bits >>= 1; + m_random_bits_cnt--; + + if (m_random_bits_cnt == 0) { + m_random_bits = m_rng(); + m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. + } + } + + return val; + } + + mpz get_random(sort * s) { + if (m_bv_util.is_bv_sort(s)) + return get_random_bv(s); + else if (m_manager.is_bool(s)) + return get_random_bool(); + else + NOT_IMPLEMENTED_YET(); // This only works for bit-vectors for now. + } + + void randomize() { + TRACE("sls", tout << "Abandoned model:" << std::endl; show_model(tout); ); + + for (entry_point_type::iterator it = m_entry_points.begin(); it != m_entry_points.end(); it++) { + func_decl * fd = it->m_key; + sort * s = fd->get_range(); + mpz temp = get_random(s); + set_value(it->m_value, temp); + m_mpz_manager.del(temp); + } + + TRACE("sls", tout << "Randomized model:" << std::endl; show_model(tout); ); + } + +#define _SCORE_AND_MIN + + double score_bool(expr * n, bool negated = false) { + TRACE("sls_score", tout << ((negated)?"NEG ":"") << "BOOL: " << mk_ismt2_pp(n, m_manager) << std::endl; ); + + double res = 0.0; + + if (is_uninterp_const(n)) { + const mpz & r = get_value(n); + if (negated) + res = (m_mpz_manager.is_one(r)) ? 0.0 : 1.0; + else + res = (m_mpz_manager.is_one(r)) ? 1.0 : 0.0; + } + else if (m_manager.is_and(n)) { + SASSERT(!negated); + app * a = to_app(n); + expr * const * args = a->get_args(); + #ifdef _SCORE_AND_MIN + double min = 1.0; + for (unsigned i = 0; i < a->get_num_args(); i++) { + double cur = get_score(args[i]); + if (cur < min) min = cur; + } + res = min; + #else + double sum = 0.0; + for (unsigned i = 0; i < a->get_num_args(); i++) + sum += get_score(args[i]); + res = sum / (double) a->get_num_args(); + #endif + } + else if (m_manager.is_or(n)) { + SASSERT(!negated); + app * a = to_app(n); + expr * const * args = a->get_args(); + double max = 0.0; + for (unsigned i = 0; i < a->get_num_args(); i++) { + double cur = get_score(args[i]); + if (cur > max) max = cur; + } + res = max; + } + else if (m_manager.is_ite(n)) { + SASSERT(!negated); + app * a = to_app(n); + SASSERT(a->get_num_args() == 3); + const mpz & cond = get_value(a->get_arg(0)); + double s_t = get_score(a->get_arg(1)); + double s_f = get_score(a->get_arg(2)); + res = (m_mpz_manager.is_one(cond)) ? s_t : s_f; + } + else if (m_manager.is_eq(n) || m_manager.is_iff(n)) { + app * a = to_app(n); + SASSERT(a->get_num_args() == 2); + expr * arg0 = a->get_arg(0); + expr * arg1 = a->get_arg(1); + const mpz & v0 = get_value(arg0); + const mpz & v1 = get_value(arg1); + + if (negated) { + res = (m_mpz_manager.eq(v0, v1)) ? 0.0 : 1.0; + TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << + m_mpz_manager.to_string(v1) << std::endl; ); + } + else if (m_manager.is_bool(arg0)) { + res = m_mpz_manager.eq(v0, v1) ? 1.0 : 0.0; + TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << + m_mpz_manager.to_string(v1) << std::endl; ); + } + else if (m_bv_util.is_bv(arg0)) { + mpz diff, diff_m1; + m_mpz_manager.bitwise_xor(v0, v1, diff); + unsigned hamming_distance = 0; + unsigned bv_sz = m_bv_util.get_bv_size(arg0); + #if 1 // unweighted hamming distance + while (!m_mpz_manager.is_zero(diff)) { + //m_mpz_manager.set(diff_m1, diff); + //m_mpz_manager.dec(diff_m1); + //m_mpz_manager.bitwise_and(diff, diff_m1, diff); + //hamming_distance++; + if (!m_mpz_manager.is_even(diff)) { + hamming_distance++; + } + m_mpz_manager.machine_div(diff, m_two, diff); + } + res = 1.0 - (hamming_distance / (double) bv_sz); + #else + rational r(diff); + r /= m_powers(bv_sz); + double dbl = r.get_double(); + res = (dbl < 0.0) ? 1.0 : (dbl > 1.0) ? 0.0 : 1.0 - dbl; + #endif + TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << + m_mpz_manager.to_string(v1) << " ; HD = " << hamming_distance << + " ; SZ = " << bv_sz << std::endl; ); + m_mpz_manager.del(diff); + m_mpz_manager.del(diff_m1); + } + else + NOT_IMPLEMENTED_YET(); + } + else if (m_bv_util.is_bv_ule(n)) { // x <= y + app * a = to_app(n); + SASSERT(a->get_num_args() == 2); + const mpz & x = get_value(a->get_arg(0)); + const mpz & y = get_value(a->get_arg(1)); + unsigned bv_sz = m_bv_util.get_bv_size(a->get_decl()->get_domain()[0]); + + if (negated) { + if (m_mpz_manager.gt(x, y)) + res = 1.0; + else { + mpz diff; + m_mpz_manager.sub(y, x, diff); + m_mpz_manager.inc(diff); + rational n(diff); + n /= rational(m_powers(bv_sz)); + double dbl = n.get_double(); + // In extreme cases, n is 0.9999 but to_double returns something > 1.0 + res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; + m_mpz_manager.del(diff); + } + } + else { + if (m_mpz_manager.le(x, y)) + res = 1.0; + else { + mpz diff; + m_mpz_manager.sub(x, y, diff); + rational n(diff); + n /= rational(m_powers(bv_sz)); + double dbl = n.get_double(); + res = (dbl > 1.0) ? 1.0 : (dbl < 0.0) ? 0.0 : dbl; + m_mpz_manager.del(diff); + } + } + TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << + m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); + } + else if (m_bv_util.is_bv_sle(n)) { // x <= y + app * a = to_app(n); + SASSERT(a->get_num_args() == 2); + mpz x; m_mpz_manager.set(x, get_value(a->get_arg(0))); + mpz y; m_mpz_manager.set(y, get_value(a->get_arg(1))); + unsigned bv_sz = m_bv_util.get_bv_size(a->get_decl()->get_domain()[0]); + const mpz & p = m_powers(bv_sz); + const mpz & p_half = m_powers(bv_sz-1); + if (x >= p_half) { m_mpz_manager.sub(x, p, x); } + if (y >= p_half) { m_mpz_manager.sub(y, p, y); } + + if (negated) { + if (x > y) + res = 1.0; + else { + mpz diff; + m_mpz_manager.sub(y, x, diff); + m_mpz_manager.inc(diff); + rational n(diff); + n /= p; + double dbl = n.get_double(); + res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; + m_mpz_manager.del(diff); + } + TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << + m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); + } + else { + if (x <= y) + res = 1.0; + else { + mpz diff; + m_mpz_manager.sub(x, y, diff); + rational n(diff); + n /= p; + double dbl = n.get_double(); + res = (dbl > 1.0) ? 1.0 : (dbl < 0.0) ? 0.0 : dbl; + m_mpz_manager.del(diff); + } + TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << + m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); + } + m_mpz_manager.del(x); + m_mpz_manager.del(y); + } + else if (m_manager.is_not(n)) { + SASSERT(!negated); + app * a = to_app(n); + SASSERT(a->get_num_args() == 1); + expr * child = a->get_arg(0); + if (m_manager.is_and(child) || m_manager.is_or(child)) // Precondition: Assertion set is in NNF. + NOT_IMPLEMENTED_YET(); + res = score_bool(child, true); + } + else if (m_manager.is_distinct(n)) { + app * a = to_app(n); + unsigned pairs = 0, distinct_pairs = 0; + unsigned sz = a->get_num_args(); + for (unsigned i = 0; i < sz; i++) { + for (unsigned j = i+1; j < sz; j++) { + // pair i/j + const mpz & v0 = get_value(a->get_arg(0)); + const mpz & v1 = get_value(a->get_arg(1)); + pairs++; + if (v0 != v1) + distinct_pairs++; + } + } + res = (distinct_pairs/(double)pairs); + if (negated) res = 1.0 - res; + } + else + NOT_IMPLEMENTED_YET(); + + SASSERT(res >= 0.0 && res <= 1.0); + + TRACE("sls_score", tout << "SCORE = " << res << std::endl; ); + return res; + } + + double score_bv(expr * n) { + return 0.0; // a bv-expr is always scored as 0.0; we won't use those scores. + } + + void value2mpz(expr * n, mpz & result) { + m_mpz_manager.set(result, m_zero); + + if (m_manager.is_bool(n)) { + m_mpz_manager.set(result, m_manager.is_true(n) ? m_one : m_zero); + } + else if (m_bv_util.is_bv(n)) { + unsigned bv_sz = m_bv_util.get_bv_size(n); + rational q; + if (!m_bv_util.is_numeral(n, q, bv_sz)) + NOT_IMPLEMENTED_YET(); + mpq temp = q.to_mpq(); + SASSERT(m_mpz_manager.is_one(temp.denominator())); + m_mpz_manager.set(result, temp.numerator()); + } + else + NOT_IMPLEMENTED_YET(); + } + + expr_ref mpz2value(sort * s, const mpz & r) { + expr_ref res(m_manager); + if (m_manager.is_bool(s)) + res = (m_mpz_manager.is_zero(r)) ? m_manager.mk_false() : m_manager.mk_true(); + else if (m_bv_util.is_bv_sort(s)) { + rational rat(r); + res = m_bv_util.mk_numeral(rat, s); + } + else + NOT_IMPLEMENTED_YET(); + return res; + } + + double score(expr * n) { + if (m_manager.is_bool(n)) + return score_bool(n); + else if (m_bv_util.is_bv(n)) + return score_bv(n); + else + NOT_IMPLEMENTED_YET(); + } + + ptr_vector & get_unsat_constants(goal_ref const & g) { + unsigned sz = g->size(); + + if (sz == 1) { + return get_constants(); + } + else { + m_temp_constants.reset(); + for (unsigned i = 0; i < sz; i++) { + expr * q = g->form(i); + if (m_mpz_manager.eq(get_value(q), m_one)) + continue; + ptr_vector const & this_decls = m_constants_occ.find(q); + unsigned sz2 = this_decls.size(); + for (unsigned j = 0; j < sz2; j++) { + func_decl * fd = this_decls[j]; + if (!m_temp_constants.contains(fd)) + m_temp_constants.push_back(fd); + } + } + return m_temp_constants; + } + } +}; + +#endif \ No newline at end of file From 41c9e2b1a47e23959f66fc655e3e142fd2864919 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 20 Sep 2013 11:27:52 -0700 Subject: [PATCH 169/509] check equalities with unknown evaluations Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_util.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/muz/pdr/pdr_util.cpp b/src/muz/pdr/pdr_util.cpp index d021d5fb1..f122f1e2c 100644 --- a/src/muz/pdr/pdr_util.cpp +++ b/src/muz/pdr/pdr_util.cpp @@ -112,8 +112,8 @@ namespace pdr { set_value(e, val); } else { - IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << "\n";); - TRACE("pdr", tout << "Variable is not tracked: " << mk_pp(e, m) << "\n";); + IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";); + TRACE("pdr", tout << "Variable is not tracked: " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";); set_x(e); } } @@ -672,7 +672,19 @@ namespace pdr { eval_array_eq(e, arg1, arg2); } else if (is_x(arg1) || is_x(arg2)) { - set_x(e); + expr_ref eq(m), vl(m); + eq = m.mk_eq(arg1, arg2); + m_model->eval(eq, vl); + if (m.is_true(vl)) { + set_bool(e, true); + } + else if (m.is_false(vl)) { + set_bool(e, false); + } + else { + TRACE("pdr", tout << "cannot evaluate: " << mk_pp(vl, m) << "\n";); + set_x(e); + } } else if (m.is_bool(arg1)) { bool val = is_true(arg1) == is_true(arg2); From 0a964c324eba0b511c11b14f76c253d55b8d0e0c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 20 Sep 2013 12:32:16 -0700 Subject: [PATCH 170/509] test for undetermined accessor for PDR Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_context.cpp | 2 +- src/muz/base/dl_rule.cpp | 19 ++++++++++++++++--- src/muz/base/dl_rule.h | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index f08efb00a..8183744b5 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -576,7 +576,7 @@ namespace datalog { void context::check_uninterpreted_free(rule_ref& r) { func_decl* f = 0; - if (r->has_uninterpreted_non_predicates(f)) { + if (r->has_uninterpreted_non_predicates(m, f)) { std::stringstream stm; stm << "Uninterpreted '" << f->get_name() diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index 96e2b163a..184a5fa02 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -43,6 +43,7 @@ Revision History: #include"expr_safe_replace.h" #include"filter_model_converter.h" #include"scoped_proof.h" +#include"datatype_decl_plugin.h" namespace datalog { @@ -881,9 +882,12 @@ namespace datalog { } struct uninterpreted_function_finder_proc { + ast_manager& m; + datatype_util m_dt; bool m_found; func_decl* m_func; - uninterpreted_function_finder_proc() : m_found(false), m_func(0) {} + uninterpreted_function_finder_proc(ast_manager& m): + m(m), m_dt(m), m_found(false), m_func(0) {} void operator()(var * n) { } void operator()(quantifier * n) { } void operator()(app * n) { @@ -891,6 +895,14 @@ namespace datalog { m_found = true; m_func = n->get_decl(); } + else if (m_dt.is_accessor(n)) { + sort* s = m.get_sort(n->get_arg(0)); + SASSERT(m_dt.is_datatype(s)); + if (m_dt.get_datatype_constructors(s)->size() > 1) { + m_found = true; + m_func = n->get_decl(); + } + } } bool found(func_decl*& f) const { f = m_func; return m_found; } @@ -900,9 +912,9 @@ namespace datalog { // non-predicates may appear only in the interpreted tail, it is therefore // sufficient only to check the tail. // - bool rule::has_uninterpreted_non_predicates(func_decl*& f) const { + bool rule::has_uninterpreted_non_predicates(ast_manager& m, func_decl*& f) const { unsigned sz = get_tail_size(); - uninterpreted_function_finder_proc proc; + uninterpreted_function_finder_proc proc(m); expr_mark visited; for (unsigned i = get_uninterpreted_tail_size(); i < sz && !proc.found(f); ++i) { for_each_expr(proc, visited, get_tail(i)); @@ -910,6 +922,7 @@ namespace datalog { return proc.found(f); } + struct quantifier_finder_proc { bool m_exist; bool m_univ; diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h index 77bf9ac74..1c31dc6b4 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -293,7 +293,7 @@ namespace datalog { */ bool is_in_tail(const func_decl * p, bool only_positive=false) const; - bool has_uninterpreted_non_predicates(func_decl*& f) const; + bool has_uninterpreted_non_predicates(ast_manager& m, func_decl*& f) const; void has_quantifiers(bool& existential, bool& universal) const; bool has_quantifiers() const; bool has_negation() const; From fd1f4b91911cb9628e3419244fcb3646540bad97 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2013 04:07:08 +0300 Subject: [PATCH 171/509] fix bugs reported by Anvesh Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 2 +- src/smt/diff_logic.h | 7 +++++++ src/smt/theory_utvpi_def.h | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 739a9e61a..f68457383 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -6449,7 +6449,7 @@ class Tactic: def _to_goal(a): if isinstance(a, BoolRef): - goal = Goal() + goal = Goal(a.ctx) goal.add(a) return goal else: diff --git a/src/smt/diff_logic.h b/src/smt/diff_logic.h index 6fd156e41..2717e4a92 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.h @@ -820,6 +820,7 @@ public: } } +private: // Update the assignment of variable v, that is, // m_assignment[v] += inc // This method also stores the old value of v in the assignment stack. @@ -829,6 +830,12 @@ public: m_assignment[v] += inc; } +public: + + void inc_assignment(dl_var v, numeral const& inc) { + m_assignment[v] += inc; + } + struct every_var_proc { bool operator()(dl_var v) const { diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 8463eb17f..6039b208a 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -752,7 +752,8 @@ namespace smt { for (unsigned j = 0; j < zero_v.size(); ++j) { int v = zero_v[j]; - m_graph.acc_assignment(v, numeral(-1)); + + m_graph.inc_assignment(v, numeral(-1)); th_var k = from_var(v); if (!is_parity_ok(k)) { todo.push_back(k); From 2e7f5303ebfe9fe7e77da8af34d97bf7063b0844 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2013 04:56:38 +0300 Subject: [PATCH 172/509] address incompleteness bug in axiomatization of int2bv Signed-off-by: Nikolaj Bjorner --- src/smt/theory_bv.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 559ce155b..a338be50a 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -581,10 +581,13 @@ namespace smt { void theory_bv::assert_int2bv_axiom(app* n) { // // create the axiom: - // bv2int(n) = e mod 2^bit_width - // + // bv2int(n) = e mod 2^bit_width // where n = int2bv(e) // + // Create the axioms: + // bit2bool(i,n) == ((e div 2^i) mod 2 != 0) + // for i = 0,.., sz-1 + // SASSERT(get_context().e_internalized(n)); SASSERT(m_util.is_int2bv(n)); ast_manager & m = get_manager(); @@ -592,10 +595,12 @@ namespace smt { parameter param(m_autil.mk_int()); expr* n_expr = n; - expr* lhs = m.mk_app(get_id(), OP_BV2INT, 1, ¶m, 1, &n_expr); + expr* e = n->get_arg(0); + expr_ref lhs(m), rhs(m); + lhs = m.mk_app(get_id(), OP_BV2INT, 1, ¶m, 1, &n_expr); unsigned sz = m_util.get_bv_size(n); numeral mod = power(numeral(2), sz); - expr* rhs = m_autil.mk_mod(n->get_arg(0), m_autil.mk_numeral(mod, true)); + rhs = m_autil.mk_mod(e, m_autil.mk_numeral(mod, true)); literal l(mk_eq(lhs, rhs, false)); ctx.mark_as_relevant(l); @@ -605,6 +610,24 @@ namespace smt { tout << mk_pp(lhs, m) << " == \n"; tout << mk_pp(rhs, m) << "\n"; ); + + expr_ref_vector n_bits(m); + enode * n_enode = mk_enode(n); + get_bits(n_enode, n_bits); + + for (unsigned i = 0; i < sz; ++i) { + numeral div = power(numeral(2), i); + mod = numeral(2); + rhs = m_autil.mk_idiv(e, m_autil.mk_numeral(div,true)); + rhs = m_autil.mk_mod(rhs, m_autil.mk_numeral(mod, true)); + rhs = m.mk_eq(rhs, m_autil.mk_numeral(rational(1), true)); + lhs = n_bits.get(i); + TRACE("bv", tout << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n";); + l = literal(mk_eq(lhs, rhs, false)); + ctx.mark_as_relevant(l); + ctx.mk_th_axiom(get_id(), 1, &l); + + } } From c1384095f356a478d91ec08759ed86d053d06b9c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Sep 2013 21:44:24 +0300 Subject: [PATCH 173/509] fix default argument identification Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index f68457383..aaad24256 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -6449,7 +6449,7 @@ class Tactic: def _to_goal(a): if isinstance(a, BoolRef): - goal = Goal(a.ctx) + goal = Goal(ctx = a.ctx) goal.add(a) return goal else: From 6554ac787a5cd53e60801f9df739da2cf87e0e60 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Sep 2013 05:13:11 +0300 Subject: [PATCH 174/509] add test case for substitution Signed-off-by: Nikolaj Bjorner --- src/test/expr_substitution.cpp | 50 ++++++++++++++++++++++++++++++++++ src/test/main.cpp | 1 + 2 files changed, 51 insertions(+) create mode 100644 src/test/expr_substitution.cpp diff --git a/src/test/expr_substitution.cpp b/src/test/expr_substitution.cpp new file mode 100644 index 000000000..915ac96f3 --- /dev/null +++ b/src/test/expr_substitution.cpp @@ -0,0 +1,50 @@ +#include "expr_substitution.h" +#include "smt_params.h" +#include "substitution.h" +#include "unifier.h" +#include "bv_decl_plugin.h" +#include "ast_pp.h" +#include "arith_decl_plugin.h" +#include "reg_decl_plugins.h" +#include "th_rewriter.h" + +expr* mk_bv_xor(bv_util& bv, expr* a, expr* b) { + expr* args[2]; + args[0] = a; + args[1] = b; + return bv.mk_bv_xor(2, args); +} + +expr* mk_bv_and(bv_util& bv, expr* a, expr* b) { + expr* args[2]; + args[0] = a; + args[1] = b; + ast_manager& m = bv.get_manager(); + return m.mk_app(bv.get_family_id(), OP_BAND, 2, args); +} + +void tst_expr_substitution() { + memory::initialize(0); + ast_manager m; + reg_decl_plugins(m); + bv_util bv(m); + + expr_ref a(m), b(m), c(m); + expr_ref x(m); + x = m.mk_const(symbol("x"), bv.mk_sort(8)); + a = mk_bv_and(bv, mk_bv_xor(bv, x,bv.mk_numeral(8,8)), mk_bv_xor(bv,x,x)); + b = x; + c = bv.mk_bv_sub(x, bv.mk_numeral(4, 8)); + + expr_substitution subst(m); + subst.insert(b, c); + th_rewriter rw(m); + rw.set_substitution(&subst); + + expr_ref new_a(m); + proof_ref pr(m); + rw(a, new_a, pr); + + std::cout << mk_pp(new_a, m) << "\n"; + +} diff --git a/src/test/main.cpp b/src/test/main.cpp index 333456369..bc7e04124 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -215,6 +215,7 @@ int main(int argc, char ** argv) { TST(rcf); TST(polynorm); TST(qe_arith); + TST(expr_substitution); } void initialize_mam() {} From 1733af2641e0d0b37ef3adf2ad5a6c8090ffdab3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Sep 2013 05:33:16 +0300 Subject: [PATCH 175/509] test case for non-termination of substitution/rewriting Signed-off-by: Nikolaj Bjorner --- src/test/expr_substitution.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/test/expr_substitution.cpp b/src/test/expr_substitution.cpp index 915ac96f3..f83bde97d 100644 --- a/src/test/expr_substitution.cpp +++ b/src/test/expr_substitution.cpp @@ -29,20 +29,26 @@ void tst_expr_substitution() { reg_decl_plugins(m); bv_util bv(m); - expr_ref a(m), b(m), c(m); + expr_ref a(m), b(m), c(m), d(m); expr_ref x(m); + expr_ref new_a(m); + proof_ref pr(m); x = m.mk_const(symbol("x"), bv.mk_sort(8)); a = mk_bv_and(bv, mk_bv_xor(bv, x,bv.mk_numeral(8,8)), mk_bv_xor(bv,x,x)); b = x; c = bv.mk_bv_sub(x, bv.mk_numeral(4, 8)); expr_substitution subst(m); - subst.insert(b, c); th_rewriter rw(m); + + // normalizing c does not help. + rw(c, d, pr); + subst.insert(b, d); + rw.set_substitution(&subst); - - expr_ref new_a(m); - proof_ref pr(m); + + + enable_trace("th_rewriter_step"); rw(a, new_a, pr); std::cout << mk_pp(new_a, m) << "\n"; From 2d01c4d50f77963028ea39b45f89896502495c5e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Sep 2013 06:41:46 +0300 Subject: [PATCH 176/509] update join planner to take projected columns into account Signed-off-by: Nikolaj Bjorner --- src/muz/rel/dl_mk_simple_joins.cpp | 145 ++++++++++++++++------------- 1 file changed, 82 insertions(+), 63 deletions(-) diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp index 4b9ce582a..c2214dad9 100644 --- a/src/muz/rel/dl_mk_simple_joins.cpp +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -47,9 +47,9 @@ namespace datalog { being notified about it, it will surely see the decrease from length 3 to 2 which the threshold for rule being counted in this counter. */ - unsigned m_consumers; - bool m_stratified; - unsigned m_src_stratum; + unsigned m_consumers; + bool m_stratified; + unsigned m_src_stratum; public: var_idx_set m_all_nonlocal_vars; rule_vector m_rules; @@ -57,16 +57,13 @@ namespace datalog { pair_info() : m_consumers(0), m_stratified(true), m_src_stratum(0) {} bool can_be_joined() const { - return m_consumers>0; + return m_consumers > 0; } cost get_cost() const { - /*if(m_instantiated) { - return std::numeric_limits::min(); - }*/ - SASSERT(m_consumers>0); + SASSERT(m_consumers > 0); cost amortized = m_total_cost/m_consumers; - if(m_stratified) { + if (m_stratified) { return amortized * ( (amortized>0) ? (1/16.0f) : 16.0f); } else { @@ -81,19 +78,20 @@ namespace datalog { by the time of a call to this function */ void add_rule(join_planner & pl, app * t1, app * t2, rule * r, - const var_idx_set & non_local_vars_normalized) { - if(m_rules.empty()) { - m_total_cost = pl.compute_cost(t1, t2); + const var_idx_set & non_local_vars_normalized, + const var_idx_set & non_local_vars) { + if (m_rules.empty()) { + m_total_cost = pl.compute_cost(t1, t2, non_local_vars); m_src_stratum = std::max(pl.get_stratum(t1->get_decl()), pl.get_stratum(t2->get_decl())); } m_rules.push_back(r); - if(pl.m_rules_content.find_core(r)->get_data().m_value.size()>2) { + if (pl.m_rules_content.find(r).size()>2) { m_consumers++; } - if(m_stratified) { + if (m_stratified) { unsigned head_stratum = pl.get_stratum(r->get_decl()); SASSERT(head_stratum>=m_src_stratum); - if(head_stratum==m_src_stratum) { + if (head_stratum==m_src_stratum) { m_stratified = false; } } @@ -105,7 +103,7 @@ namespace datalog { */ bool remove_rule(rule * r, unsigned original_length) { TRUSTME( remove_from_vector(m_rules, r) ); - if(original_length>2) { + if (original_length>2) { SASSERT(m_consumers>0); m_consumers--; } @@ -165,7 +163,7 @@ namespace datalog { SASSERT(is_var(t->get_arg(i))); var * v = to_var(t->get_arg(i)); unsigned var_idx = v->get_idx(); - if(result[res_ofs-var_idx]==0) { + if (result[res_ofs-var_idx]==0) { result[res_ofs-var_idx]=m.mk_var(next_var, v->get_sort()); next_var++; } @@ -174,7 +172,7 @@ namespace datalog { void get_normalizer(app * t1, app * t2, expr_ref_vector & result) const { SASSERT(result.empty()); - if(t1->get_num_args()==0 && t2->get_num_args()==0) { + if (t1->get_num_args()==0 && t2->get_num_args()==0) { return; //nothing to normalize } SASSERT(!t1->is_ground() || !t2->is_ground()); @@ -186,14 +184,14 @@ namespace datalog { var_idx_set::iterator ovend = orig_var_set.end(); for(; ovit!=ovend; ++ovit) { unsigned var_idx = *ovit; - if(var_idx>max_var_idx) { + if (var_idx>max_var_idx) { max_var_idx = var_idx; } } } - if(t1->get_decl()!=t2->get_decl()) { - if(t1->get_decl()->get_id()get_decl()->get_id()) { + if (t1->get_decl()!=t2->get_decl()) { + if (t1->get_decl()->get_id()get_decl()->get_id()) { std::swap(t1, t2); } } @@ -207,9 +205,9 @@ namespace datalog { //so the only literals which appear in pairs are the ones that contain only variables. var * v1 = to_var(t1->get_arg(i)); var * v2 = to_var(t2->get_arg(i)); - if(v1->get_sort()!=v2->get_sort()) { + if (v1->get_sort()!=v2->get_sort()) { //different sorts mean we can distinguish the two terms - if(v1->get_sort()->get_id()get_sort()->get_id()) { + if (v1->get_sort()->get_id()get_sort()->get_id()) { std::swap(t1, t2); } break; @@ -221,9 +219,9 @@ namespace datalog { SASSERT(norm1[v1_idx]==-1); SASSERT(norm2[v2_idx]==-1); - if(norm2[v1_idx]!=norm1[v2_idx]) { + if (norm2[v1_idx]!=norm1[v2_idx]) { //now we can distinguish the two terms - if(norm2[v1_idx]t2n) { + if (t1n>t2n) { std::swap(t1n, t2n); } m_pinned.push_back(t1n); @@ -274,12 +272,10 @@ namespace datalog { by the time of a call to this function */ void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) { - TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << "\n"; - r->display(m_context, tout); tout << "\n";); SASSERT(t1!=t2); cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), 0); pair_info * & ptr_inf = e->get_data().m_value; - if(ptr_inf==0) { + if (ptr_inf==0) { ptr_inf = alloc(pair_info); } pair_info & inf = *ptr_inf; @@ -288,25 +284,30 @@ namespace datalog { get_normalizer(t1, t2, normalizer); unsigned norm_ofs = normalizer.size()-1; var_idx_set normalized_vars; - var_idx_set::iterator vit = non_local_vars.begin(); + var_idx_set::iterator vit = non_local_vars.begin(); var_idx_set::iterator vend = non_local_vars.end(); for(; vit!=vend; ++vit) { unsigned norm_var = to_var(normalizer.get(norm_ofs-*vit))->get_idx(); normalized_vars.insert(norm_var); } - inf.add_rule(*this, t1, t2, r, normalized_vars); + inf.add_rule(*this, t1, t2, r, normalized_vars, non_local_vars); + TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << " "; + vit = non_local_vars.begin(); + for (; vit != vend; ++vit) tout << *vit << " "; + tout << "\n"; + r->display(m_context, tout); + if (inf.can_be_joined()) tout << "cost: " << inf.get_cost() << "\n";); + } pair_info & get_pair(app_pair key) const { - cost_map::entry * e = m_costs.find_core(key); - SASSERT(e); - return *e->get_data().m_value; + return *m_costs.find(key); } void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) { pair_info * ptr = &get_pair(key); - if(ptr->remove_rule(r, original_len)) { + if (ptr->remove_rule(r, original_len)) { SASSERT(ptr->m_rules.empty()); m_costs.remove(key); dealloc(ptr); @@ -349,7 +350,7 @@ namespace datalog { unsigned n=t->get_num_args(); for(unsigned i=0; iget_arg(i)); - if(v->get_idx()==var_idx) { + if (v->get_idx()==var_idx) { args.push_back(v); domain.push_back(m.get_sort(v)); return true; @@ -375,7 +376,7 @@ namespace datalog { unsigned var_idx=*ovit; bool found=extract_argument_info(var_idx, t1, args, domain); - if(!found) { + if (!found) { found=extract_argument_info(var_idx, t2, args, domain); } SASSERT(found); @@ -389,7 +390,7 @@ namespace datalog { func_decl* parent_head = one_parent->get_decl(); const char * one_parent_name = parent_head->get_name().bare_str(); std::string parent_name; - if(inf.m_rules.size()>1) { + if (inf.m_rules.size()>1) { parent_name = one_parent_name + std::string("_and_") + to_string(inf.m_rules.size()-1); } else { @@ -443,7 +444,7 @@ namespace datalog { } //remove edges between surviving tails and removed tails for(unsigned i=0; i & rule_content = m_rules_content.find_core(r)->get_data().m_value; + ptr_vector & rule_content = m_rules_content.find(r); unsigned len = rule_content.size(); - if(len==1) { + if (len==1) { return; } @@ -515,16 +516,16 @@ namespace datalog { ptr_vector added_tails; for(unsigned i1=0; i1get_decl()!=t1_pred) { + if (rt1->get_decl()!=t1_pred) { continue; } unsigned i2start = (t1_pred==t2_pred) ? (i1+1) : 0; for(unsigned i2=i2start; i2get_decl()!=t2_pred) { + if (i1==i2 || rt2->get_decl()!=t2_pred) { continue; } - if(get_key(rt1, rt2)!=pair_key) { + if (get_key(rt1, rt2)!=pair_key) { continue; } expr_ref_vector normalizer(m); @@ -558,7 +559,7 @@ namespace datalog { relation_sort sort = pred->get_domain(arg_index); return static_cast(m_context.get_sort_size_estimate(sort)); //unsigned sz; - //if(!m_context.get_sort_size(sort, sz)) { + //if (!m_context.get_sort_size(sort, sz)) { // sz=UINT_MAX; //} //return static_cast(sz); @@ -576,15 +577,15 @@ namespace datalog { return cost(1); } relation_manager& rm = rel->get_rmanager(); - if( (m_context.saturation_was_run() && rm.try_get_relation(pred)) + if ( (m_context.saturation_was_run() && rm.try_get_relation(pred)) || rm.is_saturated(pred)) { SASSERT(rm.try_get_relation(pred)); //if it is saturated, it should exist unsigned rel_size_int = rel->get_relation(pred).get_size_estimate_rows(); - if(rel_size_int!=0) { + if (rel_size_int!=0) { cost rel_size = static_cast(rel_size_int); cost curr_size = rel_size; for(unsigned i=0; iget_arg(i))) { + if (!is_var(t->get_arg(i))) { curr_size /= get_domain_size(pred, i); } } @@ -593,40 +594,58 @@ namespace datalog { } cost res = 1; for(unsigned i=0; iget_arg(i))) { + if (is_var(t->get_arg(i))) { res *= get_domain_size(pred, i); } } return res; } - cost compute_cost(app * t1, app * t2) const { + cost compute_cost(app * t1, app * t2, const var_idx_set & non_local_vars) const { func_decl * t1_pred = t1->get_decl(); func_decl * t2_pred = t2->get_decl(); cost inters_size = 1; variable_intersection vi(m_context.get_manager()); vi.populate(t1, t2); unsigned n = vi.size(); + // remove contributions from joined columns. for(unsigned i=0; iget_arg(arg_index1))); + if (non_local_vars.contains(to_var(t1->get_arg(arg_index1))->get_idx())) { + inters_size *= get_domain_size(t1_pred, arg_index1); + } //joined arguments must have the same domain SASSERT(get_domain_size(t1_pred, arg_index1)==get_domain_size(t2_pred, arg_index2)); } - cost res = estimate_size(t1)*estimate_size(t2)/(inters_size*inters_size); + // remove contributions from projected columns. + for (unsigned i = 0; i < t1->get_num_args(); ++i) { + if (is_var(t1->get_arg(i)) && + !non_local_vars.contains(to_var(t1->get_arg(i))->get_idx())) { + inters_size *= get_domain_size(t1_pred, i); + } + } + for (unsigned i = 0; i < t2->get_num_args(); ++i) { + if (is_var(t2->get_arg(i)) && + !non_local_vars.contains(to_var(t2->get_arg(i))->get_idx())) { + inters_size *= get_domain_size(t2_pred, i); + } + } + + cost res = estimate_size(t1)*estimate_size(t2)/ inters_size; // (inters_size*inters_size); //cost res = -inters_size; /*unsigned t1_strat = get_stratum(t1_pred); SASSERT(t1_strat<=m_head_stratum); - if(t1_strat0) { + if (res>0) { res /= 2; } else { @@ -653,17 +672,17 @@ namespace datalog { for(; it!=end; ++it) { app_pair key = it->m_key; pair_info & inf = *it->m_value; - if(!inf.can_be_joined()) { + if (!inf.can_be_joined()) { continue; } cost c = inf.get_cost(); - if(!found || cm_key; ptr_vector content = rcit->m_value; SASSERT(content.size()<=2); - if(content.size()==orig_r->get_positive_tail_size()) { + if (content.size()==orig_r->get_positive_tail_size()) { //rule did not change result->add_rule(orig_r); continue; @@ -728,7 +747,7 @@ namespace datalog { rule_set * mk_simple_joins::operator()(rule_set const & source) { rule_set rs_aux_copy(m_context); rs_aux_copy.replace_rules(source); - if(!rs_aux_copy.is_closed()) { + if (!rs_aux_copy.is_closed()) { rs_aux_copy.close(); } From 1b8d1a1ccc4cff6bd6889b1bae27b24d4cd426ff Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Sep 2013 10:42:31 +0300 Subject: [PATCH 177/509] fix bug in ackerman reduction found by Anvesh Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_array_blast.cpp | 116 +++++++++++++++-------- src/muz/transforms/dl_mk_array_blast.h | 3 +- 2 files changed, 79 insertions(+), 40 deletions(-) diff --git a/src/muz/transforms/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp index 776a2da5b..93e31ff23 100644 --- a/src/muz/transforms/dl_mk_array_blast.cpp +++ b/src/muz/transforms/dl_mk_array_blast.cpp @@ -31,7 +31,6 @@ namespace datalog { rm(ctx.get_rule_manager()), m_rewriter(m, m_params), m_simplifier(ctx), - m_sub(m), m_next_var(0) { m_params.set_bool("expand_select_store",true); m_rewriter.updt_params(m_params); @@ -82,7 +81,6 @@ namespace datalog { return false; } if (v) { - m_sub.insert(e, v); m_defs.insert(e, to_var(v)); } else { @@ -92,71 +90,113 @@ namespace datalog { m_next_var = vars.size() + 1; } v = m.mk_var(m_next_var, m.get_sort(e)); - m_sub.insert(e, v); m_defs.insert(e, v); ++m_next_var; } return true; } + + bool mk_array_blast::is_select_eq_var(expr* e, app*& s, var*& v) const { + expr* x, *y; + if (m.is_eq(e, x, y) || m.is_iff(e, x, y)) { + if (a.is_select(y)) { + std::swap(x,y); + } + if (a.is_select(x) && is_var(y)) { + s = to_app(x); + v = to_var(y); + return true; + } + } + return false; + } + bool mk_array_blast::ackermanize(rule const& r, expr_ref& body, expr_ref& head) { - expr_ref_vector conjs(m); + expr_ref_vector conjs(m), trail(m); qe::flatten_and(body, conjs); m_defs.reset(); - m_sub.reset(); m_next_var = 0; ptr_vector todo; - todo.push_back(head); + obj_map cache; + ptr_vector args; + app_ref e1(m); + app* s; + var* v; + for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); - expr* x, *y; - if (m.is_eq(e, x, y) || m.is_iff(e, x, y)) { - if (a.is_select(y)) { - std::swap(x,y); - } - if (a.is_select(x) && is_var(y)) { - if (!insert_def(r, to_app(x), to_var(y))) { - return false; - } - } + if (is_select_eq_var(e, s, v)) { + todo.append(s->get_num_args(), s->get_args()); } - if (a.is_select(e) && !insert_def(r, to_app(e), 0)) { - return false; + else { + todo.push_back(e); } - todo.push_back(e); } - // now make sure to cover all occurrences. - ast_mark mark; while (!todo.empty()) { expr* e = todo.back(); - todo.pop_back(); - if (mark.is_marked(e)) { + if (cache.contains(e)) { + todo.pop_back(); continue; } - mark.mark(e, true); if (is_var(e)) { + cache.insert(e, e); + todo.pop_back(); continue; } if (!is_app(e)) { return false; } app* ap = to_app(e); - if (a.is_select(ap) && !m_defs.contains(ap)) { - if (!insert_def(r, ap, 0)) { - return false; + bool valid = true; + args.reset(); + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + expr* arg; + if (cache.find(ap->get_arg(i), arg)) { + args.push_back(arg); + } + else { + todo.push_back(ap->get_arg(i)); + valid = false; } } - if (a.is_select(e)) { - get_select_args(e, todo); - continue; + if (valid) { + todo.pop_back(); + e1 = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); + trail.push_back(e1); + if (a.is_select(ap)) { + if (m_defs.find(e1, v)) { + cache.insert(e, v); + } + else if (!insert_def(r, e1, 0)) { + return false; + } + else { + cache.insert(e, m_defs.find(e1)); + } + } + else { + cache.insert(e, e1); + } + } + } + for (unsigned i = 0; i < conjs.size(); ++i) { + expr* e = conjs[i].get(); + if (is_select_eq_var(e, s, v)) { + args.reset(); + for (unsigned j = 0; j < s->get_num_args(); ++j) { + args.push_back(cache.find(s->get_arg(j))); + } + e1 = m.mk_app(s->get_decl(), args.size(), args.c_ptr()); + if (!m_defs.contains(e1) && !insert_def(r, e1, v)) { + return false; + } + conjs[i] = m.mk_eq(v, m_defs.find(e1)); } - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - todo.push_back(ap->get_arg(i)); + else { + conjs[i] = cache.find(e); } } - m_sub(body); - m_sub(head); - conjs.reset(); // perform the Ackermann reduction by creating implications // i1 = i2 => val1 = val2 for each equality pair: @@ -171,6 +211,7 @@ namespace datalog { for (; it2 != end; ++it2) { app* a2 = it2->m_key; var* v2 = it2->m_value; + TRACE("dl", tout << mk_pp(a1, m) << " " << mk_pp(a2, m) << "\n";); if (get_select(a1) != get_select(a2)) { continue; } @@ -184,10 +225,7 @@ namespace datalog { conjs.push_back(m.mk_implies(m.mk_and(eqs.size(), eqs.c_ptr()), m.mk_eq(v1, v2))); } } - if (!conjs.empty()) { - conjs.push_back(body); - body = m.mk_and(conjs.size(), conjs.c_ptr()); - } + body = m.mk_and(conjs.size(), conjs.c_ptr()); m_rewriter(body); return true; } diff --git a/src/muz/transforms/dl_mk_array_blast.h b/src/muz/transforms/dl_mk_array_blast.h index f4b685b7a..c96573848 100644 --- a/src/muz/transforms/dl_mk_array_blast.h +++ b/src/muz/transforms/dl_mk_array_blast.h @@ -44,7 +44,6 @@ namespace datalog { mk_interp_tail_simplifier m_simplifier; defs_t m_defs; - expr_safe_replace m_sub; unsigned m_next_var; bool blast(rule& r, rule_set& new_rules); @@ -59,6 +58,8 @@ namespace datalog { bool insert_def(rule const& r, app* e, var* v); + bool is_select_eq_var(expr* e, app*& s, var*& v) const; + public: /** \brief Create rule transformer that removes array stores and selects by ackermannization. From 4363c9f44fa4f5ac0aa83a90b5d7e8a3e6c49d8a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 24 Sep 2013 18:47:19 +0300 Subject: [PATCH 178/509] use safe replace for external substitution Signed-off-by: Nikolaj Bjorner --- src/api/api_ast.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 293983c9a..c96328bf8 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -31,7 +31,7 @@ Revision History: #include"ast_smt2_pp.h" #include"th_rewriter.h" #include"var_subst.h" -#include"expr_substitution.h" +#include"expr_safe_replace.h" #include"pp.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" @@ -786,17 +786,12 @@ extern "C" { RETURN_Z3(of_expr(0)); } } - - expr_substitution subst(m); + expr_safe_replace subst(m); for (unsigned i = 0; i < num_exprs; i++) { subst.insert(from[i], to[i]); } - th_rewriter m_rw(m); - m_rw.set_substitution(&subst); - expr_ref new_a(m); - proof_ref pr(m); - m_rw(a, new_a, pr); + subst(a, new_a); mk_c(c)->save_ast_trail(new_a); r = new_a.get(); RETURN_Z3(of_expr(r)); From 8ef2fe7ddbec4e2c17002518ace7a5e0d555381d Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 25 Sep 2013 18:58:13 -0700 Subject: [PATCH 179/509] Fix z3.py doctests to reflect recent changes in the substitute API Signed-off-by: Leonardo de Moura --- src/api/python/z3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index aaad24256..22ec30ad2 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -6932,10 +6932,10 @@ def substitute(t, *m): >>> x = Int('x') >>> y = Int('y') >>> substitute(x + 1, (x, y + 1)) - 2 + y + y + 1 + 1 >>> f = Function('f', IntSort(), IntSort()) >>> substitute(f(x) + f(y), (f(x), IntVal(1)), (f(y), IntVal(1))) - 2 + 1 + 1 """ if isinstance(m, tuple): m1 = _get_args(m) From 5e6a47e2d37b1e348925d8455b06808631c942e3 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 26 Sep 2013 11:35:08 +0100 Subject: [PATCH 180/509] Example fixed (substitute does not include a rewriter call anymore). Signed-off-by: Christoph M. Wintersteiger --- src/api/python/z3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index aaad24256..22ec30ad2 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -6932,10 +6932,10 @@ def substitute(t, *m): >>> x = Int('x') >>> y = Int('y') >>> substitute(x + 1, (x, y + 1)) - 2 + y + y + 1 + 1 >>> f = Function('f', IntSort(), IntSort()) >>> substitute(f(x) + f(y), (f(x), IntVal(1)), (f(y), IntVal(1))) - 2 + 1 + 1 """ if isinstance(m, tuple): m1 = _get_args(m) From 3d910028bf4ecdd3386d73816d186588a8065a00 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 27 Sep 2013 16:55:05 +0100 Subject: [PATCH 181/509] fixed potential performance problem with fully interpreted sorts in the quantifier instantiation. Signed-off-by: Christoph M. Wintersteiger --- src/smt/smt_model_finder.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index c4a48d9f8..8303d05ac 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -975,9 +975,11 @@ namespace smt { // Moreover, a model assigns an arbitrary intepretation to these sorts using "model_values" a model value. // If these module values "leak" inside the logical context, they may affect satisfiability. // - // n->insert(m_model->get_some_value(n->get_sort()), 0); - // TODO: we can use get_some_value if the sort n->get_sort() does not depend on any uninterpreted sorts. - n->insert(m_manager.mk_fresh_const("elem", n->get_sort()), 0); + sort * ns = n->get_sort(); + if (m_manager.is_fully_interp(ns)) + n->insert(m_model->get_some_value(ns), 0); + else + n->insert(m_manager.mk_fresh_const("elem", ns), 0); } } } From 9a9f8bbb341be84285d31956b030e7c89f3641a4 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 8 Oct 2013 20:01:39 +0100 Subject: [PATCH 182/509] rewriter and value recognition bugfixes for floats Signed-off-by: Christoph M. Wintersteiger --- src/ast/float_decl_plugin.cpp | 21 +++++++++++++++++++++ src/ast/rewriter/float_rewriter.cpp | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index 4aab6ab32..128dd2ce7 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -85,6 +85,24 @@ bool float_decl_plugin::is_value(expr * n, mpf & val) { m_fm.set(val, m_values[to_app(n)->get_decl()->get_parameter(0).get_ext_id()]); return true; } + else if (is_app_of(n, m_family_id, OP_FLOAT_MINUS_INF)) { + unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); + unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); + m_fm.mk_ninf(ebits, sbits, val); + return true; + } + else if (is_app_of(n, m_family_id, OP_FLOAT_PLUS_INF)) { + unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); + unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); + m_fm.mk_pinf(ebits, sbits, val); + return true; + } + else if (is_app_of(n, m_family_id, OP_FLOAT_NAN)) { + unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); + unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); + m_fm.mk_nan(ebits, sbits, val); + return true; + } return false; } @@ -523,6 +541,9 @@ bool float_decl_plugin::is_value(app * e) const { case OP_RM_TOWARD_NEGATIVE: case OP_RM_TOWARD_ZERO: case OP_FLOAT_VALUE: + case OP_FLOAT_PLUS_INF: + case OP_FLOAT_MINUS_INF: + case OP_FLOAT_NAN: return true; case OP_TO_FLOAT: return m_manager->is_value(e->get_arg(0)); diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index 0a4c3fc4e..4f8c90301 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -354,7 +354,7 @@ br_status float_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.is_minus_inf(arg1)) { // -oo < arg2 --> not(arg2 = -oo) and not(arg2 = NaN) result = m().mk_and(m().mk_not(m().mk_eq(arg2, arg1)), mk_neq_nan(arg2)); - return BR_REWRITE2; + return BR_REWRITE3; } if (m_util.is_minus_inf(arg2)) { // arg1 < -oo --> false @@ -369,7 +369,7 @@ br_status float_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.is_plus_inf(arg2)) { // arg1 < +oo --> not(arg1 = +oo) and not(arg1 = NaN) result = m().mk_and(m().mk_not(m().mk_eq(arg1, arg2)), mk_neq_nan(arg1)); - return BR_REWRITE2; + return BR_REWRITE3; } scoped_mpf v1(m_util.fm()), v2(m_util.fm()); From 65a202873f2851b79cb0d498b5714be56e48d8a3 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 10 Oct 2013 15:38:54 +0100 Subject: [PATCH 183/509] Bugfix for equality rewriting on floats. Signed-off-by: Christoph M. Wintersteiger --- src/ast/rewriter/float_rewriter.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index 4f8c90301..6ef147f1c 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -490,7 +490,11 @@ br_status float_rewriter::mk_is_sign_minus(expr * arg1, expr_ref & result) { br_status float_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) { scoped_mpf v1(m_util.fm()), v2(m_util.fm()); if (m_util.is_value(arg1, v1) && m_util.is_value(arg2, v2)) { - result = (v1 == v2) ? m().mk_true() : m().mk_false(); + // Note: == is the floats-equality, here we need normal equality. + result = (m_fm.is_nan(v1) && m_fm.is_nan(v2)) ? m().mk_true() : + (m_fm.is_zero(v1) && m_fm.is_zero(v2) && m_fm.sgn(v1)!=m_fm.sgn(v2)) ? m().mk_false() : + (v1 == v2) ? m().mk_true() : + m().mk_false(); return BR_DONE; } From 9b34350646c4a75c718a914f2ffc0d14cec09c93 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 13 Oct 2013 06:25:26 -0700 Subject: [PATCH 184/509] test output predicates Signed-off-by: Nikolaj Bjorner --- src/muz/bmc/dl_bmc_engine.cpp | 4 ++++ src/muz/clp/clp_context.cpp | 3 +++ src/tactic/core/elim_uncnstr_tactic.cpp | 1 - 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index eb4bc72c4..7a88c1188 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -1448,6 +1448,10 @@ namespace datalog { transformer.register_plugin(slice); m_ctx.transform_rules(transformer); } + if (m_ctx.get_rules().get_output_predicates().empty()) { + return l_false; + } + m_query_pred = m_ctx.get_rules().get_output_predicate(); m_rules.replace_rules(m_ctx.get_rules()); m_rules.close(); diff --git a/src/muz/clp/clp_context.cpp b/src/muz/clp/clp_context.cpp index 5f3a09e2d..0cd08e5b3 100644 --- a/src/muz/clp/clp_context.cpp +++ b/src/muz/clp/clp_context.cpp @@ -70,6 +70,9 @@ namespace datalog { m_goals.reset(); rm.mk_query(query, m_ctx.get_rules()); apply_default_transformation(m_ctx); + if (m_ctx.get_rules().get_output_predicates().empty()) { + return l_false; + } func_decl* head_decl = m_ctx.get_rules().get_output_predicate(); rule_set& rules = m_ctx.get_rules(); rule_vector const& rv = rules.get_predicate_rules(head_decl); diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 00e14a41f..4d64bf061 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -38,7 +38,6 @@ class elim_uncnstr_tactic : public tactic { typedef std::pair frame; svector m_stack; ptr_vector m_vars; - expr_sparse_mark m_uncnstr_vars; bool visit(expr * t) { if (m_visited.is_marked(t)) { From eb4c10c037c8ed920e36710bcc27dbd34efa0739 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 15 Oct 2013 03:53:33 -0700 Subject: [PATCH 185/509] fixing bugs with validation code Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_context.cpp | 4 +--- src/muz/transforms/dl_mk_array_blast.cpp | 11 ++++++++++- src/muz/transforms/dl_mk_bit_blast.cpp | 4 +++- src/muz/transforms/dl_mk_quantifier_abstraction.cpp | 1 - 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index aab7b1388..b09280d35 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -1123,9 +1123,7 @@ namespace pdr { n->mk_instantiate(r0, r1, binding); proof_ref p1(m), p2(m); p1 = r0->get_proof(); - if (!p1) { - r0->display(dctx, std::cout); - } + IF_VERBOSE(0, if (!p1) r0->display(dctx, verbose_stream());); SASSERT(p1); pfs[0] = p1; rls[0] = r1; diff --git a/src/muz/transforms/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp index 93e31ff23..9303181b6 100644 --- a/src/muz/transforms/dl_mk_array_blast.cpp +++ b/src/muz/transforms/dl_mk_array_blast.cpp @@ -19,6 +19,7 @@ Revision History: #include "dl_mk_array_blast.h" #include "qe_util.h" +#include "scoped_proof.h" namespace datalog { @@ -270,7 +271,7 @@ namespace datalog { } } - expr_ref fml2(m), body(m), head(m); + expr_ref fml1(m), fml2(m), body(m), head(m); body = m.mk_and(new_conjs.size(), new_conjs.c_ptr()); head = r.get_head(); sub(body); @@ -287,9 +288,17 @@ namespace datalog { proof_ref p(m); rule_set new_rules(m_ctx); rm.mk_rule(fml2, p, new_rules, r.name()); + rule_ref new_rule(rm); if (m_simplifier.transform_rule(new_rules.last(), new_rule)) { + if (r.get_proof()) { + scoped_proof _sc(m); + r.to_formula(fml1); + p = m.mk_rewrite(fml1, fml2); + p = m.mk_modus_ponens(r.get_proof(), p); + new_rule->set_proof(m, p); + } rules.add_rule(new_rule.get()); rm.mk_rule_rewrite_proof(r, *new_rule.get()); TRACE("dl", new_rule->display(m_ctx, tout << "new rule\n");); diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 91481ed5a..112541541 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -25,6 +25,7 @@ Revision History: #include "filter_model_converter.h" #include "dl_mk_interp_tail_simplifier.h" #include "fixedpoint_params.hpp" +#include "scoped_proof.h" namespace datalog { @@ -268,7 +269,8 @@ namespace datalog { r->to_formula(fml); if (blast(r, fml)) { proof_ref pr(m); - if (m_context.generate_proof_trace()) { + if (r->get_proof()) { + scoped_proof _sc(m); pr = m.mk_asserted(fml); // loses original proof of r. } // TODO add logic for pc: diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp index 0ad4d61ab..57948a2ff 100644 --- a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp @@ -341,7 +341,6 @@ namespace datalog { } head = mk_head(source, *result, r.get_head(), cnt); fml = m.mk_implies(m.mk_and(tail.size(), tail.c_ptr()), head); - rule_ref_vector added_rules(rm); proof_ref pr(m); rm.mk_rule(fml, pr, *result); TRACE("dl", result->last()->display(m_ctx, tout);); From f54b068669107059e80dafe70c1d96466c1c6144 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 17 Oct 2013 15:29:55 +0100 Subject: [PATCH 186/509] added floating point standard draft version 3 function symbols --- src/ast/float_decl_plugin.cpp | 46 ++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index 128dd2ce7..eb8aba9ae 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -475,6 +475,7 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("plusInfinity", OP_FLOAT_PLUS_INF)); op_names.push_back(builtin_name("minusInfinity", OP_FLOAT_MINUS_INF)); op_names.push_back(builtin_name("NaN", OP_FLOAT_NAN)); + op_names.push_back(builtin_name("roundNearestTiesToEven", OP_RM_NEAREST_TIES_TO_EVEN)); op_names.push_back(builtin_name("roundNearestTiesToAway", OP_RM_NEAREST_TIES_TO_AWAY)); op_names.push_back(builtin_name("roundTowardPositive", OP_RM_TOWARD_POSITIVE)); @@ -486,7 +487,7 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("/", OP_FLOAT_DIV)); op_names.push_back(builtin_name("*", OP_FLOAT_MUL)); - op_names.push_back(builtin_name("abs", OP_FLOAT_ABS)); + op_names.push_back(builtin_name("abs", OP_FLOAT_ABS)); op_names.push_back(builtin_name("remainder", OP_FLOAT_REM)); op_names.push_back(builtin_name("fusedMA", OP_FLOAT_FUSED_MA)); op_names.push_back(builtin_name("squareRoot", OP_FLOAT_SQRT)); @@ -515,6 +516,49 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co if (m_bv_plugin) op_names.push_back(builtin_name("asIEEEBV", OP_TO_IEEE_BV)); + + // We also support draft version 3 + op_names.push_back(builtin_name("fp", OP_TO_FLOAT)); + + op_names.push_back(builtin_name("RNE", OP_RM_NEAREST_TIES_TO_EVEN)); + op_names.push_back(builtin_name("RNA", OP_RM_NEAREST_TIES_TO_AWAY)); + op_names.push_back(builtin_name("RTP", OP_RM_TOWARD_POSITIVE)); + op_names.push_back(builtin_name("RTN", OP_RM_TOWARD_NEGATIVE)); + op_names.push_back(builtin_name("RTZ", OP_RM_TOWARD_ZERO)); + + op_names.push_back(builtin_name("fp.abs", OP_FLOAT_ABS)); + op_names.push_back(builtin_name("fp.neg", OP_FLOAT_UMINUS)); + op_names.push_back(builtin_name("fp.add", OP_FLOAT_ADD)); + op_names.push_back(builtin_name("fp.sub", OP_FLOAT_SUB)); + op_names.push_back(builtin_name("fp.mul", OP_FLOAT_MUL)); + op_names.push_back(builtin_name("fp.div", OP_FLOAT_DIV)); + op_names.push_back(builtin_name("fp.fma", OP_FLOAT_FUSED_MA)); + op_names.push_back(builtin_name("fp.sqrt", OP_FLOAT_SQRT)); + op_names.push_back(builtin_name("fp.rem", OP_FLOAT_REM)); + op_names.push_back(builtin_name("fp.eq", OP_FLOAT_EQ)); + op_names.push_back(builtin_name("fp.leq", OP_FLOAT_LE)); + op_names.push_back(builtin_name("fp.lt", OP_FLOAT_LT)); + op_names.push_back(builtin_name("fp.geq", OP_FLOAT_GE)); + op_names.push_back(builtin_name("fp.gt", OP_FLOAT_GT)); + op_names.push_back(builtin_name("fp.isNormal", OP_FLOAT_IS_NORMAL)); + op_names.push_back(builtin_name("fp.isSubnormal", OP_FLOAT_IS_SUBNORMAL)); + op_names.push_back(builtin_name("fp.isZero", OP_FLOAT_IS_ZERO)); + op_names.push_back(builtin_name("fp.isInfinite", OP_FLOAT_IS_INF)); + op_names.push_back(builtin_name("fp.isNaN", OP_FLOAT_IS_NAN)); + op_names.push_back(builtin_name("fp.min", OP_FLOAT_MIN)); + op_names.push_back(builtin_name("fp.max", OP_FLOAT_MAX)); + op_names.push_back(builtin_name("fp.convert", OP_TO_FLOAT)); + + if (m_bv_plugin) { + // op_names.push_back(builtin_name("fp.fromBv", OP_TO_FLOAT)); + // op_names.push_back(builtin_name("fp.fromUBv", OP_TO_FLOAT)); + // op_names.push_back(builtin_name("fp.fromSBv", OP_TO_FLOAT)); + // op_names.push_back(builtin_name("fp.toUBv", OP_TO_IEEE_BV)); + // op_names.push_back(builtin_name("fp.toSBv", OP_TO_IEEE_BV)); + } + + op_names.push_back(builtin_name("fp.fromReal", OP_TO_FLOAT)); + // op_names.push_back(builtin_name("fp.toReal", ?)); } void float_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { From 2b627b0821bdf37d3aa7680cbd99bb7ed87dce93 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 21 Oct 2013 17:28:21 +0100 Subject: [PATCH 187/509] fixed parameters to disallow overwriting them with illegal combinations on the command line Signed-off-by: Christoph M. Wintersteiger --- src/tactic/smtlogics/qfbv_tactic.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tactic/smtlogics/qfbv_tactic.cpp b/src/tactic/smtlogics/qfbv_tactic.cpp index 51b040332..ac53ca0c8 100644 --- a/src/tactic/smtlogics/qfbv_tactic.cpp +++ b/src/tactic/smtlogics/qfbv_tactic.cpp @@ -43,6 +43,8 @@ tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p) { simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); + simp2_p.set_bool("flat", true); // required by som + simp2_p.set_bool("hoist_mul", false); // required by som params_ref local_ctx_p = p; local_ctx_p.set_bool("local_ctx", true); From ff265c6c6ccf9d11087388910cdc1702a4711bca Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 24 Oct 2013 17:48:03 +0100 Subject: [PATCH 188/509] bugfix for variable unmarking in the sat solver. Signed-off-by: Christoph M. Wintersteiger --- src/sat/sat_solver.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 3e9b60260..d9e11724c 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1290,13 +1290,13 @@ namespace sat { bool solver::resolve_conflict() { while (true) { bool r = resolve_conflict_core(); + CASSERT("sat_check_marks", check_marks()); // after pop, clauses are reinitialized, this may trigger another conflict. if (!r) return false; if (!inconsistent()) return true; } - CASSERT("sat_check_marks", check_marks()); } bool solver::resolve_conflict_core() { @@ -1323,7 +1323,7 @@ namespace sat { TRACE("sat_conflict", tout << "not_l: " << m_not_l << "\n";); process_antecedent(m_not_l, num_marks); } - + literal consequent = m_not_l; justification js = m_conflict; @@ -1399,6 +1399,8 @@ namespace sat { dyn_sub_res(); TRACE("sat_lemma", tout << "new lemma (after minimization) size: " << m_lemma.size() << "\n" << m_lemma << "\n";); } + else + reset_lemma_var_marks(); literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); From 79b0f83ab3931ec70f6a8041d1aea11b3d9999a4 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 25 Oct 2013 13:58:56 -0700 Subject: [PATCH 189/509] working on new interpolation --- src/interp/iz3mgr.h | 7 + src/interp/iz3proof_itp.cpp | 649 ++++++++++++++++++++++++++++++++++-- src/interp/iz3translate.cpp | 70 ++++ 3 files changed, 691 insertions(+), 35 deletions(-) diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 218651520..dc5af9e26 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -325,6 +325,13 @@ class iz3mgr { return rational(1); } + ast get_linear_var(const ast& t){ + rational res; + if(op(t) == Times && is_numeral(arg(t,0),res)) + return arg(t,1); + return t; + } + int get_quantifier_num_bound(const ast &t) { return to_quantifier(t.raw())->get_num_decls(); } diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index f460dfaff..41237200c 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -89,12 +89,35 @@ class iz3proof_itp_impl : public iz3proof_itp { and q of ~Q and returns a proof of false. */ symb modpon; + /* This oprerator represents a concatenation of rewrites. The term + a=b;c=d represents an A rewrite from a to b, followed by a B + rewrite fron b to c, followed by an A rewrite from c to d. + */ + symb concat; + /* This represents a lack of a proof */ ast no_proof; // This is used to represent an infinitessimal value ast epsilon; + // Represents the top position of a term + ast top_pos; + + // add_pos(i,pos) represents position pos if the ith argument + symb add_pos; + + // rewrite proof rules + + /* rewrite_A(pos,cond,x=y) derives A |- cond => t[x]_p = t[y]_p + where t is an arbitrary term */ + symb rewrite_A; + + /* rewrite_B(pos,cond,x=y) derives B |- cond => t[x]_p = t[y]_p, + where t is an arbitrary term */ + symb rewrite_B; + + ast get_placeholder(ast t){ hash_map::iterator it = placeholders.find(t); if(it != placeholders.end()) @@ -143,6 +166,14 @@ class iz3proof_itp_impl : public iz3proof_itp { return pv->ranges_intersect(r,rng) && !pv->range_contained(r,rng); } + bool term_in_vocab(LitType ty, const ast &lit){ + prover::range r = pv->ast_scope(lit); + if(ty == LitA){ + return pv->ranges_intersect(r,rng); + } + return !pv->range_contained(r,rng); + } + /** Make a resolution node with given pivot literal and premises. The conclusion of premise1 should contain the negation of the pivot literal, while the conclusion of premise2 should contain the @@ -537,16 +568,38 @@ class iz3proof_itp_impl : public iz3proof_itp { return rotate_sum_rec(pl,pf,cond,ineq); } + bool is_rewrite_chain(const ast &chain){ + return sym(chain) == concat; + } + + ast ineq_from_chain(const ast &chain, ast &cond){ + if(is_rewrite_chain(chain)){ + ast last = chain_last(chain); + ast rest = chain_rest(chain); + if(is_true(rest) && is_rewrite_side(LitA,last) + && is_true(rewrite_lhs(last))){ + cond = my_and(cond,rewrite_cond(last)); + return rewrite_rhs(last); + } + if(is_rewrite_side(LitB,last) && is_true(rewrite_cond(last))) + return ineq_from_chain(rest,cond); + } + return chain; + } + void sum_cond_ineq(ast &ineq, ast &cond, const ast &coeff2, const ast &ineq2){ opr o = op(ineq2); if(o == Implies){ sum_cond_ineq(ineq,cond,coeff2,arg(ineq2,1)); cond = my_and(cond,arg(ineq2,0)); } - else if(is_ineq(ineq2)) - linear_comb(ineq,coeff2,ineq2); - else - throw cannot_simplify(); + else { + ast the_ineq = ineq_from_chain(ineq2,cond); + if(is_ineq(the_ineq)) + linear_comb(ineq,coeff2,the_ineq); + else + throw cannot_simplify(); + } } bool is_ineq(const ast &ineq){ @@ -556,7 +609,12 @@ class iz3proof_itp_impl : public iz3proof_itp { } // divide both sides of inequality by a non-negative integer divisor - ast idiv_ineq(const ast &ineq, const ast &divisor){ + ast idiv_ineq(const ast &ineq1, const ast &divisor){ + if(divisor == make_int(rational(1))) + return ineq1; + ast ineq = ineq1; + if(op(ineq) == Lt) + ineq = simplify_ineq(make(Leq,arg(ineq,0),make(Sub,arg(ineq,1),make_int("1")))); return make(op(ineq),mk_idiv(arg(ineq,0),divisor),mk_idiv(arg(ineq,1),divisor)); } @@ -580,21 +638,31 @@ class iz3proof_itp_impl : public iz3proof_itp { ast equality = arg(neg_equality,0); ast x = arg(equality,0); ast y = arg(equality,1); - ast xleqy = round_ineq(arg(pf,1)); - ast yleqx = round_ineq(arg(pf,2)); - ast itpeq; - if(get_term_type(x) == LitA) - itpeq = make(Equal,x,z3_simplify(make(Plus,x,get_ineq_rhs(xleqy)))); - else if(get_term_type(y) == LitA) - itpeq = make(Equal,z3_simplify(make(Plus,y,get_ineq_rhs(yleqx))),y); - else - throw cannot_simplify(); - ast cond = mk_true(); - ast ineq = make(Leq,make_int("0"),make_int("0")); - sum_cond_ineq(ineq,cond,make_int("-1"),xleqy); - sum_cond_ineq(ineq,cond,make_int("-1"),yleqx); - cond = z3_simplify(my_and(cond,ineq)); - return my_implies(cond,itpeq); + ast cond1 = mk_true(); + ast xleqy = round_ineq(ineq_from_chain(arg(pf,1),cond1)); + ast yleqx = round_ineq(ineq_from_chain(arg(pf,2),cond1)); + ast ineq1 = make(Leq,make_int("0"),make_int("0")); + sum_cond_ineq(ineq1,cond1,make_int("-1"),xleqy); + sum_cond_ineq(ineq1,cond1,make_int("-1"),yleqx); + cond1 = my_and(cond1,z3_simplify(ineq1)); + ast cond2 = mk_true(); + ast ineq2 = make(Leq,make_int("0"),make_int("0")); + sum_cond_ineq(ineq2,cond2,make_int("1"),xleqy); + sum_cond_ineq(ineq2,cond2,make_int("1"),yleqx); + cond2 = z3_simplify(ineq2); + if(get_term_type(x) == LitA){ + ast iter = z3_simplify(make(Plus,x,get_ineq_rhs(xleqy))); + ast rewrite1 = make_rewrite(LitA,top_pos,cond1,make(Equal,x,iter)); + ast rewrite2 = make_rewrite(LitB,top_pos,cond2,make(Equal,iter,y)); + return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); + } + if(get_term_type(y) == LitA){ + ast iter = z3_simplify(make(Plus,y,get_ineq_rhs(yleqx))); + ast rewrite2 = make_rewrite(LitA,top_pos,cond1,make(Equal,iter,y)); + ast rewrite1 = make_rewrite(LitB,top_pos,cond2,make(Equal,x,iter)); + return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); + } + throw cannot_simplify(); } throw cannot_simplify(); } @@ -612,10 +680,13 @@ class iz3proof_itp_impl : public iz3proof_itp { if(pl == arg(pf,1)){ ast cond = mk_true(); ast equa = sep_cond(arg(pf,0),cond); - if(op(equa) == Equal) - return my_implies(cond,z3_simplify(make(Leq,make_int("0"),make(Sub,arg(equa,1),arg(equa,0))))); - if(op(equa) == True) - return my_implies(cond,z3_simplify(make(Leq,make_int("0"),make_int("0")))); + if(is_equivrel_chain(equa)){ + ast ineqs= chain_ineqs(LitA,equa); + cond = my_and(cond,chain_conditions(LitA,equa)); + ast Bconds = chain_conditions(LitB,equa); + if(is_true(Bconds) && op(ineqs) != And) + return my_implies(cond,ineqs); + } } throw cannot_simplify(); } @@ -626,7 +697,9 @@ class iz3proof_itp_impl : public iz3proof_itp { args[0] = arg(pf,0); args[1] = arg(pf,1); args[2] = mk_true(); - return simplify_modpon(args); + ast cond = mk_true(); + ast chain = simplify_modpon_fwd(args, cond); + return my_implies(cond,chain); } throw cannot_simplify(); } @@ -649,6 +722,7 @@ class iz3proof_itp_impl : public iz3proof_itp { int ipos = pos.get_unsigned(); ast cond = mk_true(); ast equa = sep_cond(arg(pf,0),cond); +#if 0 if(op(equa) == Equal){ ast pe = mk_not(neg_equality); ast lhs = subst_in_arg_pos(ipos,arg(equa,0),arg(pe,0)); @@ -656,6 +730,9 @@ class iz3proof_itp_impl : public iz3proof_itp { ast res = make(Equal,lhs,rhs); return my_implies(cond,res); } +#endif + ast res = chain_pos_add(ipos,equa); + return my_implies(cond,res); } } throw cannot_simplify(); @@ -666,11 +743,12 @@ class iz3proof_itp_impl : public iz3proof_itp { return mk_true(); ast cond = mk_true(); ast equa = sep_cond(args[0],cond); - if(is_equivrel(equa)) - return my_implies(cond,make(op(equa),arg(equa,1),arg(equa,0))); + if(is_equivrel_chain(equa)) + return my_implies(cond,reverse_chain(equa)); throw cannot_simplify(); } +#if 0 ast simplify_modpon(const std::vector &args){ if(op(args[1]) == True){ ast cond = mk_true(); @@ -720,6 +798,62 @@ class iz3proof_itp_impl : public iz3proof_itp { } throw cannot_simplify(); } +#else + ast simplify_modpon_fwd(const std::vector &args, ast &cond){ + ast P = sep_cond(args[0],cond); + ast PeqQ = sep_cond(args[1],cond); + ast chain; + if(is_equivrel_chain(P)){ + ast split[2]; + split_chain(PeqQ,split); + chain = reverse_chain(split[0]); + chain = concat_rewrite_chain(chain,P); + chain = concat_rewrite_chain(chain,split[1]); + } + else // if not an equavalence, must be of form T <-> pred + chain = concat_rewrite_chain(P,PeqQ); + return chain; + } + +#if 0 + ast simplify_modpon(const std::vector &args){ + ast cond = mk_true(); + ast chain = simplify_modpon_fwd(args,cond); + ast Q2 = sep_cond(args[2],cond); + ast interp; + if(is_equivrel_chain(Q2)){ + chain = concat_rewrite_chain(chain,chain_pos_add(0,chain_pos_add(0,Q2))); + interp = my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); + } + else if(is_rewrite_side(LitB,chain_last(Q2))) + interp = my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); + else + interp = my_and(chain_conditions(LitB,chain),my_implies(chain_conditions(LitA,chain),mk_not(chain_formulas(LitB,chain)))); + return my_implies(cond,interp); + } +#endif + + /* Given a chain rewrite chain deriving not P and a rewrite chain deriving P, return an interpolant. */ + ast contra_chain(const ast &neg_chain, const ast &pos_chain){ + // equality is a special case. we use the derivation of x=y to rewrite not(x=y) to not(y=y) + if(is_equivrel_chain(pos_chain)){ + ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,chain_pos_add(0,pos_chain))); + return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); + } + // otherwise, we reverse the derivation of t = P and use it to rewrite not(P) to not(t) + ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,reverse_chain(pos_chain))); + return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); + } + + ast simplify_modpon(const std::vector &args){ + ast cond = mk_true(); + ast chain = simplify_modpon_fwd(args,cond); + ast Q2 = sep_cond(args[2],cond); + ast interp = is_negation_chain(chain) ? contra_chain(chain,Q2) : contra_chain(Q2,chain); + return my_implies(cond,interp); + } + +#endif bool is_equivrel(const ast &p){ opr o = op(p); @@ -801,6 +935,121 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } + bool diff_rec(LitType t, const ast &p, const ast &q, ast &pd, ast &qd){ + if(p == q) + return false; + if(term_in_vocab(t,p) && term_in_vocab(t,q)){ + pd = p; + qd = q; + return true; + } + else { + if(sym(p) != sym(q)) return false; + int nargs = num_args(p); + if(num_args(q) != nargs) return false; + for(int i = 0; i < nargs; i++) + if(diff_rec(t,arg(p,i),arg(q,i),pd,qd)) + return true; + return false; + } + } + + void diff(LitType t, const ast &p, const ast &q, ast &pd, ast &qd){ + if(!diff_rec(t,p,q,pd,qd)) + throw cannot_simplify(); + } + + bool apply_diff_rec(LitType t, const ast &inp, const ast &p, const ast &q, ast &out){ + if(p == q) + return false; + if(term_in_vocab(t,p) && term_in_vocab(t,q)){ + if(inp != p) + throw cannot_simplify(); + out = q; + return true; + } + else { + int nargs = num_args(p); + if(sym(p) != sym(q)) throw cannot_simplify(); + if(num_args(q) != nargs) throw cannot_simplify(); + if(sym(p) != sym(inp)) throw cannot_simplify(); + if(num_args(inp) != nargs) throw cannot_simplify(); + for(int i = 0; i < nargs; i++) + if(apply_diff_rec(t,arg(inp,i),arg(p,i),arg(q,i),out)) + return true; + return false; + } + } + + ast apply_diff(LitType t, const ast &inp, const ast &p, const ast &q){ + ast out; + if(!apply_diff_rec(t,inp,p,q,out)) + throw cannot_simplify(); + return out; + } + + bool merge_A_rewrites(const ast &A1, const ast &A2, ast &merged) { + if(arg(A1,1) == arg(A2,0)){ + merged = make(op(A1),arg(A1,0),arg(A2,1)); + return true; + } + ast diff1l, diff1r, diff2l, diff2r,diffBl,diffBr; + diff(LitA,arg(A1,0),arg(A1,1),diff1l,diff1r); + diff(LitA,arg(A2,0),arg(A2,1),diff2l,diff2r); + diff(LitB,arg(A1,1),arg(A2,0),diffBl,diffBr); + if(!term_common(diff2l) && !term_common(diffBr)){ + ast A1r = apply_diff(LitB,arg(A2,1),arg(A2,0),arg(A1,1)); + merged = make(op(A1),arg(A1,0),A1r); + return true; + } + if(!term_common(diff1r) && !term_common(diffBl)){ + ast A2l = apply_diff(LitB,arg(A1,0),arg(A1,1),arg(A2,0)); + merged = make(op(A1),A2l,arg(A2,1)); + return true; + } + return false; + } + + void collect_A_rewrites(const ast &t, std::vector &res){ + if(is_true(t)) + return; + if(sym(t) == concat){ + res.push_back(arg(t,0)); + collect_A_rewrites(arg(t,0),res); + return; + } + res.push_back(t); + } + + ast concat_A_rewrites(const std::vector &rew){ + if(rew.size() == 0) + return mk_true(); + ast res = rew[0]; + for(unsigned i = 1; i < rew.size(); i++) + res = make(concat,res,rew[i]); + return res; + } + + ast merge_concat_rewrites(const ast &A1, const ast &A2){ + std::vector rew; + collect_A_rewrites(A1,rew); + int first = rew.size(), last = first; // range that might need merging + collect_A_rewrites(A2,rew); + while(first > 0 && first < (int)rew.size() && first <= last){ + ast merged; + if(merge_A_rewrites(rew[first-1],rew[first],merged)){ + rew[first] = merged; + first--; + rew.erase(rew.begin()+first); + last--; + if(first >= last) last = first+1; + } + else + first++; + } + return concat_A_rewrites(rew); + } + ast sep_cond(const ast &t, ast &cond){ if(op(t) == Implies){ cond = my_and(cond,arg(t,0)); @@ -809,6 +1058,326 @@ class iz3proof_itp_impl : public iz3proof_itp { return t; } + + /* operations on term positions */ + + /** Finds the difference between two positions. If p1 < p2 (p1 is a + position below p2), returns -1 and sets diff to p2-p1 (the psath + from position p2 to position p1). If p2 < p1 (p2 is a position + below p1), returns 1 and sets diff to p1-p2 (the psath from + position p1 to position p2). If equal, return 0 and set diff to + top_pos. Else (if p1 and p2 are independent) returns 2 and + leaves diff unchanged. */ + + int pos_diff(const ast &p1, const ast &p2, ast &diff){ + if(p1 == top_pos && p2 != top_pos){ + diff = p2; + return 1; + } + if(p2 == top_pos && p1 != top_pos){ + diff = p1; + return -1; + } + if(p1 == top_pos && p2 == top_pos){ + diff = p1; + return 0; + } + if(arg(p1,0) == arg(p2,0)) // same argument position, recur + return pos_diff(arg(p1,1),arg(p2,1),diff); + return 2; + } + + /* return the position of pos in the argth argument */ + ast pos_add(int arg, const ast &pos){ + return make(add_pos,make_int(rational(arg)),pos); + } + + /* return the argument number of position, if not top */ + int pos_arg(const ast &pos){ + rational r; + if(is_numeral(arg(pos,0),r)) + return r.get_unsigned(); + throw "bad position!"; + } + + /* substitute y into position pos in x */ + ast subst_in_pos(const ast &x, const ast &pos, const ast &y){ + if(pos == top_pos) + return y; + int p = pos_arg(pos); + int nargs = num_args(x); + if(p >= 0 && p < nargs){ + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = i == p ? subst_in_pos(arg(x,i),arg(pos,1),y) : arg(x,i); + return clone(x,args); + } + throw "bad term position!"; + } + + + /* operations on rewrites */ + ast make_rewrite(LitType t, const ast &pos, const ast &cond, const ast &equality){ + return make(t == LitA ? rewrite_A : rewrite_B, pos, cond, equality); + } + + ast rewrite_pos(const ast &rew){ + return arg(rew,0); + } + + ast rewrite_cond(const ast &rew){ + return arg(rew,1); + } + + ast rewrite_equ(const ast &rew){ + return arg(rew,2); + } + + ast rewrite_lhs(const ast &rew){ + return arg(arg(rew,2),0); + } + + ast rewrite_rhs(const ast &rew){ + return arg(arg(rew,2),1); + } + + /* operations on rewrite chains */ + + ast chain_cons(const ast &chain, const ast &elem){ + return make(concat,chain,elem); + } + + ast chain_rest(const ast &chain){ + return arg(chain,0); + } + + ast chain_last(const ast &chain){ + return arg(chain,1); + } + + ast rewrite_update_rhs(const ast &rew, const ast &pos, const ast &new_rhs, const ast &new_cond){ + ast foo = subst_in_pos(rewrite_rhs(rew),pos,new_rhs); + ast equality = arg(rew,2); + return make(sym(rew),rewrite_pos(rew),my_and(rewrite_cond(rew),new_cond),make(op(equality),arg(equality,0),foo)); + } + + ast rewrite_update_lhs(const ast &rew, const ast &pos, const ast &new_lhs, const ast &new_cond){ + ast foo = subst_in_pos(rewrite_lhs(rew),pos,new_lhs); + ast equality = arg(rew,2); + return make(sym(rew),rewrite_pos(rew),my_and(rewrite_cond(rew),new_cond),make(op(equality),foo,arg(equality,1))); + } + + bool is_common_rewrite(const ast &rew){ + return term_common(arg(rew,2)); + } + + bool is_right_mover(const ast &rew){ + return term_common(rewrite_lhs(rew)) && !term_common(rewrite_rhs(rew)); + } + + bool is_left_mover(const ast &rew){ + return term_common(rewrite_rhs(rew)) && !term_common(rewrite_lhs(rew)); + } + + bool same_side(const ast &rew1, const ast &rew2){ + return sym(rew1) == sym(rew2); + } + + bool is_rewrite_side(LitType t, const ast &rew){ + if(t == LitA) + return sym(rew) == rewrite_A; + return sym(rew) == rewrite_B; + } + + ast rewrite_to_formula(const ast &rew){ + return my_implies(arg(rew,1),arg(rew,2)); + } + + // make rewrite rew conditon on rewrite cond + ast rewrite_conditional(const ast &cond, const ast &rew){ + ast cf = rewrite_to_formula(cond); + return make(sym(rew),arg(rew,0),my_and(arg(rew,1),cf),arg(rew,2)); + } + + ast reverse_rewrite(const ast &rew){ + ast equ = arg(rew,2); + return make(sym(rew),arg(rew,0),arg(rew,1),make(op(equ),arg(equ,1),arg(equ,0))); + } + + ast rewrite_pos_add(int apos, const ast &rew){ + return make(sym(rew),pos_add(apos,arg(rew,0)),arg(rew,1),arg(rew,2)); + } + + ast rewrite_up(const ast &rew){ + return make(sym(rew),arg(arg(rew,0),1),arg(rew,1),arg(rew,2)); + } + + /** Adds a rewrite to a chain of rewrites, keeping the chain in + normal form. An empty chain is represented by true.*/ + + ast add_rewrite_to_chain(const ast &chain, const ast &rewrite){ + if(is_true(chain)) + return chain_cons(chain,rewrite); + ast last = chain_last(chain); + ast rest = chain_rest(chain); + if(same_side(last,rewrite)){ + ast p1 = rewrite_pos(last); + ast p2 = rewrite_pos(rewrite); + ast diff; + switch(pos_diff(p1,p2,diff)){ + case 1: { + ast absorb = rewrite_update_rhs(last,diff,rewrite_rhs(rewrite),rewrite_cond(rewrite)); + return add_rewrite_to_chain(rest,absorb); + } + case 0: + case -1: { + ast absorb = rewrite_update_lhs(rewrite,diff,rewrite_lhs(last),rewrite_cond(last)); + return add_rewrite_to_chain(rest,absorb); + } + default: {// independent case + bool rm = is_right_mover(last); + bool lm = is_left_mover(rewrite); + if((lm && !rm) || (rm && !lm)) + return chain_swap(rest,last,rewrite); + } + } + } + else { + if(is_left_mover(rewrite)){ + if(is_common_rewrite(last)) + return add_rewrite_to_chain(chain_cons(rest,flip_rewrite(last)),rewrite); + if(!is_left_mover(last)) + return chain_swap(rest,last,rewrite); + } + if(is_right_mover(last)){ + if(is_common_rewrite(rewrite)) + return add_rewrite_to_chain(chain,flip_rewrite(rewrite)); + if(!is_right_mover(rewrite)) + return chain_swap(rest,last,rewrite); + } + } + return chain_cons(chain,rewrite); + } + + ast chain_swap(const ast &rest, const ast &last, const ast &rewrite){ + return chain_cons(add_rewrite_to_chain(rest,rewrite),last); + } + + ast flip_rewrite(const ast &rew){ + symb flip_sym = (sym(rew) == rewrite_A) ? rewrite_B : rewrite_A; + ast cf = rewrite_to_formula(rew); + return make(flip_sym,arg(rew,0),my_implies(arg(rew,1),cf),arg(rew,2)); + } + + /** concatenates two rewrite chains, keeping result in normal form. */ + + ast concat_rewrite_chain(const ast &chain1, const ast &chain2){ + if(is_true(chain2)) return chain1; + if(is_true(chain1)) return chain2; + ast foo = concat_rewrite_chain(chain1,chain_rest(chain2)); + return add_rewrite_to_chain(foo,chain_last(chain2)); + } + + /** reverse a chain of rewrites */ + + ast reverse_chain_rec(const ast &chain, const ast &prefix){ + if(is_true(chain)) + return prefix; + ast last = reverse_rewrite(chain_last(chain)); + ast rest = chain_rest(chain); + return reverse_chain_rec(rest,chain_cons(prefix,last)); + } + + ast reverse_chain(const ast &chain){ + return reverse_chain_rec(chain,mk_true()); + } + + bool is_equivrel_chain(const ast &chain){ + if(is_true(chain)) + return true; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + if(is_true(rest)) + return !is_true(rewrite_lhs(last)); + return is_equivrel_chain(rest); + } + + bool is_negation_chain(const ast &chain){ + if(is_true(chain)) + return false; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + if(is_true(rest)) + return op(rewrite_rhs(last)) == Not; + return is_negation_chain(rest); + } + + /** Split a chain of rewrites two chains, operating on positions 0 and 1. + Fail if any rewrite in the chain operates on top position. */ + void split_chain_rec(const ast &chain, ast *res){ + if(is_true(chain)) + return; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + split_chain_rec(rest,res); + ast pos = rewrite_pos(last); + if(pos == top_pos) + throw "bad rewrite chain"; + int arg = pos_arg(pos); + if(arg<0 || arg > 1) + throw "bad position!"; + res[arg] = chain_cons(res[arg],rewrite_up(last)); + } + + void split_chain(const ast &chain, ast *res){ + res[0] = res[1] = mk_true(); + split_chain_rec(chain,res); + } + + ast chain_conditions(LitType t, const ast &chain){ + if(is_true(chain)) + return mk_true(); + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast cond = chain_conditions(t,rest); + if(is_rewrite_side(t,last)) + cond = my_and(cond,rewrite_cond(last)); + return cond; + } + + ast chain_formulas(LitType t, const ast &chain){ + if(is_true(chain)) + return mk_true(); + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast cond = chain_formulas(t,rest); + if(is_rewrite_side(t,last)) + cond = my_and(cond,rewrite_equ(last)); + return cond; + } + + ast chain_ineqs(LitType t, const ast &chain){ + if(is_true(chain)) + return mk_true(); + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast cond = chain_ineqs(t,rest); + if(is_rewrite_side(t,last)){ + ast equa = rewrite_equ(last); + cond = my_and(cond,z3_simplify(make(Leq,make_int("0"),make(Sub,arg(equa,1),arg(equa,0))))); + } + return cond; + } + + ast chain_pos_add(int arg, const ast &chain){ + if(is_true(chain)) + return mk_true(); + ast last = rewrite_pos_add(arg,chain_last(chain)); + ast rest = chain_pos_add(arg,chain_rest(chain)); + return chain_cons(rest,last); + } + + /** Make an assumption node. The given clause is assumed in the given frame. */ virtual node make_assumption(int frame, const std::vector &assumption){ if(pv->in_range(frame,rng)){ @@ -822,7 +1391,12 @@ class iz3proof_itp_impl : public iz3proof_itp { else { return mk_true(); } -} + } + + ast make_local_rewrite(LitType t, const ast &p){ + ast rew = is_equivrel(p) ? p : make(Iff,mk_true(),p); + return chain_cons(mk_true(),make_rewrite(t, top_pos, mk_true(), rew)); + } ast triv_interp(const symb &rule, const std::vector &premises){ std::vector ps; ps.resize(premises.size()); @@ -830,12 +1404,11 @@ class iz3proof_itp_impl : public iz3proof_itp { int mask = 0; for(unsigned i = 0; i < ps.size(); i++){ ast p = premises[i]; - switch(get_term_type(p)){ + LitType t = get_term_type(p); + switch(t){ case LitA: - ps[i] = p; - break; case LitB: - ps[i] = mk_true(); + ps[i] = make_local_rewrite(t,p); break; default: ps[i] = get_placeholder(p); // can only prove consequent! @@ -1159,11 +1732,11 @@ class iz3proof_itp_impl : public iz3proof_itp { virtual ast make_mixed_congruence(const ast &x, const ast &y, const ast &p, const ast &con, const ast &prem1){ ast foo = p; std::vector conjs; - switch(get_term_type(foo)){ + LitType t = get_term_type(foo); + switch(t){ case LitA: - break; case LitB: - foo = mk_true(); + foo = make_local_rewrite(t,foo); break; case LitMixed: conjs.push_back(foo); @@ -1561,6 +2134,7 @@ public: type booldom[1] = {bool_type()}; type boolbooldom[2] = {bool_type(),bool_type()}; type boolboolbooldom[3] = {bool_type(),bool_type(),bool_type()}; + type intbooldom[2] = {int_type(),bool_type()}; contra = function("@contra",2,boolbooldom,bool_type()); sum = function("@sum",3,boolintbooldom,bool_type()); rotate_sum = function("@rotsum",2,boolbooldom,bool_type()); @@ -1572,6 +2146,11 @@ public: epsilon = make_var("@eps",int_type()); modpon = function("@mp",3,boolboolbooldom,bool_type()); no_proof = make_var("@nop",bool_type()); + concat = function("@concat",2,boolbooldom,bool_type()); + top_pos = make_var("@top_pos",bool_type()); + add_pos = function("@add_pos",2,intbooldom,bool_type()); + rewrite_A = function("@rewrite_A",3,boolboolbooldom,bool_type()); + rewrite_B = function("@rewrite_B",3,boolboolbooldom,bool_type()); } }; diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 042dbe192..a41fdcba9 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -829,6 +829,42 @@ public: return make_int(d); } + ast get_bounded_variable(const ast &ineq, bool &lb){ + ast nineq = normalize_inequality(ineq); + ast lhs = arg(nineq,0); + switch(op(lhs)){ + case Uninterpreted: + lb = false; + return lhs; + case Times: + if(arg(lhs,0) == make_int(rational(1))) + lb = false; + else if(arg(lhs,0) == make_int(rational(-1))) + lb = true; + else + throw unsupported(); + return arg(lhs,1); + default: + throw unsupported(); + } + } + + rational get_term_coefficient(const ast &t1, const ast &v){ + ast t = arg(normalize_inequality(t1),0); + if(op(t) == Plus){ + int nargs = num_args(t); + for(int i = 0; i < nargs; i++){ + if(get_linear_var(arg(t,i)) == v) + return get_coeff(arg(t,i)); + } + } + else + if(get_linear_var(t) == v) + return get_coeff(t); + return rational(0); + } + + Iproof::node GCDtoDivRule(const ast &proof, bool pol, std::vector &coeffs, std::vector &prems, ast &cut_con){ // gather the summands of the desired polarity std::vector my_prems; @@ -843,6 +879,24 @@ public: } } ast my_con = sum_inequalities(my_coeffs,my_prem_cons); + + // handle generalized GCD test. sadly, we dont' get the coefficients... + if(coeffs[0].is_zero()){ + bool lb; + int xtra_prem = 0; + ast bv = get_bounded_variable(conc(prem(proof,0)),lb); + rational bv_coeff = get_term_coefficient(my_con,bv); + if(bv_coeff.is_pos() != lb) + xtra_prem = 1; + if(bv_coeff.is_neg()) + bv_coeff = -bv_coeff; + + my_prems.push_back(prems[xtra_prem]); + my_coeffs.push_back(make_int(bv_coeff)); + my_prem_cons.push_back(conc(prem(proof,xtra_prem))); + my_con = sum_inequalities(my_coeffs,my_prem_cons); + } + my_con = normalize_inequality(my_con); Iproof::node hyp = iproof->make_hypothesis(mk_not(my_con)); my_prems.push_back(hyp); @@ -961,6 +1015,12 @@ public: else lits.push_back(from_ast(con)); + // special case + if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST && pr(prem(proof,1)) == PR_REWRITE ) { + res = iproof->make_axiom(lits); + return res; + } + // translate all the premises std::vector args(nprems); for(unsigned i = 0; i < nprems; i++) @@ -1057,6 +1117,10 @@ public: res = iproof->make_hypothesis(conc(proof)); break; } + case PR_QUANT_INST: { + res = iproof->make_axiom(lits); + break; + } default: assert(0 && "translate_main: unsupported proof rule"); throw unsupported(); @@ -1066,6 +1130,11 @@ public: return res; } + void clear_translation(){ + translation.first.clear(); + translation.second.clear(); + } + // We actually compute the interpolant here and then produce a proof consisting of just a lemma iz3proof::node translate(ast proof, iz3proof &dst){ @@ -1075,6 +1144,7 @@ public: ast itp = translate_main(proof); itps.push_back(itp); delete iproof; + clear_translation(); } // Very simple proof -- lemma of the empty clause with computed interpolation iz3proof::node Ipf = dst.make_lemma(std::vector(),itps); // builds result in dst From 81df4932fbc8563befd15775cc960ba906dde1a8 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 25 Oct 2013 18:40:26 -0700 Subject: [PATCH 190/509] added quantifiers in new interpolation --- src/api/api_interp.cpp | 4 ++ src/interp/iz3mgr.cpp | 90 ++++++++++++++++++++++++++++++ src/interp/iz3mgr.h | 19 +++++++ src/interp/iz3proof_itp.cpp | 15 +++++ src/interp/iz3proof_itp.h | 3 + src/interp/iz3translate.cpp | 3 +- src/interp/iz3translate_direct.cpp | 80 -------------------------- 7 files changed, 133 insertions(+), 81 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 926493c6b..00503566e 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -407,6 +407,10 @@ extern "C" { cnsts_vec[i] = a; } Z3_ast tree = parents_vector_to_tree(ctx,num,cnsts,parents); + for(int i = 0; i < num_theory; i++){ + expr *a = to_expr(theory[i]); + cnsts_vec.push_back(a); + } iz3pp(mk_c(ctx)->m(),cnsts_vec,to_expr(tree),f); Z3_dec_ref(ctx,tree); } diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 376e2a14f..1b91fd521 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -640,3 +640,93 @@ iz3mgr::ast iz3mgr::mk_idiv(const ast& t, const ast &d){ return mk_idiv(t,r); return make(Idiv,t,d); } + + +// does variable occur in expression? +int iz3mgr::occurs_in1(hash_map &occurs_in_memo,ast var, ast e){ + std::pair foo(e,false); + std::pair::iterator,bool> bar = occurs_in_memo.insert(foo); + bool &res = bar.first->second; + if(bar.second){ + if(e == var) res = true; + int nargs = num_args(e); + for(int i = 0; i < nargs; i++) + res |= occurs_in1(occurs_in_memo,var,arg(e,i)); + } + return res; +} + +int iz3mgr::occurs_in(ast var, ast e){ + hash_map memo; + return occurs_in1(memo,var,e); +} + + +// find a controlling equality for a given variable v in a term +// a controlling equality is of the form v = t, which, being +// false would force the formula to have the specifid truth value +// returns t, or null if no such + +iz3mgr::ast iz3mgr::cont_eq(hash_set &cont_eq_memo, bool truth, ast v, ast e){ + if(is_not(e)) return cont_eq(cont_eq_memo, !truth,v,arg(e,0)); + if(cont_eq_memo.find(e) != cont_eq_memo.end()) + return ast(); + cont_eq_memo.insert(e); + if(!truth && op(e) == Equal){ + if(arg(e,0) == v) return(arg(e,1)); + if(arg(e,1) == v) return(arg(e,0)); + } + if((!truth && op(e) == And) || (truth && op(e) == Or)){ + int nargs = num_args(e); + for(int i = 0; i < nargs; i++){ + ast res = cont_eq(cont_eq_memo, truth, v, arg(e,i)); + if(!res.null()) return res; + } + } + if(truth && op(e) == Implies){ + ast res = cont_eq(cont_eq_memo, !truth, v, arg(e,0)); + if(!res.null()) return res; + res = cont_eq(cont_eq_memo, truth, v, arg(e,1)); + if(!res.null()) return res; + } + return ast(); +} + + // substitute a term t for unbound occurrences of variable v in e + +iz3mgr::ast iz3mgr::subst(hash_map &subst_memo, ast var, ast t, ast e){ + if(e == var) return t; + std::pair foo(e,ast()); + std::pair::iterator,bool> bar = subst_memo.insert(foo); + ast &res = bar.first->second; + if(bar.second){ + int nargs = num_args(e); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = subst(subst_memo,var,t,arg(e,i)); + opr f = op(e); + if(f == Equal && args[0] == args[1]) res = mk_true(); + else res = clone(e,args); + } + return res; +} + +iz3mgr::ast iz3mgr::subst(ast var, ast t, ast e){ + hash_map memo; + return subst(memo,var,t,e); +} + + // apply a quantifier to a formula, with some optimizations + // 1) bound variable does not occur -> no quantifier + // 2) bound variable must be equal to some term -> substitute + +iz3mgr::ast iz3mgr::apply_quant(opr quantifier, ast var, ast e){ + if(!occurs_in(var,e))return e; + hash_set cont_eq_memo; + ast cterm = cont_eq(cont_eq_memo, quantifier == Forall, var, e); + if(!cterm.null()){ + return subst(var,cterm,e); + } + std::vector bvs; bvs.push_back(var); + return make_quant(quantifier,bvs,e); +} diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index dc5af9e26..a648c3f84 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -612,6 +612,20 @@ class iz3mgr { z3pf conc(const z3pf &t){return arg(t,num_prems(t));} + + /* quantifier handling */ + + // substitute a term t for unbound occurrences of variable v in e + + ast subst(ast var, ast t, ast e); + + // apply a quantifier to a formula, with some optimizations + // 1) bound variable does not occur -> no quantifier + // 2) bound variable must be equal to some term -> substitute + + ast apply_quant(opr quantifier, ast var, ast e); + + /** For debugging */ void show(ast); @@ -662,6 +676,11 @@ class iz3mgr { ast mki(family_id fid, decl_kind sk, int n, raw_ast **args); ast make(opr op, int n, raw_ast **args); ast make(symb sym, int n, raw_ast **args); + int occurs_in1(stl_ext::hash_map &occurs_in_memo, ast var, ast e); + int occurs_in(ast var, ast e); + ast cont_eq(stl_ext::hash_set &cont_eq_memo, bool truth, ast v, ast e); + ast subst(stl_ext::hash_map &subst_memo, ast var, ast t, ast e); + family_id m_basic_fid; family_id m_array_fid; diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 41237200c..d786f95ab 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2056,11 +2056,26 @@ class iz3proof_itp_impl : public iz3proof_itp { return new_var; } + ast add_quants(ast e){ + for(int i = localization_vars.size() - 1; i >= 0; i--){ + LocVar &lv = localization_vars[i]; + opr quantifier = (pv->in_range(lv.frame,rng)) ? Exists : Forall; + e = apply_quant(quantifier,lv.var,e); + } + return e; + } + node make_resolution(ast pivot, node premise1, node premise2) { std::vector lits; return make_resolution(pivot,lits,premise1,premise2); } + /* Return an interpolant from a proof of false */ + ast interpolate(const node &pf){ + // proof of false must be a formula, with quantified symbols + return add_quants(pf); + } + ast resolve_with_quantifier(const ast &pivot1, const ast &conj1, const ast &pivot2, const ast &conj2){ if(is_not(arg(pivot1,1))) diff --git a/src/interp/iz3proof_itp.h b/src/interp/iz3proof_itp.h index 1d5579b75..299f391ea 100644 --- a/src/interp/iz3proof_itp.h +++ b/src/interp/iz3proof_itp.h @@ -118,6 +118,9 @@ class iz3proof_itp : public iz3mgr { is an affine term divisble by d and c is an integer constant */ virtual node make_cut_rule(const ast &tleqc, const ast &d, const ast &con, const ast &prem) = 0; + /* Return an interpolant from a proof of false */ + virtual ast interpolate(const node &pf) = 0; + /** Create proof object to construct an interpolant. */ static iz3proof_itp *create(prover *p, const prover::range &r, bool _weak); diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index a41fdcba9..2be7c22e5 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1141,7 +1141,8 @@ public: std::vector itps; for(int i = 0; i < frames -1; i++){ iproof = iz3proof_itp::create(this,range_downward(i),weak_mode()); - ast itp = translate_main(proof); + Iproof::node ipf = translate_main(proof); + ast itp = iproof->interpolate(ipf); itps.push_back(itp); delete iproof; clear_translation(); diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index 7a6238201..037cabee9 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -219,11 +219,8 @@ public: AstToAst localization_map; // maps terms to their localization vars typedef hash_map AstToBool; - AstToBool occurs_in_memo; // memo of occurs_in function - AstHashSet cont_eq_memo; // memo of cont_eq function - AstToAst subst_memo; // memo of subst function iz3secondary *secondary; // the secondary prover @@ -613,83 +610,6 @@ public: } } - // does variable occur in expression? - int occurs_in1(ast var, ast e){ - std::pair foo(e,false); - std::pair bar = occurs_in_memo.insert(foo); - bool &res = bar.first->second; - if(bar.second){ - if(e == var) res = true; - int nargs = num_args(e); - for(int i = 0; i < nargs; i++) - res |= occurs_in1(var,arg(e,i)); - } - return res; - } - - int occurs_in(ast var, ast e){ - occurs_in_memo.clear(); - return occurs_in1(var,e); - } - - // find a controlling equality for a given variable v in a term - // a controlling equality is of the form v = t, which, being - // false would force the formula to have the specifid truth value - // returns t, or null if no such - - ast cont_eq(bool truth, ast v, ast e){ - if(is_not(e)) return cont_eq(!truth,v,arg(e,0)); - if(cont_eq_memo.find(e) != cont_eq_memo.end()) - return ast(); - cont_eq_memo.insert(e); - if(!truth && op(e) == Equal){ - if(arg(e,0) == v) return(arg(e,1)); - if(arg(e,1) == v) return(arg(e,0)); - } - if((!truth && op(e) == And) || (truth && op(e) == Or)){ - int nargs = num_args(e); - for(int i = 0; i < nargs; i++){ - ast res = cont_eq(truth, v, arg(e,i)); - if(!res.null()) return res; - } - } - return ast(); - } - - // substitute a term t for unbound occurrences of variable v in e - - ast subst(ast var, ast t, ast e){ - if(e == var) return t; - std::pair foo(e,ast()); - std::pair bar = subst_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - int nargs = num_args(e); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = subst(var,t,arg(e,i)); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else res = clone(e,args); - } - return res; - } - - // apply a quantifier to a formula, with some optimizations - // 1) bound variable does not occur -> no quantifier - // 2) bound variable must be equal to some term -> substitute - - ast apply_quant(opr quantifier, ast var, ast e){ - if(!occurs_in(var,e))return e; - cont_eq_memo.clear(); - ast cterm = cont_eq(quantifier == Forall, var, e); - if(!cterm.null()){ - subst_memo.clear(); - return subst(var,cterm,e); - } - std::vector bvs; bvs.push_back(var); - return make_quant(quantifier,bvs,e); - } // add quantifiers over the localization vars // to an interpolant for frames lo-hi From ac212ec54c15cb5f02fd71478d81d6d336d5f7e4 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 1 Nov 2013 11:03:55 -0700 Subject: [PATCH 191/509] fixing interpolation bugs --- src/cmd_context/interpolant_cmds.cpp | 6 + src/interp/iz3checker.cpp | 13 + src/interp/iz3interp.cpp | 2 + src/interp/iz3mgr.cpp | 50 +++ src/interp/iz3mgr.h | 4 + src/interp/iz3profiling.cpp | 16 +- src/interp/iz3proof_itp.cpp | 475 +++++++++++---------------- src/interp/iz3translate.cpp | 63 +++- 8 files changed, 336 insertions(+), 293 deletions(-) diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index e202f1f69..2a7de3176 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -31,6 +31,8 @@ Notes: #include"pp_params.hpp" #include"iz3interp.h" #include"iz3checker.h" +#include"iz3profiling.h" +#include"interp_params.hpp" static void show_interpolant_and_maybe_check(cmd_context & ctx, ptr_vector &cnsts, @@ -82,6 +84,10 @@ static void show_interpolant_and_maybe_check(cmd_context & ctx, ctx.m().dec_ref(interps[i]); } + interp_params itp_params(m_params); + if(itp_params.profile()) + profiling::print(ctx.regular_stream()); + } static void check_can_interpolate(cmd_context & ctx){ diff --git a/src/interp/iz3checker.cpp b/src/interp/iz3checker.cpp index b30e52d38..7792c2401 100755 --- a/src/interp/iz3checker.cpp +++ b/src/interp/iz3checker.cpp @@ -96,6 +96,19 @@ struct iz3checker : iz3base { lbool result = s->check_sat(0,0); if(result != l_false){ err << "interpolant " << i << " is incorrect"; + + s->pop(1); + for(unsigned j = 0; j < theory.size(); j++) + s->assert_expr(to_expr(theory[j].raw())); + for(unsigned j = 0; j < cnsts.size(); j++) + if(in_range(j,range_downward(i))) + s->assert_expr(to_expr(cnsts[j].raw())); + if(i != num-1) + s->assert_expr(to_expr(mk_not(itp[i]).raw())); + lbool result = s->check_sat(0,0); + if(result != l_false) + err << "interpolant " << i << " is not implied by its downeard closurn"; + return false; } s->pop(1); diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index ac6970231..6767deacf 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -391,7 +391,9 @@ lbool iz3interpolate(ast_manager &_m_manager, iz3mgr::ast _tree = itp.cook(tree); std::vector _cnsts; itp.assert_conjuncts(s,_cnsts,_tree); + profiling::timer_start("solving"); lbool res = s.check_sat(0,0); + profiling::timer_stop("solving"); if(res == l_false){ ast *proof = s.get_proof(); iz3mgr::ast _proof = itp.cook(proof); diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 1b91fd521..bd82d235a 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -536,6 +536,56 @@ void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& r } rats[i-1] = r; } + if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them + for(unsigned i = 1; i < rats.size(); i++){ + if(!rats[i].is_neg()) + throw "Bad Farkas coefficients"; + rats[i] = -rats[i]; + } + } + extract_lcd(rats); +} + +void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector& coeffs){ + std::vector rats; + get_assign_bounds_rule_coeffs(proof,rats); + coeffs.resize(rats.size()); + for(unsigned i = 0; i < rats.size(); i++){ + coeffs[i] = make_int(rats[i]); + } +} + +void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats){ + symb s = sym(proof); + int numps = s->get_num_parameters(); + rats.resize(numps-1); + rats[0] = rational(1); + ast conseq = arg(conc(proof),0); + opr conseq_o = is_not(conseq) ? op(arg(conseq,0)) : op(conseq); + bool conseq_neg = is_not(conseq) ? (conseq_o == Leq || conseq_o == Lt) : (conseq_o == Geq || conseq_o == Gt); + for(int i = 2; i < numps; i++){ + rational r; + bool ok = s->get_parameter(i).is_rational(r); + if(!ok) + throw "Bad Farkas coefficient"; + { + ast con = conc(prem(proof,i-2)); + ast temp = make_real(r); // for debugging + opr o = is_not(con) ? op(arg(con,0)) : op(con); + if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) + r = -r; + if(conseq_neg) + r = -r; + } + rats[i-1] = r; + } + if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them + for(unsigned i = 1; i < rats.size(); i++){ + if(!rats[i].is_neg()) + throw "Bad Farkas coefficients"; + rats[i] = -rats[i]; + } + } extract_lcd(rats); } diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index a648c3f84..213c23e7e 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -413,6 +413,10 @@ class iz3mgr { void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); + void get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats); + + void get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats); + bool is_true(ast t){ return op(t) == True; } diff --git a/src/interp/iz3profiling.cpp b/src/interp/iz3profiling.cpp index f18866f30..262254271 100755 --- a/src/interp/iz3profiling.cpp +++ b/src/interp/iz3profiling.cpp @@ -24,18 +24,26 @@ Revision History: #include #include #include +#include "stopwatch.h" // FIXME fill in these stubs -#define clock_t int +#define clock_t double -static clock_t current_time(){ - return 0; +static double current_time() +{ + static stopwatch sw; + static bool started = false; + if(!started){ + sw.start(); + started = true; + } + return sw.get_current_seconds(); } static void output_time(std::ostream &os, clock_t time){ - os << ((double)time)/1000; + os << time; } diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index d786f95ab..3bf4dd620 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -23,6 +23,7 @@ Revision History: using namespace stl_ext; #endif +// #define INVARIANT_CHECKING class iz3proof_itp_impl : public iz3proof_itp { @@ -124,11 +125,13 @@ class iz3proof_itp_impl : public iz3proof_itp { return it->second; ast &res = placeholders[t]; res = mk_fresh_constant("@p",get_type(t)); +#if 0 std::cout << "placeholder "; print_expr(std::cout,res); std::cout << " = "; print_expr(std::cout,t); std::cout << std::endl; +#endif return res; } @@ -188,62 +191,15 @@ class iz3proof_itp_impl : public iz3proof_itp { /* the mixed case is a bit complicated */ - return resolve_arith(pivot,conc,premise1,premise2); - } - -#if 0 - /* Handles the case of resolution on a mixed equality atom. */ - - ast resolve_equality(const ast &pivot, const std::vector &conc, node premise1, node premise2){ - ast atom = get_lit_atom(pivot); - - /* Get the A term in the mixed equality. */ - ast A_term = arg(atom,0); - if(get_term_type(A_term) != LitA) - A_term = arg(atom,1); - - /* Resolve it out. */ - hash_map memo; - // ast neg_pivot_placeholder = get_placeholder(mk_not(atom)); - ast neg_pivot_placeholder = mk_not(atom); - ast A_term_placeholder = get_placeholder(atom); - if(op(pivot) != Not) - std::swap(premise1,premise2); - return resolve_equality_rec(memo, neg_pivot_placeholder, premise1, A_term_placeholder, premise2); - } - - ast resolve_equality_rec(hash_map &memo, const ast &neg_pivot_placeholder, const ast &premise1, - const ast &A_term, const ast &premise2){ - ast &res = memo[premise1]; - if(!res.null()) - return res; - - if(op(premise1) == And - && num_args(premise1) == 2 - && arg(premise1,1) == neg_pivot_placeholder){ - ast common_term = arg(arg(premise1,0),1); - res = subst_term_and_simp(A_term,common_term,premise2); - } - else { - switch(op(premise1)){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(premise1); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - args[i] = resolve_equality_rec(memo, neg_pivot_placeholder, arg(premise1,i), A_term, premise2); - ast foo = premise1; // get rid of const - res = clone(foo,args); - break; - } - default: - res = premise1; - } - } + static int non_local_count = 0; + ast res = resolve_arith(pivot,conc,premise1,premise2); +#ifdef INVARIANT_CHECKING + check_contra(conc,res); +#endif + non_local_count++; return res; } -#endif + /* Handles the case of resolution on a mixed arith atom. */ @@ -285,25 +241,6 @@ class iz3proof_itp_impl : public iz3proof_itp { return make(sum_op,sum_sides[0],sum_sides[1]); } -#if 0 - ast resolve_farkas(const ast &pivot1, const ast &conj1, const ast &implicant1, - const ast &pivot2, const ast &conj2, const ast &implicant2){ - std::vector resolvent; - ast coeff1 = get_farkas_coeff(pivot1); - ast coeff2 = get_farkas_coeff(pivot2); - ast s1 = resolve_arith_placeholders(pivot2,conj2,arg(conj1,0)); - ast s2 = resolve_arith_placeholders(pivot1,conj1,arg(conj2,0)); - resolvent.push_back(sum_ineq(coeff2,s1,coeff1,s2)); - collect_farkas_resolvents(pivot1,coeff2,conj1,resolvent); - collect_farkas_resolvents(pivot2,coeff1,conj2,resolvent); - ast res = make(And,resolvent); - if(implicant1.null() && implicant2.null()) - return res; - ast i1 = implicant1.null() ? mk_false() : resolve_arith_placeholders(pivot2,conj2,implicant1); - ast i2 = implicant2.null() ? mk_false() : resolve_arith_placeholders(pivot1,conj1,implicant2); - return make(Implies,res,my_or(i1,i2)); - } -#endif void collect_contra_resolvents(int from, const ast &pivot1, const ast &pivot, const ast &conj, std::vector &res){ int nargs = num_args(conj); @@ -349,14 +286,6 @@ class iz3proof_itp_impl : public iz3proof_itp { ast resolve_contra(const ast &pivot1, const ast &conj1, const ast &pivot2, const ast &conj2){ -#if 0 - int nargs = num_args(conj1); - for(int i = 0; i < nargs; i++){ - ast foo = arg(conj1,i); - if(!(foo == pivot1) && is_negative_equality(arg(foo,1))) - return resolve_contra_nf(pivot2, conj2, pivot1, conj1); - } -#endif if(arg(pivot1,0) != no_proof) return resolve_contra_nf(pivot1, conj1, pivot2, conj2); if(arg(pivot2,0) != no_proof) @@ -410,6 +339,7 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } + ast resolve_arith_rec1(hash_map &memo, const ast &neg_pivot_lit, const ast &itp1, const ast &itp2){ ast &res = memo[itp1]; if(!res.null()) @@ -439,26 +369,40 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } -#if 0 - ast resolve_arith_placeholders(const ast &pivot1, const ast &conj1, const ast &itp2){ - ast coeff = arg(pivot1,0); - ast val = arg(arg(conj1,0),1); - if(op(conj1)==Lt) - val = make(Sub,val,epsilon); // represent x < c by x <= c - epsilon - coeff = z3_simplify(coeff); - ast soln = mk_idiv(val,coeff); - int nargs = num_args(conj1); - for(int i = 1; i < nargs; i++){ - ast c = arg(conj1,i); - if(!(c == pivot1)){ - soln = make(Plus,soln,get_placeholder(arg(c,1))); + void check_contra(hash_set &memo, hash_set &neg_lits, const ast &foo){ + if(memo.find(foo) != memo.end()) + return; + memo.insert(foo); + if(op(foo) == Uninterpreted && sym(foo) == contra){ + ast neg_lit = arg(foo,1); + if(!is_false(neg_lit) && neg_lits.find(neg_lit) == neg_lits.end()) + throw "lost a literal"; + return; + } + else { + switch(op(foo)){ + case Or: + case And: + case Implies: { + unsigned nargs = num_args(foo); + std::vector args; args.resize(nargs); + for(unsigned i = 0; i < nargs; i++) + check_contra(memo, neg_lits, arg(foo,i)); + break; + } + default: break; } } - ast pl = get_placeholder(mk_not(arg(pivot1,1))); - ast res = subst_term_and_simp(pl,soln,itp2); - return res; } -#endif + + void check_contra(const std::vector &neg_lits, const ast &foo){ + hash_set memo; + hash_set neg_lits_set; + for(unsigned i = 0; i < neg_lits.size(); i++) + if(get_term_type(neg_lits[i]) == LitMixed) + neg_lits_set.insert(mk_not(neg_lits[i])); + check_contra(memo,neg_lits_set,foo); + } hash_map subst_memo; // memo of subst function @@ -681,7 +625,8 @@ class iz3proof_itp_impl : public iz3proof_itp { ast cond = mk_true(); ast equa = sep_cond(arg(pf,0),cond); if(is_equivrel_chain(equa)){ - ast ineqs= chain_ineqs(LitA,equa); + ast lhs,rhs; eq_from_ineq(arg(neg_equality,0),lhs,rhs); // get inequality we need to prove + ast ineqs= chain_ineqs(LitA,equa,lhs,rhs); // chain must be from lhs to rhs cond = my_and(cond,chain_conditions(LitA,equa)); ast Bconds = chain_conditions(LitB,equa); if(is_true(Bconds) && op(ineqs) != And) @@ -748,97 +693,63 @@ class iz3proof_itp_impl : public iz3proof_itp { throw cannot_simplify(); } -#if 0 - ast simplify_modpon(const std::vector &args){ - if(op(args[1]) == True){ - ast cond = mk_true(); - ast P = sep_cond(args[0],cond); - ast notQ = sep_cond(args[2],cond); - ast Q = mk_not(notQ); - ast d = op(notQ) == True ? P : op(P) == True ? notQ : mk_not(delta(P,Q)); - return my_implies(cond,d); - } - else { - ast cond = mk_true(); - ast P = sep_cond(args[0],cond); - ast PeqQ = sep_cond(args[1],cond); - ast Q2 = sep_cond(args[2],cond); - ast P1 = arg(PeqQ,0); - ast Q1 = arg(PeqQ,1); - if(op(P) == True){ - if(is_equivrel(P1) && is_equivrel(Q1)){ // special case of transitivity - if(arg(P1,0) == arg(Q1,0)) - if(op(args[2]) == True) - return my_implies(cond,make(op(P1),arg(P1,1),arg(Q1,1))); - } - if(op(args[2]) == True) - throw cannot_simplify(); // return my_implies(cond,make(rewrite(P1,Q1))); - ast A_rewrites = mk_true(); - ast foo = commute_rewrites(P1,Q1,mk_not(Q2),A_rewrites); - return my_implies(cond,my_implies(my_implies(A_rewrites,foo),mk_false())); - } - else if(op(args[2]) == True){ - ast P2 = apply_common_rewrites(P,P,P1,cond); - ast A_rewrites = mk_true(); - if(is_equivrel(P2)){ - try { - ast foo = commute_rewrites(arg(P2,0),arg(P2,1),arg(Q1,1),A_rewrites); - ast P3 = make(op(P1),arg(P1,0),foo); - if(P3 == P2) - return my_implies(cond,Q1); - // return my_implies(cond,make(rewrite,my_implies(A_rewrites,P3),Q1)); - } - catch(const rewrites_failed &){ - std::cout << "foo!\n"; - } - } - ast Q2 = apply_all_rewrites(P2,P1,Q1); - return my_implies(cond,Q2); - } - } - throw cannot_simplify(); - } -#else ast simplify_modpon_fwd(const std::vector &args, ast &cond){ ast P = sep_cond(args[0],cond); ast PeqQ = sep_cond(args[1],cond); ast chain; if(is_equivrel_chain(P)){ - ast split[2]; - split_chain(PeqQ,split); - chain = reverse_chain(split[0]); - chain = concat_rewrite_chain(chain,P); - chain = concat_rewrite_chain(chain,split[1]); + try { + ast split[2]; + split_chain(PeqQ,split); + chain = reverse_chain(split[0]); + chain = concat_rewrite_chain(chain,P); + chain = concat_rewrite_chain(chain,split[1]); + } + catch(const cannot_split &){ + static int this_count = 0; + this_count++; + ast tail, pref = get_head_chain(PeqQ,tail,false); // pref is x=y, tail is x=y -> x'=y' + ast split[2]; split_chain(tail,split); // rewrites from x to x' and y to y' + ast head = chain_last(pref); + ast prem = make_rewrite(rewrite_side(head),top_pos,rewrite_cond(head),make(Iff,mk_true(),mk_not(rewrite_lhs(head)))); + ast back_chain = chain_cons(mk_true(),prem); + back_chain = concat_rewrite_chain(back_chain,chain_pos_add(0,reverse_chain(chain_rest(pref)))); + ast cond = contra_chain(back_chain,P); + if(is_rewrite_side(LitA,head)) + cond = mk_not(cond); + ast fwd_rewrite = make_rewrite(rewrite_side(head),top_pos,cond,rewrite_rhs(head)); + P = chain_cons(mk_true(),fwd_rewrite); + chain = reverse_chain(split[0]); + chain = concat_rewrite_chain(chain,P); + chain = concat_rewrite_chain(chain,split[1]); + } } - else // if not an equavalence, must be of form T <-> pred + else // if not an equivalence, must be of form T <-> pred chain = concat_rewrite_chain(P,PeqQ); return chain; } -#if 0 - ast simplify_modpon(const std::vector &args){ - ast cond = mk_true(); - ast chain = simplify_modpon_fwd(args,cond); - ast Q2 = sep_cond(args[2],cond); - ast interp; - if(is_equivrel_chain(Q2)){ - chain = concat_rewrite_chain(chain,chain_pos_add(0,chain_pos_add(0,Q2))); - interp = my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); - } - else if(is_rewrite_side(LitB,chain_last(Q2))) - interp = my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); - else - interp = my_and(chain_conditions(LitB,chain),my_implies(chain_conditions(LitA,chain),mk_not(chain_formulas(LitB,chain)))); - return my_implies(cond,interp); - } -#endif - /* Given a chain rewrite chain deriving not P and a rewrite chain deriving P, return an interpolant. */ ast contra_chain(const ast &neg_chain, const ast &pos_chain){ // equality is a special case. we use the derivation of x=y to rewrite not(x=y) to not(y=y) if(is_equivrel_chain(pos_chain)){ - ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,chain_pos_add(0,pos_chain))); - return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); + ast tail, pref = get_head_chain(neg_chain,tail); // pref is not(x=y), tail is not(x,y) -> not(x',y') + ast split[2]; split_chain(down_chain(tail),split); // rewrites from x to x' and y to y' + ast chain = split[0]; + chain = concat_rewrite_chain(chain,pos_chain); // rewrites from x to y' + chain = concat_rewrite_chain(chain,reverse_chain(split[1])); // rewrites from x to y + chain = concat_rewrite_chain(pref,chain_pos_add(0,chain_pos_add(0,chain))); // rewrites t -> not(y=y) + ast head = chain_last(pref); + if(is_rewrite_side(LitB,head)){ + ast condition = chain_conditions(LitB,chain); + return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),condition); + } + else { + ast condition = chain_conditions(LitA,chain); + return my_and(chain_conditions(LitB,chain),my_implies(condition,mk_not(chain_formulas(LitB,chain)))); + } + // ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,chain_pos_add(0,pos_chain))); + // return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); } // otherwise, we reverse the derivation of t = P and use it to rewrite not(P) to not(t) ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,reverse_chain(pos_chain))); @@ -853,7 +764,6 @@ class iz3proof_itp_impl : public iz3proof_itp { return my_implies(cond,interp); } -#endif bool is_equivrel(const ast &p){ opr o = op(p); @@ -1115,9 +1025,24 @@ class iz3proof_itp_impl : public iz3proof_itp { throw "bad term position!"; } + ast diff_chain(LitType t, const ast &pos, const ast &x, const ast &y, const ast &prefix){ + int nargs = num_args(x); + if(x == y) return prefix; + if(sym(x) == sym(y) && nargs == num_args(y)){ + ast res = prefix; + for(int i = 0; i < nargs; i++) + res = diff_chain(t,pos_add(i,pos),arg(x,i),arg(y,i),res); + return res; + } + return chain_cons(prefix,make_rewrite(t,pos,mk_true(),make_equiv_rel(x,y))); + } /* operations on rewrites */ ast make_rewrite(LitType t, const ast &pos, const ast &cond, const ast &equality){ +#if 0 + if(pos == top_pos && op(equality) == Iff && !is_true(arg(equality,0))) + throw "bad rewrite"; +#endif return make(t == LitA ? rewrite_A : rewrite_B, pos, cond, equality); } @@ -1189,6 +1114,10 @@ class iz3proof_itp_impl : public iz3proof_itp { return sym(rew) == rewrite_B; } + LitType rewrite_side(const ast &rew){ + return (sym(rew) == rewrite_A) ? LitA : LitB; + } + ast rewrite_to_formula(const ast &rew){ return my_implies(arg(rew,1),arg(rew,2)); } @@ -1312,6 +1241,24 @@ class iz3proof_itp_impl : public iz3proof_itp { return is_negation_chain(rest); } + // split a rewrite chain into head and tail at last top-level rewrite + ast get_head_chain(const ast &chain, ast &tail, bool is_not = true){ + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast pos = rewrite_pos(last); + if(pos == top_pos || (is_not && arg(pos,1) == top_pos)){ + tail = mk_true(); + return chain; + } + if(is_true(rest)) + throw "bad rewrite chain"; + ast head = get_head_chain(rest,tail,is_not); + tail = chain_cons(tail,last); + return head; + } + + struct cannot_split {}; + /** Split a chain of rewrites two chains, operating on positions 0 and 1. Fail if any rewrite in the chain operates on top position. */ void split_chain_rec(const ast &chain, ast *res){ @@ -1321,11 +1268,14 @@ class iz3proof_itp_impl : public iz3proof_itp { ast rest = chain_rest(chain); split_chain_rec(rest,res); ast pos = rewrite_pos(last); - if(pos == top_pos) - throw "bad rewrite chain"; + if(pos == top_pos){ + if(rewrite_lhs(last) == rewrite_rhs(last)) + return; // skip if it's a noop + throw cannot_split(); + } int arg = pos_arg(pos); if(arg<0 || arg > 1) - throw "bad position!"; + throw cannot_split(); res[arg] = chain_cons(res[arg],rewrite_up(last)); } @@ -1334,6 +1284,12 @@ class iz3proof_itp_impl : public iz3proof_itp { split_chain_rec(chain,res); } + ast down_chain(const ast &chain){ + ast split[2]; + split_chain(chain,split); + return split[0]; + } + ast chain_conditions(LitType t, const ast &chain){ if(is_true(chain)) return mk_true(); @@ -1356,19 +1312,51 @@ class iz3proof_itp_impl : public iz3proof_itp { return cond; } - ast chain_ineqs(LitType t, const ast &chain){ - if(is_true(chain)) - return mk_true(); + + ast chain_ineqs(LitType t, const ast &chain, const ast &lhs, const ast &rhs){ + if(is_true(chain)){ + if(lhs != rhs) + throw "bad ineq inference"; + return make(Leq,make_int(rational(0)),make_int(rational(0))); + } ast last = chain_last(chain); ast rest = chain_rest(chain); - ast cond = chain_ineqs(t,rest); + ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); + ast cond = chain_ineqs(t,rest,lhs,mid); if(is_rewrite_side(t,last)){ - ast equa = rewrite_equ(last); - cond = my_and(cond,z3_simplify(make(Leq,make_int("0"),make(Sub,arg(equa,1),arg(equa,0))))); + ast foo = z3_simplify(make(Leq,make_int("0"),make(Sub,mid,rhs))); + if(is_true(cond)) + cond = foo; + else { + linear_comb(cond,make_int(rational(1)),foo); + cond = simplify_ineq(cond); + } } return cond; } + ast ineq_to_lhs(const ast &ineq){ + ast s = make(Leq,make_int(rational(0)),make_int(rational(0))); + linear_comb(s,make_int(rational(1)),ineq); + return simplify_ineq(s); + } + + void eq_from_ineq(const ast &ineq, ast &lhs, ast &rhs){ + ast s = ineq_to_lhs(ineq); + ast srhs = arg(s,1); + if(op(srhs) == Plus && num_args(srhs) == 2){ + lhs = arg(srhs,0); + rhs = arg(srhs,1); + if(op(lhs) == Times) + std::swap(lhs,rhs); + if(op(rhs) == Times){ + rhs = arg(rhs,1); + return; + } + } + throw "bad ineq"; + } + ast chain_pos_add(int arg, const ast &chain){ if(is_true(chain)) return mk_true(); @@ -1395,6 +1383,10 @@ class iz3proof_itp_impl : public iz3proof_itp { ast make_local_rewrite(LitType t, const ast &p){ ast rew = is_equivrel(p) ? p : make(Iff,mk_true(),p); +#if 0 + if(op(rew) == Iff && !is_true(arg(rew,0))) + return diff_chain(t,top_pos,arg(rew,0),arg(rew,1), mk_true()); +#endif return chain_cons(mk_true(),make_rewrite(t, top_pos, mk_true(), rew)); } @@ -1466,7 +1458,7 @@ class iz3proof_itp_impl : public iz3proof_itp { } /* Resolve it with the premises */ - std::vector conc; conc.push_back(q); conc.push_back(mk_not(make(Equal,p,q))); + std::vector conc; conc.push_back(q); conc.push_back(mk_not(p_eq_q)); itp = make_resolution(p,conc,itp,prem1); conc.pop_back(); itp = make_resolution(p_eq_q,conc,itp,prem2); @@ -1555,8 +1547,6 @@ class iz3proof_itp_impl : public iz3proof_itp { ast x = arg(con,0); ast y = arg(con,1); ast p = make(op(con),y,x); - if(p == premcon) - std::cout << "ok\n"; if(get_term_type(con) != LitMixed) return prem; // symmetry shmymmetry... #if 0 @@ -1581,82 +1571,27 @@ class iz3proof_itp_impl : public iz3proof_itp { return itp; } + ast make_equiv_rel(const ast &x, const ast &y){ + if(is_bool_type(get_type(x))) + return make(Iff,x,y); + return make(Equal,x,y); + } + /** Make a transitivity node. This takes derivations of |- x = y and |- y = z produces | x = z */ virtual node make_transitivity(const ast &x, const ast &y, const ast &z, node prem1, node prem2){ /* Interpolate the axiom x=y,y=z,-> x=z */ - ast p = make(Equal,x,y); - ast q = make(Equal,y,z); - ast r = make(Equal,x,z); + ast p = make_equiv_rel(x,y); + ast q = make_equiv_rel(y,z); + ast r = make_equiv_rel(x,z); ast equiv = make(Iff,p,r); ast itp; itp = make_congruence(q,equiv,prem2); itp = make_mp(equiv,prem1,itp); -#if 0 - if(get_term_type(p) == LitA){ - if(get_term_type(q) == LitA){ - if(get_term_type(r) == LitA) - itp = mk_false(); - else - itp = r; - } - else { - if(get_term_type(r) == LitA) - itp = mk_not(q); - else { - if(get_term_type(r) == LitB) - itp = make(Equal,x,get_placeholder(q)); - else { - ast mid = (get_term_type(y) == LitA) ? get_placeholder(q) : y; - itp = make(And,make(Equal,x,mid),mk_not(r)); - } - } - } - } - else { - if(get_term_type(q) == LitA){ - if(get_term_type(r) == LitA) - itp = mk_not(p); - else { - if(get_term_type(r) == LitB) - itp = make(Equal,get_placeholder(p),z); - else { - ast mid = (get_term_type(y) == LitA) ? get_placeholder(p) : y; - itp = make(And,make(Equal,z,mid),mk_not(r)); - } - } - } - else { - if(get_term_type(r) == LitA){ - ast xr = (get_term_type(x) == LitA) ? get_placeholder(p) : x; - ast zr = (get_term_type(z) == LitA) ? get_placeholder(q) : z; - itp = mk_not(make(Equal,xr,zr)); - } - else { - LitType xt = get_term_type(x); - LitType zt = get_term_type(z); - if(xt == LitA && zt == LitB) - itp = make(And,make(Equal,x,get_placeholder(p)),mk_not(r)); - else if(zt == LitA && xt == LitB) - itp = make(And,make(Equal,z,get_placeholder(q)),mk_not(r)); - else - itp = mk_true(); - } - } - } - - /* Resolve it with the premises */ - std::vector conc; conc.push_back(r); conc.push_back(mk_not(q)); - itp = make_resolution(p,conc,itp,prem1); - conc.pop_back(); - itp = make_resolution(q,conc,itp,prem2); -#endif - - return itp; } @@ -1753,19 +1688,6 @@ class iz3proof_itp_impl : public iz3proof_itp { ast bar = make(cong,foo,make_int(rational(pos)),get_placeholder(mk_not(con))); conjs.push_back(mk_not(con)); return make_contra_node(bar,conjs); -#if 0 - ast A_term = x; - ast f_A_term = arg(con,0); - if(get_term_type(y) == LitA){ - A_term = y; - f_A_term = arg(con,1); - } - - - ast res = make(Equal,f_A_term,subst_in_arg_pos(pos,get_placeholder(make(Equal,x,y)),f_A_term)); - res = make(And,res,mk_not(con)); - return res; -#endif } ast subst_in_arg_pos(int pos, ast term, ast app){ @@ -1901,19 +1823,6 @@ class iz3proof_itp_impl : public iz3proof_itp { itp = mk_true(); break; default: { // mixed equality -#if 0 - ast mid = get_placeholder(make(Equal,x,y)); - if(get_term_type(y) == LitA){ - std::swap(x,y); - mid = make(Sub,x,mid); - } - else { - mid = make(Sub,mid,x); - } - ast zleqmid = make(Leq,make_int("0"),mid); - ast fark = make(contra,make_int("1"),mk_not(xleqy)); - itp = make(And,zleqmid,fark); -#endif std::vector conjs; conjs.resize(2); conjs[0] = make(Equal,x,y); conjs[1] = mk_not(xleqy); @@ -1942,12 +1851,6 @@ class iz3proof_itp_impl : public iz3proof_itp { conjs[1] = mk_not(con); itp = make(sum,get_placeholder(conjs[0]),d,get_placeholder(conjs[1])); itp = make_contra_node(itp,conjs); -#if 0 - ast t = arg(tleqc,0); - ast c = arg(tleqc,1); - ast thing = make(contra,make_int("1"),mk_not(con)); - itp = make(And,make(Leq,make_int("0"),make(Idiv,get_placeholder(tleqc),d)),thing); -#endif } } std::vector conc; conc.push_back(con); @@ -2073,7 +1976,7 @@ class iz3proof_itp_impl : public iz3proof_itp { /* Return an interpolant from a proof of false */ ast interpolate(const node &pf){ // proof of false must be a formula, with quantified symbols - return add_quants(pf); + return add_quants(z3_simplify(pf)); } ast resolve_with_quantifier(const ast &pivot1, const ast &conj1, diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 2be7c22e5..26a1cc8fc 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -626,7 +626,7 @@ public: if(!is_literal_or_lit_iff(lit)){ if(is_not(lit)) std::cout << "~"; std::cout << "["; - print_expr(std::cout,abslit); + // print_expr(std::cout,abslit); std::cout << "]"; } else @@ -960,6 +960,52 @@ public: return res; } + ast AssignBoundsRule2Farkas(const ast &proof, const ast &con, std::vector prems){ + std::vector farkas_coeffs; + get_assign_bounds_rule_coeffs(proof,farkas_coeffs); + std::vector lits; + int nargs = num_prems(proof)+1; + if(nargs != (int)(farkas_coeffs.size())) + throw "bad assign-bounds theory lemma"; + std::vector my_coeffs; + std::vector my_cons; + for(int i = 1; i < nargs; i++){ + my_cons.push_back(conc(prem(proof,i-1))); + my_coeffs.push_back(farkas_coeffs[i]); + } + ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons)); + std::vector my_hyps; + for(int i = 1; i < nargs; i++) + my_hyps.push_back(prems[i-1]); + my_cons.push_back(mk_not(farkas_con)); + my_coeffs.push_back(make_int("1")); + my_hyps.push_back(iproof->make_hypothesis(mk_not(farkas_con))); + ast res = iproof->make_farkas(mk_false(),my_hyps,my_cons,my_coeffs); + res = iproof->make_cut_rule(farkas_con,farkas_coeffs[0],conc(proof),res); + return res; + } + + Iproof::node RewriteClause(Iproof::node clause, const ast &rew){ + if(pr(rew) == PR_MONOTONICITY){ + int nequivs = num_prems(rew); + for(int i = 0; i < nequivs; i++){ + Iproof::node equiv_pf = translate_main(prem(rew,i),false); + ast equiv = conc(prem(rew,i)); + clause = iproof->make_mp(equiv,clause,equiv_pf); + } + return clause; + } + if(pr(rew) == PR_TRANSITIVITY){ + clause = RewriteClause(clause,prem(rew,0)); + clause = RewriteClause(clause,prem(rew,1)); + return clause; + } + if(pr(rew) == PR_REWRITE){ + return clause; // just hope the rewrite does nothing! + } + throw unsupported(); + } + // translate a Z3 proof term into interpolating proof system Iproof::node translate_main(ast proof, bool expect_clause = true){ @@ -1015,11 +1061,19 @@ public: else lits.push_back(from_ast(con)); - // special case + // pattern match some idioms if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST && pr(prem(proof,1)) == PR_REWRITE ) { res = iproof->make_axiom(lits); return res; } + if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or){ + Iproof::node clause = translate_main(prem(proof,0),true); + res = RewriteClause(clause,prem(proof,1)); + return res; + } + + if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or) + std::cout << "foo!\n"; // translate all the premises std::vector args(nprems); @@ -1098,7 +1152,10 @@ public: break; } case AssignBoundsKind: { - res = AssignBounds2Farkas(proof,conc(proof)); + if(args.size() > 0) + res = AssignBoundsRule2Farkas(proof, conc(proof), args); + else + res = AssignBounds2Farkas(proof,conc(proof)); break; } default: From 7ca6c744fd18af79715e4710235c3247366cfe64 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 1 Nov 2013 15:58:59 -0700 Subject: [PATCH 192/509] added binary interpolation --- src/interp/iz3base.cpp | 20 +++++++++++++ src/interp/iz3base.h | 20 +++++++++++++ src/interp/iz3interp.cpp | 45 ++++++++++++++++++++++++++++-- src/interp/iz3translate.cpp | 33 ++++++++++++++-------- src/interp/iz3translate.h | 8 +++--- src/interp/iz3translate_direct.cpp | 17 +++-------- 6 files changed, 113 insertions(+), 30 deletions(-) diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 0fdce156a..6ea8c2d7f 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -201,6 +201,26 @@ void iz3base::initialize(const std::vector &_parts, const std::vector add_frame_range(SHRT_MIN, _theory[i]); add_frame_range(SHRT_MAX, _theory[i]); } + for(unsigned i = 0; i < cnsts.size(); i++) + frame_map[cnsts[i]] = i; + for(unsigned i = 0; i < theory.size(); i++) + frame_map[theory[i]] = INT_MAX; +} + +void iz3base::initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory){ + cnsts.resize(_parts.size()); + theory = _theory; + for(unsigned i = 0; i < _parts.size(); i++) + for(unsigned j = 0; j < _parts[i].size(); j++){ + cnsts[i] = make(And,_parts[i]); + add_frame_range(i, _parts[i][j]); + frame_map[_parts[i][j]] = i; + } + for(unsigned i = 0; i < _theory.size(); i++){ + add_frame_range(SHRT_MIN, _theory[i]); + add_frame_range(SHRT_MAX, _theory[i]); + frame_map[theory[i]] = INT_MAX; + } } void iz3base::check_interp(const std::vector &itps, std::vector &theory){ diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index da0a3ce4a..0e57cf5e3 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -74,6 +74,15 @@ class iz3base : public iz3mgr, public scopes { weak = false; } + iz3base(const iz3mgr& other, + const std::vector > &_cnsts, + const std::vector &_parents, + const std::vector &_theory) + : iz3mgr(other), scopes(_parents) { + initialize(_cnsts,_parents,_theory); + weak = false; + } + iz3base(const iz3mgr& other) : iz3mgr(other), scopes() { weak = false; @@ -107,6 +116,14 @@ class iz3base : public iz3mgr, public scopes { return make(And,cs); } + int frame_of_assertion(const ast &ass){ + stl_ext::hash_map::iterator it = frame_map.find(ass); + if(it == frame_map.end()) + throw "unknown assertion"; + return it->second; + } + + void to_parents_vec_representation(const std::vector &_cnsts, const ast &tree, std::vector &cnsts, @@ -132,12 +149,15 @@ class iz3base : public iz3mgr, public scopes { stl_ext::hash_map sym_range_hash; stl_ext::hash_map ast_ranges_hash; stl_ext::hash_map simplify_memo; + stl_ext::hash_map frame_map; // map assertions to frames + int frames; // number of frames void add_frame_range(int frame, ast t); void initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory); + void initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory); bool is_literal(ast n); void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 6767deacf..2ef36f4e2 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -232,8 +232,13 @@ public: iz3secondary *sp = iz3foci::create(this,num,parents_vec.empty()?0:&parents_vec[0]); sp_killer.set(sp); // kill this on exit +#define BINARY_INTERPOLATION +#ifndef BINARY_INTERPOLATION // create a translator - iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec,parents_vec,theory); + std::vector > cnsts_vec_vec(cnsts_vec.size()); + for(unsigned i = 0; i < cnsts_vec.size(); i++) + cnsts_vec_vec[i].push_back(cnsts_vec[i]); + iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec_vec,parents_vec,theory); tr_killer.set(tr); // set the translation options, if needed @@ -258,7 +263,43 @@ public: interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(i)); } profiling::timer_stop("Proof interpolation"); - +#else + iz3base the_base(*this,cnsts_vec,parents_vec,theory); + + profiling::timer_stop("Interpolation prep"); + + for(int i = 0; i < num-1; i++){ + range rng = the_base.range_downward(i); + std::vector > cnsts_vec_vec(2); + for(unsigned j = 0; j < cnsts_vec.size(); j++){ + bool is_A = the_base.in_range(j,rng); + cnsts_vec_vec[is_A ? 0 : 1].push_back(cnsts_vec[j]); + } + + killme tr_killer_i; + iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec_vec,std::vector(),theory); + tr_killer_i.set(tr); + + // set the translation options, if needed + if(options) + for(hash_map::iterator it = options->map.begin(), en = options->map.end(); it != en; ++it) + tr->set_option(it->first, it->second); + + // create a proof object to hold the translation + iz3proof pf(tr); + + // translate into an interpolatable proof + profiling::timer_start("Proof translation"); + tr->translate(proof,pf); + profiling::timer_stop("Proof translation"); + + // translate the proof into interpolants + profiling::timer_start("Proof interpolation"); + interps_vec[i] = pf.interpolate(tr->range_downward(0),tr->weak_mode()); + interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(0)); + profiling::timer_stop("Proof interpolation"); + } +#endif // put back in the removed frames fr.fix_interpolants(interps_vec); diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 26a1cc8fc..ff5d24228 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -68,7 +68,6 @@ public: typedef std::pair, hash_map > AstToIpf; AstToIpf translation; // Z3 proof nodes to Iproof nodes - AstToInt frame_map; // map assertions to frames int frames; // number of frames typedef std::set AstSet; @@ -98,6 +97,12 @@ public: #define from_ast(x) (x) + // #define NEW_LOCALITY + +#ifdef NEW_LOCALITY + range rng; // the range of frames in the "A" part of the interpolant +#endif + // determine locality of a proof term // return frame of derivation if local, or -1 if not // result INT_MAX means the proof term is a tautology @@ -110,9 +115,13 @@ public: if(!bar.second) return res; if(pr(proof) == PR_ASSERTED){ ast ass = conc(proof); - AstToInt::iterator it = frame_map.find(ass); - assert(it != frame_map.end()); - res = it->second; + res = frame_of_assertion(ass); +#ifdef NEW_LOCALITY + if(in_range(res,rng)) + res = range_max(rng); + else + res = frames-1; +#endif } else { unsigned nprems = num_prems(proof); @@ -166,6 +175,7 @@ public: return res; } + AstSet &get_hyps(ast proof){ std::pair foo(proof,AstSet()); std::pair bar = hyp_map.insert(foo); @@ -279,7 +289,7 @@ public: } } - +#if 0 // clear the localization variables void clear_localization(){ localization_vars.clear(); @@ -534,6 +544,7 @@ public: if(hi >= frames) return frames - 1; return hi; } +#endif int num_lits(ast ast){ opr dk = op(ast); @@ -1197,6 +1208,10 @@ public: iz3proof::node translate(ast proof, iz3proof &dst){ std::vector itps; for(int i = 0; i < frames -1; i++){ +#ifdef NEW_LOCALITY + rng = range_downward(i); + locality.clear(); +#endif iproof = iz3proof_itp::create(this,range_downward(i),weak_mode()); Iproof::node ipf = translate_main(proof); ast itp = iproof->interpolate(ipf); @@ -1211,15 +1226,11 @@ public: iz3translation_full(iz3mgr &mgr, iz3secondary *_secondary, - const std::vector &cnsts, + const std::vector > &cnsts, const std::vector &parents, const std::vector &theory) : iz3translation(mgr, cnsts, parents, theory) { - for(unsigned i = 0; i < cnsts.size(); i++) - frame_map[cnsts[i]] = i; - for(unsigned i = 0; i < theory.size(); i++) - frame_map[theory[i]] = INT_MAX; frames = cnsts.size(); traced_lit = ast(); } @@ -1235,7 +1246,7 @@ public: iz3translation *iz3translation::create(iz3mgr &mgr, iz3secondary *secondary, - const std::vector &cnsts, + const std::vector > &cnsts, const std::vector &parents, const std::vector &theory){ return new iz3translation_full(mgr,secondary,cnsts,parents,theory); diff --git a/src/interp/iz3translate.h b/src/interp/iz3translate.h index 64e3bdea6..6dc01dfa7 100755 --- a/src/interp/iz3translate.h +++ b/src/interp/iz3translate.h @@ -40,15 +40,15 @@ public: static iz3translation *create(iz3mgr &mgr, iz3secondary *secondary, - const std::vector &frames, + const std::vector > &frames, const std::vector &parents, const std::vector &theory); protected: iz3translation(iz3mgr &mgr, - const std::vector &_cnsts, - const std::vector &_parents, - const std::vector &_theory) + const std::vector > &_cnsts, + const std::vector &_parents, + const std::vector &_theory) : iz3base(mgr,_cnsts,_parents,_theory) {} }; diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index 037cabee9..44c907d1d 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -200,7 +200,6 @@ public: Iproof *iproof; // the interpolating proof we are constructing - AstToInt frame_map; // map assertions to frames int frames; // number of frames typedef std::set AstSet; @@ -250,9 +249,7 @@ public: if(!bar.second) return res; if(pr(proof) == PR_ASSERTED){ ast ass = conc(proof); - AstToInt::iterator it = frame_map.find(ass); - assert(it != frame_map.end()); - res = it->second; + res = frame_of_assertion(ass); } else { unsigned nprems = num_prems(proof); @@ -590,9 +587,7 @@ public: pfrule dk = pr(proof); if(dk == PR_ASSERTED){ ast ass = conc(proof); - AstToInt::iterator it = frame_map.find(ass); - assert(it != frame_map.end()); - frame = it->second; + frame = frame_of_assertion(ass); if(frame >= frames) frame = frames-1; // can happen if a theory fact antes.push_back(std::pair(ass,frame)); return; @@ -1655,16 +1650,12 @@ public: iz3translation_direct(iz3mgr &mgr, iz3secondary *_secondary, - const std::vector &cnsts, + const std::vector > &cnsts, const std::vector &parents, const std::vector &theory) : iz3translation(mgr, cnsts, parents, theory) { secondary = _secondary; - for(unsigned i = 0; i < cnsts.size(); i++) - frame_map[cnsts[i]] = i; - for(unsigned i = 0; i < theory.size(); i++) - frame_map[theory[i]] = INT_MAX; frames = cnsts.size(); traced_lit = ast(); } @@ -1693,7 +1684,7 @@ public: iz3translation *iz3translation::create(iz3mgr &mgr, iz3secondary *secondary, - const std::vector &cnsts, + const std::vector > &cnsts, const std::vector &parents, const std::vector &theory){ return new iz3translation_direct(mgr,secondary,cnsts,parents,theory); From 8b10e1325141cb9af1c3776a36a36c56019b186a Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 4 Nov 2013 11:02:53 -0800 Subject: [PATCH 193/509] fix bug in factor_tactic Signed-off-by: Leonardo de Moura --- src/tactic/arith/factor_tactic.cpp | 58 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/tactic/arith/factor_tactic.cpp b/src/tactic/arith/factor_tactic.cpp index 752f6681d..4eec83037 100644 --- a/src/tactic/arith/factor_tactic.cpp +++ b/src/tactic/arith/factor_tactic.cpp @@ -50,7 +50,7 @@ class factor_tactic : public tactic { return args[0]; return m_util.mk_mul(sz, args); } - + expr * mk_zero_for(expr * arg) { return m_util.mk_numeral(rational(0), m_util.is_int(arg)); } @@ -92,10 +92,10 @@ class factor_tactic : public tactic { return k; } } - + // p1^{2*k1} * p2^{2*k2 + 1} >=< 0 // --> - // (p1^2)*p2 >=<0 + // (p1^2)*p2 >=<0 void mk_comp(decl_kind k, polynomial::factors const & fs, expr_ref & result) { SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE); expr_ref_buffer args(m); @@ -127,7 +127,7 @@ class factor_tactic : public tactic { } } } - + // Strict case // p1^{2*k1} * p2^{2*k2 + 1} >< 0 // --> @@ -158,11 +158,11 @@ class factor_tactic : public tactic { args.push_back(m.mk_app(m_util.get_family_id(), k, mk_mul(odd_factors.size(), odd_factors.c_ptr()), mk_zero_for(odd_factors[0]))); } SASSERT(!args.empty()); - if (args.size() == 1) + if (args.size() == 1) result = args[0]; else if (strict) result = m.mk_and(args.size(), args.c_ptr()); - else + else result = m.mk_or(args.size(), args.c_ptr()); } @@ -173,7 +173,7 @@ class factor_tactic : public tactic { scoped_mpz d2(m_qm); m_expr2poly.to_polynomial(lhs, p1, d1); m_expr2poly.to_polynomial(rhs, p2, d2); - TRACE("factor_tactic_bug", + TRACE("factor_tactic_bug", tout << "lhs: " << mk_ismt2_pp(lhs, m) << "\n"; tout << "p1: " << p1 << "\n"; tout << "d1: " << d1 << "\n"; @@ -195,18 +195,18 @@ class factor_tactic : public tactic { SASSERT(fs.distinct_factors() > 0); TRACE("factor_tactic_bug", tout << "factors:\n"; fs.display(tout); tout << "\n";); if (fs.distinct_factors() == 1 && fs.get_degree(0) == 1) - return BR_FAILED; + return BR_FAILED; if (m.is_eq(f)) { if (m_split_factors) mk_split_eq(fs, result); - else + else mk_eq(fs, result); } else { decl_kind k = f->get_decl_kind(); if (m_qm.is_neg(fs.get_constant())) k = flip(k); - + if (m_split_factors) mk_split_comp(k, fs, result); else @@ -215,10 +215,10 @@ class factor_tactic : public tactic { return BR_DONE; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num != 2) return BR_FAILED; - if (m.is_eq(f) && (m_util.is_arith_expr(args[0]) || m_util.is_arith_expr(args[1]))) + if (m.is_eq(f) && (m_util.is_arith_expr(args[0]) || m_util.is_arith_expr(args[1])) && (!m.is_bool(args[0]))) return factor(f, args[0], args[1], result); if (f->get_family_id() != m_util.get_family_id()) return BR_FAILED; @@ -232,10 +232,10 @@ class factor_tactic : public tactic { return BR_FAILED; } }; - + struct rw : public rewriter_tpl { rw_cfg m_cfg; - + rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { @@ -245,24 +245,24 @@ class factor_tactic : public tactic { struct imp { ast_manager & m; rw m_rw; - + imp(ast_manager & _m, params_ref const & p): m(_m), m_rw(m, p) { } - + void set_cancel(bool f) { m_rw.set_cancel(f); m_rw.cfg().m_pm.set_cancel(f); } - + void updt_params(params_ref const & p) { m_rw.cfg().updt_params(p); } - - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, + + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); @@ -288,7 +288,7 @@ class factor_tactic : public tactic { SASSERT(g->is_well_sorted()); } }; - + imp * m_imp; params_ref m_params; public: @@ -300,7 +300,7 @@ public: virtual tactic * translate(ast_manager & m) { return alloc(factor_tactic, m, m_params); } - + virtual ~factor_tactic() { dealloc(m_imp); } @@ -311,14 +311,14 @@ public: } virtual void collect_param_descrs(param_descrs & r) { - r.insert("split_factors", CPK_BOOL, + r.insert("split_factors", CPK_BOOL, "(default: true) apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0))."); polynomial::factor_params::get_param_descrs(r); } - - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, + + virtual void operator()(goal_ref const & in, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { @@ -331,7 +331,7 @@ public: throw tactic_exception(ex.msg()); } } - + virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = m_imp; From 825b72719ca60e978aef1137699744177199372f Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 4 Nov 2013 11:57:29 -0800 Subject: [PATCH 194/509] fix https://z3.codeplex.com/workitem/62 Signed-off-by: Leonardo de Moura --- src/sat/sat_simplifier.cpp | 142 ++++++++++++------------ src/sat/sat_solver.cpp | 220 ++++++++++++++++++++----------------- 2 files changed, 189 insertions(+), 173 deletions(-) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index ba0c67235..37d41a5fd 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -9,7 +9,7 @@ Abstract: SAT simplification procedures that use a "full" occurrence list: Subsumption, Blocked Clause Removal, Variable Elimination, ... - + Author: @@ -54,21 +54,21 @@ namespace sat { m_use_list[l2.index()].erase(c); } } - + simplifier::simplifier(solver & _s, params_ref const & p): s(_s), m_num_calls(0) { updt_params(p); reset_statistics(); } - + simplifier::~simplifier() { } inline watch_list & simplifier::get_wlist(literal l) { return s.get_wlist(l); } inline watch_list const & simplifier::get_wlist(literal l) const { return s.get_wlist(l); } - + inline bool simplifier::is_external(bool_var v) const { return s.is_external(v); } inline bool simplifier::was_eliminated(bool_var v) const { return s.was_eliminated(v); } @@ -78,7 +78,7 @@ namespace sat { lbool simplifier::value(literal l) const { return s.value(l); } inline void simplifier::checkpoint() { s.checkpoint(); } - + void simplifier::register_clauses(clause_vector & cs) { std::stable_sort(cs.begin(), cs.end(), size_lt()); clause_vector::iterator it = cs.begin(); @@ -117,7 +117,7 @@ namespace sat { SASSERT(s.get_wlist(~l1).contains(watched(l2, learned))); s.get_wlist(~l1).erase(watched(l2, learned)); } - + void simplifier::init_visited() { m_visited.reset(); m_visited.resize(2*s.num_vars(), false); @@ -155,7 +155,7 @@ namespace sat { if (!learned && (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls)) elim_blocked_clauses(); - + if (!learned) m_num_calls++; @@ -180,6 +180,7 @@ namespace sat { bool vars_eliminated = m_num_elim_vars > old_num_elim_vars; if (!m_need_cleanup) { + TRACE("after_simplifier", tout << "skipping cleanup...\n";); if (vars_eliminated) { // must remove learned clauses with eliminated variables cleanup_clauses(s.m_learned, true, true, learned_in_use_lists); @@ -189,6 +190,7 @@ namespace sat { free_memory(); return; } + TRACE("after_simplifier", tout << "cleanning watches...\n";); cleanup_watches(); cleanup_clauses(s.m_learned, true, vars_eliminated, learned_in_use_lists); cleanup_clauses(s.m_clauses, false, vars_eliminated, true); @@ -234,7 +236,7 @@ namespace sat { s.del_clause(c); continue; } - + if (learned && vars_eliminated) { unsigned sz = c.size(); unsigned i; @@ -293,7 +295,7 @@ namespace sat { mark_visited(c[i]); } } - + void simplifier::unmark_all(clause const & c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) @@ -325,7 +327,7 @@ namespace sat { */ bool simplifier::subsumes1(clause const & c1, clause const & c2, literal & l) { unsigned sz2 = c2.size(); - for (unsigned i = 0; i < sz2; i++) + for (unsigned i = 0; i < sz2; i++) mark_visited(c2[i]); bool r = true; @@ -344,7 +346,7 @@ namespace sat { } } - for (unsigned i = 0; i < sz2; i++) + for (unsigned i = 0; i < sz2; i++) unmark_visited(c2[i]); return r; } @@ -353,7 +355,7 @@ namespace sat { \brief Return the clauses subsumed by c1 and the clauses that can be subsumed resolved using c1. The collections is populated using the use list of target. */ - void simplifier::collect_subsumed1_core(clause const & c1, clause_vector & out, literal_vector & out_lits, + void simplifier::collect_subsumed1_core(clause const & c1, clause_vector & out, literal_vector & out_lits, literal target) { clause_use_list const & cs = m_use_list.get(target); clause_use_list::iterator it = cs.mk_iterator(); @@ -362,7 +364,7 @@ namespace sat { CTRACE("resolution_bug", c2.was_removed(), tout << "clause has been removed:\n" << c2 << "\n";); SASSERT(!c2.was_removed()); if (&c2 != &c1 && - c1.size() <= c2.size() && + c1.size() <= c2.size() && approx_subset(c1.approx(), c2.approx())) { m_sub_counter -= c1.size() + c2.size(); literal l; @@ -373,7 +375,7 @@ namespace sat { } it.next(); } - } + } /** \brief Return the clauses subsumed by c1 and the clauses that can be subsumed resolved using c1. @@ -400,7 +402,7 @@ namespace sat { if (*l_it == null_literal) { // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) - c1.unset_learned(); + c1.unset_learned(); TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2); m_num_subsumed++; @@ -447,9 +449,9 @@ namespace sat { */ bool simplifier::subsumes0(clause const & c1, clause const & c2) { unsigned sz2 = c2.size(); - for (unsigned i = 0; i < sz2; i++) + for (unsigned i = 0; i < sz2; i++) mark_visited(c2[i]); - + bool r = true; unsigned sz1 = c1.size(); for (unsigned i = 0; i < sz1; i++) { @@ -459,12 +461,12 @@ namespace sat { } } - for (unsigned i = 0; i < sz2; i++) + for (unsigned i = 0; i < sz2; i++) unmark_visited(c2[i]); - + return r; } - + /** \brief Collect the clauses subsumed by c1 (using the occurrence list of target). */ @@ -475,7 +477,7 @@ namespace sat { clause & c2 = it.curr(); SASSERT(!c2.was_removed()); if (&c2 != &c1 && - c1.size() <= c2.size() && + c1.size() <= c2.size() && approx_subset(c1.approx(), c2.approx())) { m_sub_counter -= c1.size() + c2.size(); if (subsumes0(c1, c2)) { @@ -485,7 +487,7 @@ namespace sat { it.next(); } } - + /** \brief Collect the clauses subsumed by c1 */ @@ -493,8 +495,8 @@ namespace sat { literal l = get_min_occ_var0(c1); collect_subsumed0_core(c1, out, l); } - - + + /** \brief Perform backward subsumption using c1. */ @@ -507,16 +509,16 @@ namespace sat { clause & c2 = *(*it); // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) - c1.unset_learned(); + c1.unset_learned(); TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2); m_num_subsumed++; } } - + /** \brief Eliminate false literals from c, and update occurrence lists - + Return true if the clause is satisfied */ bool simplifier::cleanup_clause(clause & c, bool in_use_list) { @@ -666,7 +668,7 @@ namespace sat { back_subsumption1(c); if (w.is_learned() && !c.is_learned()) { SASSERT(wlist[j] == w); - TRACE("mark_not_learned_bug", + TRACE("mark_not_learned_bug", tout << "marking as not learned: " << l2 << " " << wlist[j].is_learned() << "\n";); wlist[j].mark_not_learned(); mark_as_not_learned_core(get_wlist(~l2), l); @@ -735,7 +737,7 @@ namespace sat { continue; } if (it2->get_literal() == last_lit) { - TRACE("subsumption", tout << "eliminating: " << ~to_literal(l_idx) + TRACE("subsumption", tout << "eliminating: " << ~to_literal(l_idx) << " " << it2->get_literal() << "\n";); elim++; } @@ -762,12 +764,12 @@ namespace sat { m_num_sub_res(s.m_num_sub_res) { m_watch.start(); } - + ~subsumption_report() { m_watch.stop(); - IF_VERBOSE(SAT_VB_LVL, + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-subsumer :subsumed " - << (m_simplifier.m_num_subsumed - m_num_subsumed) + << (m_simplifier.m_num_subsumed - m_num_subsumed) << " :subsumption-resolution " << (m_simplifier.m_num_sub_res - m_num_sub_res) << " :threshold " << m_simplifier.m_sub_counter << mem_stat() @@ -847,12 +849,12 @@ namespace sat { vector const & m_watches; public: literal_lt(use_list const & l, vector const & ws):m_use_list(l), m_watches(ws) {} - + unsigned weight(unsigned l_idx) const { return 2*m_use_list.get(~to_literal(l_idx)).size() + m_watches[l_idx].size(); } - - bool operator()(unsigned l_idx1, unsigned l_idx2) const { + + bool operator()(unsigned l_idx1, unsigned l_idx2) const { return weight(l_idx1) < weight(l_idx2); } }; @@ -861,9 +863,9 @@ namespace sat { heap m_queue; public: queue(use_list const & l, vector const & ws):m_queue(128, literal_lt(l, ws)) {} - void insert(literal l) { + void insert(literal l) { unsigned idx = l.index(); - m_queue.reserve(idx + 1); + m_queue.reserve(idx + 1); SASSERT(!m_queue.contains(idx)); m_queue.insert(idx); } @@ -877,14 +879,14 @@ namespace sat { literal next() { SASSERT(!empty()); return to_literal(m_queue.erase_min()); } bool empty() const { return m_queue.empty(); } }; - + simplifier & s; int m_counter; model_converter & mc; queue m_queue; clause_vector m_to_remove; - blocked_clause_elim(simplifier & _s, unsigned limit, model_converter & _mc, use_list & l, + blocked_clause_elim(simplifier & _s, unsigned limit, model_converter & _mc, use_list & l, vector & wlist): s(_s), m_counter(limit), @@ -946,7 +948,7 @@ namespace sat { clause_vector::iterator it = m_to_remove.begin(); clause_vector::iterator end = m_to_remove.end(); for (; it != end; ++it) { - s.remove_clause(*(*it)); + s.remove_clause(*(*it)); } } { @@ -1025,12 +1027,12 @@ namespace sat { m_num_blocked_clauses(s.m_num_blocked_clauses) { m_watch.start(); } - + ~blocked_cls_report() { m_watch.stop(); - IF_VERBOSE(SAT_VB_LVL, + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-blocked-clauses :elim-blocked-clauses " - << (m_simplifier.m_num_blocked_clauses - m_num_blocked_clauses) + << (m_simplifier.m_num_blocked_clauses - m_num_blocked_clauses) << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } @@ -1062,8 +1064,8 @@ namespace sat { unsigned num_neg = m_use_list.get(neg_l).size(); unsigned num_bin_pos = get_num_non_learned_bin(pos_l); unsigned num_bin_neg = get_num_non_learned_bin(neg_l); - unsigned cost = 2 * num_pos * num_neg + num_pos * num_bin_neg + num_neg * num_bin_pos; - CTRACE("elim_vars_detail", cost == 0, tout << v << " num_pos: " << num_pos << " num_neg: " << num_neg << " num_bin_pos: " << num_bin_pos + unsigned cost = 2 * num_pos * num_neg + num_pos * num_bin_neg + num_neg * num_bin_pos; + CTRACE("elim_vars_detail", cost == 0, tout << v << " num_pos: " << num_pos << " num_neg: " << num_neg << " num_bin_pos: " << num_bin_pos << " num_bin_neg: " << num_bin_neg << " cost: " << cost << "\n";); return cost; } @@ -1071,7 +1073,7 @@ namespace sat { typedef std::pair bool_var_and_cost; struct bool_var_and_cost_lt { - bool operator()(bool_var_and_cost const & p1, bool_var_and_cost const & p2) const { return p1.second < p2.second; } + bool operator()(bool_var_and_cost const & p1, bool_var_and_cost const & p2) const { return p1.second < p2.second; } }; void simplifier::order_vars_for_elim(bool_var_vector & r) { @@ -1104,7 +1106,7 @@ namespace sat { r.push_back(it2->first); } } - + /** \brief Collect clauses and binary clauses containing l. */ @@ -1116,7 +1118,7 @@ namespace sat { SASSERT(r.back().size() == it.curr().size()); it.next(); } - + watch_list & wlist = get_wlist(~l); watch_list::iterator it2 = wlist.begin(); watch_list::iterator end2 = wlist.end(); @@ -1129,7 +1131,7 @@ namespace sat { } /** - \brief Resolve clauses c1 and c2. + \brief Resolve clauses c1 and c2. c1 must contain l. c2 must contain ~l. Store result in r. @@ -1149,7 +1151,7 @@ namespace sat { m_visited[l2.index()] = true; r.push_back(l2); } - + literal not_l = ~l; sz = c2.size(); m_elim_counter -= sz; @@ -1164,7 +1166,7 @@ namespace sat { if (!m_visited[l2.index()]) r.push_back(l2); } - + sz = c1.size(); for (unsigned i = 0; i < sz; i++) { literal l2 = c1[i]; @@ -1200,7 +1202,7 @@ namespace sat { break; } } - CTRACE("resolve_bug", it2 == end2, + CTRACE("resolve_bug", it2 == end2, tout << ~l1 << " -> "; display(tout, s.m_cls_allocator, wlist1); tout << "\n" << ~l2 << " -> "; display(tout, s.m_cls_allocator, wlist2); tout << "\n";); @@ -1262,7 +1264,7 @@ namespace sat { TRACE("resolution_bug", tout << "processing: " << v << "\n";); if (value(v) != l_undef) return false; - + literal pos_l(v, false); literal neg_l(v, true); unsigned num_bin_pos = get_num_non_learned_bin(pos_l); @@ -1274,12 +1276,12 @@ namespace sat { m_elim_counter -= num_pos + num_neg; TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << "\n";); - + if (num_pos >= m_res_occ_cutoff && num_neg >= m_res_occ_cutoff) return false; unsigned before_lits = num_bin_pos*2 + num_bin_neg*2; - + { clause_use_list::iterator it = pos_occs.mk_iterator(); while (!it.at_end()) { @@ -1287,7 +1289,7 @@ namespace sat { it.next(); } } - + { clause_use_list::iterator it2 = neg_occs.mk_iterator(); while (!it2.at_end()) { @@ -1297,23 +1299,23 @@ namespace sat { } TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << " before_lits: " << before_lits << "\n";); - + if (num_pos >= m_res_occ_cutoff3 && num_neg >= m_res_occ_cutoff3 && before_lits > m_res_lit_cutoff3 && s.m_clauses.size() > m_res_cls_cutoff2) return false; - if (num_pos >= m_res_occ_cutoff2 && num_neg >= m_res_occ_cutoff2 && before_lits > m_res_lit_cutoff2 && + if (num_pos >= m_res_occ_cutoff2 && num_neg >= m_res_occ_cutoff2 && before_lits > m_res_lit_cutoff2 && s.m_clauses.size() > m_res_cls_cutoff1 && s.m_clauses.size() <= m_res_cls_cutoff2) return false; - if (num_pos >= m_res_occ_cutoff1 && num_neg >= m_res_occ_cutoff1 && before_lits > m_res_lit_cutoff1 && + if (num_pos >= m_res_occ_cutoff1 && num_neg >= m_res_occ_cutoff1 && before_lits > m_res_lit_cutoff1 && s.m_clauses.size() <= m_res_cls_cutoff1) return false; - + m_pos_cls.reset(); m_neg_cls.reset(); collect_clauses(pos_l, m_pos_cls); collect_clauses(neg_l, m_neg_cls); - + m_elim_counter -= num_pos * num_neg + before_lits; - + TRACE("resolution_detail", tout << "collecting number of after_clauses\n";); unsigned before_clauses = num_pos + num_neg; unsigned after_clauses = 0; @@ -1350,7 +1352,7 @@ namespace sat { neg_occs.reset(); m_elim_counter -= num_pos * num_neg + before_lits; - + it1 = m_pos_cls.begin(); end1 = m_pos_cls.end(); for (; it1 != end1; ++it1) { @@ -1393,7 +1395,7 @@ namespace sat { return true; } } - + return true; } @@ -1406,10 +1408,10 @@ namespace sat { m_num_elim_vars(s.m_num_elim_vars) { m_watch.start(); } - + ~elim_var_report() { m_watch.stop(); - IF_VERBOSE(SAT_VB_LVL, + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-resolution :elim-bool-vars " << (m_simplifier.m_num_elim_vars - m_num_elim_vars) << " :threshold " << m_simplifier.m_elim_counter @@ -1417,12 +1419,12 @@ namespace sat { << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; - + void simplifier::elim_vars() { elim_var_report rpt(*this); bool_var_vector vars; order_vars_for_elim(vars); - + bool_var_vector::iterator it = vars.begin(); bool_var_vector::iterator end = vars.end(); for (; it != end; ++it) { @@ -1463,7 +1465,7 @@ namespace sat { void simplifier::collect_param_descrs(param_descrs & r) { sat_simplifier_params::collect_param_descrs(r); } - + void simplifier::collect_statistics(statistics & st) { st.update("subsumed", m_num_subsumed); st.update("subsumption resolution", m_num_sub_res); @@ -1471,7 +1473,7 @@ namespace sat { st.update("elim bool vars", m_num_elim_vars); st.update("elim blocked clauses", m_num_blocked_clauses); } - + void simplifier::reset_statistics() { m_num_blocked_clauses = 0; m_num_subsumed = 0; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index d9e11724c..bb08ae5c3 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -27,7 +27,7 @@ Revision History: // define to create a copy of the solver before starting the search // useful for checking models // #define CLONE_BEFORE_SOLVING - + namespace sat { solver::solver(params_ref const & p, extension * ext): @@ -103,7 +103,7 @@ namespace sat { } } } - + // ----------------------- // // Variable & Clause creation @@ -312,7 +312,7 @@ namespace sat { /** \brief Select a watch literal starting the search at the given position. This method is only used for clauses created during the search. - + I use the following rules to select a watch literal. 1- select a literal l in idx >= starting_at such that value(l) = l_true, @@ -329,7 +329,7 @@ namespace sat { lvl(l) >= lvl(l') Without rule 3, boolean propagation is incomplete, that is, it may miss possible propagations. - + \remark The method select_lemma_watch_lit is used to select the watch literal for regular learned clauses. */ unsigned solver::select_watch_lit(clause const & cls, unsigned starting_at) const { @@ -443,7 +443,7 @@ namespace sat { erase_clause_watch(get_wlist(~c[0]), cls_off); erase_clause_watch(get_wlist(~c[1]), cls_off); } - + void solver::dettach_ter_clause(clause & c) { erase_ternary_watch(get_wlist(~c[0]), c[1], c[2]); erase_ternary_watch(get_wlist(~c[1]), c[0], c[2]); @@ -498,10 +498,10 @@ namespace sat { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { switch (value(c[i])) { - case l_true: + case l_true: return l_true; - case l_undef: - found_undef = true; + case l_undef: + found_undef = true; break; default: break; @@ -515,7 +515,7 @@ namespace sat { // Propagation // // ----------------------- - + bool solver::propagate_core(bool update) { if (m_inconsistent) return false; @@ -545,7 +545,7 @@ namespace sat { } for (; it != end; ++it) { switch (it->get_kind()) { - case watched::BINARY: + case watched::BINARY: l1 = it->get_literal(); switch (value(l1)) { case l_false: @@ -585,12 +585,26 @@ namespace sat { break; case watched::CLAUSE: { if (value(it->get_blocked_literal()) == l_true) { + TRACE("propagate_clause_bug", tout << "blocked literal " << it->get_blocked_literal() << "\n"; + clause_offset cls_off = it->get_clause_offset(); + clause & c = *(m_cls_allocator.get_clause(cls_off)); + tout << c << "\n";); *it2 = *it; it2++; break; } clause_offset cls_off = it->get_clause_offset(); clause & c = *(m_cls_allocator.get_clause(cls_off)); + TRACE("propagate_clause_bug", tout << "processing... " << c << "\nwas_removed: " << c.was_removed() << "\n";); + if (c.was_removed()) { + // Remark: this method may be invoked when the watch lists are not in a consistent state, + // and may contain dead/removed clauses. + // See: sat_simplifier.cpp + // So, we must check whether the clause was marked for deletion, and ignore it. + *it2 = *it; + it2++; + break; + } if (c[0] == not_l) std::swap(c[0], c[1]); CTRACE("propagate_bug", c[1] != not_l, tout << "l: " << l << " " << c << "\n";); @@ -693,7 +707,7 @@ namespace sat { m_conflicts_since_restart = 0; m_restart_threshold = m_config.m_restart_initial; } - + // iff3_finder(*this)(); simplify_problem(); @@ -704,10 +718,10 @@ namespace sat { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"abort: max-conflicts = 0\"\n";); return l_undef; } - + while (true) { SASSERT(!inconsistent()); - + lbool r = bounded_search(); if (r != l_undef) return r; @@ -716,7 +730,7 @@ namespace sat { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"abort: max-conflicts = " << m_conflicts << "\"\n";); return l_undef; } - + restart(); if (m_conflicts >= m_next_simplify) { simplify_problem(); @@ -734,7 +748,7 @@ namespace sat { bool_var solver::next_var() { bool_var next; - + if (m_rand() < static_cast(m_config.m_random_freq * random_gen::max_value())) { if (num_vars() == 0) return null_bool_var; @@ -743,16 +757,16 @@ namespace sat { if (value(next) == l_undef && !was_eliminated(next)) return next; } - + while (!m_case_split_queue.empty()) { next = m_case_split_queue.next_var(); if (value(next) == l_undef && !was_eliminated(next)) return next; } - + return null_bool_var; } - + bool solver::decide() { bool_var next = next_var(); if (next == null_bool_var) @@ -760,7 +774,7 @@ namespace sat { push(); m_stats.m_decision++; lbool phase = m_ext ? m_ext->get_phase(next) : l_undef; - + if (phase == l_undef) { switch (m_config.m_phase) { case PS_ALWAYS_TRUE: @@ -784,7 +798,7 @@ namespace sat { break; } } - + SASSERT(phase != l_undef); literal next_lit(next, phase == l_false); assign(next_lit, justification()); @@ -807,23 +821,23 @@ namespace sat { return l_undef; if (scope_lvl() == 0) { cleanup(); // cleaner may propagate frozen clauses - if (inconsistent()) + if (inconsistent()) return l_false; gc(); } } - + gc(); if (!decide()) { if (m_ext) { switch (m_ext->check()) { - case CR_DONE: + case CR_DONE: mk_model(); return l_true; - case CR_CONTINUE: + case CR_CONTINUE: break; - case CR_GIVEUP: + case CR_GIVEUP: throw abort_solver(); } } @@ -849,23 +863,23 @@ namespace sat { m_stopwatch.reset(); m_stopwatch.start(); } - + /** \brief Apply all simplifications. */ void solver::simplify_problem() { SASSERT(scope_lvl() == 0); - + m_cleaner(); CASSERT("sat_simplify_bug", check_invariant()); - + m_scc(); CASSERT("sat_simplify_bug", check_invariant()); - - m_simplifier(false); + + m_simplifier(false); CASSERT("sat_simplify_bug", check_invariant()); CASSERT("sat_missed_prop", check_missed_propagation()); - + if (!m_learned.empty()) { m_simplifier(true); CASSERT("sat_missed_prop", check_missed_propagation()); @@ -878,11 +892,11 @@ namespace sat { m_probing(); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); - + m_asymm_branch(); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); - + if (m_ext) { m_ext->clauses_modifed(); m_ext->simplify(); @@ -956,7 +970,7 @@ namespace sat { } } } - if (!m_mc.check_model(m)) + if (!m_mc.check_model(m)) ok = false; CTRACE("sat_model_bug", !ok, tout << m << "\n";); return ok; @@ -964,9 +978,9 @@ namespace sat { void solver::restart() { m_stats.m_restart++; - IF_VERBOSE(1, + IF_VERBOSE(1, verbose_stream() << "(sat-restart :conflicts " << m_stats.m_conflict << " :decisions " << m_stats.m_decision - << " :restarts " << m_stats.m_restart << mk_stat(*this) + << " :restarts " << m_stats.m_restart << mk_stat(*this) << " :time " << std::fixed << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n";); IF_VERBOSE(30, display_status(verbose_stream());); pop(scope_lvl()); @@ -991,9 +1005,9 @@ namespace sat { // GC // // ----------------------- - + void solver::gc() { - if (m_conflicts_since_gc <= m_gc_threshold) + if (m_conflicts_since_gc <= m_gc_threshold) return; CASSERT("sat_gc_bug", check_invariant()); switch (m_config.m_gc_strategy) { @@ -1073,7 +1087,7 @@ namespace sat { std::stable_sort(m_learned.begin(), m_learned.end(), glue_lt()); gc_half("glue"); } - + void solver::gc_psm() { save_psm(); std::stable_sort(m_learned.begin(), m_learned.end(), psm_lt()); @@ -1134,8 +1148,8 @@ namespace sat { void solver::gc_dyn_psm() { // To do gc at scope_lvl() > 0, I will need to use the reinitialization stack, or live with the fact // that I may miss some propagations for reactivated clauses. - SASSERT(scope_lvl() == 0); - // compute + SASSERT(scope_lvl() == 0); + // compute // d_tk unsigned h = 0; unsigned V_tk = 0; @@ -1152,7 +1166,7 @@ namespace sat { double d_tk = V_tk == 0 ? static_cast(num_vars() + 1) : static_cast(h)/static_cast(V_tk); if (d_tk < m_min_d_tk) m_min_d_tk = d_tk; - TRACE("sat_frozen", tout << "m_min_d_tk: " << m_min_d_tk << "\n";); + TRACE("sat_frozen", tout << "m_min_d_tk: " << m_min_d_tk << "\n";); unsigned frozen = 0; unsigned deleted = 0; unsigned activated = 0; @@ -1218,15 +1232,15 @@ namespace sat { ++it2; } m_learned.set_end(it2); - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :d_tk " << d_tk << " :min-d_tk " << m_min_d_tk << + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :d_tk " << d_tk << " :min-d_tk " << m_min_d_tk << " :frozen " << frozen << " :activated " << activated << " :deleted " << deleted << ")\n";); } - + // return true if should keep the clause, and false if we should delete it. - bool solver::activate_frozen_clause(clause & c) { + bool solver::activate_frozen_clause(clause & c) { TRACE("sat_gc", tout << "reactivating:\n" << c << "\n";); SASSERT(scope_lvl() == 0); - // do some cleanup + // do some cleanup unsigned sz = c.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { @@ -1292,7 +1306,7 @@ namespace sat { bool r = resolve_conflict_core(); CASSERT("sat_check_marks", check_marks()); // after pop, clauses are reinitialized, this may trigger another conflict. - if (!r) + if (!r) return false; if (!inconsistent()) return true; @@ -1311,7 +1325,7 @@ namespace sat { if (m_conflict_lvl == 0) return false; m_lemma.reset(); - + forget_phase_of_vars(m_conflict_lvl); unsigned idx = skip_literals_above_conflict_level(); @@ -1326,7 +1340,7 @@ namespace sat { literal consequent = m_not_l; justification js = m_conflict; - + do { TRACE("sat_conflict_detail", tout << "processing consequent: " << consequent << "\n"; tout << "num_marks: " << num_marks << ", js kind: " << js.get_kind() << "\n";); @@ -1362,7 +1376,7 @@ namespace sat { fill_ext_antecedents(consequent, js); literal_vector::iterator it = m_ext_antecedents.begin(); literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) + for (; it != end; ++it) process_antecedent(*it, num_marks); break; } @@ -1370,10 +1384,10 @@ namespace sat { UNREACHABLE(); break; } - + while (true) { literal l = m_trail[idx]; - if (is_marked(l.var())) + if (is_marked(l.var())) break; SASSERT(idx > 0); idx--; @@ -1386,9 +1400,9 @@ namespace sat { idx--; num_marks--; reset_mark(c_var); - } + } while (num_marks > 0); - + m_lemma[0] = ~consequent; TRACE("sat_lemma", tout << "new lemma size: " << m_lemma.size() << "\n" << m_lemma << "\n";); @@ -1401,7 +1415,7 @@ namespace sat { } else reset_lemma_var_marks(); - + literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); unsigned new_scope_lvl = 0; @@ -1432,10 +1446,10 @@ namespace sat { return 0; unsigned r = 0; - + if (consequent != null_literal) r = lvl(consequent); - + switch (js.get_kind()) { case justification::NONE: break; @@ -1468,7 +1482,7 @@ namespace sat { fill_ext_antecedents(consequent, js); literal_vector::iterator it = m_ext_antecedents.begin(); literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) + for (; it != end; ++it) r = std::max(r, lvl(*it)); break; } @@ -1497,7 +1511,7 @@ namespace sat { } return idx; } - + void solver::process_antecedent(literal antecedent, unsigned & num_marks) { bool_var var = antecedent.var(); unsigned var_lvl = lvl(var); @@ -1511,7 +1525,7 @@ namespace sat { m_lemma.push_back(~antecedent); } } - + /** \brief js is an external justification. Collect its antecedents and store at m_ext_antecedents. */ @@ -1578,7 +1592,7 @@ namespace sat { unsigned var_lvl = lvl(var); if (!is_marked(var) && var_lvl > 0) { if (m_lvl_set.may_contain(var_lvl)) { - mark(var); + mark(var); m_unmark.push_back(var); m_lemma_min_stack.push_back(var); } @@ -1590,17 +1604,17 @@ namespace sat { } /** - \brief Return true if lit is implied by other marked literals - and/or literals assigned at the base level. - The set lvl_set is used as an optimization. + \brief Return true if lit is implied by other marked literals + and/or literals assigned at the base level. + The set lvl_set is used as an optimization. The idea is to stop the recursive search with a failure - as soon as we find a literal assigned in a level that is not in lvl_set. + as soon as we find a literal assigned in a level that is not in lvl_set. */ bool solver::implied_by_marked(literal lit) { m_lemma_min_stack.reset(); // avoid recursive function m_lemma_min_stack.push_back(lit.var()); unsigned old_size = m_unmark.size(); - + while (!m_lemma_min_stack.empty()) { bool_var var = m_lemma_min_stack.back(); m_lemma_min_stack.pop_back(); @@ -1701,7 +1715,7 @@ namespace sat { void solver::minimize_lemma() { m_unmark.reset(); updt_lemma_lvl_set(); - + unsigned sz = m_lemma.size(); unsigned i = 1; // the first literal is the FUIP unsigned j = 1; @@ -1717,12 +1731,12 @@ namespace sat { j++; } } - + reset_unmark(0); m_lemma.shrink(j); m_stats.m_minimized_lits += sz - j; } - + /** \brief Reset the mark of the variables in the current lemma. */ @@ -1742,17 +1756,17 @@ namespace sat { Only binary and ternary clauses are used. */ void solver::dyn_sub_res() { - unsigned sz = m_lemma.size(); + unsigned sz = m_lemma.size(); for (unsigned i = 0; i < sz; i++) { mark_lit(m_lemma[i]); } - + literal l0 = m_lemma[0]; // l0 is the FUIP, and we never remove the FUIP. - // + // // In the following loop, we use unmark_lit(l) to remove a // literal from m_lemma. - + for (unsigned i = 0; i < sz; i++) { literal l = m_lemma[i]; if (!is_marked_lit(l)) @@ -1764,8 +1778,8 @@ namespace sat { for (; it != end; ++it) { // In this for-loop, the conditions l0 != ~l2 and l0 != ~l3 // are not really needed if the solver does not miss unit propagations. - // However, we add them anyway because we don't want to rely on this - // property of the propagator. + // However, we add them anyway because we don't want to rely on this + // property of the propagator. // For example, if this property is relaxed in the future, then the code // without the conditions l0 != ~l2 and l0 != ~l3 may remove the FUIP if (it->is_binary_clause()) { @@ -1811,10 +1825,10 @@ namespace sat { // p1 \/ ~p2 // p2 \/ ~p3 // p3 \/ ~p4 - // q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 - // q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 - // ~q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 - // ~q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // ~q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // ~q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 // ... // // 2. Now suppose we learned the lemma @@ -1825,15 +1839,15 @@ namespace sat { // That is, l \/ l2 is an implied clause. Note that probing does not add // this clause to the clause database (there are too many). // - // 4. Lemma (*) is deleted (garbage collected). + // 4. Lemma (*) is deleted (garbage collected). // // 5. l is decided to be false, p1, p2, p3 and p4 are propagated using BCP, // but l2 is not since the lemma (*) was deleted. - // + // // Probing module still "knows" that l \/ l2 is valid binary clause - // + // // 6. A new lemma is created where ~l2 is the FUIP and the lemma also contains l. - // If we remove l0 != ~l2 may try to delete the FUIP. + // If we remove l0 != ~l2 may try to delete the FUIP. if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); @@ -1844,7 +1858,7 @@ namespace sat { // can't eliminat FUIP SASSERT(is_marked_lit(m_lemma[0])); - + unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = m_lemma[i]; @@ -1856,7 +1870,7 @@ namespace sat { } m_stats.m_dyn_sub_res += sz - j; - + SASSERT(j >= 1); m_lemma.shrink(j); } @@ -1949,7 +1963,7 @@ namespace sat { // Misc // // ----------------------- - + void solver::updt_params(params_ref const & p) { m_params = p; m_config.updt_params(p); @@ -1971,7 +1985,7 @@ namespace sat { void solver::set_cancel(bool f) { m_cancel = f; } - + void solver::collect_statistics(statistics & st) { m_stats.collect_statistics(st); m_cleaner.collect_statistics(st); @@ -2067,7 +2081,7 @@ namespace sat { } } } - + void solver::display_units(std::ostream & out) const { unsigned end = scope_lvl() == 0 ? m_trail.size() : m_scopes[0].m_trail_lim; for (unsigned i = 0; i < end; i++) { @@ -2221,26 +2235,26 @@ namespace sat { // Simplification // // ----------------------- - void solver::cleanup() { - if (scope_lvl() > 0 || inconsistent()) - return; + void solver::cleanup() { + if (scope_lvl() > 0 || inconsistent()) + return; if (m_cleaner() && m_ext) m_ext->clauses_modifed(); } - void solver::simplify(bool learned) { - if (scope_lvl() > 0 || inconsistent()) - return; - m_simplifier(learned); - m_simplifier.free_memory(); + void solver::simplify(bool learned) { + if (scope_lvl() > 0 || inconsistent()) + return; + m_simplifier(learned); + m_simplifier.free_memory(); if (m_ext) m_ext->clauses_modifed(); } - unsigned solver::scc_bin() { - if (scope_lvl() > 0 || inconsistent()) - return 0; - unsigned r = m_scc(); + unsigned solver::scc_bin() { + if (scope_lvl() > 0 || inconsistent()) + return 0; + unsigned r = m_scc(); if (r > 0 && m_ext) m_ext->clauses_modifed(); return r; @@ -2314,10 +2328,10 @@ namespace sat { out << " :inconsistent " << (m_inconsistent ? "true" : "false") << "\n"; out << " :vars " << num_vars() << "\n"; out << " :elim-vars " << num_elim << "\n"; - out << " :lits " << num_lits << "\n"; + out << " :lits " << num_lits << "\n"; out << " :assigned " << m_trail.size() << "\n"; - out << " :binary-clauses " << num_bin << "\n"; - out << " :ternary-clauses " << num_ter << "\n"; + out << " :binary-clauses " << num_bin << "\n"; + out << " :ternary-clauses " << num_ter << "\n"; out << " :clauses " << num_cls << "\n"; out << " :del-clause " << m_stats.m_del_clause << "\n"; out << " :avg-clause-size " << (total_cls == 0 ? 0.0 : static_cast(num_lits) / static_cast(total_cls)) << "\n"; From 88675ec728695d7df4e6f6ce8622332c23f1ed8f Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 4 Nov 2013 12:24:25 -0800 Subject: [PATCH 195/509] fix assertion violations (reported by Christoph Wintersteiger) at sage/bench_1300.smt2 and sage/bench/2861.smt2 Signed-off-by: Leonardo de Moura --- src/sat/sat_solver.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index bb08ae5c3..f4bf5393c 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -596,18 +596,19 @@ namespace sat { clause_offset cls_off = it->get_clause_offset(); clause & c = *(m_cls_allocator.get_clause(cls_off)); TRACE("propagate_clause_bug", tout << "processing... " << c << "\nwas_removed: " << c.was_removed() << "\n";); - if (c.was_removed()) { + if (c[0] == not_l) + std::swap(c[0], c[1]); + CTRACE("propagate_bug", c[1] != not_l, tout << "l: " << l << " " << c << "\n";); + if (c.was_removed() || c[1] != not_l) { // Remark: this method may be invoked when the watch lists are not in a consistent state, - // and may contain dead/removed clauses. - // See: sat_simplifier.cpp - // So, we must check whether the clause was marked for deletion, and ignore it. + // and may contain dead/removed clauses, or clauses with removed literals. + // See: method propagate_unit at sat_simplifier.cpp + // So, we must check whether the clause was marked for deletion, or + // c[1] != not_l *it2 = *it; it2++; break; } - if (c[0] == not_l) - std::swap(c[0], c[1]); - CTRACE("propagate_bug", c[1] != not_l, tout << "l: " << l << " " << c << "\n";); SASSERT(c[1] == not_l); if (value(c[0]) == l_true) { it2->set_clause(c[0], cls_off); From 063f6fe15f7a0269359a59e6129b796a1e6950ae Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 4 Nov 2013 12:26:20 -0800 Subject: [PATCH 196/509] fix assertion violations (reported by Christoph Wintersteiger) at sage\app8\bench_2174.smt2, sage\app9\bench_1450.smt2, sage\app9\bench_1546.smt2 Signed-off-by: Leonardo de Moura --- src/sat/sat_simplifier.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 37d41a5fd..f20ffa7c7 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -407,7 +407,7 @@ namespace sat { remove_clause(c2); m_num_subsumed++; } - else { + else if (!c2.was_removed()) { // subsumption resolution TRACE("subsumption_resolution", tout << c1 << " sub-ref(" << *l_it << ") " << c2 << "\n";); elim_lit(c2, *l_it); From 49c72abb2ddf9621e4a209397b6b82bf1698fac2 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 5 Nov 2013 12:17:09 -0800 Subject: [PATCH 197/509] new interpolation fixes; re-added fixedpoint-push/pop --- src/ast/ast.cpp | 2 +- src/interp/iz3checker.cpp | 17 +++++ src/interp/iz3checker.h | 8 +++ src/interp/iz3interp.cpp | 1 + src/interp/iz3mgr.cpp | 15 +++++ src/interp/iz3proof_itp.cpp | 83 +++++++++++++++++++----- src/interp/iz3translate.cpp | 6 +- src/muz/base/dl_context.cpp | 4 ++ src/muz/duality/duality_dl_interface.cpp | 1 + src/muz/fp/dl_cmds.cpp | 40 +++++++++++- 10 files changed, 156 insertions(+), 21 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 9de402082..36aeea9a0 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1307,7 +1307,7 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): void ast_manager::init() { // TODO: the following is a HACK to enable proofs in the old smt solver // When we stop using that solver, this hack can be removed - m_proof_mode = PGM_FINE; + // m_proof_mode = PGM_FINE; m_int_real_coercions = true; m_debug_ref_count = false; diff --git a/src/interp/iz3checker.cpp b/src/interp/iz3checker.cpp index 7792c2401..b4e55af20 100755 --- a/src/interp/iz3checker.cpp +++ b/src/interp/iz3checker.cpp @@ -170,6 +170,11 @@ struct iz3checker : iz3base { iz3checker(ast_manager &_m) : iz3base(_m) { } + + iz3checker(iz3mgr &_m) + : iz3base(_m) { + } + }; template @@ -193,6 +198,18 @@ bool iz3check(ast_manager &_m_manager, return chk.check(s,err,chk.cook(cnsts),to_std_vector(parents),chk.cook(interps),chk.cook(theory)); } +bool iz3check(iz3mgr &mgr, + solver *s, + std::ostream &err, + const std::vector &cnsts, + const std::vector &parents, + const std::vector &interps, + const std::vector &theory) +{ + iz3checker chk(mgr); + return chk.check(s,err,cnsts,parents,interps,theory); +} + bool iz3check(ast_manager &_m_manager, solver *s, std::ostream &err, diff --git a/src/interp/iz3checker.h b/src/interp/iz3checker.h index 5402e4d68..d46ea0654 100644 --- a/src/interp/iz3checker.h +++ b/src/interp/iz3checker.h @@ -38,4 +38,12 @@ bool iz3check(ast_manager &_m_manager, ast *tree, const ptr_vector &interps); +bool iz3check(iz3mgr &mgr, + solver *s, + std::ostream &err, + const std::vector &cnsts, + const std::vector &parents, + const std::vector &interps, + const ptr_vector &theory); + #endif diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 2ef36f4e2..92afc5723 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -304,6 +304,7 @@ public: fr.fix_interpolants(interps_vec); interps = interps_vec; + } diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index bd82d235a..b9a3fd0b0 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -476,6 +476,15 @@ void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& coeffs){ } } +static void abs_rat(std::vector &rats){ + // check that they are all non-neg -- if neg, take abs val and warn! + for(unsigned i = 0; i < rats.size(); i++) + if(rats[i].is_neg()){ + // std::cout << "negative Farkas coeff!\n"; + rats[i] = -rats[i]; + } +} + void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ symb s = sym(proof); int numps = s->get_num_parameters(); @@ -494,12 +503,15 @@ void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ } rats[i-2] = r; } +#if 0 if(rats.size() != 0 && rats[0].is_neg()){ for(unsigned i = 0; i < rats.size(); i++){ assert(rats[i].is_neg()); rats[i] = -rats[i]; } } +#endif + abs_rat(rats); extract_lcd(rats); } @@ -536,6 +548,7 @@ void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& r } rats[i-1] = r; } +#if 0 if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them for(unsigned i = 1; i < rats.size(); i++){ if(!rats[i].is_neg()) @@ -543,6 +556,8 @@ void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& r rats[i] = -rats[i]; } } +#endif + abs_rat(rats); extract_lcd(rats); } diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 3bf4dd620..9d15912c2 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -626,7 +626,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast equa = sep_cond(arg(pf,0),cond); if(is_equivrel_chain(equa)){ ast lhs,rhs; eq_from_ineq(arg(neg_equality,0),lhs,rhs); // get inequality we need to prove - ast ineqs= chain_ineqs(LitA,equa,lhs,rhs); // chain must be from lhs to rhs + ast ineqs= chain_ineqs(op(arg(neg_equality,0)),LitA,equa,lhs,rhs); // chain must be from lhs to rhs cond = my_and(cond,chain_conditions(LitA,equa)); ast Bconds = chain_conditions(LitB,equa); if(is_true(Bconds) && op(ineqs) != And) @@ -1313,7 +1313,7 @@ class iz3proof_itp_impl : public iz3proof_itp { } - ast chain_ineqs(LitType t, const ast &chain, const ast &lhs, const ast &rhs){ + ast chain_ineqs(opr comp_op, LitType t, const ast &chain, const ast &lhs, const ast &rhs){ if(is_true(chain)){ if(lhs != rhs) throw "bad ineq inference"; @@ -1322,9 +1322,12 @@ class iz3proof_itp_impl : public iz3proof_itp { ast last = chain_last(chain); ast rest = chain_rest(chain); ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); - ast cond = chain_ineqs(t,rest,lhs,mid); + ast cond = chain_ineqs(comp_op,t,rest,lhs,mid); if(is_rewrite_side(t,last)){ - ast foo = z3_simplify(make(Leq,make_int("0"),make(Sub,mid,rhs))); + ast diff; + if(comp_op == Leq) diff = make(Sub,rhs,mid); + else diff = make(Sub,mid,rhs); + ast foo = z3_simplify(make(Leq,make_int("0"),diff)); if(is_true(cond)) cond = foo; else { @@ -1351,6 +1354,8 @@ class iz3proof_itp_impl : public iz3proof_itp { std::swap(lhs,rhs); if(op(rhs) == Times){ rhs = arg(rhs,1); + if(op(ineq) == Leq) + std::swap(lhs,rhs); return; } } @@ -1368,16 +1373,31 @@ class iz3proof_itp_impl : public iz3proof_itp { /** Make an assumption node. The given clause is assumed in the given frame. */ virtual node make_assumption(int frame, const std::vector &assumption){ - if(pv->in_range(frame,rng)){ - std::vector itp_clause; - for(unsigned i = 0; i < assumption.size(); i++) - if(get_term_type(assumption[i]) != LitA) - itp_clause.push_back(assumption[i]); - ast res = my_or(itp_clause); - return res; + if(!weak){ + if(pv->in_range(frame,rng)){ + std::vector itp_clause; + for(unsigned i = 0; i < assumption.size(); i++) + if(get_term_type(assumption[i]) != LitA) + itp_clause.push_back(assumption[i]); + ast res = my_or(itp_clause); + return res; + } + else { + return mk_true(); + } } else { - return mk_true(); + if(pv->in_range(frame,rng)){ + return mk_false(); + } + else { + std::vector itp_clause; + for(unsigned i = 0; i < assumption.size(); i++) + if(get_term_type(assumption[i]) != LitB) + itp_clause.push_back(assumption[i]); + ast res = my_or(itp_clause); + return mk_not(res); + } } } @@ -1602,17 +1622,20 @@ class iz3proof_itp_impl : public iz3proof_itp { virtual node make_congruence(const ast &p, const ast &con, const ast &prem1){ ast x = arg(p,0), y = arg(p,1); ast itp; + LitType con_t = get_term_type(con); if(get_term_type(p) == LitA){ - if(get_term_type(con) == LitA) + if(con_t == LitA) itp = mk_false(); + else if(con_t == LitB) + itp = p; else itp = make_mixed_congruence(x, y, p, con, prem1); } else { - if(get_term_type(con) == LitA) + if(con_t == LitA) itp = mk_not(p); else{ - if(get_term_type(con) == LitB) + if(con_t == LitB) itp = mk_true(); else itp = make_mixed_congruence(x, y, p, con, prem1); @@ -2047,30 +2070,58 @@ public: { pv = p; rng = r; - weak = w; + weak = false ; //w; type boolintbooldom[3] = {bool_type(),int_type(),bool_type()}; type booldom[1] = {bool_type()}; type boolbooldom[2] = {bool_type(),bool_type()}; type boolboolbooldom[3] = {bool_type(),bool_type(),bool_type()}; type intbooldom[2] = {int_type(),bool_type()}; contra = function("@contra",2,boolbooldom,bool_type()); + m().inc_ref(contra); sum = function("@sum",3,boolintbooldom,bool_type()); + m().inc_ref(sum); rotate_sum = function("@rotsum",2,boolbooldom,bool_type()); + m().inc_ref(rotate_sum); leq2eq = function("@leq2eq",3,boolboolbooldom,bool_type()); + m().inc_ref(leq2eq); eq2leq = function("@eq2leq",2,boolbooldom,bool_type()); + m().inc_ref(eq2leq); cong = function("@cong",3,boolintbooldom,bool_type()); + m().inc_ref(cong); exmid = function("@exmid",3,boolboolbooldom,bool_type()); + m().inc_ref(exmid); symm = function("@symm",1,booldom,bool_type()); + m().inc_ref(symm); epsilon = make_var("@eps",int_type()); modpon = function("@mp",3,boolboolbooldom,bool_type()); + m().inc_ref(modpon); no_proof = make_var("@nop",bool_type()); concat = function("@concat",2,boolbooldom,bool_type()); + m().inc_ref(concat); top_pos = make_var("@top_pos",bool_type()); add_pos = function("@add_pos",2,intbooldom,bool_type()); + m().inc_ref(add_pos); rewrite_A = function("@rewrite_A",3,boolboolbooldom,bool_type()); + m().inc_ref(rewrite_A); rewrite_B = function("@rewrite_B",3,boolboolbooldom,bool_type()); + m().inc_ref(rewrite_B); } + ~iz3proof_itp_impl(){ + m().dec_ref(contra); + m().dec_ref(sum); + m().dec_ref(rotate_sum); + m().dec_ref(leq2eq); + m().dec_ref(eq2leq); + m().dec_ref(cong); + m().dec_ref(exmid); + m().dec_ref(symm); + m().dec_ref(modpon); + m().dec_ref(concat); + m().dec_ref(add_pos); + m().dec_ref(rewrite_A); + m().dec_ref(rewrite_B); + } }; iz3proof_itp *iz3proof_itp::create(prover *p, const prover::range &r, bool w){ diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index ff5d24228..04a9dd2c1 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -881,11 +881,11 @@ public: std::vector my_prems; std::vector my_coeffs; std::vector my_prem_cons; - for(unsigned i = 0; i < coeffs.size(); i++){ + for(unsigned i = pol ? 0 : 1; i < coeffs.size(); i+= 2){ rational &c = coeffs[i]; - if(pol ? c.is_pos() : c.is_neg()){ + if(c.is_pos()){ my_prems.push_back(prems[i]); - my_coeffs.push_back(pol ? make_int(c) : make_int(-c)); + my_coeffs.push_back(make_int(c)); my_prem_cons.push_back(conc(prem(proof,i))); } } diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 89fb19569..e540f5aaf 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -187,6 +187,10 @@ namespace datalog { if (m_trail.get_num_scopes() == 0) { throw default_exception("there are no backtracking points to pop to"); } + if(m_engine.get()){ + if(get_engine() != DUALITY_ENGINE) + throw default_exception("operation is not supported by engine"); + } m_trail.pop_scope(1); } diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 7de8f54a0..eb74f797f 100644 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -82,6 +82,7 @@ dl_interface::dl_interface(datalog::context& dl_ctx) : { _d = 0; + dl_ctx.get_manager().toggle_proof_mode(PGM_FINE); } diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index 612d319ad..827b90e60 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -454,6 +454,44 @@ public: } }; +/** + \brief fixedpoint-push command. +*/ +class dl_push_cmd : public cmd { + ref m_dl_ctx; +public: + dl_push_cmd(dl_context * dl_ctx): + cmd("fixedpoint-push"), + m_dl_ctx(dl_ctx) + {} + + virtual char const * get_usage() const { return ""; } + virtual char const * get_descr(cmd_context & ctx) const { return "push the fixedpoint context"; } + virtual unsigned get_arity() const { return 0; } + virtual void execute(cmd_context & ctx) { + m_dl_ctx->push(); + } +}; + +/** + \brief fixedpoint-pop command. +*/ +class dl_pop_cmd : public cmd { + ref m_dl_ctx; +public: + dl_pop_cmd(dl_context * dl_ctx): + cmd("fixedpoint-pop"), + m_dl_ctx(dl_ctx) + {} + + virtual char const * get_usage() const { return ""; } + virtual char const * get_descr(cmd_context & ctx) const { return "pop the fixedpoint context"; } + virtual unsigned get_arity() const { return 0; } + virtual void execute(cmd_context & ctx) { + m_dl_ctx->pop(); + } +}; + static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_cmds) { dl_context * dl_ctx = alloc(dl_context, ctx, collected_cmds); @@ -463,7 +501,7 @@ static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_c ctx.insert(alloc(dl_declare_var_cmd, dl_ctx)); // #ifndef _EXTERNAL_RELEASE // TODO: we need these! -#if 0 +#if 1 ctx.insert(alloc(dl_push_cmd, dl_ctx)); // not exposed to keep command-extensions simple. ctx.insert(alloc(dl_pop_cmd, dl_ctx)); #endif From d8972d4b178e978f7d236527b12055aa883ece47 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 5 Nov 2013 13:35:37 -0800 Subject: [PATCH 198/509] removed commented-out code --- src/ast/ast.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 36aeea9a0..640bc7ceb 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1305,10 +1305,6 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): } void ast_manager::init() { - // TODO: the following is a HACK to enable proofs in the old smt solver - // When we stop using that solver, this hack can be removed - // m_proof_mode = PGM_FINE; - m_int_real_coercions = true; m_debug_ref_count = false; m_fresh_id = 0; From 9f78c454c969264d8f02e6516f3545255f6990cd Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 5 Nov 2013 13:58:44 -0800 Subject: [PATCH 199/509] removed foci instructions --- README | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/README b/README index 5e3f4d11d..1ca0cd577 100644 --- a/README +++ b/README @@ -3,43 +3,26 @@ Z3 is licensed under MSR-LA (Microsoft Research License Agreement). See http://z3.codeplex.com/license for more information about this license. Z3 can be built using Visual Studio Command Prompt and make/g++. -++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -This is the interpolating branch. Build instruction differ from the -main branch. Please read carefully. - -Interpolation depends on the Cadence foci2 library obtainable from -here: - -http://www.kenmcmil.com/foci2/ - -Download the appropriate files for your operating system and -architecture. Note, the default Z3 build on intel architecture -is x86, not x64, so you need the 32-bit version of foci2. -++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 1) Building Z3 on Windows using Visual Studio Command Prompt - python scripts/mk_make.py -f + python scripts/mk_make.py cd build nmake - copy libfoci.dll 2) Building Z3 using make/g++ and Python Execute: - python scripts/mk_make.py -f + python scripts/mk_make.py cd build make sudo make install -Then put libfoci.so somewhere in your LD_LIBRARY_PATH. - By default, it will install z3 executable at PREFIX/bin, libraries at PREFIX/lib, and include files at PREFIX/include, where PREFIX is the installation prefix used for installing Python in your system. It is usually /usr for most Linux distros, and /usr/local for FreeBSD. Use the following commands to install in a different prefix (e.g., /home/leo) - python scripts/mk_make.py --prefix=/home/leo -f + python scripts/mk_make.py --prefix=/home/leo cd build make make install @@ -54,7 +37,7 @@ To uninstall Z3, use 4) Building Z3 using clang and clang++ on Linux/OSX Remark: clang does not support OpenMP yet. - CXX=clang++ CC=clang python scripts/mk_make.py -f + CXX=clang++ CC=clang python scripts/mk_make.py cd build make From f83bca11a03b7a394ae91d724fd02d2629c90e5d Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 5 Nov 2013 14:20:22 -0800 Subject: [PATCH 200/509] added interpolation options --- src/interp/iz3params.pyg | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/interp/iz3params.pyg diff --git a/src/interp/iz3params.pyg b/src/interp/iz3params.pyg new file mode 100644 index 000000000..5b574c859 --- /dev/null +++ b/src/interp/iz3params.pyg @@ -0,0 +1,5 @@ +def_module_params('interp', + description='interpolation parameters', + export=True, + params=(('profile', BOOL, False, '(INTERP) profile interpolation'), + )) From fa05116e662c58ce8a254e1fe7b2af6bc38454b9 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 5 Nov 2013 14:45:44 -0800 Subject: [PATCH 201/509] fixed vc++ compaibility issues --- src/interp/iz3mgr.cpp | 6 +++--- src/interp/iz3proof_itp.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index b9a3fd0b0..75948f348 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -708,7 +708,7 @@ iz3mgr::ast iz3mgr::mk_idiv(const ast& t, const ast &d){ // does variable occur in expression? -int iz3mgr::occurs_in1(hash_map &occurs_in_memo,ast var, ast e){ +int iz3mgr::occurs_in1(stl_ext::hash_map &occurs_in_memo,ast var, ast e){ std::pair foo(e,false); std::pair::iterator,bool> bar = occurs_in_memo.insert(foo); bool &res = bar.first->second; @@ -732,7 +732,7 @@ int iz3mgr::occurs_in(ast var, ast e){ // false would force the formula to have the specifid truth value // returns t, or null if no such -iz3mgr::ast iz3mgr::cont_eq(hash_set &cont_eq_memo, bool truth, ast v, ast e){ +iz3mgr::ast iz3mgr::cont_eq(stl_ext::hash_set &cont_eq_memo, bool truth, ast v, ast e){ if(is_not(e)) return cont_eq(cont_eq_memo, !truth,v,arg(e,0)); if(cont_eq_memo.find(e) != cont_eq_memo.end()) return ast(); @@ -759,7 +759,7 @@ iz3mgr::ast iz3mgr::cont_eq(hash_set &cont_eq_memo, bool truth, ast v, ast // substitute a term t for unbound occurrences of variable v in e -iz3mgr::ast iz3mgr::subst(hash_map &subst_memo, ast var, ast t, ast e){ +iz3mgr::ast iz3mgr::subst(stl_ext::hash_map &subst_memo, ast var, ast t, ast e){ if(e == var) return t; std::pair foo(e,ast()); std::pair::iterator,bool> bar = subst_memo.insert(foo); diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 9d15912c2..21a455a3a 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2058,7 +2058,7 @@ class iz3proof_itp_impl : public iz3proof_itp { bool is_placeholder(const ast &e){ if(op(e) == Uninterpreted){ std::string name = string_of_symbol(sym(e)); - if(name.size() > 2 && name[0] == '@' and name[1] == 'p') + if(name.size() > 2 && name[0] == '@' && name[1] == 'p') return true; } return false; From b008d036dddc40d45b3b4c5723d94bc412980729 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 5 Nov 2013 17:38:50 -0800 Subject: [PATCH 202/509] trying to fix proof mode issue --- src/muz/duality/duality_dl_interface.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index eb74f797f..25dfcebbd 100644 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -35,6 +35,7 @@ Revision History: #include "model_smt2_pp.h" #include "model_v2_pp.h" #include "fixedpoint_params.hpp" +#include "scoped_proof.h" // template class symbol_table; @@ -82,7 +83,7 @@ dl_interface::dl_interface(datalog::context& dl_ctx) : { _d = 0; - dl_ctx.get_manager().toggle_proof_mode(PGM_FINE); + // dl_ctx.get_manager().toggle_proof_mode(PGM_FINE); } @@ -131,6 +132,8 @@ lbool dl_interface::query(::expr * query) { if(old_data) old_cex = old_data->cex; + scoped_proof generate_proofs_please(m_ctx.get_manager()); + // make a new problem and solver _d = alloc(duality_data,m_ctx.get_manager()); _d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx); From 0696a7ef500bce553d7cb8b0ded1bef6cd6d8bfe Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 6 Nov 2013 11:41:17 -0800 Subject: [PATCH 203/509] interpolation fix --- src/interp/iz3proof_itp.cpp | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 21a455a3a..07f5f0cf5 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -636,12 +636,22 @@ class iz3proof_itp_impl : public iz3proof_itp { throw cannot_simplify(); } + void reverse_modpon(std::vector &args){ + std::vector sargs(1); sargs[0] = args[1]; + args[1] = simplify_symm(sargs); + if(is_equivrel_chain(args[2])) + args[1] = down_chain(args[1]); + std::swap(args[0],args[2]); + } + ast simplify_rotate_modpon(const ast &pl, const ast &neg_equality, const ast &pf){ - if(pl == arg(pf,2)){ - std::vector args; args.resize(3); - args[0] = arg(pf,0); - args[1] = arg(pf,1); - args[2] = mk_true(); + std::vector args; args.resize(3); + args[0] = arg(pf,0); + args[1] = arg(pf,1); + args[2] = arg(pf,2); + if(pl == args[0]) + reverse_modpon(args); + if(pl == args[2]){ ast cond = mk_true(); ast chain = simplify_modpon_fwd(args, cond); return my_implies(cond,chain); @@ -1410,7 +1420,7 @@ class iz3proof_itp_impl : public iz3proof_itp { return chain_cons(mk_true(),make_rewrite(t, top_pos, mk_true(), rew)); } - ast triv_interp(const symb &rule, const std::vector &premises){ + ast triv_interp(const symb &rule, const std::vector &premises, int mask_in){ std::vector ps; ps.resize(premises.size()); std::vector conjs; int mask = 0; @@ -1424,7 +1434,7 @@ class iz3proof_itp_impl : public iz3proof_itp { break; default: ps[i] = get_placeholder(p); // can only prove consequent! - if(i == ps.size()-1) + if(mask_in & (1 << i)) mask |= (1 << conjs.size()); conjs.push_back(p); } @@ -1434,12 +1444,12 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } - ast triv_interp(const symb &rule, const ast &p0, const ast &p1, const ast &p2){ + ast triv_interp(const symb &rule, const ast &p0, const ast &p1, const ast &p2, int mask){ std::vector ps; ps.resize(3); ps[0] = p0; ps[1] = p1; ps[2] = p2; - return triv_interp(rule,ps); + return triv_interp(rule,ps,mask); } /** Make a modus-ponens node. This takes derivations of |- x @@ -1452,7 +1462,10 @@ class iz3proof_itp_impl : public iz3proof_itp { ast q = arg(p_eq_q,1); ast itp; if(get_term_type(p_eq_q) == LitMixed){ - itp = triv_interp(modpon,p,p_eq_q,mk_not(q)); + int mask = 1 << 2; + if(op(p) == Not && is_equivrel(arg(p,0))) + mask |= 1; // we may need to run this rule backward if first premise is negative equality + itp = triv_interp(modpon,p,p_eq_q,mk_not(q),mask); } else { if(get_term_type(p) == LitA){ @@ -1850,7 +1863,7 @@ class iz3proof_itp_impl : public iz3proof_itp { conjs[0] = make(Equal,x,y); conjs[1] = mk_not(xleqy); itp = make(eq2leq,get_placeholder(conjs[0]),get_placeholder(conjs[1])); - itp = make_contra_node(itp,conjs); + itp = make_contra_node(itp,conjs,2); } } return itp; From 33f941aaec11bf7ef754d5779e581ba4a26b3018 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 6 Nov 2013 12:20:55 -0800 Subject: [PATCH 204/509] interpolation fix --- src/interp/iz3mgr.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 75948f348..2a15408f3 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -594,6 +594,7 @@ void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector Date: Thu, 7 Nov 2013 15:06:36 +0000 Subject: [PATCH 205/509] bugfix for pb2bv Signed-off-by: Christoph M. Wintersteiger --- src/tactic/arith/pb2bv_tactic.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tactic/arith/pb2bv_tactic.cpp b/src/tactic/arith/pb2bv_tactic.cpp index 83a949579..89195a9d5 100644 --- a/src/tactic/arith/pb2bv_tactic.cpp +++ b/src/tactic/arith/pb2bv_tactic.cpp @@ -480,7 +480,10 @@ private: break; } - SASSERT (i < sz); + if (i >= sz) { + // [Christoph]: In this case, all the m_a are equal to m_c. + return; + } // copy lits [0, i) to m_clause for (unsigned j = 0; j < i; j++) @@ -500,6 +503,7 @@ private: } void mk_pbc(polynomial & m_p, numeral & m_c, expr_ref & r, bool enable_split) { + TRACE("mk_pbc", display(tout, m_p, m_c); ); if (m_c.is_nonpos()) { // constraint is equivalent to true. r = m.mk_true(); @@ -507,7 +511,7 @@ private: } polynomial::iterator it = m_p.begin(); polynomial::iterator end = m_p.end(); - numeral a_gcd = it->m_a; + numeral a_gcd = (it->m_a > m_c) ? m_c : it->m_a; for (; it != end; ++it) { if (it->m_a > m_c) it->m_a = m_c; // trimming coefficients @@ -520,6 +524,7 @@ private: it->m_a /= a_gcd; m_c = ceil(m_c/a_gcd); } + TRACE("mk_pbc", tout << "GCD = " << a_gcd << "; Normalized: "; display(tout, m_p, m_c); tout << "\n"; ); it = m_p.begin(); numeral a_sum; for (; it != end; ++it) { From d9c69f5294132302d03761a5131158b5d23a9680 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 7 Nov 2013 15:13:39 -0800 Subject: [PATCH 206/509] handling commutativity rule in interpolation --- src/interp/iz3proof_itp.cpp | 24 ++---- src/interp/iz3translate.cpp | 147 ++++++++++++++++++++++++++++++++++-- 2 files changed, 146 insertions(+), 25 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 07f5f0cf5..0ede30fd9 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -1574,33 +1574,23 @@ class iz3proof_itp_impl : public iz3proof_itp { } /** Make a Symmetry node. This takes a derivation of |- x = y and - produces | y = x */ + produces | y = x. Ditto for ~(x=y) */ virtual node make_symmetry(ast con, const ast &premcon, node prem){ +#if 0 ast x = arg(con,0); ast y = arg(con,1); ast p = make(op(con),y,x); +#endif if(get_term_type(con) != LitMixed) return prem; // symmetry shmymmetry... -#if 0 - LitType xt = get_term_type(x); - LitType yt = get_term_type(y); - ast A_term; - - if(xt == LitA && yt == LitB) - A_term = x; - else if(yt == LitA && xt == LitB) - A_term = y; - else - ast itp = make(And,make(Equal,A_term,get_placeholder(p)),mk_not(con)); -#endif - ast em = make(exmid,con,make(symm,get_placeholder(p)),get_placeholder(mk_not(con))); + ast em = make(exmid,con,make(symm,get_placeholder(premcon)),get_placeholder(mk_not(con))); ast itp = make(And,make(contra,em,mk_false()), - make(contra,make(symm,get_placeholder(mk_not(con))),p), - make(contra,make(symm,get_placeholder(p)),mk_not(con))); + make(contra,make(symm,get_placeholder(mk_not(con))),premcon), + make(contra,make(symm,get_placeholder(premcon)),mk_not(con))); std::vector conc; conc.push_back(con); - itp = make_resolution(p,conc,itp,prem); + itp = make_resolution(premcon,conc,itp,prem); return itp; } diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 04a9dd2c1..5dc02d1fc 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -92,6 +92,8 @@ public: AstToAst subst_memo; // memo of subst function + symb commute; + public: @@ -1017,6 +1019,107 @@ public: throw unsupported(); } + + // Following code is for elimination of "commutativity" axiom + + Iproof::node make_commuted_modus_ponens(const ast &proof, const std::vector &args){ + ast pf = arg(args[1],0); + ast comm_equiv = arg(args[1],1); // equivalence relation with possible commutations + ast P = conc(prem(proof,0)); + ast Q = conc(proof); + Iproof::node P_pf = args[0]; + ast P_comm = arg(comm_equiv,0); + ast Q_comm = arg(comm_equiv,1); + if(P != P_comm) + P_pf = iproof->make_symmetry(P_comm,P,P_pf); + Iproof::node res = iproof->make_mp(comm_equiv,P_pf,pf); + if(Q != Q_comm) + res = iproof->make_symmetry(Q,Q_comm,res); + return res; + } + + Iproof::node make_commuted_monotonicity(const ast &proof, const std::vector &args){ + ast pf = arg(args[0],0); + ast comm_equiv = arg(args[0],1); // equivalence relation with possible commutations + ast con = make(Iff,make(Not,arg(comm_equiv,0)),make(Not,arg(comm_equiv,1))); + std::vector eqs; eqs.push_back(comm_equiv); + std::vector pfs; pfs.push_back(pf); + ast res = iproof->make_congruence(eqs,con,pfs); + res = make(commute,res,con); + return res; + } + + Iproof::node make_commuted_symmetry(const ast &proof, const std::vector &args){ + ast pf = arg(args[0],0); + ast comm_equiv = arg(args[0],1); // equivalence relation with possible commutations + ast con = make(Iff,arg(comm_equiv,1),arg(comm_equiv,0)); + ast res = iproof->make_symmetry(con,comm_equiv,pf); + res = make(commute,res,con); + return res; + } + + void unpack_commuted(const ast &proof, const ast &cm, ast &pf, ast &comm_equiv){ + if(sym(cm) == commute){ + pf = arg(cm,0); + comm_equiv = arg(cm,1); + } + else { + pf = cm; + comm_equiv = conc(proof); + } + } + + Iproof::node make_commuted_transitivity(const ast &proof, const std::vector &args){ + ast pf[2], comm_equiv[2]; + for(int i = 0; i < 2; i++) + unpack_commuted(prem(proof,i),args[i],pf[i],comm_equiv[i]); + if(!(arg(comm_equiv[0],1) == arg(comm_equiv[1],0))){ + ast tw = twist(prem(proof,1)); + ast np = translate_main(tw,false); + unpack_commuted(tw,np,pf[1],comm_equiv[1]); + } + ast con = make(Iff,arg(comm_equiv[0],0),arg(comm_equiv[1],1)); + ast res = iproof->make_transitivity(arg(comm_equiv[0],0),arg(comm_equiv[0],1),arg(comm_equiv[1],1),pf[0],pf[1]); + res = make(commute,res,con); + return res; + } + + ast commute_equality(const ast &eq){ + return make(Equal,arg(eq,1),arg(eq,0)); + } + + ast commute_equality_iff(const ast &con){ + if(op(con) != Iff || op(arg(con,0)) != Equal) + throw unsupported(); + return make(Iff,commute_equality(arg(con,0)),commute_equality(arg(con,1))); + } + + // convert a proof of a=b <-> c=d into a proof of b=a <-> d=c + // TODO: memoize this? + ast twist(const ast &proof){ + pfrule dk = pr(proof); + ast con = commute_equality_iff(conc(proof)); + int n = num_prems(proof); + std::vector prs(n); + if(dk == PR_MONOTONICITY){ + for(int i = 0; i < n; i++) + prs[i] = prem(proof,i); + } + else + for(int i = 0; i < n; i++) + prs[i] = twist(prem(proof,i)); + switch(dk){ + case PR_MONOTONICITY: + case PR_SYMMETRY: + case PR_TRANSITIVITY: + case PR_COMMUTATIVITY: + prs.push_back(con); + return clone(proof,prs); + default: + throw unsupported(); + } + } + // translate a Z3 proof term into interpolating proof system Iproof::node translate_main(ast proof, bool expect_clause = true){ @@ -1086,25 +1189,43 @@ public: if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or) std::cout << "foo!\n"; + if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,1)) == PR_COMMUTATIVITY){ + Iproof::node clause = translate_main(prem(proof,0),true); + res = make(commute,clause,conc(prem(proof,0))); // HACK -- we depend on Iproof::node being same as ast. + return res; + } + // translate all the premises std::vector args(nprems); for(unsigned i = 0; i < nprems; i++) args[i] = translate_main(prem(proof,i),false); + for(unsigned i = 0; i < nprems; i++) + if(sym(args[i]) == commute + && !(dk == PR_TRANSITIVITY || dk == PR_MODUS_PONENS || dk == PR_SYMMETRY || (dk == PR_MONOTONICITY && op(arg(con,0)) == Not))) + throw unsupported(); + switch(dk){ case PR_TRANSITIVITY: { - // assume the premises are x = y, y = z - ast x = arg(conc(prem(proof,0)),0); - ast y = arg(conc(prem(proof,0)),1); - ast z = arg(conc(prem(proof,1)),1); - res = iproof->make_transitivity(x,y,z,args[0],args[1]); + if(sym(args[0]) == commute || sym(args[1]) == commute) + res = make_commuted_transitivity(proof,args); + else { + // assume the premises are x = y, y = z + ast x = arg(conc(prem(proof,0)),0); + ast y = arg(conc(prem(proof,0)),1); + ast z = arg(conc(prem(proof,1)),1); + res = iproof->make_transitivity(x,y,z,args[0],args[1]); + } break; } case PR_MONOTONICITY: { std::vector eqs; eqs.resize(args.size()); for(unsigned i = 0; i < args.size(); i++) eqs[i] = conc(prem(proof,i)); - res = iproof->make_congruence(eqs,con,args); + if(op(arg(con,0)) == Not && sym(args[0]) == commute) + res = make_commuted_monotonicity(proof,args); + else + res = iproof->make_congruence(eqs,con,args); break; } case PR_REFLEXIVITY: { @@ -1112,11 +1233,17 @@ public: break; } case PR_SYMMETRY: { - res = iproof->make_symmetry(con,conc(prem(proof,0)),args[0]); + if(sym(args[0]) == commute) + res = make_commuted_symmetry(proof,args); + else + res = iproof->make_symmetry(con,conc(prem(proof,0)),args[0]); break; } case PR_MODUS_PONENS: { - res = iproof->make_mp(conc(prem(proof,1)),args[0],args[1]); + if(sym(args[1]) == commute) + res = make_commuted_modus_ponens(proof,args); + else + res = iproof->make_mp(conc(prem(proof,1)),args[0],args[1]); break; } case PR_TH_LEMMA: { @@ -1233,9 +1360,13 @@ public: { frames = cnsts.size(); traced_lit = ast(); + type boolbooldom[2] = {bool_type(),bool_type()}; + commute = function("@commute",2,boolbooldom,bool_type()); + m().inc_ref(commute); } ~iz3translation_full(){ + m().dec_ref(commute); } }; From cf176af48e403a15cca38c4f42d3e20458086eba Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 7 Nov 2013 15:40:44 -0800 Subject: [PATCH 207/509] looking for more farkas rules in interpolation --- src/interp/iz3mgr.cpp | 5 +++++ src/interp/iz3translate.cpp | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 2a15408f3..9ef2fa9ab 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -489,11 +489,15 @@ void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ symb s = sym(proof); int numps = s->get_num_parameters(); rats.resize(numps-2); + if(num_prems(proof) < numps-2){ + std::cout << "bad farkas rule: " << num_prems(proof) << " premises should be " << numps-2 << "\n"; + } for(int i = 2; i < numps; i++){ rational r; bool ok = s->get_parameter(i).is_rational(r); if(!ok) throw "Bad Farkas coefficient"; +#if 0 { ast con = conc(prem(proof,i-2)); ast temp = make_real(r); // for debugging @@ -501,6 +505,7 @@ void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) r = -r; } +#endif rats[i-2] = r; } #if 0 diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 5dc02d1fc..4feb35faa 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1253,6 +1253,10 @@ public: case FarkasKind: { std::vector farkas_coeffs, prem_cons; get_farkas_coeffs(proof,farkas_coeffs); + if(farkas_coeffs.size() != nprems){ + pfgoto(proof); + throw unsupported(); + } prem_cons.resize(nprems); for(unsigned i = 0; i < nprems; i++) prem_cons[i] = conc(prem(proof,i)); @@ -1279,6 +1283,10 @@ public: case GCDTestKind: { std::vector farkas_coeffs; get_farkas_coeffs(proof,farkas_coeffs); + if(farkas_coeffs.size() != nprems){ + pfgoto(proof); + throw unsupported(); + } std::vector my_prems; my_prems.resize(2); std::vector my_prem_cons; my_prem_cons.resize(2); std::vector my_farkas_coeffs; my_farkas_coeffs.resize(2); From b076c152b34ccf8f2822b83b23ee9901d53e056e Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 7 Nov 2013 16:17:56 -0800 Subject: [PATCH 208/509] adding farkas axiom to interpolation --- src/interp/iz3translate.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 4feb35faa..ede4286fb 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1253,13 +1253,23 @@ public: case FarkasKind: { std::vector farkas_coeffs, prem_cons; get_farkas_coeffs(proof,farkas_coeffs); - if(farkas_coeffs.size() != nprems){ - pfgoto(proof); - throw unsupported(); + if(nprems == 0) {// axiom, not rule + int nargs = num_args(con); + if(farkas_coeffs.size() != nargs){ + pfgoto(proof); + throw unsupported(); + } + for(unsigned i = 0; i < nargs; i++){ + ast lit = mk_not(arg(con,i)); + prem_cons.push_back(lit); + args.push_back(iproof->make_hypothesis(lit)); + } + } + else { // rule version (proves false) + prem_cons.resize(nprems); + for(unsigned i = 0; i < nprems; i++) + prem_cons[i] = conc(prem(proof,i)); } - prem_cons.resize(nprems); - for(unsigned i = 0; i < nprems; i++) - prem_cons[i] = conc(prem(proof,i)); res = iproof->make_farkas(con,args,prem_cons,farkas_coeffs); break; } From a898bad961c55f7db32e4738108d7efac0c40ff4 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 7 Nov 2013 17:38:39 -0800 Subject: [PATCH 209/509] fixed two interpolation bugs --- src/interp/iz3proof_itp.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 0ede30fd9..66ef51cd2 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -417,6 +417,10 @@ class iz3proof_itp_impl : public iz3proof_itp { std::pair::iterator,bool> bar = subst_memo.insert(foo); ast &res = bar.first->second; if(bar.second){ + if(sym(e) == rotate_sum && var == get_placeholder(arg(e,0))){ + res = e; + return res; + } int nargs = num_args(e); std::vector args(nargs); for(int i = 0; i < nargs; i++) @@ -495,6 +499,8 @@ class iz3proof_itp_impl : public iz3proof_itp { if(g == modpon) return simplify_rotate_modpon(pl,args[0],pf); // if(g == symm) return simplify_rotate_symm(pl,args[0],pf); } + if(op(pf) == Leq) + throw "foo!"; throw cannot_simplify(); } @@ -1355,17 +1361,18 @@ class iz3proof_itp_impl : public iz3proof_itp { } void eq_from_ineq(const ast &ineq, ast &lhs, ast &rhs){ - ast s = ineq_to_lhs(ineq); - ast srhs = arg(s,1); + // ast s = ineq_to_lhs(ineq); + // ast srhs = arg(s,1); + ast srhs = arg(ineq,0); if(op(srhs) == Plus && num_args(srhs) == 2){ lhs = arg(srhs,0); rhs = arg(srhs,1); - if(op(lhs) == Times) - std::swap(lhs,rhs); + // if(op(lhs) == Times) + // std::swap(lhs,rhs); if(op(rhs) == Times){ rhs = arg(rhs,1); - if(op(ineq) == Leq) - std::swap(lhs,rhs); + // if(op(ineq) == Leq) + // std::swap(lhs,rhs); return; } } From 763ae0d246871446ab2413530fb9160e9e9cc4e2 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 7 Nov 2013 17:42:18 -0800 Subject: [PATCH 210/509] removed debugginf message in interpolation --- src/interp/iz3mgr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 9ef2fa9ab..f5c7de2ad 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -489,9 +489,11 @@ void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ symb s = sym(proof); int numps = s->get_num_parameters(); rats.resize(numps-2); +#if 0 if(num_prems(proof) < numps-2){ std::cout << "bad farkas rule: " << num_prems(proof) << " premises should be " << numps-2 << "\n"; } +#endif for(int i = 2; i < numps; i++){ rational r; bool ok = s->get_parameter(i).is_rational(r); From 86f39cd4c19f285a82a4857d01eed38c742fa7b3 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 8 Nov 2013 19:21:55 +0000 Subject: [PATCH 211/509] Changed references to _DEBUG to Z3DEBUG. (gcc does not define _DEBUG for debug builds.) Signed-off-by: Christoph M. Wintersteiger --- src/ast/simplifier/array_simplifier_plugin.cpp | 2 +- src/tactic/fpa/fpa2bv_converter.cpp | 6 +++--- src/tactic/sls/sls_tactic.cpp | 4 ++-- src/util/mpn.cpp | 18 +++++++++--------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ast/simplifier/array_simplifier_plugin.cpp b/src/ast/simplifier/array_simplifier_plugin.cpp index bf9ca8ffe..065f8bd96 100644 --- a/src/ast/simplifier/array_simplifier_plugin.cpp +++ b/src/ast/simplifier/array_simplifier_plugin.cpp @@ -68,7 +68,7 @@ bool array_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * co set_reduce_invoked(); if (m_presimp) return false; -#if _DEBUG +#if Z3DEBUG for (unsigned i = 0; i < num_args && i < f->get_arity(); ++i) { SASSERT(m_manager.get_sort(args[i]) == f->get_domain(i)); } diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index ff24d1ceb..011169496 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -140,7 +140,7 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { s_sig = m_bv_util.mk_sort(sbits-1); s_exp = m_bv_util.mk_sort(ebits); -#ifdef _DEBUG +#ifdef Z3DEBUG std::string p("fpa2bv"); std::string name = f->get_name().str(); @@ -271,7 +271,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { SASSERT(is_rm_sort(f->get_range())); result = m.mk_fresh_const( - #ifdef _DEBUG + #ifdef Z3DEBUG "fpa2bv_rm" #else 0 @@ -2345,7 +2345,7 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) } void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { - #ifdef _DEBUG + #ifdef Z3DEBUG return; // CMW: This works only for quantifier-free formulas. expr_ref new_e(m); diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index fff082bd8..ea5d60668 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -189,14 +189,14 @@ class sls_tactic : public tactic { bool what_if(goal_ref const & g, func_decl * fd, const unsigned & fd_inx, const mpz & temp, double & best_score, unsigned & best_const, mpz & best_value) { - #ifdef _DEBUG + #ifdef Z3DEBUG mpz old_value; m_mpz_manager.set(old_value, m_tracker.get_value(fd)); #endif double r = incremental_score(g, fd, temp); - #ifdef _DEBUG + #ifdef Z3DEBUG TRACE("sls_whatif", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) << " --> " << r << std::endl; ); diff --git a/src/util/mpn.cpp b/src/util/mpn.cpp index 2cc35776f..6a544f583 100644 --- a/src/util/mpn.cpp +++ b/src/util/mpn.cpp @@ -31,7 +31,7 @@ mpn_manager static_mpn_manager; const mpn_digit mpn_manager::zero = 0; mpn_manager::mpn_manager() { -#ifdef _DEBUG +#ifdef Z3DEBUG trace_enabled=true; #endif } @@ -43,7 +43,7 @@ int mpn_manager::compare(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb) const { int res = 0; - #ifdef _DEBUG + #ifdef Z3DEBUG if (trace_enabled) STRACE("mpn", tout << "[mpn] "; ); #endif @@ -60,7 +60,7 @@ int mpn_manager::compare(mpn_digit const * a, size_t const lnga, res = -1; } - #ifdef _DEBUG + #ifdef Z3DEBUG if (trace_enabled) STRACE("mpn", tout << ((res == 1) ? " > " : (res == -1) ? " < " : " == "); ); #endif @@ -212,7 +212,7 @@ bool mpn_manager::div(mpn_digit const * numer, size_t const lnum, trace(numer, lnum, denom, lden, "%"); trace_nl(rem, lden); -#ifdef _DEBUG +#ifdef Z3DEBUG mpn_sbuffer temp(lnum+1, 0); mul(quot, lnum-lden+1, denom, lden, temp.c_ptr()); size_t real_size; @@ -340,7 +340,7 @@ bool mpn_manager::div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom, // Replace numer[j+n]...numer[j] with // numer[j+n]...numer[j] - q * (denom[n-1]...denom[0]) mpn_digit q_hat_small = (mpn_digit)q_hat; - #ifdef _DEBUG + #ifdef Z3DEBUG trace_enabled = false; #endif mul(&q_hat_small, 1, denom.c_ptr(), n, t_ms.c_ptr()); @@ -354,7 +354,7 @@ bool mpn_manager::div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom, for (size_t i = 0; i < n+1; i++) numer[j+i] = t_ab[i]; } - #ifdef _DEBUG + #ifdef Z3DEBUG trace_enabled = true; #endif STRACE("mpn_div", tout << "q_hat=" << q_hat << " r_hat=" << r_hat; @@ -416,7 +416,7 @@ void mpn_manager::display_raw(std::ostream & out, mpn_digit const * a, size_t co void mpn_manager::trace(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, const char * op) const { -#ifdef _DEBUG +#ifdef Z3DEBUG if (trace_enabled) STRACE("mpn", tout << "[mpn] " << to_string(a, lnga, char_buf, sizeof(char_buf)); tout << " " << op << " " << to_string(b, lngb, char_buf, sizeof(char_buf)); @@ -425,14 +425,14 @@ void mpn_manager::trace(mpn_digit const * a, size_t const lnga, } void mpn_manager::trace(mpn_digit const * a, size_t const lnga) const { -#ifdef _DEBUG +#ifdef Z3DEBUG if (trace_enabled) STRACE("mpn", tout << to_string(a, lnga, char_buf, sizeof(char_buf)); ); #endif } void mpn_manager::trace_nl(mpn_digit const * a, size_t const lnga) const { -#ifdef _DEBUG +#ifdef Z3DEBUG if (trace_enabled) STRACE("mpn", tout << to_string(a, lnga, char_buf, sizeof(char_buf)) << std::endl; ); #endif From 749f95c9d71b5e0aee5ebfc50a2dac8529b84c41 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 8 Nov 2013 16:18:48 -0800 Subject: [PATCH 212/509] handle eq-propagate arithetic rule --- src/interp/iz3base.cpp | 31 +++++++--- src/interp/iz3base.h | 2 +- src/interp/iz3mgr.cpp | 20 ++++++ src/interp/iz3mgr.h | 10 ++- src/interp/iz3translate.cpp | 117 +++++++++++++++++++++++++++++++++++- 5 files changed, 166 insertions(+), 14 deletions(-) diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 6ea8c2d7f..f316c22cf 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -24,6 +24,8 @@ Revision History: #include #include #include +#include "solver.h" +#include "../smt/smt_solver.h" #ifndef WIN32 @@ -254,16 +256,25 @@ void iz3base::check_interp(const std::vector &itps, std::vector &theor #endif } -bool iz3base::is_sat(ast f){ - assert(0 && "iz3base::is_sat() not implemented"); -#if 0 - Z3_push(ctx); - Z3_assert_cnstr(ctx,f); - Z3_lbool res = Z3_check(ctx); - Z3_pop(ctx,1); - return res != Z3_L_FALSE; -#endif - return false; +bool iz3base::is_sat(const std::vector &q, ast &_proof){ + + params_ref p; + p.set_bool("proof", true); // this is currently useless + p.set_bool("model", true); + p.set_bool("unsat_core", true); + scoped_ptr sf = mk_smt_solver_factory(); + ::solver *m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); + ::solver &s = *m_solver; + + for(unsigned i = 0; i < q.size(); i++) + s.assert_expr(to_expr(q[i].raw())); + lbool res = s.check_sat(0,0); + if(res == l_false){ + ::ast *proof = s.get_proof(); + _proof = cook(proof); + } + dealloc(m_solver); + return res != l_false; } diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index 0e57cf5e3..0b7521b38 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -103,7 +103,7 @@ class iz3base : public iz3mgr, public scopes { void check_interp(const std::vector &itps, std::vector &theory); /** For convenience -- is this formula SAT? */ - bool is_sat(ast); + bool is_sat(const std::vector &consts, ast &_proof); /** Interpolator for clauses, to be implemented */ virtual void interpolate_clause(std::vector &lits, std::vector &itps){ diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index f5c7de2ad..2422cb0b4 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -446,6 +446,17 @@ iz3mgr::ast iz3mgr::z3_simplify(const ast &e){ return cook(result); } +iz3mgr::ast iz3mgr::z3_really_simplify(const ast &e){ + ::expr * a = to_expr(e.raw()); + params_ref simp_params; + simp_params.set_bool(":som",true); + simp_params.set_bool(":sort-sums",true); + th_rewriter m_rw(m(), simp_params); + expr_ref result(m()); + m_rw(a, result); + return cook(result); +} + #if 0 static rational lcm(const rational &x, const rational &y){ @@ -485,6 +496,15 @@ static void abs_rat(std::vector &rats){ } } +bool iz3mgr::is_farkas_coefficient_negative(const ast &proof, int n){ + rational r; + symb s = sym(proof); + bool ok = s->get_parameter(n+2).is_rational(r); + if(!ok) + throw "Bad Farkas coefficient"; + return r.is_neg(); +} + void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ symb s = sym(proof); int numps = s->get_num_parameters(); diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 213c23e7e..f6c0bdf87 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -386,7 +386,7 @@ class iz3mgr { return UnknownTheory; } - enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,UnknownKind}; + enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,UnknownKind}; lemma_kind get_theory_lemma_kind(const ast &proof){ symb s = sym(proof); @@ -402,6 +402,8 @@ class iz3mgr { return GCDTestKind; if(foo == "assign-bounds") return AssignBoundsKind; + if(foo == "eq-propagate") + return EqPropagateKind; return UnknownKind; } @@ -417,6 +419,8 @@ class iz3mgr { void get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats); + bool is_farkas_coefficient_negative(const ast &proof, int n); + bool is_true(ast t){ return op(t) == True; } @@ -441,6 +445,10 @@ class iz3mgr { ast z3_simplify(const ast& e); + /** Simplify, sorting sums */ + ast z3_really_simplify(const ast &e); + + // Some constructors that simplify things ast mk_not(ast x){ diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index ede4286fb..002c00599 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -782,6 +782,16 @@ public: return thing; } + bool check_farkas(const std::vector &prems, const ast &con){ + ast zero = make_int("0"); + ast thing = make(Leq,zero,zero); + for(unsigned i = 0; i < prems.size(); i++) + linear_comb(thing,make_int(rational(1)),prems[i]); + linear_comb(thing,make_int(rational(-1)),con); + thing = simplify_ineq(thing); + return arg(thing,1) == make_int(rational(0)); + } + // get an inequality in the form t <= c or t < c, there t is affine and c constant ast normalize_inequality(const ast &ineq){ ast zero = make_int("0"); @@ -1120,6 +1130,92 @@ public: } } + struct TermLt { + iz3mgr &m; + bool operator()(const ast &x, const ast &y){ + unsigned xid = m.ast_id(x); + unsigned yid = m.ast_id(y); + return xid < yid; + } + TermLt(iz3mgr &_m) : m(_m) {} + }; + + void SortTerms(std::vector &terms){ + TermLt foo(*this); + std::sort(terms.begin(),terms.end(),foo); + } + + ast SortSum(const ast &t){ + if(!(op(t) == Plus)) + return t; + int nargs = num_args(t); + if(nargs < 2) return t; + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = arg(t,i); + SortTerms(args); + return make(Plus,args); + } + + ast really_normalize_ineq(const ast &ineq){ + ast res = normalize_inequality(ineq); + res = make(op(res),SortSum(arg(res,0)),arg(res,1)); + return res; + } + + Iproof::node reconstruct_farkas(const std::vector &prems, const std::vector &pfs, const ast &con){ + int nprems = prems.size(); + std::vector pcons(nprems),npcons(nprems); + hash_map pcon_to_pf, npcon_to_pcon; + for(int i = 0; i < nprems; i++){ + pcons[i] = conc(prems[i]); + npcons[i] = really_normalize_ineq(pcons[i]); + pcon_to_pf[npcons[i]] = pfs[i]; + npcon_to_pcon[npcons[i]] = pcons[i]; + } + // ast leq = make(Leq,arg(con,0),arg(con,1)); + ast ncon = really_normalize_ineq(mk_not(con)); + pcons.push_back(mk_not(con)); + npcons.push_back(ncon); + // ast assumps = make(And,pcons); + ast new_proof; + if(is_sat(npcons,new_proof)) + throw "Proof error!"; + pfrule dk = pr(new_proof); + int nnp = num_prems(new_proof); + std::vector my_prems; + std::vector farkas_coeffs, my_pcons; + + if(dk == PR_TH_LEMMA + && get_theory_lemma_theory(new_proof) == ArithTheory + && get_theory_lemma_kind(new_proof) == FarkasKind) + get_farkas_coeffs(new_proof,farkas_coeffs); + else if(dk == PR_UNIT_RESOLUTION && nnp == 2){ + for(int i = 0; i < nprems; i++) + farkas_coeffs.push_back(make_int(rational(1))); + } + else + throw "cannot reconstruct farkas proof"; + + for(int i = 0; i < nnp; i++){ + ast p = conc(prem(new_proof,i)); + p = really_normalize_ineq(p); + if(pcon_to_pf.find(p) != pcon_to_pf.end()){ + my_prems.push_back(pcon_to_pf[p]); + my_pcons.push_back(npcon_to_pcon[p]); + } + else if(p == ncon){ + my_prems.push_back(iproof->make_hypothesis(mk_not(con))); + my_pcons.push_back(mk_not(con)); + } + else + throw "cannot reconstruct farkas proof"; + } + Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); + return res; + } + + // translate a Z3 proof term into interpolating proof system Iproof::node translate_main(ast proof, bool expect_clause = true){ @@ -1255,11 +1351,11 @@ public: get_farkas_coeffs(proof,farkas_coeffs); if(nprems == 0) {// axiom, not rule int nargs = num_args(con); - if(farkas_coeffs.size() != nargs){ + if(farkas_coeffs.size() != (unsigned)nargs){ pfgoto(proof); throw unsupported(); } - for(unsigned i = 0; i < nargs; i++){ + for(int i = 0; i < nargs; i++){ ast lit = mk_not(arg(con,i)); prem_cons.push_back(lit); args.push_back(iproof->make_hypothesis(lit)); @@ -1314,6 +1410,23 @@ public: res = AssignBounds2Farkas(proof,conc(proof)); break; } + case EqPropagateKind: { + std::vector prems(nprems); + Iproof::node fps[2]; + ast ineq_con[2]; + for(int i = 0; i < nprems; i++) + prems[i] = prem(proof,i); + for(int i = 0; i < 2; i++){ + opr o = i == 0 ? Leq : Geq; + ineq_con[i] = make(o, arg(con,0), arg(con,1)); + fps[i] = reconstruct_farkas(prems,args,ineq_con[i]); + } + res = iproof->make_leq2eq(arg(con,0), arg(con,1), ineq_con[0], ineq_con[1]); + std::vector dummy_clause; + for(int i = 0; i < 2; i++) + res = iproof->make_resolution(ineq_con[i],dummy_clause,res,fps[i]); + return res; + } default: throw unsupported(); } From 2924b1acc60b7d63c114a3da621dd670dce9e329 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sat, 9 Nov 2013 14:51:44 +0000 Subject: [PATCH 213/509] fixed reference to _DEBUG --- src/util/mpn.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/mpn.h b/src/util/mpn.h index f995004c2..f8ae1eabf 100644 --- a/src/util/mpn.h +++ b/src/util/mpn.h @@ -101,7 +101,7 @@ private: bool div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom, mpn_digit * quot, mpn_digit * rem); - #ifdef _DEBUG + #ifdef Z3DEBUG mutable char char_buf[4096]; bool trace_enabled; #endif From 7a718d4e07a20804f73c494d4b095fd9cf9c331a Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sat, 9 Nov 2013 14:57:45 +0000 Subject: [PATCH 214/509] fixed tabs --- src/util/mpn.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/mpn.h b/src/util/mpn.h index f8ae1eabf..219368de3 100644 --- a/src/util/mpn.h +++ b/src/util/mpn.h @@ -101,7 +101,7 @@ private: bool div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom, mpn_digit * quot, mpn_digit * rem); - #ifdef Z3DEBUG + #ifdef Z3DEBUG mutable char char_buf[4096]; bool trace_enabled; #endif From c4f7b4d0d49dd6a057b1b8abafdbd6ed9a181d9c Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sat, 9 Nov 2013 13:55:01 -0800 Subject: [PATCH 215/509] remove duality junk on stdout --- src/duality/duality_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 4437f0591..0043998d2 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -2107,7 +2107,7 @@ namespace Duality { if(j < old_chs.size() && BaseName(chs[i]->Name.name().str()) == BaseName(old_chs[j]->Name.name().str())) cex_map[chs[i]] = old_chs[j++]; else { - std::cout << "unmatched child: " << chs[i]->Name.name() << std::endl; + std::cerr << "WARNING: duality: unmatched child: " << chs[i]->Name.name() << std::endl; cex_map[chs[i]] = 0; } } From e1a6c5098d47385e4d68f2488cc0f45659046b86 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 11 Nov 2013 17:33:02 +0000 Subject: [PATCH 216/509] fixed memory leak Signed-off-by: Christoph M. Wintersteiger --- src/ast/ast.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 640bc7ceb..bdf1c18db 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -922,6 +922,7 @@ void basic_decl_plugin::finalize() { DEC_REF(m_and_decl); DEC_REF(m_or_decl); DEC_REF(m_not_decl); + DEC_REF(m_interp_decl); DEC_REF(m_iff_decl); DEC_REF(m_xor_decl); DEC_REF(m_implies_decl); From d73310cfa126e5ad273a92fd5bde7281a10e9b40 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 12 Nov 2013 12:38:30 -0800 Subject: [PATCH 217/509] working on eq-propagate rule in interpolation --- src/interp/iz3proof_itp.cpp | 2 + src/interp/iz3translate.cpp | 81 ++++++++++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 66ef51cd2..e71e3704c 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -1831,6 +1831,8 @@ class iz3proof_itp_impl : public iz3proof_itp { itp = mk_true(); break; default: { // mixed equality + if(get_term_type(x) == LitMixed || get_term_type(y) == LitMixed) + std::cerr << "WARNING: mixed term in leq2eq\n"; std::vector conjs; conjs.resize(3); conjs[0] = mk_not(con); conjs[1] = xleqy; diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 002c00599..f755e6772 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1215,7 +1215,61 @@ public: return res; } - + bool is_eq_propagate(const ast &proof){ + return pr(proof) == PR_TH_LEMMA && get_theory_lemma_theory(proof) == ArithTheory && get_theory_lemma_kind(proof) == EqPropagateKind; + } + + ast EqPropagate(const ast &con, const std::vector &prems, const std::vector &args){ + Iproof::node fps[2]; + ast ineq_con[2]; + for(int i = 0; i < 2; i++){ + opr o = i == 0 ? Leq : Geq; + ineq_con[i] = make(o, arg(con,0), arg(con,1)); + fps[i] = reconstruct_farkas(prems,args,ineq_con[i]); + } + ast res = iproof->make_leq2eq(arg(con,0), arg(con,1), ineq_con[0], ineq_con[1]); + std::vector dummy_clause; + for(int i = 0; i < 2; i++) + res = iproof->make_resolution(ineq_con[i],dummy_clause,res,fps[i]); + return res; + } + + struct CannotCombineEqPropagate {}; + + void CombineEqPropagateRec(const ast &proof, std::vector &prems, std::vector &args, ast &eqprem){ + if(pr(proof) == PR_TRANSITIVITY && is_eq_propagate(prem(proof,1))){ + CombineEqPropagateRec(prem(proof,0), prems, args, eqprem); + ast dummy; + CombineEqPropagateRec(prem(proof,1), prems, args, dummy); + return; + } + if(is_eq_propagate(proof)){ + int nprems = num_prems(proof); + for(int i = 0; i < nprems; i++){ + prems.push_back(prem(proof,i)); + ast ppf = translate_main(prem(proof,i),false); + args.push_back(ppf); + } + return; + } + eqprem = proof; + } + + ast CombineEqPropagate(const ast &proof){ + std::vector prems, args; + ast eq1; + CombineEqPropagateRec(proof, prems, args, eq1); + ast eq2con = conc(proof); + if(!eq1.null()) + eq2con = make(Equal,arg(conc(eq1),1),arg(conc(proof),1)); + ast eq2 = EqPropagate(eq2con,prems,args); + if(!eq1.null()){ + Iproof::node foo = translate_main(eq1,false); + eq2 = iproof->make_transitivity(arg(conc(eq1),0), arg(conc(eq1),1), arg(conc(proof),1), foo, eq2); + } + return eq2; + } + // translate a Z3 proof term into interpolating proof system Iproof::node translate_main(ast proof, bool expect_clause = true){ @@ -1290,6 +1344,15 @@ public: res = make(commute,clause,conc(prem(proof,0))); // HACK -- we depend on Iproof::node being same as ast. return res; } + + if(dk == PR_TRANSITIVITY && is_eq_propagate(prem(proof,1))){ + try { + res = CombineEqPropagate(proof); + return res; + } + catch(const CannotCombineEqPropagate &){ + } + } // translate all the premises std::vector args(nprems); @@ -1412,20 +1475,10 @@ public: } case EqPropagateKind: { std::vector prems(nprems); - Iproof::node fps[2]; - ast ineq_con[2]; - for(int i = 0; i < nprems; i++) + for(unsigned i = 0; i < nprems; i++) prems[i] = prem(proof,i); - for(int i = 0; i < 2; i++){ - opr o = i == 0 ? Leq : Geq; - ineq_con[i] = make(o, arg(con,0), arg(con,1)); - fps[i] = reconstruct_farkas(prems,args,ineq_con[i]); - } - res = iproof->make_leq2eq(arg(con,0), arg(con,1), ineq_con[0], ineq_con[1]); - std::vector dummy_clause; - for(int i = 0; i < 2; i++) - res = iproof->make_resolution(ineq_con[i],dummy_clause,res,fps[i]); - return res; + res = EqPropagate(con,prems,args); + break; } default: throw unsupported(); From 6a2f987fb7953063bfa462edf1938b307dbde529 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 14 Nov 2013 16:56:13 +0000 Subject: [PATCH 218/509] optimizations for float to float conversions Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 232 ++++++++++++++-------------- src/util/mpf.cpp | 2 + 2 files changed, 120 insertions(+), 114 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 011169496..71c6b689e 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1825,146 +1825,150 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a mk_triple(args[0], args[1], args[2], result); } else if (num == 2 && is_app(args[1]) && m_util.is_float(m.get_sort(args[1]))) { - // We also support float to float conversion + // We also support float to float conversion + sort * s = f->get_range(); expr_ref rm(m), x(m); rm = args[0]; x = args[1]; - - expr_ref c1(m), c2(m), c3(m), c4(m), c5(m); - expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m); - expr_ref one1(m); - one1 = m_bv_util.mk_numeral(1, 1); - expr_ref ninf(m), pinf(m); - mk_plus_inf(f, pinf); - mk_minus_inf(f, ninf); - - // NaN -> NaN - mk_is_nan(x, c1); - mk_nan(f, v1); - - // +0 -> +0 - mk_is_pzero(x, c2); - mk_pzero(f, v2); - - // -0 -> -0 - mk_is_nzero(x, c3); - mk_nzero(f, v3); - - // +oo -> +oo - mk_is_pinf(x, c4); - v4 = pinf; - - // -oo -> -oo - mk_is_ninf(x, c5); - v5 = ninf; - - // otherwise: the actual conversion with rounding. - sort * s = f->get_range(); - expr_ref sgn(m), sig(m), exp(m), lz(m); - unpack(x, sgn, sig, exp, lz, true); - - dbg_decouple("fpa2bv_to_float_x_sig", sig); - dbg_decouple("fpa2bv_to_float_x_exp", exp); - dbg_decouple("fpa2bv_to_float_lz", lz); - - expr_ref res_sgn(m), res_sig(m), res_exp(m); - - res_sgn = sgn; - unsigned from_sbits = m_util.get_sbits(m.get_sort(args[1])); unsigned from_ebits = m_util.get_ebits(m.get_sort(args[1])); unsigned to_sbits = m_util.get_sbits(s); unsigned to_ebits = m_util.get_ebits(s); - SASSERT(m_bv_util.get_bv_size(sgn) == 1); - SASSERT(m_bv_util.get_bv_size(sig) == from_sbits); - SASSERT(m_bv_util.get_bv_size(exp) == from_ebits); - SASSERT(m_bv_util.get_bv_size(lz) == from_ebits); + if (from_sbits == to_sbits && from_ebits == to_ebits) + result = x; + else { + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m); + expr_ref one1(m); - if (from_sbits < (to_sbits + 3)) - { - // make sure that sig has at least to_sbits + 3 - res_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, to_sbits+3-from_sbits)); - } - else if (from_sbits > (to_sbits + 3)) - { - // collapse the extra bits into a sticky bit. - expr_ref sticky(m), low(m), high(m); - low = m_bv_util.mk_extract(from_sbits - to_sbits - 3, 0, sig); - high = m_bv_util.mk_extract(from_sbits - 1, from_sbits - to_sbits - 2, sig); - sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, low.get()); - res_sig = m_bv_util.mk_concat(high, sticky); - } - else - res_sig = sig; + one1 = m_bv_util.mk_numeral(1, 1); + expr_ref ninf(m), pinf(m); + mk_plus_inf(f, pinf); + mk_minus_inf(f, ninf); - res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder. - unsigned sig_sz = m_bv_util.get_bv_size(res_sig); - SASSERT(sig_sz == to_sbits+4); + // NaN -> NaN + mk_is_nan(x, c1); + mk_nan(f, v1); - expr_ref exponent_overflow(m); - exponent_overflow = m.mk_false(); + // +0 -> +0 + mk_is_pzero(x, c2); + mk_pzero(f, v2); - if (from_ebits < (to_ebits + 2)) - { - res_exp = m_bv_util.mk_sign_extend(to_ebits-from_ebits+2, exp); - } - else if (from_ebits > (to_ebits + 2)) - { - expr_ref high(m), low(m), lows(m), high_red_or(m), high_red_and(m), h_or_eq(m), h_and_eq(m); - expr_ref no_ovf(m), zero1(m), s_is_one(m), s_is_zero(m); - high = m_bv_util.mk_extract(from_ebits - 1, to_ebits + 2, exp); - low = m_bv_util.mk_extract(to_ebits+1, 0, exp); - lows = m_bv_util.mk_extract(to_ebits+1, to_ebits+1, low); + // -0 -> -0 + mk_is_nzero(x, c3); + mk_nzero(f, v3); + + // +oo -> +oo + mk_is_pinf(x, c4); + v4 = pinf; + + // -oo -> -oo + mk_is_ninf(x, c5); + v5 = ninf; - high_red_or = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, high.get()); - high_red_and = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, high.get()); + // otherwise: the actual conversion with rounding. + expr_ref sgn(m), sig(m), exp(m), lz(m); + unpack(x, sgn, sig, exp, lz, true); - zero1 = m_bv_util.mk_numeral(0, 1); - m_simp.mk_eq(high_red_and, one1, h_and_eq); - m_simp.mk_eq(high_red_or, zero1, h_or_eq); - m_simp.mk_eq(lows, zero1, s_is_zero); - m_simp.mk_eq(lows, one1, s_is_one); + dbg_decouple("fpa2bv_to_float_x_sig", sig); + dbg_decouple("fpa2bv_to_float_x_exp", exp); + dbg_decouple("fpa2bv_to_float_lz", lz); + + expr_ref res_sgn(m), res_sig(m), res_exp(m); + + res_sgn = sgn; + + SASSERT(m_bv_util.get_bv_size(sgn) == 1); + SASSERT(m_bv_util.get_bv_size(sig) == from_sbits); + SASSERT(m_bv_util.get_bv_size(exp) == from_ebits); + SASSERT(m_bv_util.get_bv_size(lz) == from_ebits); + + if (from_sbits < (to_sbits + 3)) + { + // make sure that sig has at least to_sbits + 3 + res_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, to_sbits+3-from_sbits)); + } + else if (from_sbits > (to_sbits + 3)) + { + // collapse the extra bits into a sticky bit. + expr_ref sticky(m), low(m), high(m); + low = m_bv_util.mk_extract(from_sbits - to_sbits - 3, 0, sig); + high = m_bv_util.mk_extract(from_sbits - 1, from_sbits - to_sbits - 2, sig); + sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, low.get()); + res_sig = m_bv_util.mk_concat(high, sticky); + } + else + res_sig = sig; + + res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder. + unsigned sig_sz = m_bv_util.get_bv_size(res_sig); + SASSERT(sig_sz == to_sbits+4); + + expr_ref exponent_overflow(m); + exponent_overflow = m.mk_false(); + + if (from_ebits < (to_ebits + 2)) + { + res_exp = m_bv_util.mk_sign_extend(to_ebits-from_ebits+2, exp); + } + else if (from_ebits > (to_ebits + 2)) + { + expr_ref high(m), low(m), lows(m), high_red_or(m), high_red_and(m), h_or_eq(m), h_and_eq(m); + expr_ref no_ovf(m), zero1(m), s_is_one(m), s_is_zero(m); + high = m_bv_util.mk_extract(from_ebits - 1, to_ebits + 2, exp); + low = m_bv_util.mk_extract(to_ebits+1, 0, exp); + lows = m_bv_util.mk_extract(to_ebits+1, to_ebits+1, low); - expr_ref c2(m); - m_simp.mk_ite(h_or_eq, s_is_one, m.mk_false(), c2); - m_simp.mk_ite(h_and_eq, s_is_zero, c2, exponent_overflow); + high_red_or = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, high.get()); + high_red_and = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, high.get()); + + zero1 = m_bv_util.mk_numeral(0, 1); + m_simp.mk_eq(high_red_and, one1, h_and_eq); + m_simp.mk_eq(high_red_or, zero1, h_or_eq); + m_simp.mk_eq(lows, zero1, s_is_zero); + m_simp.mk_eq(lows, one1, s_is_one); - // Note: Upon overflow, we _could_ try to shift the significand around... + expr_ref c2(m); + m_simp.mk_ite(h_or_eq, s_is_one, m.mk_false(), c2); + m_simp.mk_ite(h_and_eq, s_is_zero, c2, exponent_overflow); + + // Note: Upon overflow, we _could_ try to shift the significand around... - res_exp = low; - } - else - res_exp = exp; + res_exp = low; + } + else + res_exp = exp; - // subtract lz for subnormal numbers. - expr_ref lz_ext(m); - lz_ext = m_bv_util.mk_zero_extend(to_ebits-from_ebits+2, lz); - res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); - SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits+2); + // subtract lz for subnormal numbers. + expr_ref lz_ext(m); + lz_ext = m_bv_util.mk_zero_extend(to_ebits-from_ebits+2, lz); + res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); + SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits+2); - dbg_decouple("fpa2bv_to_float_res_sig", res_sig); - dbg_decouple("fpa2bv_to_float_res_exp", res_exp); + dbg_decouple("fpa2bv_to_float_res_sig", res_sig); + dbg_decouple("fpa2bv_to_float_res_exp", res_exp); - expr_ref rounded(m); - round(s, rm, res_sgn, res_sig, res_exp, rounded); + expr_ref rounded(m); + round(s, rm, res_sgn, res_sig, res_exp, rounded); - expr_ref is_neg(m), sig_inf(m); - m_simp.mk_eq(sgn, one1, is_neg); - mk_ite(is_neg, ninf, pinf, sig_inf); + expr_ref is_neg(m), sig_inf(m); + m_simp.mk_eq(sgn, one1, is_neg); + mk_ite(is_neg, ninf, pinf, sig_inf); - dbg_decouple("fpa2bv_to_float_exp_ovf", exponent_overflow); + dbg_decouple("fpa2bv_to_float_exp_ovf", exponent_overflow); - mk_ite(exponent_overflow, sig_inf, rounded, v6); + mk_ite(exponent_overflow, sig_inf, rounded, v6); - // And finally, we tie them together. - mk_ite(c5, v5, v6, result); - mk_ite(c4, v4, result, result); - mk_ite(c3, v3, result, result); - mk_ite(c2, v2, result, result); - mk_ite(c1, v1, result, result); + // And finally, we tie them together. + mk_ite(c5, v5, v6, result); + mk_ite(c4, v4, result, result); + mk_ite(c3, v3, result, result); + mk_ite(c2, v2, result, result); + mk_ite(c1, v1, result, result); + } } else { // .. other than that, we only support rationals for asFloat diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 735d21c99..076caaf4c 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -360,6 +360,8 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode mk_inf(ebits, sbits, x.sign, o); else if (is_zero(x)) mk_zero(ebits, sbits, x.sign, o); + else if (x.ebits == ebits && x.sbits == sbits) + set(o, x); else { set(o, x); unpack(o, true); From 9cba5d7c8523fdca4c653e41728c246cb1a7228f Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 14 Nov 2013 10:18:44 -0800 Subject: [PATCH 219/509] working on quantifiers in interpolation --- src/interp/iz3base.h | 2 + src/interp/iz3mgr.cpp | 8 ++- src/interp/iz3proof_itp.cpp | 90 +++++++++++++++++---------- src/interp/iz3translate.cpp | 120 ++++++++++++++++++++++++++++++++++-- 4 files changed, 180 insertions(+), 40 deletions(-) diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index 0b7521b38..30ac57bae 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -153,8 +153,10 @@ class iz3base : public iz3mgr, public scopes { int frames; // number of frames + protected: void add_frame_range(int frame, ast t); + private: void initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory); void initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory); diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 2422cb0b4..faa4a636d 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -240,7 +240,9 @@ iz3mgr::ast iz3mgr::clone(const ast &t, const std::vector &_args){ void iz3mgr::show(ast t){ - std::cout << mk_pp(t.raw(), m()) << std::endl; + params_ref p; + p.set_bool("flat_assoc",false); + std::cout << mk_pp(t.raw(), m(), p) << std::endl; } void iz3mgr::show_symb(symb s){ @@ -248,7 +250,9 @@ void iz3mgr::show_symb(symb s){ } void iz3mgr::print_expr(std::ostream &s, const ast &e){ - s << mk_pp(e.raw(), m()); + params_ref p; + p.set_bool("flat_assoc",false); + s << mk_pp(e.raw(), m(), p); } diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index e71e3704c..a2d05f7f9 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -417,7 +417,16 @@ class iz3proof_itp_impl : public iz3proof_itp { std::pair::iterator,bool> bar = subst_memo.insert(foo); ast &res = bar.first->second; if(bar.second){ - if(sym(e) == rotate_sum && var == get_placeholder(arg(e,0))){ + symb g = sym(e); + if(g == rotate_sum){ + if(var == get_placeholder(arg(e,0))){ + res = e; + } + else + res = make(rotate_sum,arg(e,0),subst_term_and_simp_rec(var,t,arg(e,1))); + return res; + } + if(g == concat){ res = e; return res; } @@ -453,18 +462,26 @@ class iz3proof_itp_impl : public iz3proof_itp { int nargs = num_args(e); std::vector args(nargs); bool placeholder_arg = false; + symb g = sym(e); + if(g == concat){ + res = e; + return res; + } for(int i = 0; i < nargs; i++){ - args[i] = simplify_rec(arg(e,i)); + if(i == 0 && g == rotate_sum) + args[i] = arg(e,i); + else + args[i] = simplify_rec(arg(e,i)); placeholder_arg |= is_placeholder(args[i]); } try { opr f = op(e); if(f == Equal && args[0] == args[1]) res = mk_true(); else if(f == And) res = my_and(args); - else if(f == Or) res = my_or(args); + else if(f == Or) + res = my_or(args); else if(f == Idiv) res = mk_idiv(args[0],args[1]); else if(f == Uninterpreted && !placeholder_arg){ - symb g = sym(e); if(g == rotate_sum) res = simplify_rotate(args); else if(g == symm) res = simplify_symm(args); else if(g == modpon) res = simplify_modpon(args); @@ -1945,38 +1962,45 @@ class iz3proof_itp_impl : public iz3proof_itp { hash_map::iterator it = localization_map.find(e); if(it != localization_map.end()){ pf = localization_pf_map[e]; - return it->second; + e = it->second; } - // if is is non-local, we must first localize the arguments to - // the range of its function symbol - - int nargs = num_args(e); - if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ - prover::range frng = rng; - if(op(e) == Uninterpreted){ - symb f = sym(e); - prover::range srng = pv->sym_range(f); - if(pv->ranges_intersect(srng,rng)) // localize to desired range if possible - frng = pv->range_glb(srng,rng); - } - std::vector largs(nargs); - std::vector eqs; - std::vector pfs; - for(int i = 0; i < nargs; i++){ - ast argpf; - largs[i] = localize_term(arg(e,i),frng,argpf); - frng = pv->range_glb(frng,pv->ast_scope(largs[i])); - if(largs[i] != arg(e,i)){ - eqs.push_back(make_equiv(largs[i],arg(e,i))); - pfs.push_back(argpf); + else { + // if it is non-local, we must first localize the arguments to + // the range of its function symbol + + int nargs = num_args(e); + if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ + prover::range frng = rng; + if(op(e) == Uninterpreted){ + symb f = sym(e); + prover::range srng = pv->sym_range(f); + if(pv->ranges_intersect(srng,rng)) // localize to desired range if possible + frng = pv->range_glb(srng,rng); + else + frng = srng; // this term will be localized } + std::vector largs(nargs); + std::vector eqs; + std::vector pfs; + for(int i = 0; i < nargs; i++){ + ast argpf; + largs[i] = localize_term(arg(e,i),frng,argpf); + frng = pv->range_glb(frng,pv->ast_scope(largs[i])); + if(largs[i] != arg(e,i)){ + eqs.push_back(make_equiv(largs[i],arg(e,i))); + pfs.push_back(argpf); + } + } + + e = clone(e,largs); + if(pfs.size()) + pf = make_congruence(eqs,make_equiv(e,orig_e),pfs); + // assert(is_local(e)); } - - e = clone(e,largs); - if(pfs.size()) - pf = make_congruence(eqs,make_equiv(e,orig_e),pfs); - // assert(is_local(e)); + + localization_pf_map[orig_e] = pf; + localization_map[orig_e] = e; } if(pv->ranges_intersect(pv->ast_scope(e),rng)) @@ -1986,7 +2010,7 @@ class iz3proof_itp_impl : public iz3proof_itp { int frame = pv->range_near(pv->ast_scope(e),rng); ast new_var = fresh_localization_var(e,frame); - localization_map[e] = new_var; + localization_map[orig_e] = new_var; std::vector foo; foo.push_back(make_equiv(new_var,e)); ast bar = make_assumption(frame,foo); pf = make_transitivity(new_var,e,orig_e,bar,pf); diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index f755e6772..4ea755eff 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -105,6 +105,42 @@ public: range rng; // the range of frames in the "A" part of the interpolant #endif + /* To handle skolemization, we have to scan the proof for skolem + symbols and assign each to a frame. THe assignment is heuristic. + */ + + void scan_skolems_rec(hash_set &memo, const ast &proof){ + std::pair::iterator,bool> bar = memo.insert(proof); + if(!bar.second) + return; + pfrule dk = pr(proof); + if(dk == PR_SKOLEMIZE){ + ast quanted = arg(conc(proof),0); + if(op(quanted) == Not) + quanted = arg(quanted,0); + range r = ast_range(quanted); + if(range_is_empty(r)) + r = ast_scope(quanted); + if(range_is_empty(r)) + throw "can't skolemize"; + int frame = range_max(r); + if(frame >= frames) frame = frames - 1; + add_frame_range(frame,arg(conc(proof),1)); + r = ast_scope(arg(conc(proof),1)); + } + else { + unsigned nprems = num_prems(proof); + for(unsigned i = 0; i < nprems; i++){ + scan_skolems_rec(memo,prem(proof,i)); + } + } + } + + void scan_skolems(const ast &proof){ + hash_set memo; + scan_skolems_rec(memo,proof); + } + // determine locality of a proof term // return frame of derivation if local, or -1 if not // result INT_MAX means the proof term is a tautology @@ -115,7 +151,8 @@ public: std::pair bar = locality.insert(foo); int &res = bar.first->second; if(!bar.second) return res; - if(pr(proof) == PR_ASSERTED){ + pfrule dk = pr(proof); + if(dk == PR_ASSERTED){ ast ass = conc(proof); res = frame_of_assertion(ass); #ifdef NEW_LOCALITY @@ -125,6 +162,12 @@ public: res = frames-1; #endif } + else if(dk == PR_QUANT_INST){ + std::vector lits; + ast con = conc(proof); + get_Z3_lits(con, lits); + iproof->make_axiom(lits); + } else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ @@ -563,6 +606,33 @@ public: return 1; } + void symbols_out_of_scope_rec(hash_set &memo, hash_set &symb_memo, int frame, const ast &t){ + if(memo.find(t) != memo.end()) + return; + memo.insert(t); + if(op(t) == Uninterpreted){ + symb s = sym(t); + range r = sym_range(s); + if(!in_range(frame,r) && symb_memo.find(s) == symb_memo.end()){ + std::cout << string_of_symbol(s) << "\n"; + symb_memo.insert(s); + } + } + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + symbols_out_of_scope_rec(memo,symb_memo,frame,arg(t,i)); + } + + void symbols_out_of_scope(int frame, const ast &t){ + hash_set memo; + hash_set symb_memo; + symbols_out_of_scope_rec(memo,symb_memo,frame,t); + } + + void conc_symbols_out_of_scope(int frame, const ast &t){ + symbols_out_of_scope(frame,conc(t)); + } + std::vector lit_trace; hash_set marked_proofs; @@ -634,18 +704,36 @@ public: return !(f == And || f == Or || f == Iff); } + hash_map asts_by_id; + void print_lit(const ast &lit){ ast abslit = is_not(lit) ? arg(lit,0) : lit; if(!is_literal_or_lit_iff(lit)){ if(is_not(lit)) std::cout << "~"; - std::cout << "["; - // print_expr(std::cout,abslit); - std::cout << "]"; + int id = ast_id(abslit); + asts_by_id[id] = abslit; + std::cout << "[" << id << "]"; } else print_expr(std::cout,lit); } + void expand(int id){ + if(asts_by_id.find(id) == asts_by_id.end()) + std::cout << "undefined\n"; + else { + ast lit = asts_by_id[id]; + std::string s = string_of_symbol(sym(lit)); + std::cout << "(" << s; + unsigned nargs = num_args(lit); + for(unsigned i = 0; i < nargs; i++){ + std::cout << " "; + print_lit(arg(lit,i)); + } + std::cout << ")\n";; + } + } + void show_lit(const ast &lit){ print_lit(lit); std::cout << "\n"; @@ -682,6 +770,8 @@ public: print_lit(lits[i]); std::cout << " "; } + range r = ast_scope(con); + std::cout << " {" << r.lo << "," << r.hi << "}"; std::cout << "\n"; } @@ -753,8 +843,12 @@ public: ast prem0 = prem(proof,0); Iproof::node itp = translate_main(prem0,true); std::vector clause; - get_Z3_lits(conc(prem0),clause); + ast conc0 = conc(prem0); int nprems = num_prems(proof); + if(nprems == 2 && conc0 == mk_not(conc(prem(proof,1)))) + clause.push_back(conc0); + else + get_Z3_lits(conc0,clause); for(int position = 1; position < nprems; position++){ ast ante = prem(proof,position); ast pnode = conc(ante); @@ -1500,6 +1594,10 @@ public: res = iproof->make_axiom(lits); break; } + case PR_DEF_AXIOM: { // this should only happen for formulas resulting from quantifier instantiation + res = iproof->make_axiom(lits); + break; + } default: assert(0 && "translate_main: unsupported proof rule"); throw unsupported(); @@ -1518,6 +1616,7 @@ public: iz3proof::node translate(ast proof, iz3proof &dst){ std::vector itps; + scan_skolems(proof); for(int i = 0; i < frames -1; i++){ #ifdef NEW_LOCALITY rng = range_downward(i); @@ -1609,6 +1708,17 @@ void iz3translation_full_pfprem(iz3translation_full *p, int i){ p->pfprem(i); } +void iz3translation_full_expand(iz3translation_full *p, int i){ + p->expand(i); +} + +void iz3translation_full_symbols_out_of_scope(iz3translation_full *p, int i, const iz3mgr::ast &t){ + p->symbols_out_of_scope(i,t); +} + +void iz3translation_full_conc_symbols_out_of_scope(iz3translation_full *p, int i, const iz3mgr::ast &t){ + p->conc_symbols_out_of_scope(i,t); +} struct stdio_fixer { stdio_fixer(){ From b77d408128a2f7455a29e821961df1e6582a2b8f Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 14 Nov 2013 19:11:19 +0000 Subject: [PATCH 220/509] bugfix for FPA rounding when ebits is very small. --- src/tactic/fpa/fpa2bv_converter.cpp | 26 +++++++++++++------------- src/util/mpf.cpp | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 71c6b689e..03f8be0a5 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -2171,14 +2171,14 @@ void fpa2bv_converter::mk_bot_exp(unsigned sz, expr_ref & result) { } void fpa2bv_converter::mk_min_exp(unsigned ebits, expr_ref & result) { - SASSERT(ebits > 0); + SASSERT(ebits >= 2); const mpz & z = m_mpf_manager.m_powers2.m1(ebits-1, true); result = m_bv_util.mk_numeral(z + mpz(1), ebits); } void fpa2bv_converter::mk_max_exp(unsigned ebits, expr_ref & result) { - SASSERT(ebits > 0); - result = m_bv_util.mk_numeral(m_mpf_manager.m_powers2.m1(ebits-1, false), ebits); + SASSERT(ebits >= 2); + result = m_bv_util.mk_numeral(m_mpf_manager.m_powers2.m1(ebits-1, false), ebits); } void fpa2bv_converter::mk_leading_zeros(expr * e, unsigned max_bits, expr_ref & result) { @@ -2224,9 +2224,9 @@ void fpa2bv_converter::mk_bias(expr * e, expr_ref & result) { unsigned ebits = m_bv_util.get_bv_size(e); SASSERT(ebits >= 2); - expr_ref mask(m); - mask = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits); - result = m_bv_util.mk_bv_add(e, mask); + expr_ref bias(m); + bias = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits); + result = m_bv_util.mk_bv_add(e, bias); } void fpa2bv_converter::mk_unbias(expr * e, expr_ref & result) { @@ -2440,13 +2440,12 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & t = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(1, ebits+2)); t = m_bv_util.mk_bv_sub(t, lz); t = m_bv_util.mk_bv_sub(t, m_bv_util.mk_sign_extend(2, e_min)); - expr_ref TINY(m); + dbg_decouple("fpa2bv_rnd_t", t); + expr_ref TINY(m); TINY = m_bv_util.mk_sle(t, m_bv_util.mk_numeral((unsigned)-1, ebits+2)); - - TRACE("fpa2bv_dbg", tout << "TINY = " << mk_ismt2_pp(TINY, m) << std::endl;); - SASSERT(is_well_sorted(m, TINY)); - dbg_decouple("fpa2bv_rnd_TINY", TINY); + TRACE("fpa2bv_dbg", tout << "TINY = " << mk_ismt2_pp(TINY, m) << std::endl;); + SASSERT(is_well_sorted(m, TINY)); expr_ref beta(m); beta = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(exp, lz), m_bv_util.mk_numeral(1, ebits+2)); @@ -2459,7 +2458,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & dbg_decouple("fpa2bv_rnd_e_min", e_min); dbg_decouple("fpa2bv_rnd_e_max", e_max); - expr_ref sigma(m), sigma_add(m), e_min_p2(m); + expr_ref sigma(m), sigma_add(m); sigma_add = m_bv_util.mk_bv_sub(exp, m_bv_util.mk_sign_extend(2, e_min)); sigma_add = m_bv_util.mk_bv_add(sigma_add, m_bv_util.mk_numeral(1, ebits+2)); m_simp.mk_ite(TINY, sigma_add, lz, sigma); @@ -2481,9 +2480,10 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & rs_sig(m), ls_sig(m), big_sh_sig(m), sigma_le_cap(m); sigma_neg = m_bv_util.mk_bv_neg(sigma); sigma_cap = m_bv_util.mk_numeral(sbits+2, sigma_size); - sigma_le_cap = m_bv_util.mk_sle(sigma_neg, sigma_cap); + sigma_le_cap = m_bv_util.mk_ule(sigma_neg, sigma_cap); m_simp.mk_ite(sigma_le_cap, sigma_neg, sigma_cap, sigma_neg_capped); dbg_decouple("fpa2bv_rnd_sigma_neg", sigma_neg); + dbg_decouple("fpa2bv_rnd_sigma_cap", sigma_cap); dbg_decouple("fpa2bv_rnd_sigma_neg_capped", sigma_neg_capped); sigma_lt_zero = m_bv_util.mk_sle(sigma, m_bv_util.mk_numeral((unsigned)-1, sigma_size)); dbg_decouple("fpa2bv_rnd_sigma_lt_zero", sigma_lt_zero); diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 076caaf4c..5910f55c9 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -1380,12 +1380,12 @@ bool mpf_manager::has_top_exp(mpf const & x) { } mpf_exp_t mpf_manager::mk_bot_exp(unsigned ebits) { - SASSERT(ebits > 0); + SASSERT(ebits >= 2); return m_mpz_manager.get_int64(m_powers2.m1(ebits-1, true)); } mpf_exp_t mpf_manager::mk_top_exp(unsigned ebits) { - SASSERT(ebits > 0); + SASSERT(ebits >= 2); return m_mpz_manager.get_int64(m_powers2(ebits-1)); } From c96f7b5a51a871d404b8939c49bbec4dcc3b58ac Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 14 Nov 2013 20:13:37 +0000 Subject: [PATCH 221/509] bugfixes for float to float conversion Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 36 ++++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 03f8be0a5..adc0a9a98 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1831,8 +1831,8 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a rm = args[0]; x = args[1]; - unsigned from_sbits = m_util.get_sbits(m.get_sort(args[1])); - unsigned from_ebits = m_util.get_ebits(m.get_sort(args[1])); + unsigned from_sbits = m_util.get_sbits(m.get_sort(x)); + unsigned from_ebits = m_util.get_ebits(m.get_sort(x)); unsigned to_sbits = m_util.get_sbits(s); unsigned to_ebits = m_util.get_ebits(s); @@ -1911,7 +1911,12 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a if (from_ebits < (to_ebits + 2)) { - res_exp = m_bv_util.mk_sign_extend(to_ebits-from_ebits+2, exp); + res_exp = m_bv_util.mk_sign_extend(to_ebits-from_ebits+2, exp); + + // subtract lz for subnormal numbers. + expr_ref lz_ext(m); + lz_ext = m_bv_util.mk_zero_extend(to_ebits-from_ebits+2, lz); + res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); } else if (from_ebits > (to_ebits + 2)) { @@ -1936,30 +1941,33 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a // Note: Upon overflow, we _could_ try to shift the significand around... - res_exp = low; - } - else - res_exp = exp; + // subtract lz for subnormal numbers. + expr_ref lz_ext(m), lz_rest(m), lz_redor(m), lz_redor_bool(m); + lz_ext = m_bv_util.mk_extract(to_ebits+1, 0, lz); + lz_rest = m_bv_util.mk_extract(from_ebits-1, to_ebits+2, lz); + lz_redor = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, lz_rest.get()); + m_simp.mk_eq(lz_redor, one1, lz_redor_bool); + m_simp.mk_or(exponent_overflow, lz_redor_bool, exponent_overflow); + + res_exp = m_bv_util.mk_bv_sub(low, lz_ext); + } + else // from_ebits == (to_ebits + 2) + res_exp = m_bv_util.mk_bv_sub(exp, lz); - // subtract lz for subnormal numbers. - expr_ref lz_ext(m); - lz_ext = m_bv_util.mk_zero_extend(to_ebits-from_ebits+2, lz); - res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits+2); + SASSERT(is_well_sorted(m, res_exp)); dbg_decouple("fpa2bv_to_float_res_sig", res_sig); dbg_decouple("fpa2bv_to_float_res_exp", res_exp); expr_ref rounded(m); - round(s, rm, res_sgn, res_sig, res_exp, rounded); - + round(s, rm, res_sgn, res_sig, res_exp, rounded); expr_ref is_neg(m), sig_inf(m); m_simp.mk_eq(sgn, one1, is_neg); mk_ite(is_neg, ninf, pinf, sig_inf); dbg_decouple("fpa2bv_to_float_exp_ovf", exponent_overflow); - mk_ite(exponent_overflow, sig_inf, rounded, v6); // And finally, we tie them together. From 31495bb9d91f9ffaa5bebb3c60c9e7010e185f33 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 15 Nov 2013 17:19:41 +0000 Subject: [PATCH 222/509] bugfix for float rounding to integral values for cases where ebits >= sbits Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index adc0a9a98..ebccf4d52 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1588,8 +1588,7 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * v2 = x; unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); - SASSERT(ebits < sbits); + unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); @@ -1619,9 +1618,22 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * expr_ref shift(m), r_shifted(m), l_shifted(m); shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits-1, ebits+1), m_bv_util.mk_sign_extend(1, a_exp)); - r_shifted = m_bv_util.mk_bv_lshr(a_sig, m_bv_util.mk_zero_extend(sbits-ebits-1, shift)); + if (sbits > (ebits+1)) + r_shifted = m_bv_util.mk_bv_lshr(a_sig, m_bv_util.mk_zero_extend(sbits-(ebits+1), shift)); + else if (sbits < (ebits+1)) + r_shifted = m_bv_util.mk_extract(ebits, ebits-sbits+1, m_bv_util.mk_bv_lshr(m_bv_util.mk_zero_extend(ebits+1-sbits, a_sig), shift)); + else // sbits == ebits+1 + r_shifted = m_bv_util.mk_bv_lshr(a_sig, shift); + SASSERT(is_well_sorted(m, r_shifted)); SASSERT(m_bv_util.get_bv_size(r_shifted) == sbits); - l_shifted = m_bv_util.mk_bv_shl(r_shifted, m_bv_util.mk_zero_extend(sbits-ebits-1, shift)); + + if (sbits > (ebits+1)) + l_shifted = m_bv_util.mk_bv_shl(r_shifted, m_bv_util.mk_zero_extend(sbits-(ebits+1), shift)); + else if (sbits < (ebits+1)) + l_shifted = m_bv_util.mk_extract(ebits, ebits-sbits+1, m_bv_util.mk_bv_shl(m_bv_util.mk_zero_extend(ebits+1-sbits, r_shifted), shift)); + else // sbits == ebits+1 + l_shifted = m_bv_util.mk_bv_shl(r_shifted, shift); + SASSERT(is_well_sorted(m, l_shifted)); SASSERT(m_bv_util.get_bv_size(l_shifted) == sbits); res_sig = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, 1), @@ -2239,7 +2251,7 @@ void fpa2bv_converter::mk_bias(expr * e, expr_ref & result) { void fpa2bv_converter::mk_unbias(expr * e, expr_ref & result) { unsigned ebits = m_bv_util.get_bv_size(e); - SASSERT(ebits >= 2); + SASSERT(ebits >= 3); expr_ref e_plus_one(m); e_plus_one = m_bv_util.mk_bv_add(e, m_bv_util.mk_numeral(1, ebits)); @@ -2275,6 +2287,7 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref expr_ref denormal_sig(m), denormal_exp(m); denormal_sig = m_bv_util.mk_zero_extend(1, sig); + SASSERT(ebits >= 3); // Note: when ebits=2 there is no 1-exponent, so mk_unbias will produce a 0. denormal_exp = m_bv_util.mk_numeral(1, ebits); mk_unbias(denormal_exp, denormal_exp); dbg_decouple("fpa2bv_unpack_denormal_exp", denormal_exp); From 8320144af00269061870a058bfdd7b6c7d3b29ac Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 15 Nov 2013 11:24:02 -0800 Subject: [PATCH 223/509] fixed bug in duality logging --- src/duality/duality_rpfp.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 1c2927da8..fe5ad8672 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -89,15 +89,17 @@ namespace Duality { if(memo.find(t) != memo.end()) return; memo.insert(t); - decl_kind k = t.decl().get_decl_kind(); - if(k == And || k == Or || k == Not || k == Implies || k == Iff){ - ops++; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - SummarizeRec(memo,lits,ops,t.arg(i)); + if(t.is_app()){ + decl_kind k = t.decl().get_decl_kind(); + if(k == And || k == Or || k == Not || k == Implies || k == Iff){ + ops++; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + SummarizeRec(memo,lits,ops,t.arg(i)); + return; + } } - else - lits.push_back(t); + lits.push_back(t); } int Z3User::CumulativeDecisions(){ From 61385c8489b7fda11b518a67fe308ea3cfe28c3d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Nov 2013 09:54:37 -0800 Subject: [PATCH 224/509] a.ctx -> self.ctx, thanks gario Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 2 +- src/interp/iz3translate_direct.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 22ec30ad2..959566619 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -6424,7 +6424,7 @@ class Tactic: _z3_assert(isinstance(goal, Goal) or isinstance(goal, BoolRef), "Z3 Goal or Boolean expressions expected") goal = _to_goal(goal) if len(arguments) > 0 or len(keywords) > 0: - p = args2params(arguments, keywords, a.ctx) + p = args2params(arguments, keywords, self.ctx) return ApplyResult(Z3_tactic_apply_ex(self.ctx.ref(), self.tactic, goal.goal, p.params), self.ctx) else: return ApplyResult(Z3_tactic_apply(self.ctx.ref(), self.tactic, goal.goal), self.ctx) diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index 44c907d1d..853ecbe86 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -19,6 +19,7 @@ Revision History: --*/ + #include "iz3translate.h" #include "iz3proof.h" #include "iz3profiling.h" From a93f8b04e56235531e46cfbdb5df1df128ed6122 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 21 Nov 2013 18:10:21 -0800 Subject: [PATCH 225/509] working on duality and quantified arithmetic in interpolation --- src/duality/duality.h | 75 ++++++- src/duality/duality_rpfp.cpp | 129 ++++++++++-- src/duality/duality_solver.cpp | 208 +++++++++++++++++- src/duality/duality_wrapper.cpp | 19 +- src/duality/duality_wrapper.h | 8 + src/interp/iz3interp.cpp | 62 +++++- src/interp/iz3interp.h | 10 + src/interp/iz3mgr.cpp | 16 ++ src/interp/iz3mgr.h | 3 + src/interp/iz3proof_itp.cpp | 359 ++++++++++++++++++++++++++++++-- 10 files changed, 829 insertions(+), 60 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 166e8ef0d..158ad84d9 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -277,6 +277,7 @@ namespace Duality { public: std::list edges; std::list nodes; + std::list constraints; }; @@ -286,6 +287,8 @@ namespace Duality { literals dualLabels; std::list stack; std::vector axioms; // only saved here for printing purposes + solver aux_solver; + public: @@ -296,7 +299,7 @@ namespace Duality { inherit the axioms. */ - RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx), *(_ls->slvr)), dualModel(*(_ls->ctx)) + RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx), *(_ls->slvr)), dualModel(*(_ls->ctx)), aux_solver(*(_ls->ctx)) { ls = _ls; nodeCount = 0; @@ -351,10 +354,10 @@ namespace Duality { bool SubsetEq(const Transformer &other){ Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); expr test = Formula && !t; - owner->slvr.push(); - owner->slvr.add(test); - check_result res = owner->slvr.check(); - owner->slvr.pop(1); + owner->aux_solver.push(); + owner->aux_solver.add(test); + check_result res = owner->aux_solver.check(); + owner->aux_solver.pop(1); return res == unsat; } @@ -444,6 +447,19 @@ namespace Duality { return n; } + /** Delete a node. You can only do this if not connected to any edges.*/ + void DeleteNode(Node *node){ + if(node->Outgoing || !node->Incoming.empty()) + throw "cannot delete RPFP node"; + for(std::vector::iterator it = nodes.end(), en = nodes.begin(); it != en;){ + if(*(--it) == node){ + nodes.erase(it); + break; + } + } + delete node; + } + /** This class represents a hyper-edge in the RPFP graph */ class Edge @@ -460,6 +476,7 @@ namespace Duality { hash_map varMap; Edge *map; Term labeled; + std::vector constraints; Edge(Node *_Parent, const Transformer &_F, const std::vector &_Children, RPFP *_owner, int _number) : F(_F), Parent(_Parent), Children(_Children), dual(expr(_owner->ctx)) { @@ -480,6 +497,29 @@ namespace Duality { return e; } + + /** Delete a hyper-edge and unlink it from any nodes. */ + void DeleteEdge(Edge *edge){ + if(edge->Parent) + edge->Parent->Outgoing = 0; + for(unsigned int i = 0; i < edge->Children.size(); i++){ + std::vector &ic = edge->Children[i]->Incoming; + for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ + if(*it == edge){ + ic.erase(it); + break; + } + } + } + for(std::vector::iterator it = edges.end(), en = edges.begin(); it != en;){ + if(*(--it) == edge){ + edges.erase(it); + break; + } + } + delete edge; + } + /** Create an edge that lower-bounds its parent. */ Edge *CreateLowerBoundEdge(Node *_Parent) { @@ -494,13 +534,25 @@ namespace Duality { void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); - + /* Constrain an edge by the annotation of one of its children. */ + + void ConstrainParent(Edge *parent, Node *child); + /** For incremental solving, asserts the negation of the upper bound associated * with a node. * */ void AssertNode(Node *n); + /** Assert a constraint on an edge in the SMT context. + */ + void ConstrainEdge(Edge *e, const Term &t); + + /** Fix the truth values of atomic propositions in the given + edge to their values in the current assignment. */ + void FixCurrentState(Edge *root); + + /** Declare a constant in the background theory. */ void DeclareConstant(const FuncDecl &f); @@ -592,6 +644,9 @@ namespace Duality { Term ComputeUnderapprox(Node *root, int persist); + /** Try to strengthen the annotation of a node by removing disjuncts. */ + void Generalize(Node *node); + /** Push a scope. Assertions made after Push can be undone by Pop. */ void Push(); @@ -803,7 +858,15 @@ namespace Duality { Term SubstBound(hash_map &subst, const Term &t); + void ConstrainEdgeLocalized(Edge *e, const Term &t); + void GreedyReduce(solver &s, std::vector &conjuncts); + + void NegateLits(std::vector &lits); + + expr SimplifyOr(std::vector &lits); + + void SetAnnotation(Node *root, const expr &t); }; /** RPFP solver base class. */ diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index fe5ad8672..17e3de6bb 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -283,7 +283,10 @@ namespace Duality { children[i] = ToTermTree(e->Children[i]); // Term top = ReducedDualEdge(e); Term top = e->dual.null() ? ctx.bool_val(true) : e->dual; - return new TermTree(top, children); + TermTree *res = new TermTree(top, children); + for(unsigned i = 0; i < e->constraints.size(); i++) + res->addTerm(e->constraints[i]); + return res; } TermTree *RPFP::GetGoalTree(Node *root){ @@ -375,6 +378,19 @@ namespace Duality { x = x && y; } + void RPFP::SetAnnotation(Node *root, const expr &t){ + hash_map memo; + Term b; + std::vector v; + RedVars(root, b, v); + memo[b] = ctx.bool_val(true); + for (unsigned i = 0; i < v.size(); i++) + memo[v[i]] = root->Annotation.IndParams[i]; + Term annot = SubstRec(memo, t); + // Strengthen(ref root.Annotation.Formula, annot); + root->Annotation.Formula = annot; + } + void RPFP::DecodeTree(Node *root, TermTree *interp, int persist) { std::vector &ic = interp->getChildren(); @@ -384,16 +400,7 @@ namespace Duality { for (unsigned i = 0; i < nc.size(); i++) DecodeTree(nc[i], ic[i], persist); } - hash_map memo; - Term b; - std::vector v; - RedVars(root, b, v); - memo[b] = ctx.bool_val(true); - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = root->Annotation.IndParams[i]; - Term annot = SubstRec(memo, interp->getTerm()); - // Strengthen(ref root.Annotation.Formula, annot); - root->Annotation.Formula = annot; + SetAnnotation(root,interp->getTerm()); #if 0 if(persist != 0) Z3_persist_ast(ctx,root->Annotation.Formula,persist); @@ -511,6 +518,10 @@ namespace Duality { timer_stop("solver add"); } + void RPFP::ConstrainParent(Edge *parent, Node *child){ + ConstrainEdgeLocalized(parent,GetAnnotation(child)); + } + /** For incremental solving, asserts the negation of the upper bound associated * with a node. @@ -526,6 +537,24 @@ namespace Duality { } } + /** Assert a constraint on an edge in the SMT context. + */ + + void RPFP::ConstrainEdge(Edge *e, const Term &t) + { + Term tl = Localize(e, t); + ConstrainEdgeLocalized(e,tl); + } + + void RPFP::ConstrainEdgeLocalized(Edge *e, const Term &tl) + { + e->constraints.push_back(tl); + stack.back().constraints.push_back(e); + slvr.add(tl); + } + + + /** Declare a constant in the background theory. */ void RPFP::DeclareConstant(const FuncDecl &f){ @@ -1064,7 +1093,7 @@ namespace Duality { } } /* Unreachable! */ - throw "error in RPFP::ImplicantRed"; + std::cerr << "error in RPFP::ImplicantRed"; goto done; } else if(k == Not) { @@ -1671,6 +1700,17 @@ namespace Duality { return eu; } + void RPFP::FixCurrentState(Edge *edge){ + hash_set dont_cares; + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); + Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; + Term eu = UnderapproxFormula(dual,dont_cares); + timer_stop("UnderapproxFormula"); + ConstrainEdgeLocalized(edge,eu); + } + + RPFP::Term RPFP::ModelValueAsConstraint(const Term &t){ if(t.is_array()){ @@ -1714,6 +1754,69 @@ namespace Duality { res = CreateRelation(p->Annotation.IndParams,funder); } + void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ + // verify + s.push(); + expr conj = ctx.make(And,conjuncts); + s.add(conj); + check_result res = s.check(); + s.pop(1); + if(res != unsat) + throw "should be unsat"; + + for(unsigned i = 0; i < conjuncts.size(); ){ + std::swap(conjuncts[i],conjuncts.back()); + expr save = conjuncts.back(); + conjuncts.pop_back(); + s.push(); + expr conj = ctx.make(And,conjuncts); + s.add(conj); + check_result res = s.check(); + s.pop(1); + if(res != unsat){ + conjuncts.push_back(save); + std::swap(conjuncts[i],conjuncts.back()); + i++; + } + } + } + + void RPFP::NegateLits(std::vector &lits){ + for(unsigned i = 0; i < lits.size(); i++){ + expr &f = lits[i]; + if(f.is_app() && f.decl().get_decl_kind() == Not) + f = f.arg(0); + else + f = !f; + } + } + + expr RPFP::SimplifyOr(std::vector &lits){ + if(lits.size() == 0) + return ctx.bool_val(false); + if(lits.size() == 1) + return lits[0]; + return ctx.make(Or,lits); + } + + void RPFP::Generalize(Node *node){ + std::vector conjuncts; + expr fmla = GetAnnotation(node); + CollectConjuncts(fmla,conjuncts,false); + // try to remove conjuncts one at a tme + aux_solver.push(); + Edge *edge = node->Outgoing; + 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); + } + GreedyReduce(aux_solver,conjuncts); + aux_solver.pop(1); + NegateLits(conjuncts); + SetAnnotation(node,SimplifyOr(conjuncts)); + } /** Push a scope. Assertions made after Push can be undone by Pop. */ @@ -1735,6 +1838,8 @@ namespace Duality { (*it)->dual = expr(ctx,NULL); for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) (*it)->dual = expr(ctx,NULL); + for(std::list::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) + (*it)->constraints.pop_back(); stack.pop_back(); } } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 0043998d2..afa1c7683 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1270,18 +1270,24 @@ namespace Duality { } } + bool UpdateNodeToNode(Node *node, Node *top){ + if(!node->Annotation.SubsetEq(top->Annotation)){ + reporter->Update(node,top->Annotation); + indset->Update(node,top->Annotation); + updated_nodes.insert(node->map); + node->Annotation.IntersectWith(top->Annotation); + return true; + } + return false; + } + /** Update the unwinding solution, using an interpolant for the derivation tree. */ void UpdateWithInterpolant(Node *node, RPFP *tree, Node *top){ if(top->Outgoing) for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) UpdateWithInterpolant(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); - if(!node->Annotation.SubsetEq(top->Annotation)){ - reporter->Update(node,top->Annotation); - indset->Update(node,top->Annotation); - updated_nodes.insert(node->map); - node->Annotation.IntersectWith(top->Annotation); - } + UpdateNodeToNode(node, top); heuristic->Update(node); } @@ -1305,7 +1311,8 @@ namespace Duality { if(node->Bound.IsFull()) return true; reporter->Bound(node); int start_decs = rpfp->CumulativeDecisions(); - DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); + DerivationTree *dtp = new DerivationTreeSlow(this,unwinding,reporter,heuristic,FullExpand); + DerivationTree &dt = *dtp; bool res = dt.Derive(unwinding,node,UseUnderapprox); int end_decs = rpfp->CumulativeDecisions(); // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; @@ -1321,6 +1328,7 @@ namespace Duality { UpdateWithInterpolant(node,dt.tree,dt.top); delete dt.tree; } + delete dtp; return !res; } @@ -1491,7 +1499,7 @@ namespace Duality { return res != unsat; } - bool Build(){ + virtual bool Build(){ #ifdef EFFORT_BOUNDED_STRAT start_decs = tree->CumulativeDecisions(); #endif @@ -1545,7 +1553,7 @@ namespace Duality { } } - void ExpandNode(RPFP::Node *p){ + virtual void ExpandNode(RPFP::Node *p){ // tree->RemoveEdge(p->Outgoing); Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); std::vector &cs = edge->Children; @@ -1573,6 +1581,7 @@ namespace Duality { } #else #if 0 + void ExpansionChoices(std::set &best){ std::vector unused_set, used_set; std::set choices; @@ -1668,7 +1677,7 @@ namespace Duality { #endif #endif - bool ExpandSomeNodes(bool high_priority = false){ + bool ExpandSomeNodes(bool high_priority = false, int max = INT_MAX){ #ifdef EFFORT_BOUNDED_STRAT last_decs = tree->CumulativeDecisions() - start_decs; #endif @@ -1679,17 +1688,194 @@ namespace Duality { timer_stop("ExpansionChoices"); std::list leaves_copy = leaves; // copy so can modify orig leaves.clear(); + int count = 0; for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ - if(choices.find(*it) != choices.end()) + if(choices.find(*it) != choices.end() && count < max){ + count++; ExpandNode(*it); + } else leaves.push_back(*it); } timer_stop("ExpandSomeNodes"); return !choices.empty(); } + void RemoveExpansion(RPFP::Node *p){ + Edge *edge = p->Outgoing; + Node *parent = edge->Parent; + std::vector cs = edge->Children; + tree->DeleteEdge(edge); + for(unsigned i = 0; i < cs.size(); i++) + tree->DeleteNode(cs[i]); + leaves.push_back(parent); + } }; + class DerivationTreeSlow : public DerivationTree { + public: + + struct stack_entry { + unsigned level; // SMT solver stack level + std::vector expansions; + }; + + std::vector stack; + + hash_map updates; + + DerivationTreeSlow(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) + : DerivationTree(_duality, rpfp, _reporter, _heuristic, _full_expand) { + stack.push_back(stack_entry()); + } + + virtual bool Build(){ + + stack.back().level = tree->slvr.get_scope_level(); + + while (true) + { + lbool res; + + unsigned slvr_level = tree->slvr.get_scope_level(); + if(slvr_level != stack.back().level) + throw "stacks out of sync!"; + + res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop + + if (res == l_false) { + if (stack.empty()) // should never happen + return false; + + std::vector &expansions = stack.back().expansions; + int update_count = 0; + for(unsigned i = 0; i < expansions.size(); i++){ + tree->Generalize(expansions[i]); + if(RecordUpdate(expansions[i])) + update_count++; + } + if(update_count == 0) + std::cout << "backtracked without learning\n"; + tree->Pop(1); + hash_set leaves_to_remove; + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + // if(node != top) + // tree->ConstrainParent(node->Incoming[0],node); + std::vector &cs = node->Outgoing->Children; + for(unsigned i = 0; i < cs.size(); i++){ + leaves_to_remove.insert(cs[i]); + UnmapNode(cs[i]); + if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) + throw "help!"; + } + RemoveExpansion(node); + } + RemoveLeaves(leaves_to_remove); + stack.pop_back(); + HandleUpdatedNodes(); + if(stack.size() == 1) + return false; + } + else { + tree->Push(); + std::vector &expansions = stack.back().expansions; + for(unsigned i = 0; i < expansions.size(); i++){ + tree->FixCurrentState(expansions[i]->Outgoing); + } + if(tree->slvr.check() == unsat) + throw "help!"; + stack.push_back(stack_entry()); + stack.back().level = tree->slvr.get_scope_level(); + if(ExpandSomeNodes(false,1)){ + continue; + } + while(stack.size() > 1){ + tree->Pop(1); + stack.pop_back(); + } + return true; + } + } + } + + void RemoveLeaves(hash_set &leaves_to_remove){ + std::list leaves_copy; + leaves_copy.swap(leaves); + for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ + if(leaves_to_remove.find(*it) == leaves_to_remove.end()) + leaves.push_back(*it); + } + } + + hash_map > node_map; + std::list updated_nodes; + + virtual void ExpandNode(RPFP::Node *p){ + stack.back().expansions.push_back(p); + DerivationTree::ExpandNode(p); + std::vector &new_nodes = p->Outgoing->Children; + for(unsigned i = 0; i < new_nodes.size(); i++){ + Node *n = new_nodes[i]; + node_map[n->map].push_back(n); + } + } + + bool RecordUpdate(Node *node){ + bool res = duality->UpdateNodeToNode(node->map,node); + if(res){ + std::vector to_update = node_map[node->map]; + for(unsigned i = 0; i < to_update.size(); i++){ + Node *node2 = to_update[i]; + // maintain invariant that no nodes on updated list are created at current stack level + if(node2 == node || !(node->Incoming.size() > 0 && AtCurrentStackLevel(node2->Incoming[0]->Parent))){ + updated_nodes.push_back(node2); + if(node2 != node) + node2->Annotation = node->Annotation; + } + } + } + return res; + } + + void HandleUpdatedNodes(){ + for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ + Node *node = *it; + node->Annotation = node->map->Annotation; + if(node->Incoming.size() > 0) + tree->ConstrainParent(node->Incoming[0],node); + if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ + std::list::iterator victim = it; + ++it; + updated_nodes.erase(victim); + } + else + ++it; + } + } + + bool AtCurrentStackLevel(Node *node){ + std::vector vec = stack.back().expansions; + for(unsigned i = 0; i < vec.size(); i++) + if(vec[i] == node) + return true; + return false; + } + + void UnmapNode(Node *node){ + std::vector &vec = node_map[node->map]; + for(unsigned i = 0; i < vec.size(); i++){ + if(vec[i] == node){ + std::swap(vec[i],vec.back()); + vec.pop_back(); + return; + } + } + throw "can't unmap node"; + } + + }; + + class Covering { struct cover_info { diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index fef70e031..dd64052a0 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -425,15 +425,18 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st static int linearize_assumptions(int num, TermTree *assumptions, - std::vector &linear_assumptions, + std::vector > &linear_assumptions, std::vector &parents){ for(unsigned i = 0; i < assumptions->getChildren().size(); i++) num = linearize_assumptions(num, assumptions->getChildren()[i], linear_assumptions, parents); - linear_assumptions[num] = assumptions->getTerm(); + // linear_assumptions[num].push_back(assumptions->getTerm()); for(unsigned i = 0; i < assumptions->getChildren().size(); i++) parents[assumptions->getChildren()[i]->getNumber()] = num; parents[num] = SHRT_MAX; // in case we have no parent - linear_assumptions[num] = assumptions->getTerm(); + linear_assumptions[num].push_back(assumptions->getTerm()); + std::vector &ts = assumptions->getTerms(); + for(unsigned i = 0; i < ts.size(); i++) + linear_assumptions[num].push_back(ts[i]); return num + 1; } @@ -462,14 +465,15 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st { int size = assumptions->number(0); - std::vector linear_assumptions(size); + std::vector > linear_assumptions(size); std::vector parents(size); linearize_assumptions(0,assumptions,linear_assumptions,parents); ptr_vector< ::ast> _interpolants(size-1); - ptr_vector< ::ast>_assumptions(size); + vector >_assumptions(size); for(int i = 0; i < size; i++) - _assumptions[i] = linear_assumptions[i]; + for(unsigned j = 0; j < linear_assumptions[i].size(); j++) + _assumptions[i].push_back(linear_assumptions[i][j]); ::vector _parents; _parents.resize(parents.size()); for(unsigned i = 0; i < parents.size(); i++) _parents[i] = parents[i]; @@ -481,7 +485,8 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st if(!incremental){ for(unsigned i = 0; i < linear_assumptions.size(); i++) - add(linear_assumptions[i]); + for(unsigned j = 0; j < linear_assumptions[i].size(); j++) + add(linear_assumptions[i][j]); } check_result res = check(); diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 21ed45479..291ddfbcf 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -867,6 +867,9 @@ namespace Duality { if(m_solver) m_solver->cancel(); } + + unsigned get_scope_level(){return m_solver->get_scope_level();} + }; #if 0 @@ -1199,6 +1202,8 @@ namespace Duality { inline expr getTerm(){return term;} + inline std::vector &getTerms(){return terms;} + inline std::vector &getChildren(){ return children; } @@ -1215,6 +1220,8 @@ namespace Duality { } inline void setTerm(expr t){term = t;} + + inline void addTerm(expr t){terms.push_back(t);} inline void setChildren(const std::vector & _children){ children = _children; @@ -1231,6 +1238,7 @@ namespace Duality { private: expr term; + std::vector terms; std::vector children; int num; }; diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 92afc5723..7ef7e6aa2 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -75,15 +75,16 @@ struct frame_reducer : public iz3mgr { } } - void get_frames(const std::vector &z3_preds, + void get_frames(const std::vector >&z3_preds, const std::vector &orig_parents, - std::vector &assertions, + std::vector >&assertions, std::vector &parents, z3pf proof){ frames = z3_preds.size(); orig_parents_copy = orig_parents; for(unsigned i = 0; i < z3_preds.size(); i++) - frame_map[z3_preds[i]] = i; + for(unsigned j = 0; j < z3_preds[i].size(); j++) + frame_map[z3_preds[i][j]] = i; used_frames.resize(frames); hash_set memo; get_proof_assumptions_rec(proof,memo,used_frames); @@ -202,7 +203,7 @@ public: } void proof_to_interpolant(z3pf proof, - const std::vector &cnsts, + const std::vector > &cnsts, const std::vector &parents, std::vector &interps, const std::vector &theory, @@ -216,7 +217,7 @@ public: // get rid of frames not used in proof - std::vector cnsts_vec; + std::vector > cnsts_vec; std::vector parents_vec; frame_reducer fr(*this); fr.get_frames(cnsts,parents,cnsts_vec,parents_vec,proof); @@ -235,10 +236,7 @@ public: #define BINARY_INTERPOLATION #ifndef BINARY_INTERPOLATION // create a translator - std::vector > cnsts_vec_vec(cnsts_vec.size()); - for(unsigned i = 0; i < cnsts_vec.size(); i++) - cnsts_vec_vec[i].push_back(cnsts_vec[i]); - iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec_vec,parents_vec,theory); + iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec,parents_vec,theory); tr_killer.set(tr); // set the translation options, if needed @@ -273,7 +271,8 @@ public: std::vector > cnsts_vec_vec(2); for(unsigned j = 0; j < cnsts_vec.size(); j++){ bool is_A = the_base.in_range(j,rng); - cnsts_vec_vec[is_A ? 0 : 1].push_back(cnsts_vec[j]); + for(unsigned k = 0; k < cnsts_vec[j].size(); k++) + cnsts_vec_vec[is_A ? 0 : 1].push_back(cnsts_vec[j][k]); } killme tr_killer_i; @@ -308,6 +307,19 @@ public: } + void proof_to_interpolant(z3pf proof, + std::vector &cnsts, + const std::vector &parents, + std::vector &interps, + const std::vector &theory, + interpolation_options_struct *options = 0 + ){ + std::vector > cnsts_vec(cnsts.size()); + for(unsigned i = 0; i < cnsts.size(); i++) + cnsts_vec[i].push_back(cnsts[i]); + proof_to_interpolant(proof,cnsts_vec,parents,interps,theory,options); + } + // same as above, but represents the tree using an ast void proof_to_interpolant(const z3pf &proof, @@ -322,7 +334,6 @@ public: to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map); - //use the parents vector representation to compute interpolant proof_to_interpolant(proof,cnsts,parents,interps,theory,options); @@ -397,6 +408,35 @@ void iz3interpolate(ast_manager &_m_manager, interps[i] = itp.uncook(_interps[i]); } +void iz3interpolate(ast_manager &_m_manager, + ast *proof, + const ::vector > &cnsts, + const ::vector &parents, + ptr_vector &interps, + const ptr_vector &theory, + interpolation_options_struct * options) +{ + iz3interp itp(_m_manager); + if(options) + options->apply(itp); + std::vector > _cnsts(cnsts.size()); + std::vector _parents(parents.size()); + std::vector _interps; + std::vector _theory(theory.size()); + for(unsigned i = 0; i < cnsts.size(); i++) + for(unsigned j = 0; j < cnsts[i].size(); j++) + _cnsts[i].push_back(itp.cook(cnsts[i][j])); + for(unsigned i = 0; i < parents.size(); i++) + _parents[i] = parents[i]; + for(unsigned i = 0; i < theory.size(); i++) + _theory[i] = itp.cook(theory[i]); + iz3mgr::ast _proof = itp.cook(proof); + itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options); + interps.resize(_interps.size()); + for(unsigned i = 0; i < interps.size(); i++) + interps[i] = itp.uncook(_interps[i]); +} + void iz3interpolate(ast_manager &_m_manager, ast *proof, const ptr_vector &cnsts, diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h index 62f967c02..52aa716c3 100644 --- a/src/interp/iz3interp.h +++ b/src/interp/iz3interp.h @@ -56,6 +56,16 @@ void iz3interpolate(ast_manager &_m_manager, const ptr_vector &theory, interpolation_options_struct * options = 0); +/* Same as above, but each constraint is a vector of formulas. */ + +void iz3interpolate(ast_manager &_m_manager, + ast *proof, + const vector > &cnsts, + const ::vector &parents, + ptr_vector &interps, + const ptr_vector &theory, + interpolation_options_struct * options = 0); + /* Compute an interpolant from a proof. This version uses the ast representation, for compatibility with the new API. */ diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index faa4a636d..24df25f4e 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -815,6 +815,22 @@ iz3mgr::ast iz3mgr::subst(ast var, ast t, ast e){ return subst(memo,var,t,e); } +iz3mgr::ast iz3mgr::subst(stl_ext::hash_map &subst_memo,ast e){ + std::pair foo(e,ast()); + std::pair::iterator,bool> bar = subst_memo.insert(foo); + ast &res = bar.first->second; + if(bar.second){ + int nargs = num_args(e); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = subst(subst_memo,arg(e,i)); + opr f = op(e); + if(f == Equal && args[0] == args[1]) res = mk_true(); + else res = clone(e,args); + } + return res; +} + // apply a quantifier to a formula, with some optimizations // 1) bound variable does not occur -> no quantifier // 2) bound variable must be equal to some term -> substitute diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index f6c0bdf87..760feb000 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -631,6 +631,9 @@ class iz3mgr { ast subst(ast var, ast t, ast e); + // apply a substitution defined by a map + ast subst(stl_ext::hash_map &map, ast e); + // apply a quantifier to a formula, with some optimizations // 1) bound variable does not occur -> no quantifier // 2) bound variable must be equal to some term -> substitute diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index a2d05f7f9..60bdcde9a 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -118,6 +118,28 @@ class iz3proof_itp_impl : public iz3proof_itp { where t is an arbitrary term */ symb rewrite_B; + /* a normalization step is of the form (lhs=rhs) : proof, where "proof" + is a proof of lhs=rhs and lhs is a mixed term. If rhs is a mixed term + then it must have a greater index than lhs. */ + symb normal_step; + + /* A chain of normalization steps is either "true" (the null chain) + or normal_chain( ), where step is a normalization step + and tail is a normalization chain. The lhs of must have + a less term index than any lhs in the chain. Moreover, the rhs of + may not occur as the lhs of step in . If we wish to + add lhs=rhs to the beginning of and rhs=rhs' occurs in + we must apply transitivity, transforming to lhs=rhs'. */ + + symb normal_chain; + + /* If p is a proof of Q and c is a normalization chain, then normal(p,c) + is a proof of Q(c) (that is, Q with all substitutions in c performed). */ + + symb normal; + + + ast get_placeholder(ast t){ hash_map::iterator it = placeholders.find(t); @@ -521,10 +543,16 @@ class iz3proof_itp_impl : public iz3proof_itp { throw cannot_simplify(); } + bool is_normal_ineq(const ast &ineq){ + if(sym(ineq) == normal) + return is_ineq(arg(ineq,0)); + return is_ineq(ineq); + } + ast simplify_sum(std::vector &args){ ast cond = mk_true(); ast ineq = args[0]; - if(!is_ineq(ineq)) throw cannot_simplify(); + if(!is_normal_ineq(ineq)) throw cannot_simplify(); sum_cond_ineq(ineq,cond,args[1],args[2]); return my_implies(cond,ineq); } @@ -540,6 +568,8 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast ineq_from_chain(const ast &chain, ast &cond){ + if(sym(chain) == normal) + throw "normalized inequalities not supported here"; if(is_rewrite_chain(chain)){ ast last = chain_last(chain); ast rest = chain_rest(chain); @@ -561,6 +591,13 @@ class iz3proof_itp_impl : public iz3proof_itp { cond = my_and(cond,arg(ineq2,0)); } else { + if(sym(ineq) == normal || sym(ineq2) == normal){ + ast Aproves = mk_true(); + sum_normal_ineq(ineq,coeff2,ineq2,Aproves,cond); + if(!is_true(Aproves)) + throw "Aproves not handled in sum_cond_ineq"; + return; + } ast the_ineq = ineq_from_chain(ineq2,cond); if(is_ineq(the_ineq)) linear_comb(ineq,coeff2,the_ineq); @@ -569,6 +606,27 @@ class iz3proof_itp_impl : public iz3proof_itp { } } + void destruct_normal(const ast &pf, ast &p, ast &n){ + if(sym(pf) == normal){ + p = arg(pf,0); + n = arg(pf,1); + } + else { + p = pf; + n = mk_true(); + } + } + + void sum_normal_ineq(ast &ineq, const ast &coeff2, const ast &ineq2, ast &Aproves, ast &Bproves){ + ast in1,in2,n1,n2; + destruct_normal(ineq,in1,n1); + destruct_normal(ineq2,in2,n2); + ast dummy; + sum_cond_ineq(in1,dummy,coeff2,in2); + n1 = merge_normal_chains(n1,n2, Aproves, Bproves); + ineq = make(normal,in1,n1); + } + bool is_ineq(const ast &ineq){ opr o = op(ineq); if(o == Not) o = op(arg(ineq,0)); @@ -577,6 +635,12 @@ class iz3proof_itp_impl : public iz3proof_itp { // divide both sides of inequality by a non-negative integer divisor ast idiv_ineq(const ast &ineq1, const ast &divisor){ + if(sym(ineq1) == normal){ + ast in1,n1; + destruct_normal(ineq1,in1,n1); + in1 = idiv_ineq(in1,divisor); + return make(normal,in1,n1); + } if(divisor == make_int(rational(1))) return ineq1; ast ineq = ineq1; @@ -649,11 +713,18 @@ class iz3proof_itp_impl : public iz3proof_itp { ast equa = sep_cond(arg(pf,0),cond); if(is_equivrel_chain(equa)){ ast lhs,rhs; eq_from_ineq(arg(neg_equality,0),lhs,rhs); // get inequality we need to prove - ast ineqs= chain_ineqs(op(arg(neg_equality,0)),LitA,equa,lhs,rhs); // chain must be from lhs to rhs - cond = my_and(cond,chain_conditions(LitA,equa)); - ast Bconds = chain_conditions(LitB,equa); - if(is_true(Bconds) && op(ineqs) != And) - return my_implies(cond,ineqs); + LitType lhst = get_term_type(lhs), rhst = get_term_type(rhs); + if(lhst != LitMixed && rhst != LitMixed){ + ast ineqs= chain_ineqs(op(arg(neg_equality,0)),LitA,equa,lhs,rhs); // chain must be from lhs to rhs + cond = my_and(cond,chain_conditions(LitA,equa)); + ast Bconds = z3_simplify(chain_conditions(LitB,equa)); + if(is_true(Bconds) && op(ineqs) != And) + return my_implies(cond,ineqs); + } + else { + ast itp = make(Leq,make_int(rational(0)),make_int(rational(0))); + return make(normal,itp,cons_normal(fix_normal(lhs,rhs,equa),mk_true())); + } } } throw cannot_simplify(); @@ -757,11 +828,57 @@ class iz3proof_itp_impl : public iz3proof_itp { chain = concat_rewrite_chain(chain,split[1]); } } - else // if not an equivalence, must be of form T <-> pred + else { // if not an equivalence, must be of form T <-> pred chain = concat_rewrite_chain(P,PeqQ); + } return chain; } + void get_subterm_normals(const ast &ineq1, const ast &ineq2, const ast &chain, ast &normals, + const ast &pos, hash_set &memo, ast &Aproves, ast &Bproves){ + opr o1 = op(ineq1); + opr o2 = op(ineq2); + if(o1 == Not || o1 == Leq || o1 == Lt || o1 == Geq || o1 == Gt || o1 == Plus || o1 == Times){ + int n = num_args(ineq1); + if(o2 != o1 || num_args(ineq2) != n) + throw "bad inequality rewriting"; + for(int i = 0; i < n; i++){ + ast new_pos = add_pos_to_end(pos,i); + get_subterm_normals(arg(ineq1,i), arg(ineq2,i), chain, normals, new_pos, memo, Aproves, Bproves); + } + } + else if(get_term_type(ineq2) == LitMixed && memo.find(ineq2) == memo.end()){ + memo.insert(ineq2); + ast sub_chain = extract_rewrites(chain,pos); + if(is_true(sub_chain)) + throw "bad inequality rewriting"; + ast new_normal = make_normal(ineq2,ineq1,reverse_chain(sub_chain)); + normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); + } + } + + ast rewrite_chain_to_normal_ineq(const ast &chain, ast &Aproves, ast &Bproves){ + ast tail, pref = get_head_chain(chain,tail,false); // pref is x=y, tail is x=y -> x'=y' + ast head = chain_last(pref); + ast ineq1 = rewrite_rhs(head); + ast ineq2 = apply_rewrite_chain(ineq1,tail); + ast nc = mk_true(); + hash_set memo; + get_subterm_normals(ineq1,ineq2,tail,nc,top_pos,memo, Aproves, Bproves); + ast itp; + if(is_rewrite_side(LitA,head)){ + itp = ineq1; + ast mc = z3_simplify(chain_side_proves(LitB,pref)); + Bproves = my_and(Bproves,mc); + } + else { + itp = make(Leq,make_int(rational(0)),make_int(rational(0))); + ast mc = z3_simplify(chain_side_proves(LitA,pref)); + Aproves = my_and(Aproves,mc); + } + return make(normal,itp,nc); + } + /* Given a chain rewrite chain deriving not P and a rewrite chain deriving P, return an interpolant. */ ast contra_chain(const ast &neg_chain, const ast &pos_chain){ // equality is a special case. we use the derivation of x=y to rewrite not(x=y) to not(y=y) @@ -790,11 +907,18 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast simplify_modpon(const std::vector &args){ - ast cond = mk_true(); - ast chain = simplify_modpon_fwd(args,cond); - ast Q2 = sep_cond(args[2],cond); - ast interp = is_negation_chain(chain) ? contra_chain(chain,Q2) : contra_chain(Q2,chain); - return my_implies(cond,interp); + ast Aproves = mk_true(), Bproves = mk_true(); + ast chain = simplify_modpon_fwd(args,Bproves); + ast Q2 = sep_cond(args[2],Bproves); + ast interp; + if(is_normal_ineq(Q2)){ // inequalities are special + ast nQ2 = rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); + sum_cond_ineq(nQ2,Bproves,make_int(rational(1)),Q2); + interp = normalize(nQ2); + } + else + interp = is_negation_chain(chain) ? contra_chain(chain,Q2) : contra_chain(Q2,chain); + return my_and(Aproves,my_implies(Bproves,interp)); } @@ -1035,6 +1159,12 @@ class iz3proof_itp_impl : public iz3proof_itp { return make(add_pos,make_int(rational(arg)),pos); } + ast add_pos_to_end(const ast &pos, int i){ + if(pos == top_pos) + return pos_add(i,pos); + return make(add_pos,arg(pos,0),add_pos_to_end(arg(pos,1),i)); + } + /* return the argument number of position, if not top */ int pos_arg(const ast &pos){ rational r; @@ -1170,6 +1300,10 @@ class iz3proof_itp_impl : public iz3proof_itp { return make(sym(rew),pos_add(apos,arg(rew,0)),arg(rew,1),arg(rew,2)); } + ast rewrite_pos_set(const ast &pos, const ast &rew){ + return make(sym(rew),pos,arg(rew,1),arg(rew,2)); + } + ast rewrite_up(const ast &rew){ return make(sym(rew),arg(arg(rew,0),1),arg(rew,1),arg(rew,2)); } @@ -1317,6 +1451,28 @@ class iz3proof_itp_impl : public iz3proof_itp { split_chain_rec(chain,res); } + ast extract_rewrites(const ast &chain, const ast &pos){ + if(is_true(chain)) + return chain; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast new_rest = extract_rewrites(rest,pos); + ast p1 = rewrite_pos(last); + ast diff; + switch(pos_diff(p1,pos,diff)){ + case -1: { + ast new_last = rewrite_pos_set(diff, last); + return chain_cons(new_rest,new_last); + } + case 1: + if(rewrite_lhs(last) != rewrite_rhs(last)) + throw "bad rewrite chain"; + break; + default:; + } + return new_rest; + } + ast down_chain(const ast &chain){ ast split[2]; split_chain(chain,split); @@ -1381,7 +1537,7 @@ class iz3proof_itp_impl : public iz3proof_itp { // ast s = ineq_to_lhs(ineq); // ast srhs = arg(s,1); ast srhs = arg(ineq,0); - if(op(srhs) == Plus && num_args(srhs) == 2){ + if(op(srhs) == Plus && num_args(srhs) == 2 && arg(ineq,1) == make_int(rational(0))){ lhs = arg(srhs,0); rhs = arg(srhs,1); // if(op(lhs) == Times) @@ -1393,6 +1549,11 @@ class iz3proof_itp_impl : public iz3proof_itp { return; } } + if(op(ineq) == Leq){ + lhs = srhs; + rhs = arg(ineq,1); + return; + } throw "bad ineq"; } @@ -1404,7 +1565,171 @@ class iz3proof_itp_impl : public iz3proof_itp { return chain_cons(rest,last); } + ast apply_rewrite_chain(const ast &t, const ast &chain){ + if(is_true(chain)) + return t; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast mid = apply_rewrite_chain(t,rest); + ast res = subst_in_pos(mid,rewrite_pos(last),rewrite_rhs(last)); + return res; + } + ast drop_rewrites(LitType t, const ast &chain, ast &remainder){ + if(!is_true(chain)){ + ast last = chain_last(chain); + ast rest = chain_rest(chain); + if(is_rewrite_side(t,last)){ + ast res = drop_rewrites(t,rest,remainder); + remainder = chain_cons(remainder,last); + return res; + } + } + remainder = mk_true(); + return chain; + } + + // Normalization chains + + ast cons_normal(const ast &first, const ast &rest){ + return make(normal_chain,first,rest); + } + + ast normal_first(const ast &t){ + return arg(t,0); + } + + ast normal_rest(const ast &t){ + return arg(t,1); + } + + ast normal_lhs(const ast &t){ + return arg(arg(t,0),1); + } + + ast normal_rhs(const ast &t){ + return arg(arg(t,0),1); + } + + ast normal_proof(const ast &t){ + return arg(t,1); + } + + ast make_normal(const ast &lhs, const ast &rhs, const ast &proof){ + return make(normal_step,make_equiv(lhs,rhs),proof); + } + + ast fix_normal(const ast &lhs, const ast &rhs, const ast &proof){ + LitType rhst = get_term_type(rhs); + if(rhst != LitMixed || ast_id(lhs) < ast_id(rhs)) + return make_normal(lhs,rhs,proof); + else + return make_normal(rhs,lhs,reverse_chain(proof)); + } + + ast chain_side_proves(LitType side, const ast &chain){ + LitType other_side = side == LitA ? LitB : LitA; + return my_and(chain_conditions(other_side,chain),my_implies(chain_conditions(side,chain),chain_formulas(side,chain))); + } + + // Merge two normalization chains + ast merge_normal_chains_rec(const ast &chain1, const ast &chain2, hash_map &trans, ast &Aproves, ast &Bproves){ + if(is_true(chain1)) + return chain2; + if(is_true(chain2)) + return chain1; + ast f1 = normal_first(chain1); + ast f2 = normal_first(chain2); + ast lhs1 = normal_lhs(f1); + ast lhs2 = normal_lhs(f2); + int id1 = ast_id(lhs1); + int id2 = ast_id(lhs2); + if(id1 < id2) return cons_normal(f1,merge_normal_chains_rec(normal_rest(chain1),chain2,trans,Aproves,Bproves)); + if(id2 < id1) return cons_normal(f2,merge_normal_chains_rec(chain1,normal_rest(chain2),trans,Aproves,Bproves)); + ast rhs1 = normal_rhs(f1); + ast rhs2 = normal_rhs(f2); + LitType t1 = get_term_type(rhs1); + LitType t2 = get_term_type(rhs2); + int tid1 = ast_id(rhs1); + int tid2 = ast_id(rhs2); + ast pf1 = normal_proof(f1); + ast pf2 = normal_proof(f2); + ast new_normal; + if(t1 == LitMixed && (t2 != LitMixed || tid2 > tid1)){ + ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); + new_normal = f2; + trans[rhs1] = make_normal(rhs1,rhs2,new_proof); + } + else if(t2 == LitMixed && (t1 != LitMixed || tid1 > tid2)) + return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); + else if(t1 == LitA && t2 == LitB){ + ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); + ast Bproof, Aproof = drop_rewrites(LitB,new_proof,Bproof); + ast mcA = chain_side_proves(LitB,Aproof); + Bproves = my_and(Bproves,mcA); + ast mcB = chain_side_proves(LitA,Bproof); + Aproves = my_and(Aproves,mcB); + ast rep = apply_rewrite_chain(rhs1,Aproof); + new_proof = concat_rewrite_chain(pf1,Aproof); + new_normal = make_normal(rhs1,rep,new_proof); + } + else if(t1 == LitA && t2 == LitB) + return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); + else if(t1 == LitA) { + ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); + ast mc = chain_side_proves(LitB,new_proof); + Bproves = my_and(Bproves,mc); + new_normal = f1; // choice is arbitrary + } + else { /* t1 = t2 = LitB */ + ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); + ast mc = chain_side_proves(LitA,new_proof); + Aproves = my_and(Aproves,mc); + new_normal = f1; // choice is arbitrary + } + return cons_normal(new_normal,merge_normal_chains_rec(normal_rest(chain1),normal_rest(chain2),trans,Aproves,Bproves)); + } + + ast trans_normal_chain(const ast &chain, hash_map &trans){ + if(is_true(chain)) + return chain; + ast f = normal_first(chain); + ast r = normal_rest(chain); + ast rhs = normal_rhs(f); + hash_map::iterator it = trans.find(rhs); + ast new_normal; + if(it != trans.end()){ + const ast &f2 = it->second; + ast pf = concat_rewrite_chain(normal_proof(f),normal_proof(f2)); + new_normal = make_normal(normal_lhs(f),normal_rhs(f2),pf); + } + else + new_normal = f; + return cons_normal(new_normal,trans_normal_chain(r,trans)); + } + + ast merge_normal_chains(const ast &chain1, const ast &chain2, ast &Aproves, ast &Bproves){ + hash_map trans; + ast res = merge_normal_chains_rec(chain1,chain2,trans,Aproves,Bproves); + res = trans_normal_chain(res,trans); + return res; + } + + ast normalize(const ast &t){ + if(sym(t) != normal) + return t; + ast chain = arg(t,1); + hash_map map; + for(ast c = chain; !is_true(c); c = normal_rest(c)){ + ast first = normal_first(c); + ast lhs = normal_lhs(first); + ast rhs = normal_rhs(first); + map[lhs] = rhs; + } + ast res = subst(map,arg(t,0)); + return res; + } + /** Make an assumption node. The given clause is assumed in the given frame. */ virtual node make_assumption(int frame, const std::vector &assumption){ if(!weak){ @@ -1939,6 +2264,8 @@ class iz3proof_itp_impl : public iz3proof_itp { */ ast make_refl(const ast &e){ + if(get_term_type(e) == LitA) + return mk_false(); return mk_true(); // TODO: is this right? } @@ -2141,6 +2468,12 @@ public: m().inc_ref(rewrite_A); rewrite_B = function("@rewrite_B",3,boolboolbooldom,bool_type()); m().inc_ref(rewrite_B); + normal_step = function("@normal_step",2,boolbooldom,bool_type()); + m().inc_ref(normal_step); + normal_chain = function("@normal_chain",2,boolbooldom,bool_type()); + m().inc_ref(normal_chain); + normal = function("@normal",2,boolbooldom,bool_type()); + m().inc_ref(normal); } ~iz3proof_itp_impl(){ From a3462ba6aa47237a01e2c62669b408493e3b62d9 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 27 Nov 2013 17:39:49 -0800 Subject: [PATCH 226/509] working on duality --- src/duality/duality.h | 43 ++++- src/duality/duality_rpfp.cpp | 79 ++++++++- src/duality/duality_solver.cpp | 107 ++++++++---- src/duality/duality_wrapper.cpp | 37 ++++- src/duality/duality_wrapper.h | 37 +++++ src/interp/iz3interp.cpp | 2 - src/interp/iz3mgr.h | 6 + src/interp/iz3proof_itp.cpp | 200 +++++++++++++++-------- src/interp/iz3proof_itp.h | 3 + src/interp/iz3translate.cpp | 20 ++- src/muz/duality/duality_dl_interface.cpp | 2 +- 11 files changed, 415 insertions(+), 121 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 158ad84d9..fb3761626 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -142,6 +142,7 @@ namespace Duality { context *ctx; /** Z3 context for formulas */ solver *slvr; /** Z3 solver */ bool need_goals; /** Can the solver use the goal tree to optimize interpolants? */ + solver aux_solver; /** For temporary use -- don't leave assertions here. */ /** Tree interpolation. This method assumes the formulas in TermTree "assumptions" are currently asserted in the solver. The return @@ -178,6 +179,8 @@ namespace Duality { /** Cancel, throw Canceled object if possible. */ virtual void cancel(){ } + LogicSolver(context &c) : aux_solver(c){} + virtual ~LogicSolver(){} }; @@ -215,7 +218,7 @@ namespace Duality { } #endif - iZ3LogicSolver(context &c){ + iZ3LogicSolver(context &c) : LogicSolver(c) { ctx = ictx = &c; slvr = islvr = new interpolating_solver(*ictx); need_goals = false; @@ -287,9 +290,9 @@ namespace Duality { literals dualLabels; std::list stack; std::vector axioms; // only saved here for printing purposes - solver aux_solver; - - + solver &aux_solver; + hash_set *proof_core; + public: /** Construct an RPFP graph with a given interpolating prover context. It is allowed to @@ -299,13 +302,14 @@ namespace Duality { inherit the axioms. */ - RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx), *(_ls->slvr)), dualModel(*(_ls->ctx)), aux_solver(*(_ls->ctx)) + RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx), *(_ls->slvr)), dualModel(*(_ls->ctx)), aux_solver(_ls->aux_solver) { ls = _ls; nodeCount = 0; edgeCount = 0; stack.push_back(stack_entry()); HornClauses = false; + proof_core = 0; } ~RPFP(); @@ -606,9 +610,13 @@ namespace Duality { lbool Solve(Node *root, int persist); + /** Same as Solve, but annotates only a single node. */ + + lbool SolveSingleNode(Node *root, Node *node); + /** Get the constraint tree (but don't solve it) */ - TermTree *GetConstraintTree(Node *root); + TermTree *GetConstraintTree(Node *root, Node *skip_descendant = 0); /** Dispose of the dual model (counterexample) if there is one. */ @@ -678,6 +686,12 @@ namespace Duality { /** Pop a scope (see Push). Note, you cannot pop axioms. */ void Pop(int num_scopes); + + /** Return true if the given edge is used in the proof of unsat. + Can be called only after Solve or Check returns an unsat result. */ + + bool EdgeUsedInProof(Edge *edge); + /** Convert a collection of clauses to Nodes and Edges in the RPFP. @@ -762,8 +776,19 @@ namespace Duality { // int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); + /** Compute and save the proof core for future calls to + EdgeUsedInProof. You only need to call this if you will pop + the solver before calling EdgeUsedInProof. + */ + void ComputeProofCore(); + private: + void ClearProofCore(){ + if(proof_core) + delete proof_core; + proof_core = 0; + } Term SuffixVariable(const Term &t, int n); @@ -779,10 +804,14 @@ namespace Duality { Term ReducedDualEdge(Edge *e); - TermTree *ToTermTree(Node *root); + TermTree *ToTermTree(Node *root, Node *skip_descendant = 0); TermTree *ToGoalTree(Node *root); + void CollapseTermTreeRec(TermTree *root, TermTree *node); + + TermTree *CollapseTermTree(TermTree *node); + void DecodeTree(Node *root, TermTree *interp, int persist); Term GetUpperBound(Node *n); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 17e3de6bb..6b32bb080 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -185,6 +185,7 @@ namespace Duality { return clone_quantifier(t,new_body); } + RPFP::Term RPFP::LocalizeRec(Edge *e, hash_map &memo, const Term &t) { std::pair foo(t,expr(ctx)); @@ -274,13 +275,15 @@ namespace Duality { return implies(b, Localize(e, e->F.Formula)); } - TermTree *RPFP::ToTermTree(Node *root) + TermTree *RPFP::ToTermTree(Node *root, Node *skip_descendant) { + if(skip_descendant && root == skip_descendant) + return new TermTree(ctx.bool_val(true)); Edge *e = root->Outgoing; if(!e) return new TermTree(ctx.bool_val(true), std::vector()); std::vector children(e->Children.size()); for(unsigned i = 0; i < children.size(); i++) - children[i] = ToTermTree(e->Children[i]); + children[i] = ToTermTree(e->Children[i],skip_descendant); // Term top = ReducedDualEdge(e); Term top = e->dual.null() ? ctx.bool_val(true) : e->dual; TermTree *res = new TermTree(top, children); @@ -623,6 +626,7 @@ namespace Duality { TermTree *goals = NULL; if(ls->need_goals) goals = GetGoalTree(root); + ClearProofCore(); // if (dualModel != null) dualModel.Dispose(); // if (dualLabels != null) dualLabels.Dispose(); @@ -644,11 +648,54 @@ namespace Duality { return res; } + void RPFP::CollapseTermTreeRec(TermTree *root, TermTree *node){ + root->addTerm(node->getTerm()); + std::vector &cnsts = node->getTerms(); + for(unsigned i = 0; i < cnsts.size(); i++) + root->addTerm(cnsts[i]); + std::vector &chs = node->getChildren(); + for(unsigned i = 0; i < chs.size(); i++){ + CollapseTermTreeRec(root,chs[i]); + } + } + + TermTree *RPFP::CollapseTermTree(TermTree *node){ + std::vector &chs = node->getChildren(); + for(unsigned i = 0; i < chs.size(); i++) + CollapseTermTreeRec(node,chs[i]); + for(unsigned i = 0; i < chs.size(); i++) + delete chs[i]; + chs.clear(); + return node; + } + + lbool RPFP::SolveSingleNode(Node *root, Node *node) + { + timer_start("Solve"); + TermTree *tree = CollapseTermTree(GetConstraintTree(root,node)); + tree->getChildren().push_back(CollapseTermTree(ToTermTree(node))); + TermTree *interpolant = NULL; + ClearProofCore(); + + timer_start("interpolate_tree"); + lbool res = ls->interpolate_tree(tree, interpolant, dualModel,0,true); + timer_stop("interpolate_tree"); + if (res == l_false) + { + DecodeTree(node, interpolant->getChildren()[0], 0); + delete interpolant; + } + + delete tree; + timer_stop("Solve"); + return res; + } + /** Get the constraint tree (but don't solve it) */ - TermTree *RPFP::GetConstraintTree(Node *root) + TermTree *RPFP::GetConstraintTree(Node *root, Node *skip_descendant) { - return AddUpperBound(root, ToTermTree(root)); + return AddUpperBound(root, ToTermTree(root,skip_descendant)); } /** Dispose of the dual model (counterexample) if there is one. */ @@ -677,6 +724,7 @@ namespace Duality { check_result RPFP::Check(Node *root, std::vector underapproxes, std::vector *underapprox_core ) { + ClearProofCore(); // if (dualModel != null) dualModel.Dispose(); check_result res; if(!underapproxes.size()) @@ -713,6 +761,7 @@ namespace Duality { check_result RPFP::CheckUpdateModel(Node *root, std::vector assumps){ // check_result temp1 = slvr.check(); // no idea why I need to do this + ClearProofCore(); check_result res = slvr.check_keep_model(assumps.size(),&assumps[0]); dualModel = slvr.get_model(); return res; @@ -1760,9 +1809,9 @@ namespace Duality { expr conj = ctx.make(And,conjuncts); s.add(conj); check_result res = s.check(); - s.pop(1); if(res != unsat) throw "should be unsat"; + s.pop(1); for(unsigned i = 0; i < conjuncts.size(); ){ std::swap(conjuncts[i],conjuncts.back()); @@ -2276,8 +2325,28 @@ namespace Duality { } + void RPFP::ComputeProofCore(){ + if(!proof_core){ + std::vector assumps; + slvr.get_proof().get_assumptions(assumps); + proof_core = new hash_set; + for(unsigned i = 0; i < assumps.size(); i++) + proof_core->insert(assumps[i]); + } + } + + bool RPFP::EdgeUsedInProof(Edge *edge){ + ComputeProofCore(); + if(!edge->dual.null() && proof_core->find(edge->dual) != proof_core->end()) + return true; + for(unsigned i = 0; i < edge->constraints.size(); i++) + if(proof_core->find(edge->constraints[i]) != proof_core->end()) + return true; + return false; + } RPFP::~RPFP(){ + ClearProofCore(); for(unsigned i = 0; i < nodes.size(); i++) delete nodes[i]; for(unsigned i = 0; i < edges.size(); i++) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index afa1c7683..86b95a657 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -184,7 +184,7 @@ namespace Duality { best.insert(*it); } #else - virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority=false){ + virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority=false, bool best_only=false){ if(high_priority) return; int best_score = INT_MAX; int worst_score = 0; @@ -194,13 +194,13 @@ namespace Duality { best_score = std::min(best_score,score); worst_score = std::max(worst_score,score); } - int cutoff = best_score + (worst_score-best_score)/2; + int cutoff = best_only ? best_score : (best_score + (worst_score-best_score)/2); for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) if(scores[(*it)->map].updates <= cutoff) best.insert(*it); } #endif - + /** Called when done expanding a tree */ virtual void Done() {} }; @@ -1607,12 +1607,12 @@ namespace Duality { heuristic->ChooseExpand(choices, best); } #else - void ExpansionChoicesFull(std::set &best, bool high_priority){ + void ExpansionChoicesFull(std::set &best, bool high_priority, bool best_only = false){ std::set choices; for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) if (high_priority || !tree->Empty(*it)) // if used in the counter-model choices.insert(*it); - heuristic->ChooseExpand(choices, best, high_priority); + heuristic->ChooseExpand(choices, best, high_priority, best_only); } void ExpansionChoicesRec(std::vector &unused_set, std::vector &used_set, @@ -1650,9 +1650,9 @@ namespace Duality { std::set old_choices; - void ExpansionChoices(std::set &best, bool high_priority){ + void ExpansionChoices(std::set &best, bool high_priority, bool best_only = false){ if(!underapprox || constrained || high_priority){ - ExpansionChoicesFull(best, high_priority); + ExpansionChoicesFull(best, high_priority,best_only); return; } std::vector unused_set, used_set; @@ -1684,7 +1684,7 @@ namespace Duality { timer_start("ExpandSomeNodes"); timer_start("ExpansionChoices"); std::set choices; - ExpansionChoices(choices,high_priority); + ExpansionChoices(choices,high_priority,max != INT_MAX); timer_stop("ExpansionChoices"); std::list leaves_copy = leaves; // copy so can modify orig leaves.clear(); @@ -1740,38 +1740,54 @@ namespace Duality { if(slvr_level != stack.back().level) throw "stacks out of sync!"; - 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); + res = foo == unsat ? l_false : l_true; if (res == l_false) { if (stack.empty()) // should never happen return false; - std::vector &expansions = stack.back().expansions; - int update_count = 0; - for(unsigned i = 0; i < expansions.size(); i++){ - tree->Generalize(expansions[i]); - if(RecordUpdate(expansions[i])) - update_count++; - } - if(update_count == 0) - std::cout << "backtracked without learning\n"; - tree->Pop(1); - hash_set leaves_to_remove; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - // if(node != top) - // tree->ConstrainParent(node->Incoming[0],node); - std::vector &cs = node->Outgoing->Children; - for(unsigned i = 0; i < cs.size(); i++){ - leaves_to_remove.insert(cs[i]); - UnmapNode(cs[i]); - if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) - throw "help!"; + { + std::vector &expansions = stack.back().expansions; + int update_count = 0; + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + tree->SolveSingleNode(top,node); + tree->Generalize(node); + if(RecordUpdate(node)) + update_count++; } - RemoveExpansion(node); + if(update_count == 0) + std::cout << "backtracked without learning\n"; } - RemoveLeaves(leaves_to_remove); - stack.pop_back(); + tree->ComputeProofCore(); // need to compute the proof core before popping solver + while(1) { + std::vector &expansions = stack.back().expansions; + bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop + tree->Pop(1); + hash_set leaves_to_remove; + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + // if(node != top) + // tree->ConstrainParent(node->Incoming[0],node); + std::vector &cs = node->Outgoing->Children; + for(unsigned i = 0; i < cs.size(); i++){ + leaves_to_remove.insert(cs[i]); + UnmapNode(cs[i]); + if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) + throw "help!"; + } + RemoveExpansion(node); + } + RemoveLeaves(leaves_to_remove); + stack.pop_back(); + if(prev_level_used || stack.size() == 1) break; + RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list + std::vector &unused_ex = stack.back().expansions; + for(unsigned i = 0; i < unused_ex.size(); i++) + heuristic->Update(unused_ex[i]->map); // make it less likely to expand this node in future + } HandleUpdatedNodes(); if(stack.size() == 1) return false; @@ -1782,8 +1798,10 @@ namespace Duality { for(unsigned i = 0; i < expansions.size(); i++){ tree->FixCurrentState(expansions[i]->Outgoing); } +#if 0 if(tree->slvr.check() == unsat) throw "help!"; +#endif stack.push_back(stack_entry()); stack.back().level = tree->slvr.get_scope_level(); if(ExpandSomeNodes(false,1)){ @@ -1798,6 +1816,27 @@ namespace Duality { } } + bool LevelUsedInProof(unsigned level){ + std::vector &expansions = stack[level].expansions; + for(unsigned i = 0; i < expansions.size(); i++) + if(tree->EdgeUsedInProof(expansions[i]->Outgoing)) + return true; + return false; + } + + void RemoveUpdateNodesAtCurrentLevel() { + for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ + Node *node = *it; + if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ + std::list::iterator victim = it; + ++it; + updated_nodes.erase(victim); + } + else + ++it; + } + } + void RemoveLeaves(hash_set &leaves_to_remove){ std::list leaves_copy; leaves_copy.swap(leaves); @@ -2270,7 +2309,7 @@ namespace Duality { return name; } - virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority){ + virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ if(!high_priority || !old_cex.tree){ Heuristic::ChooseExpand(choices,best,false); return; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index dd64052a0..e70847d47 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -481,9 +481,9 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st for(unsigned i = 0; i < theory.size(); i++) _theory[i] = theory[i]; - push(); if(!incremental){ + push(); for(unsigned i = 0; i < linear_assumptions.size(); i++) for(unsigned j = 0; j < linear_assumptions[i].size(); j++) add(linear_assumptions[i][j]); @@ -522,7 +522,8 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st } #endif - pop(); + if(!incremental) + pop(); return (res == unsat) ? l_false : ((res == sat) ? l_true : l_undef); @@ -554,6 +555,29 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st return ""; } + + static void get_assumptions_rec(stl_ext::hash_set &memo, const proof &pf, std::vector &assumps){ + if(memo.find(pf) != memo.end())return; + memo.insert(pf); + pfrule dk = pf.rule(); + if(dk == PR_ASSERTED){ + expr con = pf.conc(); + assumps.push_back(con); + } + else { + unsigned nprems = pf.num_prems(); + for(unsigned i = 0; i < nprems; i++){ + proof arg = pf.prem(i); + get_assumptions_rec(memo,arg,assumps); + } + } + } + + void proof::get_assumptions(std::vector &assumps){ + stl_ext::hash_set memo; + get_assumptions_rec(memo,*this,assumps); + } + void ast::show() const{ std::cout << mk_pp(raw(), m()) << std::endl; @@ -564,6 +588,15 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st std::cout << std::endl; } + void solver::show() { + 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(std::cout, m_solver->get_assertion(n-1)); + } void include_ast_show(ast &a){ a.show(); diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 291ddfbcf..fc94e53f2 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -393,6 +393,7 @@ namespace Duality { sort array_range() const; }; + class func_decl : public ast { public: func_decl() : ast() {} @@ -593,6 +594,36 @@ namespace Duality { }; + typedef ::decl_kind pfrule; + + class proof : public ast { + public: + proof(context & c):ast(c) {} + proof(context & c, ::proof *s):ast(c, s) {} + proof(proof const & s):ast(s) {} + operator ::proof*() const { return to_app(raw()); } + proof & operator=(proof const & s) { return static_cast(ast::operator=(s)); } + + pfrule rule() const { + ::func_decl *d = to_app(raw())->get_decl(); + return d->get_decl_kind(); + } + + unsigned num_prems() const { + return to_app(raw())->get_num_args() - 1; + } + + expr conc() const { + return ctx().cook(to_app(raw())->get_arg(num_prems())); + } + + proof prem(unsigned i) const { + return proof(ctx(),to_app(to_app(raw())->get_arg(i))); + } + + void get_assumptions(std::vector &assumps); + }; + #if 0 #if Z3_MAJOR_VERSION > 4 || Z3_MAJOR_VERSION == 4 && Z3_MINOR_VERSION >= 3 @@ -870,6 +901,12 @@ namespace Duality { unsigned get_scope_level(){return m_solver->get_scope_level();} + void show(); + + proof get_proof(){ + return proof(ctx(),m_solver->get_proof()); + } + }; #if 0 diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 7ef7e6aa2..9196e24d9 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -501,5 +501,3 @@ void interpolation_options_struct::apply(iz3base &b){ b.set_option((*it).first,(*it).second); } - - diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 760feb000..6d0daa89a 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -359,6 +359,12 @@ class iz3mgr { return fid == m().get_basic_family_id() && k == BOOL_SORT; } + bool is_array_type(type t){ + family_id fid = to_sort(t)->get_family_id(); + decl_kind k = to_sort(t)->get_decl_kind(); + return fid == m_array_fid && k == ARRAY_SORT; + } + type get_range_type(symb s){ return to_func_decl(s)->get_range(); } diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 60bdcde9a..f8537d6eb 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -473,7 +473,12 @@ class iz3proof_itp_impl : public iz3proof_itp { hash_map simplify_memo; ast simplify(const ast &t){ - return simplify_rec(t); + ast res = normalize(simplify_rec(t)); +#ifdef BOGUS_QUANTS + if(localization_vars.size()) + res = add_quants(z3_simplify(res)); +#endif + return res; } ast simplify_rec(const ast &e){ @@ -550,11 +555,11 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast simplify_sum(std::vector &args){ - ast cond = mk_true(); + ast Aproves = mk_true(), Bproves = mk_true(); ast ineq = args[0]; if(!is_normal_ineq(ineq)) throw cannot_simplify(); - sum_cond_ineq(ineq,cond,args[1],args[2]); - return my_implies(cond,ineq); + sum_cond_ineq(ineq,args[1],args[2],Aproves,Bproves); + return my_and(Aproves,my_implies(Bproves,ineq)); } ast simplify_rotate_sum(const ast &pl, const ast &pf){ @@ -567,38 +572,42 @@ class iz3proof_itp_impl : public iz3proof_itp { return sym(chain) == concat; } - ast ineq_from_chain(const ast &chain, ast &cond){ - if(sym(chain) == normal) - throw "normalized inequalities not supported here"; - if(is_rewrite_chain(chain)){ - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_true(rest) && is_rewrite_side(LitA,last) - && is_true(rewrite_lhs(last))){ - cond = my_and(cond,rewrite_cond(last)); - return rewrite_rhs(last); - } - if(is_rewrite_side(LitB,last) && is_true(rewrite_cond(last))) - return ineq_from_chain(rest,cond); +#if 0 + ast ineq_from_chain_simple(const ast &chain, ast &cond){ + if(is_true(chain)) + return chain; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + if(is_true(rest) && is_rewrite_side(LitA,last) + && is_true(rewrite_lhs(last))){ + cond = my_and(cond,rewrite_cond(last)); + return rewrite_rhs(last); } + if(is_rewrite_side(LitB,last) && is_true(rewrite_cond(last))) + return ineq_from_chain_simple(rest,cond); + return chain; + } +#endif + + ast ineq_from_chain(const ast &chain, ast &Aproves, ast &Bproves){ + if(is_rewrite_chain(chain)) + return rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); return chain; } - void sum_cond_ineq(ast &ineq, ast &cond, const ast &coeff2, const ast &ineq2){ + + void sum_cond_ineq(ast &ineq, const ast &coeff2, const ast &ineq2, ast &Aproves, ast &Bproves){ opr o = op(ineq2); if(o == Implies){ - sum_cond_ineq(ineq,cond,coeff2,arg(ineq2,1)); - cond = my_and(cond,arg(ineq2,0)); + sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); + Bproves = my_and(Bproves,arg(ineq2,0)); } else { - if(sym(ineq) == normal || sym(ineq2) == normal){ - ast Aproves = mk_true(); - sum_normal_ineq(ineq,coeff2,ineq2,Aproves,cond); - if(!is_true(Aproves)) - throw "Aproves not handled in sum_cond_ineq"; + ast the_ineq = ineq_from_chain(ineq2,Aproves,Bproves); + if(sym(ineq) == normal || sym(the_ineq) == normal){ + sum_normal_ineq(ineq,coeff2,the_ineq,Aproves,Bproves); return; } - ast the_ineq = ineq_from_chain(ineq2,cond); if(is_ineq(the_ineq)) linear_comb(ineq,coeff2,the_ineq); else @@ -621,10 +630,10 @@ class iz3proof_itp_impl : public iz3proof_itp { ast in1,in2,n1,n2; destruct_normal(ineq,in1,n1); destruct_normal(ineq2,in2,n2); - ast dummy; - sum_cond_ineq(in1,dummy,coeff2,in2); + ast dummy1, dummy2; + sum_cond_ineq(in1,coeff2,in2,dummy1,dummy2); n1 = merge_normal_chains(n1,n2, Aproves, Bproves); - ineq = make(normal,in1,n1); + ineq = make_normal(in1,n1); } bool is_ineq(const ast &ineq){ @@ -639,7 +648,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast in1,n1; destruct_normal(ineq1,in1,n1); in1 = idiv_ineq(in1,divisor); - return make(normal,in1,n1); + return make_normal(in1,n1); } if(divisor == make_int(rational(1))) return ineq1; @@ -649,17 +658,23 @@ class iz3proof_itp_impl : public iz3proof_itp { return make(op(ineq),mk_idiv(arg(ineq,0),divisor),mk_idiv(arg(ineq,1),divisor)); } - ast rotate_sum_rec(const ast &pl, const ast &pf, ast &cond, ast &ineq){ + ast rotate_sum_rec(const ast &pl, const ast &pf, ast &Bproves, ast &ineq){ if(pf == pl) - return my_implies(cond,simplify_ineq(ineq)); + return my_implies(Bproves,simplify_ineq(ineq)); if(op(pf) == Uninterpreted && sym(pf) == sum){ if(arg(pf,2) == pl){ - sum_cond_ineq(ineq,cond,make_int("1"),arg(pf,0)); + ast Aproves = mk_true(); + sum_cond_ineq(ineq,make_int("1"),arg(pf,0),Aproves,Bproves); + if(!is_true(Aproves)) + throw "help!"; ineq = idiv_ineq(ineq,arg(pf,1)); - return my_implies(cond,ineq); + return my_implies(Bproves,ineq); } - sum_cond_ineq(ineq,cond,arg(pf,1),arg(pf,2)); - return rotate_sum_rec(pl,arg(pf,0),cond,ineq); + ast Aproves = mk_true(); + sum_cond_ineq(ineq,arg(pf,1),arg(pf,2),Aproves,Bproves); + if(!is_true(Aproves)) + throw "help!"; + return rotate_sum_rec(pl,arg(pf,0),Bproves,ineq); } throw cannot_simplify(); } @@ -669,28 +684,30 @@ class iz3proof_itp_impl : public iz3proof_itp { ast equality = arg(neg_equality,0); ast x = arg(equality,0); ast y = arg(equality,1); - ast cond1 = mk_true(); - ast xleqy = round_ineq(ineq_from_chain(arg(pf,1),cond1)); - ast yleqx = round_ineq(ineq_from_chain(arg(pf,2),cond1)); + ast Aproves1 = mk_true(), Bproves1 = mk_true(); + ast xleqy = round_ineq(ineq_from_chain(arg(pf,1),Aproves1,Bproves1)); + ast yleqx = round_ineq(ineq_from_chain(arg(pf,2),Aproves1,Bproves1)); ast ineq1 = make(Leq,make_int("0"),make_int("0")); - sum_cond_ineq(ineq1,cond1,make_int("-1"),xleqy); - sum_cond_ineq(ineq1,cond1,make_int("-1"),yleqx); - cond1 = my_and(cond1,z3_simplify(ineq1)); - ast cond2 = mk_true(); + sum_cond_ineq(ineq1,make_int("-1"),xleqy,Aproves1,Bproves1); + sum_cond_ineq(ineq1,make_int("-1"),yleqx,Aproves1,Bproves1); + Bproves1 = my_and(Bproves1,z3_simplify(ineq1)); + ast Aproves2 = mk_true(), Bproves2 = mk_true(); ast ineq2 = make(Leq,make_int("0"),make_int("0")); - sum_cond_ineq(ineq2,cond2,make_int("1"),xleqy); - sum_cond_ineq(ineq2,cond2,make_int("1"),yleqx); - cond2 = z3_simplify(ineq2); + sum_cond_ineq(ineq2,make_int("1"),xleqy,Aproves2,Bproves2); + sum_cond_ineq(ineq2,make_int("1"),yleqx,Aproves2,Bproves2); + Bproves2 = z3_simplify(ineq2); + if(!is_true(Aproves1) || !is_true(Aproves2)) + throw "help!"; if(get_term_type(x) == LitA){ ast iter = z3_simplify(make(Plus,x,get_ineq_rhs(xleqy))); - ast rewrite1 = make_rewrite(LitA,top_pos,cond1,make(Equal,x,iter)); - ast rewrite2 = make_rewrite(LitB,top_pos,cond2,make(Equal,iter,y)); + ast rewrite1 = make_rewrite(LitA,top_pos,Bproves1,make(Equal,x,iter)); + ast rewrite2 = make_rewrite(LitB,top_pos,Bproves2,make(Equal,iter,y)); return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); } if(get_term_type(y) == LitA){ ast iter = z3_simplify(make(Plus,y,get_ineq_rhs(yleqx))); - ast rewrite2 = make_rewrite(LitA,top_pos,cond1,make(Equal,iter,y)); - ast rewrite1 = make_rewrite(LitB,top_pos,cond2,make(Equal,x,iter)); + ast rewrite2 = make_rewrite(LitA,top_pos,Bproves1,make(Equal,iter,y)); + ast rewrite1 = make_rewrite(LitB,top_pos,Bproves2,make(Equal,x,iter)); return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); } throw cannot_simplify(); @@ -723,7 +740,7 @@ class iz3proof_itp_impl : public iz3proof_itp { } else { ast itp = make(Leq,make_int(rational(0)),make_int(rational(0))); - return make(normal,itp,cons_normal(fix_normal(lhs,rhs,equa),mk_true())); + return make_normal(itp,cons_normal(fix_normal(lhs,rhs,equa),mk_true())); } } } @@ -852,7 +869,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast sub_chain = extract_rewrites(chain,pos); if(is_true(sub_chain)) throw "bad inequality rewriting"; - ast new_normal = make_normal(ineq2,ineq1,reverse_chain(sub_chain)); + ast new_normal = make_normal_step(ineq2,ineq1,reverse_chain(sub_chain)); normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); } } @@ -876,7 +893,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast mc = z3_simplify(chain_side_proves(LitA,pref)); Aproves = my_and(Aproves,mc); } - return make(normal,itp,nc); + return make_normal(itp,nc); } /* Given a chain rewrite chain deriving not P and a rewrite chain deriving P, return an interpolant. */ @@ -913,7 +930,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast interp; if(is_normal_ineq(Q2)){ // inequalities are special ast nQ2 = rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); - sum_cond_ineq(nQ2,Bproves,make_int(rational(1)),Q2); + sum_cond_ineq(nQ2,make_int(rational(1)),Q2,Aproves,Bproves); interp = normalize(nQ2); } else @@ -1549,7 +1566,7 @@ class iz3proof_itp_impl : public iz3proof_itp { return; } } - if(op(ineq) == Leq){ + if(op(ineq) == Leq || op(ineq) == Geq){ lhs = srhs; rhs = arg(ineq,1); return; @@ -1604,7 +1621,7 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast normal_lhs(const ast &t){ - return arg(arg(t,0),1); + return arg(arg(t,0),0); } ast normal_rhs(const ast &t){ @@ -1615,16 +1632,22 @@ class iz3proof_itp_impl : public iz3proof_itp { return arg(t,1); } - ast make_normal(const ast &lhs, const ast &rhs, const ast &proof){ + ast make_normal_step(const ast &lhs, const ast &rhs, const ast &proof){ return make(normal_step,make_equiv(lhs,rhs),proof); } + ast make_normal(const ast &ineq, const ast &nrml){ + if(!is_ineq(ineq)) + throw "what?"; + return make(normal,ineq,nrml); + } + ast fix_normal(const ast &lhs, const ast &rhs, const ast &proof){ LitType rhst = get_term_type(rhs); if(rhst != LitMixed || ast_id(lhs) < ast_id(rhs)) - return make_normal(lhs,rhs,proof); + return make_normal_step(lhs,rhs,proof); else - return make_normal(rhs,lhs,reverse_chain(proof)); + return make_normal_step(rhs,lhs,reverse_chain(proof)); } ast chain_side_proves(LitType side, const ast &chain){ @@ -1658,7 +1681,7 @@ class iz3proof_itp_impl : public iz3proof_itp { if(t1 == LitMixed && (t2 != LitMixed || tid2 > tid1)){ ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); new_normal = f2; - trans[rhs1] = make_normal(rhs1,rhs2,new_proof); + trans[rhs1] = make_normal_step(rhs1,rhs2,new_proof); } else if(t2 == LitMixed && (t1 != LitMixed || tid1 > tid2)) return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); @@ -1671,7 +1694,7 @@ class iz3proof_itp_impl : public iz3proof_itp { Aproves = my_and(Aproves,mcB); ast rep = apply_rewrite_chain(rhs1,Aproof); new_proof = concat_rewrite_chain(pf1,Aproof); - new_normal = make_normal(rhs1,rep,new_proof); + new_normal = make_normal_step(rhs1,rep,new_proof); } else if(t1 == LitA && t2 == LitB) return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); @@ -1701,7 +1724,7 @@ class iz3proof_itp_impl : public iz3proof_itp { if(it != trans.end()){ const ast &f2 = it->second; ast pf = concat_rewrite_chain(normal_proof(f),normal_proof(f2)); - new_normal = make_normal(normal_lhs(f),normal_rhs(f2),pf); + new_normal = make_normal_step(normal_lhs(f),normal_rhs(f2),pf); } else new_normal = f; @@ -1715,9 +1738,36 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } - ast normalize(const ast &t){ + bool destruct_cond_ineq(ast t, ast &Aproves, ast &Bproves, ast&ineq){ + if(op(t) == And){ + Aproves = arg(t,0); + t = arg(t,1); + } + else + Aproves = mk_true(); + if(op(t) == Implies){ + Bproves = arg(t,0); + t = arg(t,1); + } + else + Bproves = mk_true(); + if(is_normal_ineq(t)){ + ineq = t; + return true; + } + return false; + } + + ast cons_cond_ineq(const ast &Aproves, const ast &Bproves, const ast &ineq){ + return my_and(Aproves,my_implies(Bproves,ineq)); + } + + ast normalize(const ast &ct){ + ast Aproves,Bproves,t; + if(!destruct_cond_ineq(ct,Aproves,Bproves,t)) + return ct; if(sym(t) != normal) - return t; + return ct; ast chain = arg(t,1); hash_map map; for(ast c = chain; !is_true(c); c = normal_rest(c)){ @@ -1727,7 +1777,7 @@ class iz3proof_itp_impl : public iz3proof_itp { map[lhs] = rhs; } ast res = subst(map,arg(t,0)); - return res; + return cons_cond_ineq(Aproves,Bproves,res); } /** Make an assumption node. The given clause is assumed in the given frame. */ @@ -1848,8 +1898,7 @@ class iz3proof_itp_impl : public iz3proof_itp { } /** Make an axiom node. The conclusion must be an instance of an axiom. */ - virtual node make_axiom(const std::vector &conclusion){ - prover::range frng = pv->range_full(); + virtual node make_axiom(const std::vector &conclusion, prover::range frng){ int nargs = conclusion.size(); std::vector largs(nargs); std::vector eqs; @@ -1874,6 +1923,10 @@ class iz3proof_itp_impl : public iz3proof_itp { return itp; } + virtual node make_axiom(const std::vector &conclusion){ + return make_axiom(conclusion,pv->range_full()); + } + /** Make a Contra node. This rule takes a derivation of the form Gamma |- False and produces |- \/~Gamma. */ @@ -2299,7 +2352,8 @@ class iz3proof_itp_impl : public iz3proof_itp { int nargs = num_args(e); if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ prover::range frng = rng; - if(op(e) == Uninterpreted){ + opr o = op(e); + if(o == Uninterpreted){ symb f = sym(e); prover::range srng = pv->sym_range(f); if(pv->ranges_intersect(srng,rng)) // localize to desired range if possible @@ -2307,6 +2361,9 @@ class iz3proof_itp_impl : public iz3proof_itp { else frng = srng; // this term will be localized } + else if(o == Plus || o == Times){ // don't want bound variables inside arith ops + frng = erng; // this term will be localized + } std::vector largs(nargs); std::vector eqs; std::vector pfs; @@ -2333,6 +2390,9 @@ class iz3proof_itp_impl : public iz3proof_itp { if(pv->ranges_intersect(pv->ast_scope(e),rng)) return e; // this term occurs in range, so it's O.K. + if(is_array_type(get_type(e))) + throw "help!"; + // choose a frame for the constraint that is close to range int frame = pv->range_near(pv->ast_scope(e),rng); @@ -2362,7 +2422,11 @@ class iz3proof_itp_impl : public iz3proof_itp { /* Return an interpolant from a proof of false */ ast interpolate(const node &pf){ // proof of false must be a formula, with quantified symbols +#ifndef BOGUS_QUANTS return add_quants(z3_simplify(pf)); +#else + return z3_simplify(pf); +#endif } ast resolve_with_quantifier(const ast &pivot1, const ast &conj1, diff --git a/src/interp/iz3proof_itp.h b/src/interp/iz3proof_itp.h index 299f391ea..4d76e3a92 100644 --- a/src/interp/iz3proof_itp.h +++ b/src/interp/iz3proof_itp.h @@ -70,6 +70,9 @@ class iz3proof_itp : public iz3mgr { /** Make an axiom node. The conclusion must be an instance of an axiom. */ virtual node make_axiom(const std::vector &conclusion) = 0; + /** Make an axiom node. The conclusion must be an instance of an axiom. Localize axiom instance to range*/ + virtual node make_axiom(const std::vector &conclusion, prover::range) = 0; + /** Make a Contra node. This rule takes a derivation of the form Gamma |- False and produces |- \/~Gamma. */ diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 4ea755eff..7dfa48cdd 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1364,6 +1364,18 @@ public: return eq2; } + bool get_store_array(const ast &t, ast &res){ + if(op(t) == Store){ + res = t; + return true; + } + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + if(get_store_array(arg(t,i),res)) + return true; + return false; + } + // translate a Z3 proof term into interpolating proof system Iproof::node translate_main(ast proof, bool expect_clause = true){ @@ -1578,9 +1590,13 @@ public: throw unsupported(); } break; - case ArrayTheory: // nothing fancy for this - res = iproof->make_axiom(lits); + case ArrayTheory: {// nothing fancy for this + ast store_array; + if(!get_store_array(con,store_array)) + throw unsupported(); + res = iproof->make_axiom(lits,ast_scope(store_array)); break; + } default: throw unsupported(); } diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 25dfcebbd..12dd4ff3e 100644 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -472,7 +472,7 @@ static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) { expr conc = f(args); - ::vector pprems; + ::vector< ::proof *> pprems; for(unsigned i = 0; i < prems.size(); i++) pprems.push_back(prems[i].get()); From a5335270042c3eeb7128e36c41790825053c93f6 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 5 Dec 2013 12:45:14 +0000 Subject: [PATCH 227/509] exception message clarity fix Signed-off-by: Christoph M. Wintersteiger --- src/api/java/BitVecNum.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/java/BitVecNum.java b/src/api/java/BitVecNum.java index 7615308b7..2dd0dd75a 100644 --- a/src/api/java/BitVecNum.java +++ b/src/api/java/BitVecNum.java @@ -35,7 +35,7 @@ public class BitVecNum extends BitVecExpr { Native.LongPtr res = new Native.LongPtr(); if (Native.getNumeralInt64(getContext().nCtx(), getNativeObject(), res) ^ true) - throw new Z3Exception("Numeral is not an int64"); + throw new Z3Exception("Numeral is not a long"); return res.value; } From 56b3406ee56c2f3fd9679131d1be02df99926d29 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 10 Dec 2013 11:41:25 -0800 Subject: [PATCH 228/509] added mbqi.id option, working on quantifiers in duality --- src/duality/duality.h | 26 +++- src/duality/duality_rpfp.cpp | 200 ++++++++++++++++++++++++--- src/duality/duality_solver.cpp | 16 ++- src/duality/duality_wrapper.cpp | 4 + src/duality/duality_wrapper.h | 4 + src/interp/iz3mgr.cpp | 26 +++- src/interp/iz3mgr.h | 3 +- src/interp/iz3proof_itp.cpp | 125 ++++++++++++++++- src/smt/params/qi_params.cpp | 1 + src/smt/params/qi_params.h | 5 +- src/smt/params/smt_params_helper.pyg | 1 + src/smt/smt_model_checker.cpp | 1 + src/smt/smt_quantifier.cpp | 24 +++- src/smt/smt_quantifier.h | 6 + 14 files changed, 409 insertions(+), 33 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index fb3761626..7a1e88f3c 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -79,9 +79,12 @@ namespace Duality { int CumulativeDecisions(); + int CountOperators(const Term &t); + private: void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); + int CountOperatorsRec(hash_set &memo, const Term &t); }; @@ -280,7 +283,7 @@ namespace Duality { public: std::list edges; std::list nodes; - std::list constraints; + std::list > constraints; }; @@ -556,6 +559,7 @@ namespace Duality { edge to their values in the current assignment. */ void FixCurrentState(Edge *root); + void FixCurrentStateFull(Edge *edge); /** Declare a constant in the background theory. */ @@ -653,7 +657,11 @@ namespace Duality { Term ComputeUnderapprox(Node *root, int persist); /** Try to strengthen the annotation of a node by removing disjuncts. */ - void Generalize(Node *node); + void Generalize(Node *root, Node *node); + + + /** Compute disjunctive interpolant for node by case splitting */ + void InterpolateByCases(Node *root, Node *node); /** Push a scope. Assertions made after Push can be undone by Pop. */ @@ -687,6 +695,10 @@ namespace Duality { void Pop(int num_scopes); + /** Erase the proof by performing a Pop, Push and re-assertion of + all the popped constraints */ + void PopPush(); + /** Return true if the given edge is used in the proof of unsat. Can be called only after Solve or Check returns an unsat result. */ @@ -861,6 +873,11 @@ namespace Duality { Term UnderapproxFormula(const Term &f, hash_set &dont_cares); + void ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set &done, hash_set &dont_cares); + + Term UnderapproxFullFormula(const Term &f, hash_set &dont_cares); + Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); hash_map resolve_ite_memo; @@ -896,6 +913,11 @@ namespace Duality { expr SimplifyOr(std::vector &lits); void SetAnnotation(Node *root, const expr &t); + + void AddEdgeToSolver(Edge *edge); + + void AddToProofCore(hash_set &core); + }; /** RPFP solver base class. */ diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 6b32bb080..c582e19fc 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -125,6 +125,32 @@ namespace Duality { } } + int Z3User::CountOperatorsRec(hash_set &memo, const Term &t){ + if(memo.find(t) != memo.end()) + return 0; + memo.insert(t); + if(t.is_app()){ + decl_kind k = t.decl().get_decl_kind(); + if(k == And || k == Or){ + int count = 1; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + count += CountOperatorsRec(memo,t.arg(i)); + return count; + } + return 0; + } + if(t.is_quantifier()) + return CountOperatorsRec(memo,t.body())+1; + return 0; + } + + int Z3User::CountOperators(const Term &t){ + hash_set memo; + return CountOperatorsRec(memo,t); + } + + Z3User::Term Z3User::conjoin(const std::vector &args){ return ctx.make(And,args); } @@ -329,7 +355,15 @@ namespace Duality { res = f(args.size(),&args[0]); } else if (t.is_quantifier()) - res = CloneQuantifier(t,SubstRec(memo, t.body())); + { + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = SubstRec(memo,pats[i]); + Term body = SubstRec(memo,t.body()); + res = clone_quantifier(t, body, pats); + } + // res = CloneQuantifier(t,SubstRec(memo, t.body())); else res = t; return res; } @@ -552,7 +586,7 @@ namespace Duality { void RPFP::ConstrainEdgeLocalized(Edge *e, const Term &tl) { e->constraints.push_back(tl); - stack.back().constraints.push_back(e); + stack.back().constraints.push_back(std::pair(e,tl)); slvr.add(tl); } @@ -1142,7 +1176,8 @@ namespace Duality { } } /* Unreachable! */ - std::cerr << "error in RPFP::ImplicantRed"; + // TODO: need to indicate this failure to caller + // std::cerr << "error in RPFP::ImplicantRed"; goto done; } else if(k == Not) { @@ -1161,6 +1196,31 @@ namespace Duality { done[truth].insert(f); } + void RPFP::ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set &done, hash_set &dont_cares){ + if(done.find(f) != done.end()) + return; /* already processed */ + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies || k == Iff || k == And || k == Or || k == Not){ + for(int i = 0; i < nargs; i++) + ImplicantFullRed(memo,f.arg(i),lits,done,dont_cares); + goto done; + } + } + { + if(dont_cares.find(f) == dont_cares.end()){ + int b = SubtermTruth(memo,f); + if(b != 0 && b != 1) goto done; + expr bv = (b==1) ? f : !f; + lits.push_back(bv); + } + } + done: + done.insert(f); + } + RPFP::Term RPFP::ResolveIte(hash_map &memo, const Term &t, std::vector &lits, hash_set *done, hash_set &dont_cares){ if(resolve_ite_memo.find(t) != resolve_ite_memo.end()) @@ -1223,6 +1283,16 @@ namespace Duality { return conjoin(lits); } + RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, hash_set &dont_cares){ + /* first compute truth values of subterms */ + hash_map memo; + hash_set done; + std::vector lits; + ImplicantFullRed(memo,f,lits,done,dont_cares); + /* return conjunction of literals */ + return conjoin(lits); + } + struct VariableProjector : Z3User { struct elim_cand { @@ -1759,6 +1829,17 @@ namespace Duality { ConstrainEdgeLocalized(edge,eu); } + void RPFP::FixCurrentStateFull(Edge *edge){ + hash_set dont_cares; + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); + Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; + for(unsigned i = 0; i < edge->constraints.size(); i++) + dual = dual && edge->constraints[i]; + Term eu = UnderapproxFullFormula(dual,dont_cares); + timer_stop("UnderapproxFormula"); + ConstrainEdgeLocalized(edge,eu); + } RPFP::Term RPFP::ModelValueAsConstraint(const Term &t){ @@ -1803,6 +1884,7 @@ namespace Duality { res = CreateRelation(p->Annotation.IndParams,funder); } +#if 0 void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ // verify s.push(); @@ -1829,6 +1911,36 @@ namespace Duality { } } } +#endif + + void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ + std::vector lits(conjuncts.size()); + for(unsigned i = 0; i < lits.size(); i++){ + func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); + lits[i] = pred(); + s.add(ctx.make(Implies,lits[i],conjuncts[i])); + } + + // verify + check_result res = s.check(lits.size(),&lits[0]); + if(res != unsat) + throw "should be unsat"; + + for(unsigned i = 0; i < conjuncts.size(); ){ + std::swap(conjuncts[i],conjuncts.back()); + std::swap(lits[i],lits.back()); + check_result res = s.check(lits.size()-1,&lits[0]); + if(res != unsat){ + std::swap(conjuncts[i],conjuncts.back()); + std::swap(lits[i],lits.back()); + i++; + } + else { + conjuncts.pop_back(); + lits.pop_back(); + } + } + } void RPFP::NegateLits(std::vector &lits){ for(unsigned i = 0; i < lits.size(); i++){ @@ -1848,20 +1960,56 @@ namespace Duality { return ctx.make(Or,lits); } - void RPFP::Generalize(Node *node){ - std::vector conjuncts; - expr fmla = GetAnnotation(node); - CollectConjuncts(fmla,conjuncts,false); - // try to remove conjuncts one at a tme - aux_solver.push(); - Edge *edge = node->Outgoing; + // set up edge constraint in aux solver + void RPFP::AddEdgeToSolver(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); } - GreedyReduce(aux_solver,conjuncts); + } + + void RPFP::InterpolateByCases(Node *root, Node *node){ + aux_solver.push(); + AddEdgeToSolver(node->Outgoing); + node->Annotation.SetEmpty(); + hash_set *core = new hash_set; + core->insert(node->Outgoing->dual); + while(1){ + aux_solver.push(); + aux_solver.add(!GetAnnotation(node)); + if(aux_solver.check() == unsat){ + aux_solver.pop(1); + break; + } + dualModel = aux_solver.get_model(); + aux_solver.pop(1); + Push(); + FixCurrentStateFull(node->Outgoing); + ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); + check_result foo = Check(root); + if(foo != unsat) + throw "should be unsat"; + AddToProofCore(*core); + Transformer old_annot = node->Annotation; + SolveSingleNode(root,node); + Pop(1); + node->Annotation.UnionWith(old_annot); + } + if(proof_core) + delete proof_core; // shouldn't happen + proof_core = core; + aux_solver.pop(1); + } + + void RPFP::Generalize(Node *root, Node *node){ + aux_solver.push(); + AddEdgeToSolver(node->Outgoing); + expr fmla = GetAnnotation(node); + std::vector conjuncts; + CollectConjuncts(fmla,conjuncts,false); + GreedyReduce(aux_solver,conjuncts); // try to remove conjuncts one at a tme aux_solver.pop(1); NegateLits(conjuncts); SetAnnotation(node,SimplifyOr(conjuncts)); @@ -1887,12 +2035,26 @@ namespace Duality { (*it)->dual = expr(ctx,NULL); for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) (*it)->dual = expr(ctx,NULL); - for(std::list::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) - (*it)->constraints.pop_back(); + for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) + (*it).first->constraints.pop_back(); stack.pop_back(); } } + /** Erase the proof by performing a Pop, Push and re-assertion of + all the popped constraints */ + + void RPFP::PopPush(){ + slvr.pop(1); + slvr.push(); + stack_entry &back = stack.back(); + for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) + slvr.add((*it)->dual); + for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) + slvr.add((*it)->dual); + for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) + slvr.add((*it).second); + } @@ -2325,13 +2487,17 @@ namespace Duality { } + void RPFP::AddToProofCore(hash_set &core){ + std::vector assumps; + slvr.get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++) + core.insert(assumps[i]); + } + void RPFP::ComputeProofCore(){ if(!proof_core){ - std::vector assumps; - slvr.get_proof().get_assumptions(assumps); proof_core = new hash_set; - for(unsigned i = 0; i < assumps.size(); i++) - proof_core->insert(assumps[i]); + AddToProofCore(*proof_core); } } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 86b95a657..40ddf25b1 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1754,12 +1754,14 @@ namespace Duality { for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; tree->SolveSingleNode(top,node); - tree->Generalize(node); + if(expansions.size() == 1 && NodeTooComplicated(node)) + SimplifyNode(node); + tree->Generalize(top,node); if(RecordUpdate(node)) update_count++; } if(update_count == 0) - std::cout << "backtracked without learning\n"; + reporter->Message("backtracked without learning"); } tree->ComputeProofCore(); // need to compute the proof core before popping solver while(1) { @@ -1816,6 +1818,16 @@ namespace Duality { } } + bool NodeTooComplicated(Node *node){ + return tree->CountOperators(node->Annotation.Formula) > 5; + } + + void SimplifyNode(Node *node){ + // have to destroy the old proof to get a new interpolant + tree->PopPush(); + tree->InterpolateByCases(top,node); + } + bool LevelUsedInProof(unsigned level){ std::vector &expansions = stack[level].expansions; for(unsigned i = 0; i < expansions.size(); i++) diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index e70847d47..988b03d6d 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -34,8 +34,12 @@ namespace Duality { p.set_bool("proof", true); // this is currently useless p.set_bool("model", true); p.set_bool("unsat_core", true); + p.set_bool("mbqi",true); + 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 scoped_ptr sf = mk_smt_solver_factory(); m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); + m_solver->updt_params(p); // why do we have to do this? canceled = false; } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index fc94e53f2..c9860941b 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -413,6 +413,7 @@ namespace Duality { expr operator()(unsigned n, expr const * args) const; expr operator()(const std::vector &args) const; + expr operator()() const; expr operator()(expr const & a) const; expr operator()(int a) const; expr operator()(expr const & a1, expr const & a2) const; @@ -1184,6 +1185,9 @@ namespace Duality { inline expr func_decl::operator()(const std::vector &args) const { return operator()(args.size(),&args[0]); } + inline expr func_decl::operator()() const { + return operator()(0,0); + } inline expr func_decl::operator()(expr const & a) const { return operator()(1,&a); } diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 24df25f4e..f35dae93f 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -190,7 +190,7 @@ iz3mgr::ast iz3mgr::make_quant(opr op, const std::vector &bvs, ast &body){ op == Forall, names.size(), &types[0], &names[0], abs_body.get(), 0, - symbol(), + symbol("itp"), symbol(), 0, 0, 0, 0 @@ -761,6 +761,19 @@ int iz3mgr::occurs_in(ast var, ast e){ } +bool iz3mgr::solve_arith(const ast &v, const ast &x, const ast &y, ast &res){ + if(op(x) == Plus){ + int n = num_args(x); + for(int i = 0; i < n; i++){ + if(arg(x,i) == v){ + res = z3_simplify(make(Sub, y, make(Sub, x, v))); + return true; + } + } + } + return false; +} + // find a controlling equality for a given variable v in a term // a controlling equality is of the form v = t, which, being // false would force the formula to have the specifid truth value @@ -774,6 +787,9 @@ iz3mgr::ast iz3mgr::cont_eq(stl_ext::hash_set &cont_eq_memo, bool truth, as if(!truth && op(e) == Equal){ if(arg(e,0) == v) return(arg(e,1)); if(arg(e,1) == v) return(arg(e,0)); + ast res; + if(solve_arith(v,arg(e,0),arg(e,1),res)) return res; + if(solve_arith(v,arg(e,1),arg(e,0),res)) return res; } if((!truth && op(e) == And) || (truth && op(e) == Or)){ int nargs = num_args(e); @@ -836,6 +852,14 @@ iz3mgr::ast iz3mgr::subst(stl_ext::hash_map &subst_memo,ast e){ // 2) bound variable must be equal to some term -> substitute iz3mgr::ast iz3mgr::apply_quant(opr quantifier, ast var, ast e){ + if((quantifier == Forall && op(e) == And) + || (quantifier == Exists && op(e) == Or)){ + int n = num_args(e); + std::vector args(n); + for(int i = 0; i < n; i++) + args[i] = apply_quant(quantifier,var,arg(e,i)); + return make(op(e),args); + } if(!occurs_in(var,e))return e; hash_set cont_eq_memo; ast cterm = cont_eq(cont_eq_memo, quantifier == Forall, var, e); diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 6d0daa89a..7bdc2ecce 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -692,13 +692,14 @@ class iz3mgr { protected: ast_manager &m_manager; + int occurs_in(ast var, ast e); private: ast mki(family_id fid, decl_kind sk, int n, raw_ast **args); ast make(opr op, int n, raw_ast **args); ast make(symb sym, int n, raw_ast **args); int occurs_in1(stl_ext::hash_map &occurs_in_memo, ast var, ast e); - int occurs_in(ast var, ast e); + bool solve_arith(const ast &v, const ast &x, const ast &y, ast &res); ast cont_eq(stl_ext::hash_set &cont_eq_memo, bool truth, ast v, ast e); ast subst(stl_ext::hash_map &subst_memo, ast var, ast t, ast e); diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index f8537d6eb..75d14bca1 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -135,10 +135,12 @@ class iz3proof_itp_impl : public iz3proof_itp { /* If p is a proof of Q and c is a normalization chain, then normal(p,c) is a proof of Q(c) (that is, Q with all substitutions in c performed). */ - + symb normal; - + /** Stand-ins for quantifiers */ + + symb sforall, sexists; ast get_placeholder(ast t){ @@ -231,6 +233,10 @@ class iz3proof_itp_impl : public iz3proof_itp { ast neg_pivot_lit = mk_not(atom); if(op(pivot) != Not) std::swap(premise1,premise2); + if(op(pivot) == Equal && op(arg(pivot,0)) == Select && op(arg(pivot,1)) == Select){ + neg_pivot_lit = mk_not(neg_pivot_lit); + std::swap(premise1,premise2); + } return resolve_arith_rec1(memo, neg_pivot_lit, premise1, premise2); } @@ -355,7 +361,13 @@ class iz3proof_itp_impl : public iz3proof_itp { break; } default: - res = itp2; + { + symb s = sym(itp2); + if(s == sforall || s == sexists) + res = make(s,arg(itp2,0),resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,1))); + else + res = itp2; + } } } return res; @@ -385,7 +397,13 @@ class iz3proof_itp_impl : public iz3proof_itp { break; } default: - res = itp1; + { + symb s = sym(itp1); + if(s == sforall || s == sexists) + res = make(s,arg(itp1,0),resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,1), itp2)); + else + res = itp1; + } } } return res; @@ -1897,6 +1915,20 @@ class iz3proof_itp_impl : public iz3proof_itp { return itp; } + ast capture_localization(ast e){ + // #define CAPTURE_LOCALIZATION +#ifdef CAPTURE_LOCALIZATION + for(int i = localization_vars.size() - 1; i >= 0; i--){ + LocVar &lv = localization_vars[i]; + if(occurs_in(lv.var,e)){ + symb q = (pv->in_range(lv.frame,rng)) ? sexists : sforall; + e = make(q,make(Equal,lv.var,lv.term),e); // use Equal because it is polymorphic + } + } +#endif + return e; + } + /** Make an axiom node. The conclusion must be an instance of an axiom. */ virtual node make_axiom(const std::vector &conclusion, prover::range frng){ int nargs = conclusion.size(); @@ -1920,7 +1952,7 @@ class iz3proof_itp_impl : public iz3proof_itp { for(unsigned i = 0; i < eqs.size(); i++) itp = make_mp(eqs[i],itp,pfs[i]); - return itp; + return capture_localization(itp); } virtual node make_axiom(const std::vector &conclusion){ @@ -2405,12 +2437,89 @@ class iz3proof_itp_impl : public iz3proof_itp { return new_var; } + ast delete_quant(hash_map &memo, const ast &v, const ast &e){ + std::pair foo(e,ast()); + std::pair::iterator,bool> bar = memo.insert(foo); + ast &res = bar.first->second; + if(bar.second){ + opr o = op(e); + switch(o){ + case Or: + case And: + case Implies: { + unsigned nargs = num_args(e); + std::vector args; args.resize(nargs); + for(unsigned i = 0; i < nargs; i++) + args[i] = delete_quant(memo, v, arg(e,i)); + res = make(o,args); + break; + } + case Uninterpreted: { + symb s = sym(e); + ast w = arg(arg(e,0),0); + if(s == sforall || s == sexists){ + res = delete_quant(memo,v,arg(e,1)); + if(w != v) + res = make(s,w,res); + break; + } + } + default: + res = e; + } + } + return res; + } + + ast insert_quants(hash_map &memo, const ast &e){ + std::pair foo(e,ast()); + std::pair::iterator,bool> bar = memo.insert(foo); + ast &res = bar.first->second; + if(bar.second){ + opr o = op(e); + switch(o){ + case Or: + case And: + case Implies: { + unsigned nargs = num_args(e); + std::vector args; args.resize(nargs); + for(unsigned i = 0; i < nargs; i++) + args[i] = insert_quants(memo, arg(e,i)); + res = make(o,args); + break; + } + case Uninterpreted: { + symb s = sym(e); + if(s == sforall || s == sexists){ + opr q = (s == sforall) ? Forall : Exists; + ast v = arg(arg(e,0),0); + hash_map dmemo; + ast body = delete_quant(dmemo,v,arg(e,1)); + body = insert_quants(memo,body); + res = apply_quant(q,v,body); + break; + } + } + default: + res = e; + } + } + return res; + } + ast add_quants(ast e){ +#ifdef CAPTURE_LOCALIZATION + if(!localization_vars.empty()){ + hash_map memo; + e = insert_quants(memo,e); + } +#else for(int i = localization_vars.size() - 1; i >= 0; i--){ LocVar &lv = localization_vars[i]; opr quantifier = (pv->in_range(lv.frame,rng)) ? Exists : Forall; e = apply_quant(quantifier,lv.var,e); } +#endif return e; } @@ -2446,7 +2555,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast npP = make_mp(make(Iff,nPloc,nP),npPloc,neqpf); ast nrP = make_resolution(nP,conj2,npP); ast res = make_resolution(Ploc,rP,nrP); - return res; + return capture_localization(res); } ast get_contra_coeff(const ast &f){ @@ -2538,6 +2647,10 @@ public: m().inc_ref(normal_chain); normal = function("@normal",2,boolbooldom,bool_type()); m().inc_ref(normal); + sforall = function("@sforall",2,boolbooldom,bool_type()); + m().inc_ref(sforall); + sexists = function("@sexists",2,boolbooldom,bool_type()); + m().inc_ref(sexists); } ~iz3proof_itp_impl(){ diff --git a/src/smt/params/qi_params.cpp b/src/smt/params/qi_params.cpp index f5506d35b..60fcd6fc4 100644 --- a/src/smt/params/qi_params.cpp +++ b/src/smt/params/qi_params.cpp @@ -27,6 +27,7 @@ void qi_params::updt_params(params_ref const & _p) { m_mbqi_max_iterations = p.mbqi_max_iterations(); m_mbqi_trace = p.mbqi_trace(); m_mbqi_force_template = p.mbqi_force_template(); + m_mbqi_id = p.mbqi_id(); m_qi_profile = p.qi_profile(); m_qi_profile_freq = p.qi_profile_freq(); m_qi_max_instances = p.qi_max_instances(); diff --git a/src/smt/params/qi_params.h b/src/smt/params/qi_params.h index 0cd817f22..bca3ad3fc 100644 --- a/src/smt/params/qi_params.h +++ b/src/smt/params/qi_params.h @@ -51,6 +51,7 @@ struct qi_params { unsigned m_mbqi_max_iterations; bool m_mbqi_trace; unsigned m_mbqi_force_template; + const char * m_mbqi_id; qi_params(params_ref const & p = params_ref()): /* @@ -97,7 +98,9 @@ struct qi_params { m_mbqi_max_cexs_incr(1), m_mbqi_max_iterations(1000), m_mbqi_trace(false), - m_mbqi_force_template(10) { + m_mbqi_force_template(10), + m_mbqi_id(0) + { updt_params(p); } diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 07c68f759..50bb6422b 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -21,6 +21,7 @@ def_module_params(module_name='smt', ('mbqi.max_iterations', UINT, 1000, 'maximum number of rounds of MBQI'), ('mbqi.trace', BOOL, False, 'generate tracing messages for Model Based Quantifier Instantiation (MBQI). It will display a message before every round of MBQI, and the quantifiers that were not satisfied'), ('mbqi.force_template', UINT, 10, 'some quantifiers can be used as templates for building interpretations for functions. Z3 uses heuristics to decide whether a quantifier will be used as a template or not. Quantifiers with weight >= mbqi.force_template are forced to be used as a template'), + ('mbqi.id', STRING, '', 'Only use model-based instantiation for quantifiers with id\'s beginning with string'), ('qi.profile', BOOL, False, 'profile quantifier instantiation'), ('qi.profile_freq', UINT, UINT_MAX, 'how frequent results are reported by qi.profile'), ('qi.max_instances', UINT, UINT_MAX, 'maximum number of quantifier instantiations'), diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index 53f3af961..526447f9f 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -322,6 +322,7 @@ namespace smt { for (; it != end; ++it) { quantifier * q = *it; + if(!m_qm->mbqi_enabled(q)) continue; if (m_context->is_relevant(q) && m_context->get_assignment(q) == l_true) { if (m_params.m_mbqi_trace && q->get_qid() != symbol::null) { verbose_stream() << "(smt.mbqi :checking " << q->get_qid() << ")\n"; diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index d56fe0cff..8fd0e08bc 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -335,6 +335,10 @@ namespace smt { return m_imp->m_plugin->model_based(); } + bool quantifier_manager::mbqi_enabled(quantifier *q) const { + return m_imp->m_plugin->mbqi_enabled(q); + } + void quantifier_manager::adjust_model(proto_model * m) { m_imp->m_plugin->adjust_model(m); } @@ -434,10 +438,24 @@ namespace smt { virtual bool model_based() const { return m_fparams->m_mbqi; } + virtual bool mbqi_enabled(quantifier *q) const { + if(!m_fparams->m_mbqi_id) return true; + const symbol &s = q->get_qid(); + unsigned len = strlen(m_fparams->m_mbqi_id); + if(s == symbol::null || s.is_numerical()) + return len == 0; + return strncmp(s.bare_str(),m_fparams->m_mbqi_id,len) == 0; + } + + /* Quantifier id's must begin with the prefix specified by + parameter mbqi.id to be instantiated with MBQI. The default + value is the empty string, so all quantifiers are + instantiated. + */ virtual void add(quantifier * q) { - if (m_fparams->m_mbqi) { - m_model_finder->register_quantifier(q); - } + if (m_fparams->m_mbqi && mbqi_enabled(q)) { + m_model_finder->register_quantifier(q); + } } virtual void del(quantifier * q) { diff --git a/src/smt/smt_quantifier.h b/src/smt/smt_quantifier.h index 19113229c..e3d626157 100644 --- a/src/smt/smt_quantifier.h +++ b/src/smt/smt_quantifier.h @@ -75,6 +75,7 @@ namespace smt { }; bool model_based() const; + bool mbqi_enabled(quantifier *q) const; // can mbqi instantiate this quantifier? void adjust_model(proto_model * m); check_model_result check_model(proto_model * m, obj_map const & root2value); @@ -144,6 +145,11 @@ namespace smt { */ virtual bool model_based() const = 0; + /** + \brief Is "model based" instantiate allowed to instantiate this quantifier? + */ + virtual bool mbqi_enabled(quantifier *q) const {return true;} + /** \brief Give a change to the plugin to adjust the interpretation of unintepreted functions. It can basically change the "else" of each uninterpreted function. From 70433869159cfb37f9c2243f5bbb0f7929b0a985 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 10 Dec 2013 14:34:14 -0800 Subject: [PATCH 229/509] enabled extensional arrays in duality and added theory axioms lazily in GreedyReduce --- src/duality/duality.h | 13 ++++++++++++- src/duality/duality_rpfp.cpp | 20 +++++++++++++++++--- src/duality/duality_wrapper.cpp | 4 +++- src/duality/duality_wrapper.h | 3 ++- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 7a1e88f3c..f1e848f0d 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -171,6 +171,9 @@ namespace Duality { /** Assert a background axiom. */ virtual void assert_axiom(const expr &axiom) = 0; + /** Get the background axioms. */ + virtual const std::vector &get_axioms() = 0; + /** Return a string describing performance. */ virtual std::string profile() = 0; @@ -182,7 +185,11 @@ namespace Duality { /** Cancel, throw Canceled object if possible. */ virtual void cancel(){ } - LogicSolver(context &c) : aux_solver(c){} + /* Note: aux solver uses extensional array theory, since it + needs to be able to produce counter-models for + interpolants the have array equalities in them. + */ + LogicSolver(context &c) : aux_solver(c,true){} virtual ~LogicSolver(){} }; @@ -208,6 +215,10 @@ namespace Duality { islvr->AssertInterpolationAxiom(axiom); } + const std::vector &get_axioms() { + return islvr->GetInterpolationAxioms(); + } + std::string profile(){ return islvr->profile(); } diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index c582e19fc..3fc755d85 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -1923,8 +1923,14 @@ namespace Duality { // verify check_result res = s.check(lits.size(),&lits[0]); - if(res != unsat) - throw "should be unsat"; + if(res != unsat){ + // add the axioms in the off chance they are useful + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + s.add(theory[i]); + if(s.check(lits.size(),&lits[0]) != unsat) + throw "should be unsat"; + } for(unsigned i = 0; i < conjuncts.size(); ){ std::swap(conjuncts[i],conjuncts.back()); @@ -1987,13 +1993,21 @@ namespace Duality { aux_solver.pop(1); Push(); FixCurrentStateFull(node->Outgoing); - ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); + // ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); check_result foo = Check(root); if(foo != unsat) throw "should be unsat"; AddToProofCore(*core); Transformer old_annot = node->Annotation; SolveSingleNode(root,node); + if(node->Annotation.IsEmpty()){ + std::cout << "bad in InterpolateByCase -- core:\n"; + std::vector assumps; + slvr.get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++) + assumps[i].show(); + throw "ack!"; + } Pop(1); node->Annotation.UnionWith(old_annot); } diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 988b03d6d..30531ce51 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -29,7 +29,7 @@ Revision History: namespace Duality { - solver::solver(Duality::context& c) : object(c), the_model(c) { + solver::solver(Duality::context& c, bool extensional) : object(c), the_model(c) { params_ref p; p.set_bool("proof", true); // this is currently useless p.set_bool("model", true); @@ -37,6 +37,8 @@ namespace Duality { p.set_bool("mbqi",true); p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants p.set_uint("mbqi.max_iterations",1); // use mbqi for quantifiers in interpolants + if(true || extensional) + p.set_bool("array.extensional",true); scoped_ptr sf = mk_smt_solver_factory(); m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); m_solver->updt_params(p); // why do we have to do this? diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index c9860941b..f69bc642e 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -807,7 +807,7 @@ namespace Duality { model the_model; bool canceled; public: - solver(context & c); + solver(context & c, bool extensional = false); solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver; canceled = false;} ~solver() { @@ -1326,6 +1326,7 @@ namespace Duality { void SetWeakInterpolants(bool weak); void SetPrintToFile(const std::string &file_name); + const std::vector &GetInterpolationAxioms() {return theory;} const char *profile(); private: From d45cbb3cb2c1de662fd43083b2addf381a6527cf Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 10 Dec 2013 16:26:35 -0800 Subject: [PATCH 230/509] fixed interpolation bug --- src/duality/duality_rpfp.cpp | 2 +- src/interp/iz3translate.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 3fc755d85..f37545b48 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -1993,7 +1993,7 @@ namespace Duality { aux_solver.pop(1); Push(); FixCurrentStateFull(node->Outgoing); - // ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); + ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); check_result foo = Check(root); if(foo != unsat) throw "should be unsat"; diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 7dfa48cdd..63f3dd251 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1460,6 +1460,21 @@ public: } } + /* this is the symmetry rule for ~=, that is, takes x ~= y and yields y ~= x. + the proof idiom uses commutativity, monotonicity and mp, but we replace it here + with symmtrey and resolution, that is, we prove y = x |- x = y, then resolve + with the proof of ~(x=y) to get ~y=x. */ + if(dk == PR_MODUS_PONENS && pr(prem(proof,1)) == PR_MONOTONICITY && pr(prem(prem(proof,1),0)) == PR_COMMUTATIVITY && num_prems(prem(proof,1)) == 1){ + Iproof::node ante = translate_main(prem(proof,0),false); + ast eq0 = arg(conc(prem(prem(proof,1),0)),0); + ast eq1 = arg(conc(prem(prem(proof,1),0)),1); + Iproof::node eq1hy = iproof->make_hypothesis(eq1); + Iproof::node eq0pf = iproof->make_symmetry(eq0,eq1,eq1hy); + std::vector clause; // just a dummy + res = iproof->make_resolution(eq0,clause,ante,eq0pf); + return res; + } + // translate all the premises std::vector args(nprems); for(unsigned i = 0; i < nprems; i++) From ea8eb74744f04da7f517d8ef09485b57dc8d330b Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 11 Dec 2013 16:25:59 -0800 Subject: [PATCH 231/509] simplifying quantified interpolants in duality --- src/duality/duality.h | 21 ++- src/duality/duality_rpfp.cpp | 235 +++++++++++++++++++++++++++++++++- src/duality/duality_wrapper.h | 2 + 3 files changed, 254 insertions(+), 4 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index f1e848f0d..4539b4711 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -81,10 +81,23 @@ namespace Duality { int CountOperators(const Term &t); + Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); + + Term RemoveRedundancy(const Term &t); + + bool IsLiteral(const expr &lit, expr &atom, expr &val); + + expr Negate(const expr &f); + + expr SimplifyAndOr(const std::vector &args, bool is_and); + private: void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); int CountOperatorsRec(hash_set &memo, const Term &t); + void RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo); + Term RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t); + Term SubstAtomTriv(const expr &foo, const expr &atom, const expr &val); }; @@ -570,7 +583,7 @@ namespace Duality { edge to their values in the current assignment. */ void FixCurrentState(Edge *root); - void FixCurrentStateFull(Edge *edge); + void FixCurrentStateFull(Edge *edge, const expr &extra); /** Declare a constant in the background theory. */ @@ -929,6 +942,12 @@ namespace Duality { void AddToProofCore(hash_set &core); + void GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under); + + Term StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits); + + expr NegateLit(const expr &f); + }; /** RPFP solver base class. */ diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index f37545b48..5260dc7e9 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -368,6 +368,136 @@ namespace Duality { return res; } + bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ + if(!lit.is_app()) + return false; + decl_kind k = lit.decl().get_decl_kind(); + if(k == Not){ + if(IsLiteral(lit.arg(0),atom,val)){ + val = eq(val,ctx.bool_val(true)) ? ctx.bool_val(false) : ctx.bool_val(true); + return true; + } + return false; + } + if(k == And || k == Or || k == Iff || k == Implies) + return false; + atom = lit; + val = ctx.bool_val(true); + return true; + } + + expr Z3User::Negate(const expr &f){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + return f.arg(0); + else if(eq(f,ctx.bool_val(true))) + return ctx.bool_val(false); + else if(eq(f,ctx.bool_val(false))) + return ctx.bool_val(true); + return !f; + } + + expr Z3User::SimplifyAndOr(const std::vector &args, bool is_and){ + std::vector sargs; + for(unsigned i = 0; i < args.size(); i++) + if(!eq(args[i],ctx.bool_val(is_and))){ + if(eq(args[i],ctx.bool_val(!is_and))) + return ctx.bool_val(!is_and); + sargs.push_back(args[i]); + } + if(sargs.size() == 0) + return ctx.bool_val(is_and); + if(sargs.size() == 1) + return sargs[0]; + return ctx.make(is_and ? And : Or,sargs); + } + + Z3User::Term Z3User::SubstAtomTriv(const expr &foo, const expr &atom, const expr &val){ + if(eq(foo,atom)) + return val; + else if(foo.is_app() && foo.decl().get_decl_kind() == Not && eq(foo.arg(0),atom)) + return Negate(val); + else + return foo; + } + + Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val){ + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()){ + func_decl f = t.decl(); + decl_kind k = f.get_decl_kind(); + + // TODO: recur here, but how much? We don't want to be quadractic in formula size + + if(k == And || k == Or){ + int nargs = t.num_args(); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = SubstAtom(memo,t.arg(i),atom,val); + res = SimplifyAndOr(args,k == And); + return res; + } + } + res = SubstAtomTriv(t,atom,val); + return res; + } + + void Z3User::RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo){ + for(unsigned i = 0; i < args.size(); i++){ + const expr &lit = args[i]; + expr atom, val; + if(IsLiteral(lit,atom,val)){ + for(unsigned j = 0; j < args.size(); j++) + if(j != i){ + smemo.clear(); + args[j] = SubstAtom(smemo,args[j],atom,pol ? val : !val); + } + } + } + } + + + Z3User::Term Z3User::RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(RemoveRedundancyRec(memo, smemo, t.arg(i))); + + decl_kind k = f.get_decl_kind(); + if(k == And) + RemoveRedundancyOp(true,args,smemo); + else if(k == Or) + RemoveRedundancyOp(false,args,smemo); + if(k == Equal && args[0].get_id() > args[1].get_id()) + std::swap(args[0],args[1]); + + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + { + Term body = RemoveRedundancyRec(memo,smemo,t.body()); + res = clone_quantifier(t, body); + } + else res = t; + return res; + } + + Z3User::Term Z3User::RemoveRedundancy(const Term &t){ + hash_map memo; + hash_map smemo; + return RemoveRedundancyRec(memo,smemo,t); + } + Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) { std::pair foo(t,expr(ctx)); @@ -1829,18 +1959,100 @@ namespace Duality { ConstrainEdgeLocalized(edge,eu); } - void RPFP::FixCurrentStateFull(Edge *edge){ + void RPFP::FixCurrentStateFull(Edge *edge, const expr &extra){ hash_set dont_cares; resolve_ite_memo.clear(); timer_start("UnderapproxFormula"); Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; for(unsigned i = 0; i < edge->constraints.size(); i++) dual = dual && edge->constraints[i]; + // dual = dual && extra; Term eu = UnderapproxFullFormula(dual,dont_cares); timer_stop("UnderapproxFormula"); ConstrainEdgeLocalized(edge,eu); } + + + + void RPFP::GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under){ + if(memo[under].find(f) != memo[under].end()) + return; + memo[under].insert(f); + if(f.is_app()){ + if(!under && !f.has_quantifiers()) + return; + decl_kind k = f.decl().get_decl_kind(); + if(k == And || k == Or || k == Implies || k == Iff){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + GetGroundLitsUnderQuants(memo,f.arg(i),res,under); + return; + } + } + else if (f.is_quantifier()){ + GetGroundLitsUnderQuants(memo,f.body(),res,1); + return; + } + if(under && f.is_ground()) + res.push_back(f); + } + + RPFP::Term RPFP::StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits){ + hash_set memo[2]; + std::vector lits; + GetGroundLitsUnderQuants(memo, f, lits, 0); + hash_set lits_hash; + for(unsigned i = 0; i < lits.size(); i++) + lits_hash.insert(lits[i]); + hash_map subst; + hash_map stt_memo; + std::vector conjuncts; + for(unsigned i = 0; i < lits.size(); i++){ + const expr &lit = lits[i]; + if(lits_hash.find(NegateLit(lit)) == lits_hash.end()){ + case_lits.push_back(lit); + bool tval = false; + expr atom = lit; + if(lit.is_app() && lit.decl().get_decl_kind() == Not){ + tval = true; + atom = lit.arg(0); + } + expr etval = ctx.bool_val(tval); + int b = SubtermTruth(stt_memo,atom); + if(b == (tval ? 1 : 0)) + subst[atom] = etval; + else { + if(b == 0 || b == 1){ + etval = ctx.bool_val(b ? true : false); + subst[atom] = etval; + conjuncts.push_back(b ? atom : !atom); + } + } + } + } + expr g = f; + if(!subst.empty()){ + g = SubstRec(subst,f); + if(conjuncts.size()) + g = g && ctx.make(And,conjuncts); + g = g.simplify(); + } +#if 0 + expr g_old = g; + g = RemoveRedundancy(g); + bool changed = !eq(g,g_old); + g = g.simplify(); + if(changed) { // a second pass can get some more simplification + g = RemoveRedundancy(g); + g = g.simplify(); + } +#else + g = RemoveRedundancy(g); + g = g.simplify(); +#endif + return g; + } RPFP::Term RPFP::ModelValueAsConstraint(const Term &t){ if(t.is_array()){ @@ -1948,6 +2160,13 @@ namespace Duality { } } + expr RPFP::NegateLit(const expr &f){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + return f.arg(0); + else + return !f; + } + void RPFP::NegateLits(std::vector &lits){ for(unsigned i = 0; i < lits.size(); i++){ expr &f = lits[i]; @@ -1984,7 +2203,8 @@ namespace Duality { core->insert(node->Outgoing->dual); while(1){ aux_solver.push(); - aux_solver.add(!GetAnnotation(node)); + expr annot = !GetAnnotation(node); + aux_solver.add(annot); if(aux_solver.check() == unsat){ aux_solver.pop(1); break; @@ -1992,7 +2212,7 @@ namespace Duality { dualModel = aux_solver.get_model(); aux_solver.pop(1); Push(); - FixCurrentStateFull(node->Outgoing); + FixCurrentStateFull(node->Outgoing,annot); ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); check_result foo = Check(root); if(foo != unsat) @@ -2000,6 +2220,15 @@ namespace Duality { AddToProofCore(*core); Transformer old_annot = node->Annotation; SolveSingleNode(root,node); + + { + expr itp = GetAnnotation(node); + dualModel = aux_solver.get_model(); + std::vector case_lits; + itp = StrengthenFormulaByCaseSplitting(itp, case_lits); + SetAnnotation(node,itp); + } + if(node->Annotation.IsEmpty()){ std::cout << "bad in InterpolateByCase -- core:\n"; std::vector assumps; diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index f69bc642e..e0824d4be 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -457,6 +457,8 @@ namespace Duality { bool is_quantifier() const {return raw()->get_kind() == AST_QUANTIFIER;} bool is_var() const {return raw()->get_kind() == AST_VAR;} bool is_label (bool &pos,std::vector &names) const ; + bool is_ground() const {return to_app(raw())->is_ground();} + bool has_quantifiers() const {return to_app(raw())->has_quantifiers();} // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} From 2cc813219172f702af7b581c2cd90cd22d68d8e7 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 12 Dec 2013 18:25:24 -0800 Subject: [PATCH 232/509] still simplifying quantified interpolants in duality --- src/duality/duality.h | 12 ++- src/duality/duality_rpfp.cpp | 179 ++++++++++++++++++++++++++++------- src/interp/iz3translate.cpp | 37 +++++--- 3 files changed, 183 insertions(+), 45 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 4539b4711..82d729104 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -91,13 +91,23 @@ namespace Duality { expr SimplifyAndOr(const std::vector &args, bool is_and); + expr ReallySimplifyAndOr(const std::vector &args, bool is_and); + + int MaxIndex(hash_map &memo, const Term &t); + + bool IsClosedFormula(const Term &t); + private: void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); int CountOperatorsRec(hash_set &memo, const Term &t); void RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo); Term RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t); - Term SubstAtomTriv(const expr &foo, const expr &atom, const expr &val); + Term SubstAtomTriv(const expr &foo, const expr &atom, const expr &val); + expr ReduceAndOr(const std::vector &args, bool is_and, std::vector &res); + expr FinishAndOr(const std::vector &args, bool is_and); + expr PullCommonFactors(std::vector &args, bool is_and); + }; diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 5260dc7e9..3fd74993c 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -25,6 +25,7 @@ Revision History: #include "duality_profiling.h" #include #include +#include #ifndef WIN32 // #define Z3OPS @@ -369,18 +370,20 @@ namespace Duality { } bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ - if(!lit.is_app()) - return false; - decl_kind k = lit.decl().get_decl_kind(); - if(k == Not){ - if(IsLiteral(lit.arg(0),atom,val)){ - val = eq(val,ctx.bool_val(true)) ? ctx.bool_val(false) : ctx.bool_val(true); - return true; + if(!(lit.is_quantifier() && IsClosedFormula(lit))){ + if(!lit.is_app()) + return false; + decl_kind k = lit.decl().get_decl_kind(); + if(k == Not){ + if(IsLiteral(lit.arg(0),atom,val)){ + val = eq(val,ctx.bool_val(true)) ? ctx.bool_val(false) : ctx.bool_val(true); + return true; + } + return false; } - return false; + if(k == And || k == Or || k == Iff || k == Implies) + return false; } - if(k == And || k == Or || k == Iff || k == Implies) - return false; atom = lit; val = ctx.bool_val(true); return true; @@ -396,19 +399,78 @@ namespace Duality { return !f; } - expr Z3User::SimplifyAndOr(const std::vector &args, bool is_and){ - std::vector sargs; + expr Z3User::ReduceAndOr(const std::vector &args, bool is_and, std::vector &res){ for(unsigned i = 0; i < args.size(); i++) if(!eq(args[i],ctx.bool_val(is_and))){ if(eq(args[i],ctx.bool_val(!is_and))) return ctx.bool_val(!is_and); - sargs.push_back(args[i]); + res.push_back(args[i]); } - if(sargs.size() == 0) + return expr(); + } + + expr Z3User::FinishAndOr(const std::vector &args, bool is_and){ + if(args.size() == 0) return ctx.bool_val(is_and); - if(sargs.size() == 1) - return sargs[0]; - return ctx.make(is_and ? And : Or,sargs); + if(args.size() == 1) + return args[0]; + return ctx.make(is_and ? And : Or,args); + } + + expr Z3User::SimplifyAndOr(const std::vector &args, bool is_and){ + std::vector sargs; + expr res = ReduceAndOr(args,is_and,sargs); + if(!res.null()) return res; + return FinishAndOr(sargs,is_and); + } + + expr Z3User::PullCommonFactors(std::vector &args, bool is_and){ + + // first check if there's anything to do... + if(args.size() < 2) + return FinishAndOr(args,is_and); + for(unsigned i = 0; i < args.size(); i++){ + const expr &a = args[i]; + if(!(a.is_app() && a.decl().get_decl_kind() == (is_and ? Or : And))) + return FinishAndOr(args,is_and); + } + std::vector common; + for(unsigned i = 0; i < args.size(); i++){ + unsigned n = args[i].num_args(); + std::vector v(n),w; + for(unsigned j = 0; j < n; j++) + v[j] = args[i].arg(j); + std::less comp; + std::sort(v.begin(),v.end(),comp); + if(i == 0) + common.swap(v); + else { + std::set_intersection(common.begin(),common.end(),v.begin(),v.end(),std::back_inserter(w),comp); + common.swap(w); + } + } + if(common.empty()) + return FinishAndOr(args,is_and); + std::set common_set(common.begin(),common.end()); + for(unsigned i = 0; i < args.size(); i++){ + unsigned n = args[i].num_args(); + std::vector lits; + for(unsigned j = 0; j < n; j++){ + const expr b = args[i].arg(j); + if(common_set.find(b) == common_set.end()) + lits.push_back(b); + } + args[i] = SimplifyAndOr(lits,!is_and); + } + common.push_back(SimplifyAndOr(args,is_and)); + return SimplifyAndOr(common,!is_and); + } + + expr Z3User::ReallySimplifyAndOr(const std::vector &args, bool is_and){ + std::vector sargs; + expr res = ReduceAndOr(args,is_and,sargs); + if(!res.null()) return res; + return PullCommonFactors(sargs,is_and); } Z3User::Term Z3User::SubstAtomTriv(const expr &foo, const expr &atom, const expr &val){ @@ -436,10 +498,17 @@ namespace Duality { std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = SubstAtom(memo,t.arg(i),atom,val); - res = SimplifyAndOr(args,k == And); + res = ReallySimplifyAndOr(args, k==And); return res; } } + else if(t.is_quantifier() && atom.is_quantifier()){ + if(eq(t,atom)) + res = val; + else + res = clone_quantifier(t,SubstAtom(memo,t.body(),atom,val)); + return res; + } res = SubstAtomTriv(t,atom,val); return res; } @@ -474,14 +543,19 @@ namespace Duality { args.push_back(RemoveRedundancyRec(memo, smemo, t.arg(i))); decl_kind k = f.get_decl_kind(); - if(k == And) + if(k == And){ RemoveRedundancyOp(true,args,smemo); - else if(k == Or) + res = ReallySimplifyAndOr(args, true); + } + else if(k == Or){ RemoveRedundancyOp(false,args,smemo); - if(k == Equal && args[0].get_id() > args[1].get_id()) - std::swap(args[0],args[1]); - - res = f(args.size(),&args[0]); + res = ReallySimplifyAndOr(args, false); + } + else { + if(k == Equal && args[0].get_id() > args[1].get_id()) + std::swap(args[0],args[1]); + res = f(args.size(),&args[0]); + } } else if (t.is_quantifier()) { @@ -1991,7 +2065,13 @@ namespace Duality { } } else if (f.is_quantifier()){ - GetGroundLitsUnderQuants(memo,f.body(),res,1); +#if 0 + // treat closed quantified formula as a literal 'cause we hate nested quantifiers + if(under && IsClosedFormula(f)) + res.push_back(f); + else +#endif + GetGroundLitsUnderQuants(memo,f.body(),res,1); return; } if(under && f.is_ground()) @@ -2019,14 +2099,18 @@ namespace Duality { atom = lit.arg(0); } expr etval = ctx.bool_val(tval); - int b = SubtermTruth(stt_memo,atom); - if(b == (tval ? 1 : 0)) - subst[atom] = etval; + if(atom.is_quantifier()) + subst[atom] = etval; // this is a bit desperate, since we can't eval quants else { - if(b == 0 || b == 1){ - etval = ctx.bool_val(b ? true : false); + int b = SubtermTruth(stt_memo,atom); + if(b == (tval ? 1 : 0)) subst[atom] = etval; - conjuncts.push_back(b ? atom : !atom); + else { + if(b == 0 || b == 1){ + etval = ctx.bool_val(b ? true : false); + subst[atom] = etval; + conjuncts.push_back(b ? atom : !atom); + } } } } @@ -2038,7 +2122,7 @@ namespace Duality { g = g && ctx.make(And,conjuncts); g = g.simplify(); } -#if 0 +#if 1 expr g_old = g; g = RemoveRedundancy(g); bool changed = !eq(g,g_old); @@ -2441,6 +2525,37 @@ namespace Duality { return SubstBoundRec(memo, subst, 0, t); } + int Z3User::MaxIndex(hash_map &memo, const Term &t) + { + std::pair foo(t,-1); + std::pair::iterator, bool> bar = memo.insert(foo); + int &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()){ + func_decl f = t.decl(); + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++){ + int m = MaxIndex(memo, t.arg(i)); + if(m > res) + res = m; + } + } + else if (t.is_quantifier()){ + int bound = t.get_quantifier_num_bound(); + res = MaxIndex(memo,t.body()) - bound; + } + else if (t.is_var()) { + res = t.get_index_value(); + } + return res; + } + + bool Z3User::IsClosedFormula(const Term &t){ + hash_map memo; + return MaxIndex(memo,t) < 0; + } + + /** Convert a collection of clauses to Nodes and Edges in the RPFP. Predicate unknowns are uninterpreted predicates not diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 63f3dd251..fae914292 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -109,36 +109,49 @@ public: symbols and assign each to a frame. THe assignment is heuristic. */ - void scan_skolems_rec(hash_set &memo, const ast &proof){ - std::pair::iterator,bool> bar = memo.insert(proof); - if(!bar.second) - return; + int scan_skolems_rec(hash_map &memo, const ast &proof, int frame){ + std::pair foo(proof,INT_MAX); + std::pair bar = memo.insert(foo); + int &res = bar.first->second; + if(!bar.second) return res; pfrule dk = pr(proof); - if(dk == PR_SKOLEMIZE){ + if(dk == PR_ASSERTED){ + ast ass = conc(proof); + res = frame_of_assertion(ass); + } + else if(dk == PR_SKOLEMIZE){ ast quanted = arg(conc(proof),0); if(op(quanted) == Not) quanted = arg(quanted,0); - range r = ast_range(quanted); - if(range_is_empty(r)) - r = ast_scope(quanted); + // range r = ast_range(quanted); + // if(range_is_empty(r)) + range r = ast_scope(quanted); if(range_is_empty(r)) throw "can't skolemize"; - int frame = range_max(r); + if(frame == INT_MAX || !in_range(frame,r)) + frame = range_max(r); // this is desperation -- may fail if(frame >= frames) frame = frames - 1; add_frame_range(frame,arg(conc(proof),1)); r = ast_scope(arg(conc(proof),1)); } + else if(dk==PR_MODUS_PONENS_OEQ){ + frame = scan_skolems_rec(memo,prem(proof,0),frame); + scan_skolems_rec(memo,prem(proof,1),frame); + } else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ - scan_skolems_rec(memo,prem(proof,i)); + int bar = scan_skolems_rec(memo,prem(proof,i),frame); + if(res == INT_MAX || res == bar) res = bar; + else if(bar != INT_MAX) res = -1; } } + return res; } void scan_skolems(const ast &proof){ - hash_set memo; - scan_skolems_rec(memo,proof); + hash_map memo; + scan_skolems_rec(memo,proof, INT_MAX); } // determine locality of a proof term From cf3ede92adb26b48496021375034e9db157f5db8 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 12 Dec 2013 18:35:43 -0800 Subject: [PATCH 233/509] fix for broken windows stl --- src/duality/duality_rpfp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 3fd74993c..bf6857b68 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -445,7 +445,7 @@ namespace Duality { if(i == 0) common.swap(v); else { - std::set_intersection(common.begin(),common.end(),v.begin(),v.end(),std::back_inserter(w),comp); + std::set_intersection(common.begin(),common.end(),v.begin(),v.end(),std::inserter(w,w.begin()),comp); common.swap(w); } } From bfa6c9967677655106a178799560fe071379e1fd Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 12 Dec 2013 18:38:09 -0800 Subject: [PATCH 234/509] still trying to get stl to work --- src/duality/duality_rpfp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index bf6857b68..e3f6da63d 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -26,6 +26,7 @@ Revision History: #include #include #include +#include #ifndef WIN32 // #define Z3OPS From a410e7f7168db5e34c7cd88e0af39dccd7d400d8 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 13 Dec 2013 12:21:54 -0800 Subject: [PATCH 235/509] fussing with qe in duality --- scripts/mk_project.py | 2 +- src/duality/duality.h | 3 ++- src/duality/duality_rpfp.cpp | 8 ++++++++ src/duality/duality_solver.cpp | 2 +- src/duality/duality_wrapper.cpp | 9 +++++++++ src/duality/duality_wrapper.h | 2 ++ 6 files changed, 23 insertions(+), 3 deletions(-) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 6e352f06f..f2c688525 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -55,8 +55,8 @@ def init_project_def(): add_lib('fpa', ['core_tactics', 'bv_tactics', 'sat_tactic'], 'tactic/fpa') add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') - add_lib('duality', ['smt', 'interp']) add_lib('qe', ['smt','sat'], 'qe') + add_lib('duality', ['smt', 'interp', 'qe']) add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base') add_lib('transforms', ['muz', 'hilbert'], 'muz/transforms') add_lib('rel', ['muz', 'transforms'], 'muz/rel') diff --git a/src/duality/duality.h b/src/duality/duality.h index 82d729104..f20d664be 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -97,7 +97,8 @@ namespace Duality { bool IsClosedFormula(const Term &t); - private: + Term AdjustQuantifiers(const Term &t); +private: void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); int CountOperatorsRec(hash_set &memo, const Term &t); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index e3f6da63d..ef62d38c1 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -573,6 +573,13 @@ namespace Duality { return RemoveRedundancyRec(memo,smemo,t); } + Z3User::Term Z3User::AdjustQuantifiers(const Term &t) + { + if(t.is_quantifier() || (t.is_app() && t.has_quantifiers())) + return t.qe_lite(); + return t; + } + Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) { std::pair foo(t,expr(ctx)); @@ -2136,6 +2143,7 @@ namespace Duality { g = RemoveRedundancy(g); g = g.simplify(); #endif + g = AdjustQuantifiers(g); return g; } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 40ddf25b1..073f4dce9 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1819,7 +1819,7 @@ namespace Duality { } bool NodeTooComplicated(Node *node){ - return tree->CountOperators(node->Annotation.Formula) > 5; + return tree->CountOperators(node->Annotation.Formula) > 3; } void SimplifyNode(Node *node){ diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 30531ce51..14fe545e0 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -26,6 +26,7 @@ Revision History: #include "expr_abstract.h" #include "stopwatch.h" #include "model_smt2_pp.h" +#include "qe_lite.h" namespace Duality { @@ -329,6 +330,14 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st return simplify(p); } + expr expr::qe_lite() const { + ::qe_lite qe(m()); + expr_ref result(to_expr(raw()),m()); + proof_ref pf(m()); + qe(result,pf); + return ctx().cook(result); + } + expr clone_quantifier(const expr &q, const expr &b){ return q.ctx().cook(q.m().update_quantifier(to_quantifier(q.raw()), to_expr(b.raw()))); } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index e0824d4be..fec1f08d1 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -558,6 +558,8 @@ namespace Duality { expr simplify(params const & p) const; + expr qe_lite() const; + friend expr clone_quantifier(const expr &, const expr &); friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); From 044959853069d6b134548151500901450db7095c Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 13 Dec 2013 12:41:51 -0800 Subject: [PATCH 236/509] fussing more with qe in duality --- src/duality/duality_rpfp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index ef62d38c1..a5e2b9167 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -143,7 +143,7 @@ namespace Duality { return 0; } if(t.is_quantifier()) - return CountOperatorsRec(memo,t.body())+1; + return CountOperatorsRec(memo,t.body())+2; // count 2 for a quantifier return 0; } From ac9a7748e827e081cb2c030c23f983fc1d5f972d Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 13 Dec 2013 13:14:04 -0800 Subject: [PATCH 237/509] trying to fix address depedency in duality_solver.cpp --- src/duality/duality.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/duality/duality.h b/src/duality/duality.h index f20d664be..ebf6c8632 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1007,3 +1007,15 @@ private: }; } + +// allow to walk sets of nodes without address dependency + +namespace std { + template <> + class less { + public: + bool operator()(Duality::RPFP::Node * const &s, Duality::RPFP::Node * const &t) const { + return s->number < t->number; // s.raw()->get_id() < t.raw()->get_id(); + } + }; +} From bb61f17989c67812f410d7ff8b067b104759d448 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 13 Dec 2013 13:45:40 -0800 Subject: [PATCH 238/509] trying to figure out address dependency --- src/duality/duality_wrapper.cpp | 9 +++++++++ src/duality/duality_wrapper.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 14fe545e0..0b1c688b9 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -513,6 +513,7 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st opts.set("weak","1"); ::ast *proof = m_solver->get_proof(); + show_assertion_ids(); iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts); std::vector linearized_interpolants(_interpolants.size()); @@ -613,6 +614,14 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st pp.display_smt2(std::cout, m_solver->get_assertion(n-1)); } + void solver::show_assertion_ids() { + unsigned n = m_solver->get_num_assertions(); + std::cerr << "assertion ids: "; + for (unsigned i = 0; i < n-1; ++i) + std::cerr << " " << m_solver->get_assertion(i)->get_id(); + std::cerr << "\n"; + } + void include_ast_show(ast &a){ a.show(); } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index fec1f08d1..f0988013d 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -907,6 +907,7 @@ namespace Duality { unsigned get_scope_level(){return m_solver->get_scope_level();} void show(); + void show_assertion_ids(); proof get_proof(){ return proof(ctx(),m_solver->get_proof()); From 3764064e98d48d6c720950837579042e28534f8d Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 13 Dec 2013 18:41:35 -0800 Subject: [PATCH 239/509] fixed some address dependencies --- src/ast/ast.cpp | 3 +++ src/ast/ast.h | 2 ++ src/duality/duality_wrapper.cpp | 18 +++++++++++++++++- src/duality/duality_wrapper.h | 4 +++- src/interp/iz3interp.cpp | 1 + src/interp/iz3mgr.h | 3 ++- src/util/id_gen.h | 5 +++++ 7 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index bdf1c18db..b000201b7 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -3161,3 +3161,6 @@ void prexpr(expr_ref &e){ std::cout << mk_pp(e.get(), e.get_manager()) << std::endl; } +void ast_manager::show_id_gen(){ + std::cout << "id_gen: " << m_expr_id_gen.show_hash() << " " << m_decl_id_gen.show_hash() << "\n"; +} diff --git a/src/ast/ast.h b/src/ast/ast.h index 2c3843587..68f08e1ac 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1418,6 +1418,8 @@ protected: public: typedef expr_dependency_array_manager::ref expr_dependency_array; + void show_id_gen(); + protected: small_object_allocator m_alloc; family_manager m_family_manager; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 0b1c688b9..08c5d1f8a 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -513,7 +513,6 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st opts.set("weak","1"); ::ast *proof = m_solver->get_proof(); - show_assertion_ids(); iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts); std::vector linearized_interpolants(_interpolants.size()); @@ -604,6 +603,14 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st std::cout << std::endl; } + void model::show_hash() const { + std::ostringstream ss; + model_smt2_pp(ss, m(), *m_model, 0); + std::hash hasher; + unsigned h = hasher(ss.str()); + std::cout << "model hash: " << h << "\n"; + } + void solver::show() { unsigned n = m_solver->get_num_assertions(); if(!n) @@ -615,11 +622,20 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st } void solver::show_assertion_ids() { +#if 0 unsigned n = m_solver->get_num_assertions(); std::cerr << "assertion ids: "; for (unsigned i = 0; i < n-1; ++i) std::cerr << " " << m_solver->get_assertion(i)->get_id(); std::cerr << "\n"; +#else + unsigned n = m_solver->get_num_assertions(); + std::cerr << "assertion ids hash: "; + unsigned h = 0; + for (unsigned i = 0; i < n-1; ++i) + h += m_solver->get_assertion(i)->get_id(); + std::cerr << h << "\n"; +#endif } void include_ast_show(ast &a){ diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index f0988013d..256560d02 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -727,6 +727,7 @@ namespace Duality { } void show() const; + void show_hash() const; unsigned num_consts() const {return m_model.get()->get_num_constants();} unsigned num_funcs() const {return m_model.get()->get_num_functions();} @@ -1386,7 +1387,8 @@ namespace std { class less { public: bool operator()(const Duality::ast &s, const Duality::ast &t) const { - return s.raw() < t.raw(); // s.raw()->get_id() < t.raw()->get_id(); + // return s.raw() < t.raw(); + return s.raw()->get_id() < t.raw()->get_id(); } }; } diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 9196e24d9..56dc1ccec 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -213,6 +213,7 @@ public: test_secondary(cnsts,parents,interps); return; #endif + profiling::timer_start("Interpolation prep"); // get rid of frames not used in proof diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 7bdc2ecce..39c14c724 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -140,7 +140,8 @@ namespace std { class less { public: bool operator()(const ast_r &s, const ast_r &t) const { - return s.raw() < t.raw(); // s.raw()->get_id() < t.raw()->get_id(); + // return s.raw() < t.raw(); + return s.raw()->get_id() < t.raw()->get_id(); } }; } diff --git a/src/util/id_gen.h b/src/util/id_gen.h index b1713b524..c6d22246d 100644 --- a/src/util/id_gen.h +++ b/src/util/id_gen.h @@ -57,6 +57,11 @@ public: m_free_ids.finalize(); } + unsigned show_hash(){ + unsigned h = string_hash((char *)&m_free_ids[0],m_free_ids.size()*sizeof(unsigned),17); + return hash_u_u(h,m_next_id); + } + /** \brief Return N if the range of ids generated by this module is in the set [0..N) */ From eee2d7af94bf0fcbf7a2b7e5d2f1fa427db4e81d Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sat, 14 Dec 2013 12:47:02 -0800 Subject: [PATCH 240/509] porting to linux --- src/duality/duality_wrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 08c5d1f8a..705d71abe 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -606,7 +606,7 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st void model::show_hash() const { std::ostringstream ss; model_smt2_pp(ss, m(), *m_model, 0); - std::hash hasher; + stl_ext::hash hasher; unsigned h = hasher(ss.str()); std::cout << "model hash: " << h << "\n"; } From 909408d6ef50dc8ff718ffebef8d21bac408d312 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Dec 2013 10:58:23 +0200 Subject: [PATCH 241/509] fix is_all_int bug Signed-off-by: Nikolaj Bjorner --- src/api/api_interp.cpp | 38 ++++++++++++------------- src/ast/proof_checker/proof_checker.cpp | 11 ++++--- src/smt/theory_arith_aux.h | 5 ++-- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 00503566e..a1d874b4f 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -305,8 +305,8 @@ static void get_file_params(const char *filename, hash_map= 0 && eqpos < (int)tok.size()){ + size_t eqpos = tok.find('='); + if(eqpos >= 0 && eqpos < tok.size()){ std::string left = tok.substr(0,eqpos); std::string right = tok.substr(eqpos+1,tok.size()-eqpos-1); params[left] = right; @@ -363,8 +363,8 @@ extern "C" { #else - static Z3_ast and_vec(Z3_context ctx,std::vector &c){ - return (c.size() > 1) ? Z3_mk_and(ctx,c.size(),&c[0]) : c[0]; + static Z3_ast and_vec(Z3_context ctx,svector &c){ + return (c.size() > 1) ? Z3_mk_and(ctx,c.size(),&c[0]) : c[0]; } static Z3_ast parents_vector_to_tree(Z3_context ctx, int num, Z3_ast *cnsts, int *parents){ @@ -381,15 +381,15 @@ extern "C" { } } else { - std::vector > chs(num); + std::vector > chs(num); for(int i = 0; i < num-1; i++){ - std::vector &c = chs[i]; + svector &c = chs[i]; c.push_back(cnsts[i]); Z3_ast foo = Z3_mk_interp(ctx,and_vec(ctx,c)); chs[parents[i]].push_back(foo); } { - std::vector &c = chs[num-1]; + svector &c = chs[num-1]; c.push_back(cnsts[num-1]); res = and_vec(ctx,c); } @@ -454,7 +454,7 @@ extern "C" { static std::string read_msg; static std::vector read_theory; - static bool iZ3_parse(Z3_context ctx, const char *filename, const char **error, std::vector &assertions){ + static bool iZ3_parse(Z3_context ctx, const char *filename, const char **error, svector &assertions){ read_error.clear(); try { std::string foo(filename); @@ -496,26 +496,26 @@ extern "C" { hash_map file_params; get_file_params(filename,file_params); - - int num_theory = 0; + + unsigned num_theory = 0; if(file_params.find("THEORY") != file_params.end()) num_theory = atoi(file_params["THEORY"].c_str()); - std::vector assertions; + svector assertions; if(!iZ3_parse(ctx,filename,error,assertions)) return false; - if(num_theory > (int)assertions.size()) - num_theory = assertions.size(); - int num = assertions.size() - num_theory; + if(num_theory > assertions.size()) + num_theory = assertions.size(); + unsigned num = assertions.size() - num_theory; read_cnsts.resize(num); read_parents.resize(num); read_theory.resize(num_theory); - for(int j = 0; j < num_theory; j++) + for(unsigned j = 0; j < num_theory; j++) read_theory[j] = assertions[j]; - for(int j = 0; j < num; j++) + for(unsigned j = 0; j < num; j++) read_cnsts[j] = assertions[j+num_theory]; if(ret_num_theory) @@ -529,12 +529,12 @@ extern "C" { return true; } - for(int j = 0; j < num; j++) + for(unsigned j = 0; j < num; j++) read_parents[j] = SHRT_MAX; hash_map pred_map; - for(int j = 0; j < num; j++){ + for(unsigned j = 0; j < num; j++){ Z3_ast lhs = 0, rhs = read_cnsts[j]; if(Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx,Z3_to_app(ctx,rhs))) == Z3_OP_IMPLIES){ @@ -588,7 +588,7 @@ extern "C" { } } - for(int j = 0; j < num-1; j++) + for(unsigned j = 0; j < num-1; j++) if(read_parents[j] == SHRT_MIN){ read_error << "formula " << j+1 << ": unreferenced"; goto fail; diff --git a/src/ast/proof_checker/proof_checker.cpp b/src/ast/proof_checker/proof_checker.cpp index 85e0cc791..41c43b26c 100644 --- a/src/ast/proof_checker/proof_checker.cpp +++ b/src/ast/proof_checker/proof_checker.cpp @@ -479,7 +479,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // otherwise t2 is also a quantifier. return true; } - UNREACHABLE(); + IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";); return false; } case PR_DER: { @@ -488,13 +488,12 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { match_fact(p, fact) && match_iff(fact.get(), t1, t2) && match_quantifier(t1, is_forall, decls1, body1) && - is_forall && - match_or(body1.get(), terms1)) { + is_forall) { // TBD: check that terms are set of equalities. // t2 is an instance of a predicate in terms1 return true; - } - UNREACHABLE(); + } + IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";); return false; } case PR_HYPOTHESIS: { @@ -832,7 +831,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } else { IF_VERBOSE(0, verbose_stream() << "Could not establish complementarity for:\n" << - mk_pp(lit1, m) << "\n" << mk_pp(lit2, m) << "\n";); + mk_pp(lit1, m) << "\n" << mk_pp(lit2, m) << "\n" << mk_pp(p, m) << "\n";); } fmls[i] = premise1; } diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 9f77934e5..7593e9a52 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -475,10 +475,11 @@ namespace smt { bool theory_arith::all_coeff_int(row const & r) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead() && !it->m_coeff.is_int()) + for (; it != end; ++it) { + if (!it->is_dead() && !it->m_coeff.is_int()) { TRACE("gomory_cut", display_row(tout, r, true);); return false; + } } return true; } From ebc8a43fe39f556b7cc47151d201befd446f7f57 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 15 Dec 2013 15:49:06 -0800 Subject: [PATCH 242/509] removing address dependencies --- src/api/api_interp.cpp | 14 ++++++++++++++ src/duality/duality.h | 23 +++++++++++++++++++++++ src/duality/duality_wrapper.h | 3 ++- src/interp/iz3base.h | 9 ++++++++- src/interp/iz3foci.cpp | 9 ++++++++- src/interp/iz3hash.h | 2 ++ src/interp/iz3mgr.h | 4 ++-- src/interp/iz3pp.cpp | 14 ++++++++++++++ src/interp/iz3translate.cpp | 11 +++++++++-- src/interp/iz3translate_direct.cpp | 18 ++++++++++++++++++ 10 files changed, 100 insertions(+), 7 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 00503566e..622c7cf94 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -42,6 +42,20 @@ Revision History: using namespace stl_ext; #endif +#ifndef WIN32 +// WARNING: don't make a hash_map with this if the range type +// has a destructor: you'll get an address dependency!!! +namespace stl_ext { + template <> + class hash { + public: + size_t operator()(const Z3_ast p) const { + return (size_t) p; + } + }; +} +#endif + typedef interpolation_options_struct *Z3_interpolation_options; extern "C" { diff --git a/src/duality/duality.h b/src/duality/duality.h index ebf6c8632..7729d2175 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1008,6 +1008,29 @@ private: }; } + +// Allow to hash on nodes and edges in deterministic way + +namespace stl_ext { + template <> + class hash { + public: + size_t operator()(const Duality::RPFP::Node *p) const { + return p->number; + } + }; +} + +namespace stl_ext { + template <> + class hash { + public: + size_t operator()(const Duality::RPFP::Edge *p) const { + return p->number; + } + }; +} + // allow to walk sets of nodes without address dependency namespace std { diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 256560d02..a36f93b40 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -1419,7 +1419,8 @@ namespace std { class less { public: bool operator()(const Duality::func_decl &s, const Duality::func_decl &t) const { - return s.raw() < t.raw(); // s.raw()->get_id() < t.raw()->get_id(); + // return s.raw() < t.raw(); + return s.raw()->get_id() < t.raw()->get_id(); } }; } diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index 30ac57bae..e19ae894d 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -146,7 +146,14 @@ class iz3base : public iz3mgr, public scopes { ranges(){scope_computed = false;} }; - stl_ext::hash_map sym_range_hash; + // We only use this for sym_range_hash, which has no range destructor + struct symb_hash { + size_t operator()(const symb &s) const { + return (size_t) s; + } + }; + + stl_ext::hash_map sym_range_hash; stl_ext::hash_map ast_ranges_hash; stl_ext::hash_map simplify_memo; stl_ext::hash_map frame_map; // map assertions to frames diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp index 85e090c5b..fe20f1372 100755 --- a/src/interp/iz3foci.cpp +++ b/src/interp/iz3foci.cpp @@ -51,7 +51,14 @@ public: typedef hash_map NodeToAst; NodeToAst node_to_ast; // maps Z3 ast's to foci expressions - typedef hash_map FuncDeclToSymbol; + // We only use this for FuncDeclToSymbol, which has no range destructor + struct symb_hash { + size_t operator()(const symb &s) const { + return (size_t) s; + } + }; + + typedef hash_map FuncDeclToSymbol; FuncDeclToSymbol func_decl_to_symbol; // maps Z3 func decls to symbols typedef hash_map SymbolToFuncDecl; diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index f6767c037..75d9aa604 100755 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -141,6 +141,7 @@ namespace std { #ifndef WIN32 +#if 0 namespace stl_ext { template class hash { @@ -150,6 +151,7 @@ namespace stl_ext { } }; } +#endif #endif diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 39c14c724..645c72ccb 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -65,7 +65,7 @@ class ast_i { return _ast == other._ast; } bool lt(const ast_i &other) const { - return _ast < other._ast; + return _ast->get_id() < other._ast->get_id(); } friend bool operator==(const ast_i &x, const ast_i&y){ return x.eq(y); @@ -76,7 +76,7 @@ class ast_i { friend bool operator<(const ast_i &x, const ast_i&y){ return x.lt(y); } - size_t hash() const {return (size_t)_ast;} + size_t hash() const {return _ast->get_id();} bool null() const {return !_ast;} }; diff --git a/src/interp/iz3pp.cpp b/src/interp/iz3pp.cpp index 1f9351453..df6fcaf53 100644 --- a/src/interp/iz3pp.cpp +++ b/src/interp/iz3pp.cpp @@ -40,6 +40,20 @@ Revision History: using namespace stl_ext; #endif +#ifndef WIN32 +// We promise not to use this for hash_map with range destructor +namespace stl_ext { + template <> + class hash { + public: + size_t operator()(const expr *p) const { + return (size_t) p; + } + }; +} +#endif + + // TBD: algebraic data-types declarations will not be printed. class free_func_visitor { ast_manager& m; diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index fae914292..88e4f1174 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -619,7 +619,14 @@ public: return 1; } - void symbols_out_of_scope_rec(hash_set &memo, hash_set &symb_memo, int frame, const ast &t){ + // We only use this for debugging purposes + struct symb_hash { + size_t operator()(const symb &s) const { + return (size_t) s; + } + }; + + void symbols_out_of_scope_rec(hash_set &memo, hash_set &symb_memo, int frame, const ast &t){ if(memo.find(t) != memo.end()) return; memo.insert(t); @@ -638,7 +645,7 @@ public: void symbols_out_of_scope(int frame, const ast &t){ hash_set memo; - hash_set symb_memo; + hash_set symb_memo; symbols_out_of_scope_rec(memo,symb_memo,frame,t); } diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index 44c907d1d..eaef956b7 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -37,6 +37,24 @@ Revision History: using namespace stl_ext; #endif +#ifndef WIN32 + +/* This can introduce an address dependency if the range type of hash_map has + a destructor. Since the code in this file is not used and only here for + historical comparisons, we allow this non-determinism. + */ +namespace stl_ext { + template + class hash { + public: + size_t operator()(const T *p) const { + return (size_t) p; + } + }; +} + +#endif + static int lemma_count = 0; static int nll_lemma_count = 0; From 852f53d6a6120d56aa8b6c06c53d560645213b71 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 15 Dec 2013 17:24:51 -0800 Subject: [PATCH 243/509] fixed memory error --- src/duality/duality_solver.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 073f4dce9..13c839186 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1780,9 +1780,12 @@ namespace Duality { if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) throw "help!"; } + } + RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; RemoveExpansion(node); } - RemoveLeaves(leaves_to_remove); stack.pop_back(); if(prev_level_used || stack.size() == 1) break; RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list From 1e8c04be8e9384d6163bd2154b8e22f657181c1f Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 15 Dec 2013 17:31:46 -0800 Subject: [PATCH 244/509] fixing templates for broken windows hash functions --- src/duality/duality.h | 4 ++-- src/duality/duality_wrapper.cpp | 2 +- src/interp/iz3base.h | 18 ++++++++++-------- src/interp/iz3foci.cpp | 2 +- src/interp/iz3translate.cpp | 11 ++--------- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 7729d2175..979071639 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1011,7 +1011,7 @@ private: // Allow to hash on nodes and edges in deterministic way -namespace stl_ext { +namespace hash_space { template <> class hash { public: @@ -1021,7 +1021,7 @@ namespace stl_ext { }; } -namespace stl_ext { +namespace hash_space { template <> class hash { public: diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 705d71abe..55883202f 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -606,7 +606,7 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st void model::show_hash() const { std::ostringstream ss; model_smt2_pp(ss, m(), *m_model, 0); - stl_ext::hash hasher; + hash_space::hash hasher; unsigned h = hasher(ss.str()); std::cout << "model hash: " << h << "\n"; } diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index e19ae894d..7c56b06a6 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -146,14 +146,7 @@ class iz3base : public iz3mgr, public scopes { ranges(){scope_computed = false;} }; - // We only use this for sym_range_hash, which has no range destructor - struct symb_hash { - size_t operator()(const symb &s) const { - return (size_t) s; - } - }; - - stl_ext::hash_map sym_range_hash; + stl_ext::hash_map sym_range_hash; stl_ext::hash_map ast_ranges_hash; stl_ext::hash_map simplify_memo; stl_ext::hash_map frame_map; // map assertions to frames @@ -187,6 +180,15 @@ class iz3base : public iz3mgr, public scopes { }; +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const iz3mgr::symb &s) const { + return (size_t) s; + } + }; +} diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp index fe20f1372..1d81a1b15 100755 --- a/src/interp/iz3foci.cpp +++ b/src/interp/iz3foci.cpp @@ -58,7 +58,7 @@ public: } }; - typedef hash_map FuncDeclToSymbol; + typedef hash_map FuncDeclToSymbol; FuncDeclToSymbol func_decl_to_symbol; // maps Z3 func decls to symbols typedef hash_map SymbolToFuncDecl; diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 88e4f1174..fae914292 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -619,14 +619,7 @@ public: return 1; } - // We only use this for debugging purposes - struct symb_hash { - size_t operator()(const symb &s) const { - return (size_t) s; - } - }; - - void symbols_out_of_scope_rec(hash_set &memo, hash_set &symb_memo, int frame, const ast &t){ + void symbols_out_of_scope_rec(hash_set &memo, hash_set &symb_memo, int frame, const ast &t){ if(memo.find(t) != memo.end()) return; memo.insert(t); @@ -645,7 +638,7 @@ public: void symbols_out_of_scope(int frame, const ast &t){ hash_set memo; - hash_set symb_memo; + hash_set symb_memo; symbols_out_of_scope_rec(memo,symb_memo,frame,t); } From 3588d4a1ca94fa0186e9a4fcc05c93173de505e7 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 16 Dec 2013 12:41:43 -0800 Subject: [PATCH 245/509] fixing templates for broken windows hash functions --- src/interp/iz3base.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index 7c56b06a6..6bf09bb85 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -24,6 +24,16 @@ Revision History: #include "iz3mgr.h" #include "iz3scopes.h" +namespace hash_space { + template <> + class hash { + public: + size_t operator()(func_decl * const &s) const { + return (size_t) s; + } + }; +} + /* Base class for interpolators. Includes an AST manager and a scoping object as bases. */ @@ -180,17 +190,6 @@ class iz3base : public iz3mgr, public scopes { }; -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const iz3mgr::symb &s) const { - return (size_t) s; - } - }; -} - - #endif From 0b3e50d6e67bace32f8d9e3a8a6c11af144371be Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 17 Dec 2013 13:53:10 +0000 Subject: [PATCH 246/509] Added #include because VS2013 needs that for std::max/std::min Signed-off-by: Christoph M. Wintersteiger --- src/interp/iz3scopes.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interp/iz3scopes.cpp b/src/interp/iz3scopes.cpp index 382779be0..198ecffe3 100755 --- a/src/interp/iz3scopes.cpp +++ b/src/interp/iz3scopes.cpp @@ -19,6 +19,8 @@ Revision History: #include +#include + #include "iz3scopes.h" From 0d6220f383c4e171ee5f19a840a172653cf1eb60 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 18 Dec 2013 21:53:04 +0200 Subject: [PATCH 247/509] revert is_all_int bugfix Signed-off-by: Nikolaj Bjorner --- src/smt/theory_arith_aux.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 7593e9a52..21b892e57 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -476,10 +476,10 @@ namespace smt { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { - if (!it->is_dead() && !it->m_coeff.is_int()) { + if (!it->is_dead() && !it->m_coeff.is_int()) TRACE("gomory_cut", display_row(tout, r, true);); return false; - } + } return true; } From 48e10a9e2dd19dbd8a47068bab0f6286e8764ad3 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 19 Dec 2013 11:05:56 -0800 Subject: [PATCH 248/509] dealing with incompleteness issues in duality --- src/duality/duality.h | 2 ++ src/duality/duality_rpfp.cpp | 22 ++++++++++++++++------ src/duality/duality_solver.cpp | 8 +++++++- src/interp/iz3translate.cpp | 4 ++++ src/muz/duality/duality_dl_interface.cpp | 3 +++ 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 979071639..cc6d81b1a 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1005,6 +1005,8 @@ private: /** Object thrown on cancellation */ struct Canceled {}; + /** Object thrown on incompleteness */ + struct Incompleteness {}; }; } diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index a5e2b9167..162175721 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2289,6 +2289,7 @@ namespace Duality { } void RPFP::InterpolateByCases(Node *root, Node *node){ + bool axioms_added = false; aux_solver.push(); AddEdgeToSolver(node->Outgoing); node->Annotation.SetEmpty(); @@ -2320,15 +2321,24 @@ namespace Duality { std::vector case_lits; itp = StrengthenFormulaByCaseSplitting(itp, case_lits); SetAnnotation(node,itp); + node->Annotation.Formula.simplify(); } if(node->Annotation.IsEmpty()){ - std::cout << "bad in InterpolateByCase -- core:\n"; - std::vector assumps; - slvr.get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++) - assumps[i].show(); - throw "ack!"; + if(!axioms_added){ + // add the axioms in the off chance they are useful + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + aux_solver.add(theory[i]); + } + else { + std::cout << "bad in InterpolateByCase -- core:\n"; + std::vector assumps; + slvr.get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++) + assumps[i].show(); + throw "ack!"; + } } Pop(1); node->Annotation.UnionWith(old_annot); diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 13c839186..6ef9d7746 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1731,6 +1731,7 @@ namespace Duality { virtual bool Build(){ stack.back().level = tree->slvr.get_scope_level(); + bool was_sat = true; while (true) { @@ -1760,8 +1761,11 @@ namespace Duality { if(RecordUpdate(node)) update_count++; } - if(update_count == 0) + if(update_count == 0){ + if(was_sat) + throw Incompleteness(); reporter->Message("backtracked without learning"); + } } tree->ComputeProofCore(); // need to compute the proof core before popping solver while(1) { @@ -1796,8 +1800,10 @@ namespace Duality { HandleUpdatedNodes(); if(stack.size() == 1) return false; + was_sat = false; } else { + was_sat = true; tree->Push(); std::vector &expansions = stack.back().expansions; for(unsigned i = 0; i < expansions.size(); i++){ diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index fae914292..2fbc173a9 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1642,6 +1642,10 @@ public: res = iproof->make_axiom(lits); break; } + case PR_IFF_TRUE: { // turns p into p <-> true, noop for us + res = args[0]; + break; + } default: assert(0 && "translate_main: unsupported proof rule"); throw unsupported(); diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 12dd4ff3e..1409212d4 100644 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -213,6 +213,9 @@ lbool dl_interface::query(::expr * query) { catch (Duality::solver::cancel_exception &exn){ throw default_exception("duality canceled"); } + catch (Duality::Solver::Incompleteness &exn){ + throw default_exception("incompleteness"); + } // profile! From c98b853917c857b9b1d434cc647329baf29b9df3 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sat, 21 Dec 2013 16:54:35 -0800 Subject: [PATCH 249/509] speeding up Generalize and adding Lazy Propagation --- src/duality/duality.h | 65 ++++++- src/duality/duality_profiling.cpp | 2 + src/duality/duality_rpfp.cpp | 313 ++++++++++++++++++++++++++---- src/duality/duality_solver.cpp | 127 +++++++++++- src/duality/duality_wrapper.cpp | 5 +- src/duality/duality_wrapper.h | 12 ++ src/interp/iz3proof_itp.cpp | 18 +- 7 files changed, 491 insertions(+), 51 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index cc6d81b1a..b29a6a610 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -98,7 +98,7 @@ namespace Duality { bool IsClosedFormula(const Term &t); Term AdjustQuantifiers(const Term &t); -private: +protected: void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); int CountOperatorsRec(hash_set &memo, const Term &t); @@ -309,7 +309,7 @@ private: LogicSolver *ls; - private: + protected: int nodeCount; int edgeCount; @@ -324,7 +324,7 @@ private: public: model dualModel; - private: + protected: literals dualLabels; std::list stack; std::vector axioms; // only saved here for printing purposes @@ -829,7 +829,7 @@ private: */ void ComputeProofCore(); - private: + protected: void ClearProofCore(){ if(proof_core) @@ -947,6 +947,8 @@ private: expr SimplifyOr(std::vector &lits); + expr SimplifyAnd(std::vector &lits); + void SetAnnotation(Node *root, const expr &t); void AddEdgeToSolver(Edge *edge); @@ -959,9 +961,11 @@ private: expr NegateLit(const expr &f); + expr GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox); }; - /** RPFP solver base class. */ + + /** RPFP solver base class. */ class Solver { @@ -1044,3 +1048,54 @@ namespace std { } }; } + +namespace Duality { + /** Caching version of RPFP. Instead of asserting constraints, returns assumption literals */ + + class RPFP_caching : public RPFP { + public: + + /** appends assumption literals for edge to lits. if with_children is true, + includes that annotation of the edge's children. + */ + void AssertEdgeCache(Edge *e, std::vector &lits, bool with_children = false); + + /** appends assumption literals for node to lits */ + void AssertNodeCache(Node *, std::vector lits); + + /** check assumption lits, and return core */ + check_result CheckCore(const std::vector &assumps, std::vector &core); + + /** Clone another RPFP into this one, keeping a map */ + void Clone(RPFP *other); + + /** Get the clone of a node */ + Node *GetNodeClone(Node *other_node); + + /** Get the clone of an edge */ + Edge *GetEdgeClone(Edge *other_edge); + + /** Try to strengthen the parent of an edge */ + void GeneralizeCache(Edge *edge); + + /** Try to propagate some facts from children to parents of edge. + Return true if success. */ + bool PropagateCache(Edge *edge); + + /** Construct a caching RPFP using a LogicSolver */ + RPFP_caching(LogicSolver *_ls) : RPFP(_ls) {} + + protected: + hash_map AssumptionLits; + hash_map NodeCloneMap; + hash_map EdgeCloneMap; + + void GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map = 0); + + void GreedyReduceCache(std::vector &assumps, std::vector &core); + + void FilterCore(std::vector &core, std::vector &full_core); + + }; + +} diff --git a/src/duality/duality_profiling.cpp b/src/duality/duality_profiling.cpp index bec32c51a..3b392a91a 100755 --- a/src/duality/duality_profiling.cpp +++ b/src/duality/duality_profiling.cpp @@ -26,6 +26,7 @@ Revision History: #include #include "duality_wrapper.h" +#include "iz3profiling.h" namespace Duality { @@ -103,6 +104,7 @@ namespace Duality { output_time(*pfs, it->second.t); (*pfs) << std::endl; } + profiling::print(os); // print the interpolation stats } void timer_start(const char *name){ diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 162175721..a95561f5d 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -519,6 +519,20 @@ namespace Duality { const expr &lit = args[i]; expr atom, val; if(IsLiteral(lit,atom,val)){ + if(atom.is_app() && atom.decl().get_decl_kind() == Equal) + if(pol ? eq(val,ctx.bool_val(true)) : eq(val,ctx.bool_val(false))){ + expr lhs = atom.arg(0), rhs = atom.arg(1); + if(lhs.is_numeral()) + std::swap(lhs,rhs); + if(rhs.is_numeral() && lhs.is_app()){ + for(unsigned j = 0; j < args.size(); j++) + if(j != i){ + smemo.clear(); + smemo[lhs] = rhs; + args[j] = SubstRec(smemo,args[j]); + } + } + } for(unsigned j = 0; j < args.size(); j++) if(j != i){ smemo.clear(); @@ -711,6 +725,39 @@ namespace Duality { #endif + expr RPFP::GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox) + { + if (e->dual.null()) { + timer_start("ReducedDualEdge"); + e->dual = ReducedDualEdge(e); + timer_stop("ReducedDualEdge"); + timer_start("getting children"); + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + e->dual = e->dual && GetAnnotation(e->Children[i]); + if(underapprox){ + std::vector cus(e->Children.size()); + for(unsigned i = 0; i < e->Children.size(); i++) + cus[i] = !UnderapproxFlag(e->Children[i]) || GetUnderapprox(e->Children[i]); + expr cnst = conjoin(cus); + e->dual = e->dual && cnst; + } + timer_stop("getting children"); + timer_start("Persisting"); + std::list::reverse_iterator it = stack.rbegin(); + for(int i = 0; i < persist && it != stack.rend(); i++) + it++; + if(it != stack.rend()) + it->edges.push_back(e); + timer_stop("Persisting"); + //Console.WriteLine("{0}", cnst); + } + return e->dual; + timer_start("solver add"); + slvr.add(e->dual); + timer_stop("solver add"); + } + /** For incremental solving, asserts the constraint associated * with this edge in the SMT context. If this edge is removed, * you must pop the context accordingly. The second argument is @@ -732,41 +779,40 @@ namespace Duality { { if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) return; - if (e->dual.null()) - { - timer_start("ReducedDualEdge"); - e->dual = ReducedDualEdge(e); - timer_stop("ReducedDualEdge"); - timer_start("getting children"); - if(with_children) - for(unsigned i = 0; i < e->Children.size(); i++) - e->dual = e->dual && GetAnnotation(e->Children[i]); - if(underapprox){ - std::vector cus(e->Children.size()); - for(unsigned i = 0; i < e->Children.size(); i++) - cus[i] = !UnderapproxFlag(e->Children[i]) || GetUnderapprox(e->Children[i]); - expr cnst = conjoin(cus); - e->dual = e->dual && cnst; - } - timer_stop("getting children"); - timer_start("Persisting"); - std::list::reverse_iterator it = stack.rbegin(); - for(int i = 0; i < persist && it != stack.rend(); i++) - it++; - if(it != stack.rend()) - it->edges.push_back(e); -#if 0 - if(persist != 0) - Z3_persist_ast(ctx,e->dual,persist); -#endif - timer_stop("Persisting"); - //Console.WriteLine("{0}", cnst); - } + expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); timer_start("solver add"); slvr.add(e->dual); timer_stop("solver add"); } + // caching verion of above + void RPFP_caching::AssertEdgeCache(Edge *e, std::vector &lits, bool with_children){ + if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) + return; + expr fmla = GetEdgeFormula(e, 0, with_children, false); + GetAssumptionLits(fmla,lits); + } + + void RPFP_caching::GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map){ + std::vector conjs; + CollectConjuncts(fmla,conjs); + for(unsigned i = 0; i < conjs.size(); i++){ + const expr &conj = conjs[i]; + std::pair foo(conj,expr(ctx)); + std::pair::iterator, bool> bar = AssumptionLits.insert(foo); + Term &res = bar.first->second; + if(bar.second){ + func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); + res = pred(); + slvr.add(ctx.make(Implies,res,conj)); + // std::cout << res << ": " << conj << "\n"; + } + if(opt_map) + (*opt_map)[res] = conj; + lits.push_back(res); + } + } + void RPFP::ConstrainParent(Edge *parent, Node *child){ ConstrainEdgeLocalized(parent,GetAnnotation(child)); } @@ -786,6 +832,53 @@ namespace Duality { } } + // caching version of above + void RPFP_caching::AssertNodeCache(Node *n, std::vector lits){ + if (n->dual.null()) + { + n->dual = GetUpperBound(n); + stack.back().nodes.push_back(n); + GetAssumptionLits(n->dual,lits); + } + } + + /** Clone another RPFP into this one, keeping a map */ + void RPFP_caching::Clone(RPFP *other){ + for(unsigned i = 0; i < other->nodes.size(); i++) + NodeCloneMap[other->nodes[i]] = CloneNode(other->nodes[i]); + for(unsigned i = 0; i < other->edges.size(); i++){ + Edge *edge = other->edges[i]; + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++) + // cs.push_back(NodeCloneMap[edge->Children[j]]); + cs.push_back(CloneNode(edge->Children[j])); + EdgeCloneMap[edge] = CreateEdge(NodeCloneMap[edge->Parent],edge->F,cs); + } + } + + /** Get the clone of a node */ + RPFP::Node *RPFP_caching::GetNodeClone(Node *other_node){ + return NodeCloneMap[other_node]; + } + + /** Get the clone of an edge */ + RPFP::Edge *RPFP_caching::GetEdgeClone(Edge *other_edge){ + return EdgeCloneMap[other_edge]; + } + + /** check assumption lits, and return core */ + check_result RPFP_caching::CheckCore(const std::vector &assumps, std::vector &core){ + core.resize(assumps.size()); + unsigned core_size; + check_result res = slvr.check(assumps.size(),(expr *)&assumps[0],&core_size,&core[0]); + if(res == unsat) + core.resize(core_size); + else + core.clear(); + return res; + } + + /** Assert a constraint on an edge in the SMT context. */ @@ -970,6 +1063,7 @@ namespace Duality { check_result RPFP::Check(Node *root, std::vector underapproxes, std::vector *underapprox_core ) { + timer_start("Check"); ClearProofCore(); // if (dualModel != null) dualModel.Dispose(); check_result res; @@ -1002,6 +1096,7 @@ namespace Duality { // check_result temp = slvr.check(); } dualModel = slvr.get_model(); + timer_stop("Check"); return res; } @@ -1927,10 +2022,14 @@ namespace Duality { for(int i = 0; i < num_args; i++) CollectConjuncts(f.arg(i),lits,false); } - else if(pos) - lits.push_back(f); - else - lits.push_back(!f); + else if(pos){ + if(!eq(f,ctx.bool_val(true))) + lits.push_back(f); + } + else { + if(!eq(f,ctx.bool_val(false))) + lits.push_back(!f); + } } struct TermLt { @@ -2253,6 +2352,45 @@ namespace Duality { } } + void RPFP_caching::FilterCore(std::vector &core, std::vector &full_core){ + hash_set core_set; + std::copy(full_core.begin(),full_core.end(),std::inserter(core_set,core_set.begin())); + std::vector new_core; + for(unsigned i = 0; i < core.size(); i++) + if(core_set.find(core[i]) != core_set.end()) + new_core.push_back(core[i]); + core.swap(new_core); + } + + void RPFP_caching::GreedyReduceCache(std::vector &assumps, std::vector &core){ + std::vector lits = assumps, full_core; + std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); + + // verify + check_result res = CheckCore(lits,full_core); + if(res != unsat) + throw "should be unsat"; + FilterCore(core,full_core); + + std::vector dummy; + if(CheckCore(full_core,dummy) != unsat) + throw "should be unsat"; + + for(unsigned i = 0; i < core.size(); ){ + expr temp = core[i]; + std::swap(core[i],core.back()); + core.pop_back(); + lits.resize(assumps.size()); + std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); + res = CheckCore(lits,full_core); + if(res != unsat){ + core.push_back(temp); + std::swap(core[i],core.back()); + i++; + } + } + } + expr RPFP::NegateLit(const expr &f){ if(f.is_app() && f.decl().get_decl_kind() == Not) return f.arg(0); @@ -2278,6 +2416,14 @@ namespace Duality { return ctx.make(Or,lits); } + expr RPFP::SimplifyAnd(std::vector &lits){ + if(lits.size() == 0) + return ctx.bool_val(true); + if(lits.size() == 1) + return lits[0]; + return ctx.make(And,lits); + } + // set up edge constraint in aux solver void RPFP::AddEdgeToSolver(Edge *edge){ if(!edge->dual.null()) @@ -2321,7 +2467,7 @@ namespace Duality { std::vector case_lits; itp = StrengthenFormulaByCaseSplitting(itp, case_lits); SetAnnotation(node,itp); - node->Annotation.Formula.simplify(); + node->Annotation.Formula = node->Annotation.Formula.simplify(); } if(node->Annotation.IsEmpty()){ @@ -2330,6 +2476,7 @@ namespace Duality { const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) aux_solver.add(theory[i]); + axioms_added = true; } else { std::cout << "bad in InterpolateByCase -- core:\n"; @@ -2350,6 +2497,7 @@ namespace Duality { } void RPFP::Generalize(Node *root, Node *node){ + timer_start("Generalize"); aux_solver.push(); AddEdgeToSolver(node->Outgoing); expr fmla = GetAnnotation(node); @@ -2359,8 +2507,103 @@ namespace Duality { aux_solver.pop(1); NegateLits(conjuncts); SetAnnotation(node,SimplifyOr(conjuncts)); + timer_stop("Generalize"); } + + // caching version of above + void RPFP_caching::GeneralizeCache(Edge *edge){ + timer_start("Generalize"); + Node *node = edge->Parent; + std::vector assumps, core, conjuncts; + AssertEdgeCache(edge,assumps); + for(unsigned i = 0; i < edge->Children.size(); i++){ + expr ass = GetAnnotation(edge->Children[i]); + std::vector clauses; + CollectConjuncts(ass.arg(1),clauses); + for(unsigned j = 0; j < clauses.size(); j++) + GetAssumptionLits(ass.arg(0) || clauses[j],assumps); + } + expr fmla = GetAnnotation(node); + assumps.push_back(fmla.arg(0).arg(0)); + std::vector lits; + CollectConjuncts(!fmla.arg(1),lits); +#if 0 + for(unsigned i = 0; i < lits.size(); i++){ + const expr &lit = lits[i]; + if(lit.is_app() && lit.decl().get_decl_kind() == Equal){ + lits[i] = ctx.make(Leq,lit.arg(0),lit.arg(1)); + lits.push_back(ctx.make(Leq,lit.arg(1),lit.arg(0))); + } + } +#endif + hash_map lit_map; + for(unsigned i = 0; i < lits.size(); i++) + GetAssumptionLits(lits[i],core,&lit_map); + GreedyReduceCache(assumps,core); + for(unsigned i = 0; i < core.size(); i++) + conjuncts.push_back(lit_map[core[i]]); + NegateLits(conjuncts); + SetAnnotation(node,SimplifyOr(conjuncts)); + timer_stop("Generalize"); + } + + // caching version of above + bool RPFP_caching::PropagateCache(Edge *edge){ + timer_start("PropagateCache"); + bool some = false; + { + std::vector candidates, skip; + Node *node = edge->Parent; + CollectConjuncts(node->Annotation.Formula,skip); + for(unsigned i = 0; i < edge->Children.size(); i++){ + Node *child = edge->Children[i]; + if(child->map == node->map){ + CollectConjuncts(child->Annotation.Formula,candidates); + break; + } + } + if(candidates.empty()) goto done; + hash_set skip_set; + std::copy(skip.begin(),skip.end(),std::inserter(skip_set,skip_set.begin())); + std::vector new_candidates; + for(unsigned i = 0; i < candidates.size(); i++) + if(skip_set.find(candidates[i]) == skip_set.end()) + new_candidates.push_back(candidates[i]); + candidates.swap(new_candidates); + if(candidates.empty()) goto done; + std::vector assumps, core, conjuncts; + AssertEdgeCache(edge,assumps); + for(unsigned i = 0; i < edge->Children.size(); i++){ + expr ass = GetAnnotation(edge->Children[i]); + std::vector clauses; + CollectConjuncts(ass.arg(1),clauses); + for(unsigned j = 0; j < clauses.size(); j++) + GetAssumptionLits(ass.arg(0) || clauses[j],assumps); + } + for(unsigned i = 0; i < candidates.size(); i++){ + unsigned old_size = assumps.size(); + node->Annotation.Formula = candidates[i]; + expr fmla = GetAnnotation(node); + assumps.push_back(fmla.arg(0).arg(0)); + GetAssumptionLits(!fmla.arg(1),assumps); + std::vector full_core; + check_result res = CheckCore(assumps,full_core); + if(res == unsat) + conjuncts.push_back(candidates[i]); + assumps.resize(old_size); + } + if(conjuncts.empty()) + goto done; + SetAnnotation(node,SimplifyAnd(conjuncts)); + some = true; + } + done: + timer_stop("PropagateCache"); + return some; + } + + /** Push a scope. Assertions made after Push can be undone by Pop. */ void RPFP::Push() diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 6ef9d7746..7318b020d 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -44,7 +44,7 @@ Revision History: // #define TOP_DOWN // #define EFFORT_BOUNDED_STRAT #define SKIP_UNDERAPPROX_NODES - +#define USE_RPFP_CLONE namespace Duality { @@ -115,8 +115,25 @@ namespace Duality { Report = false; StratifiedInlining = false; RecursionBound = -1; +#ifdef USE_RPFP_CLONE + clone_ls = new RPFP::iZ3LogicSolver(ctx); + clone_rpfp = new RPFP_caching(clone_ls); + clone_rpfp->Clone(rpfp); +#endif } + ~Duality(){ +#ifdef USE_RPFP_CLONE + delete clone_rpfp; + delete clone_ls; +#endif + } + +#ifdef USE_RPFP_CLONE + RPFP::LogicSolver *clone_ls; + RPFP_caching *clone_rpfp; +#endif + typedef RPFP::Node Node; typedef RPFP::Edge Edge; @@ -1309,6 +1326,7 @@ namespace Duality { node. */ bool SatisfyUpperBound(Node *node){ if(node->Bound.IsFull()) return true; + Propagate(); reporter->Bound(node); int start_decs = rpfp->CumulativeDecisions(); DerivationTree *dtp = new DerivationTreeSlow(this,unwinding,reporter,heuristic,FullExpand); @@ -1412,6 +1430,70 @@ namespace Duality { } } + // Propagate conjuncts up the unwinding + void Propagate(){ + reporter->Message("beginning propagation"); + timer_start("Propagate"); + std::vector sorted_nodes = unwinding->nodes; + std::sort(sorted_nodes.begin(),sorted_nodes.end(),std::less()); // sorts by sequence number + hash_map > facts; + for(unsigned i = 0; i < sorted_nodes.size(); i++){ + Node *node = sorted_nodes[i]; + std::set &node_facts = facts[node->map]; + if(!(node->Outgoing && indset->Contains(node))) + continue; + std::vector conj_vec; + unwinding->CollectConjuncts(node->Annotation.Formula,conj_vec); + std::set conjs; + std::copy(conj_vec.begin(),conj_vec.end(),std::inserter(conjs,conjs.begin())); + if(!node_facts.empty()){ + RPFP *checker = new RPFP(rpfp->ls); + slvr.push(); + Node *root = checker->CloneNode(node); + Edge *edge = node->Outgoing; + // checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + nc->Annotation = oc->Annotation; // is this needed? + cs.push_back(nc); + } + Edge *checker_edge = checker->CreateEdge(root,edge->F,cs); + checker->AssertEdge(checker_edge, 0, true, false); + std::vector propagated; + for(std::set ::iterator it = node_facts.begin(), en = node_facts.end(); it != en;){ + const expr &fact = *it; + if(conjs.find(fact) == conjs.end()){ + root->Bound.Formula = fact; + slvr.push(); + checker->AssertNode(root); + check_result res = checker->Check(root); + slvr.pop(); + if(res != unsat){ + std::set ::iterator victim = it; + ++it; + node_facts.erase(victim); // if it ain't true, nix it + continue; + } + propagated.push_back(fact); + } + ++it; + } + slvr.pop(); + for(unsigned i = 0; i < propagated.size(); i++){ + root->Annotation.Formula = propagated[i]; + UpdateNodeToNode(node,root); + } + delete checker; + } + for(std::set ::iterator it = conjs.begin(), en = conjs.end(); it != en; ++it){ + expr foo = *it; + node_facts.insert(foo); + } + } + timer_stop("Propagate"); + } /** This class represents a derivation tree. */ class DerivationTree { @@ -1757,7 +1839,9 @@ namespace Duality { tree->SolveSingleNode(top,node); if(expansions.size() == 1 && NodeTooComplicated(node)) SimplifyNode(node); - tree->Generalize(top,node); + else + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); + Generalize(node); if(RecordUpdate(node)) update_count++; } @@ -1791,7 +1875,14 @@ namespace Duality { RemoveExpansion(node); } stack.pop_back(); - if(prev_level_used || stack.size() == 1) break; + if(stack.size() == 1)break; + if(prev_level_used){ + Node *node = stack.back().expansions[0]; + if(!Propagate(node)) break; + if(!RecordUpdate(node)) break; // shouldn't happen! + RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list + continue; + } RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list std::vector &unused_ex = stack.back().expansions; for(unsigned i = 0; i < unused_ex.size(); i++) @@ -1833,8 +1924,10 @@ namespace Duality { void SimplifyNode(Node *node){ // have to destroy the old proof to get a new interpolant + timer_start("SimplifyNode"); tree->PopPush(); tree->InterpolateByCases(top,node); + timer_stop("SimplifyNode"); } bool LevelUsedInProof(unsigned level){ @@ -1933,6 +2026,34 @@ namespace Duality { throw "can't unmap node"; } + void Generalize(Node *node){ +#ifndef USE_RPFP_CLONE + tree->Generalize(top,node); +#else + RPFP_caching *clone_rpfp = duality->clone_rpfp; + Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); + Node *clone_node = clone_edge->Parent; + clone_node->Annotation = node->Annotation; + for(unsigned i = 0; i < clone_edge->Children.size(); i++) + clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; + clone_rpfp->GeneralizeCache(clone_edge); + node->Annotation = clone_node->Annotation; + } +#endif + + bool Propagate(Node *node){ + RPFP_caching *clone_rpfp = duality->clone_rpfp; + Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); + Node *clone_node = clone_edge->Parent; + clone_node->Annotation = node->map->Annotation; + for(unsigned i = 0; i < clone_edge->Children.size(); i++) + clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; + bool res = clone_rpfp->PropagateCache(clone_edge); + if(res) + node->Annotation = clone_node->Annotation; + return res; + } + }; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 55883202f..26bdf52d7 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -504,7 +504,10 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st add(linear_assumptions[i][j]); } - check_result res = check(); + check_result res = unsat; + + if(!m_solver->get_proof()) + res = check(); if(res == unsat){ diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index a36f93b40..fa4eca77b 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -1393,6 +1393,18 @@ namespace std { }; } +// to make Duality::ast usable in ordered collections +namespace std { + template <> + class less { + public: + bool operator()(const Duality::expr &s, const Duality::expr &t) const { + // return s.raw() < t.raw(); + return s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + // to make Duality::func_decl hashable namespace hash_space { template <> diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 75d14bca1..8b78a7135 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2170,7 +2170,8 @@ class iz3proof_itp_impl : public iz3proof_itp { for(unsigned i = 0; i < prem_cons.size(); i++){ const ast &lit = prem_cons[i]; if(get_term_type(lit) == LitA) - linear_comb(thing,coeffs[i],lit); + // Farkas rule seems to assume strict integer inequalities are rounded + linear_comb(thing,coeffs[i],lit,true /*round_off*/); } thing = simplify_ineq(thing); for(unsigned i = 0; i < prem_cons.size(); i++){ @@ -2195,9 +2196,9 @@ class iz3proof_itp_impl : public iz3proof_itp { /** Set P to P + cQ, where P and Q are linear inequalities. Assumes P is 0 <= y or 0 < y. */ - void linear_comb(ast &P, const ast &c, const ast &Q){ + void linear_comb(ast &P, const ast &c, const ast &Q, bool round_off = false){ ast Qrhs; - bool strict = op(P) == Lt; + bool qstrict = false; if(is_not(Q)){ ast nQ = arg(Q,0); switch(op(nQ)){ @@ -2209,11 +2210,11 @@ class iz3proof_itp_impl : public iz3proof_itp { break; case Geq: Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); - strict = true; + qstrict = true; break; case Leq: Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); - strict = true; + qstrict = true; break; default: throw proof_error(); @@ -2229,17 +2230,20 @@ class iz3proof_itp_impl : public iz3proof_itp { break; case Lt: Qrhs = make(Sub,arg(Q,1),arg(Q,0)); - strict = true; + qstrict = true; break; case Gt: Qrhs = make(Sub,arg(Q,0),arg(Q,1)); - strict = true; + qstrict = true; break; default: throw proof_error(); } } Qrhs = make(Times,c,Qrhs); + bool pstrict = op(P) == Lt, strict = pstrict || qstrict; + if(pstrict && qstrict && round_off) + Qrhs = make(Sub,Qrhs,make_int(rational(1))); if(strict) P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); else From 9e88691c69ef4f9c7f1059c225a343c9a3e5861d Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 22 Dec 2013 18:33:40 -0800 Subject: [PATCH 250/509] optimizing solver performance in duality --- src/duality/duality.h | 45 +++++- src/duality/duality_rpfp.cpp | 169 +++++++++++++++++++---- src/duality/duality_solver.cpp | 128 +++++++++++++++-- src/duality/duality_wrapper.cpp | 1 + src/duality/duality_wrapper.h | 18 ++- src/muz/duality/duality_dl_interface.cpp | 1 - 6 files changed, 311 insertions(+), 51 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index b29a6a610..b88e5011f 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -350,7 +350,7 @@ protected: proof_core = 0; } - ~RPFP(); + virtual ~RPFP(); /** Symbolic representation of a relational transformer */ class Transformer @@ -962,6 +962,22 @@ protected: expr NegateLit(const expr &f); expr GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox); + + virtual void slvr_add(const expr &e); + + virtual void slvr_pop(int i); + + virtual void slvr_push(); + + virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); + + virtual lbool ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals = 0, + bool weak = false); + + virtual bool proof_core_contains(const expr &e); }; @@ -1085,16 +1101,43 @@ namespace Duality { /** Construct a caching RPFP using a LogicSolver */ RPFP_caching(LogicSolver *_ls) : RPFP(_ls) {} + /** Constraint an edge by its child's annotation. Return + assumption lits. */ + void ConstrainParentCache(Edge *parent, Node *child, std::vector &lits); + + virtual ~RPFP_caching(){} + protected: hash_map AssumptionLits; hash_map NodeCloneMap; hash_map EdgeCloneMap; + std::vector alit_stack; + std::vector alit_stack_sizes; void GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map = 0); void GreedyReduceCache(std::vector &assumps, std::vector &core); void FilterCore(std::vector &core, std::vector &full_core); + void ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits); + + virtual void slvr_add(const expr &e); + + virtual void slvr_pop(int i); + + virtual void slvr_push(); + + virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); + + virtual lbool ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals = 0, + bool weak = false); + + virtual bool proof_core_contains(const expr &e); + + void GetTermTreeAssertionLiterals(TermTree *assumptions); }; diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index a95561f5d..04b4d075e 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -732,9 +732,6 @@ namespace Duality { e->dual = ReducedDualEdge(e); timer_stop("ReducedDualEdge"); timer_start("getting children"); - if(with_children) - for(unsigned i = 0; i < e->Children.size(); i++) - e->dual = e->dual && GetAnnotation(e->Children[i]); if(underapprox){ std::vector cus(e->Children.size()); for(unsigned i = 0; i < e->Children.size(); i++) @@ -753,9 +750,6 @@ namespace Duality { //Console.WriteLine("{0}", cnst); } return e->dual; - timer_start("solver add"); - slvr.add(e->dual); - timer_stop("solver add"); } /** For incremental solving, asserts the constraint associated @@ -781,8 +775,11 @@ namespace Duality { return; expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); timer_start("solver add"); - slvr.add(e->dual); + slvr_add(e->dual); timer_stop("solver add"); + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + ConstrainParent(e,e->Children[i]); } // caching verion of above @@ -791,8 +788,97 @@ namespace Duality { return; expr fmla = GetEdgeFormula(e, 0, with_children, false); GetAssumptionLits(fmla,lits); + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + ConstrainParentCache(e,e->Children[i],lits); } + void RPFP::slvr_add(const expr &e){ + slvr.add(e); + } + + void RPFP_caching::slvr_add(const expr &e){ + GetAssumptionLits(e,alit_stack); + } + + void RPFP::slvr_pop(int i){ + slvr.pop(i); + } + + void RPFP::slvr_push(){ + slvr.push(); + } + + void RPFP_caching::slvr_pop(int i){ + for(int j = 0; j < i; j++){ + alit_stack.resize(alit_stack_sizes.back()); + alit_stack_sizes.pop_back(); + } + } + + void RPFP_caching::slvr_push(){ + alit_stack_sizes.push_back(alit_stack.size()); + } + + check_result RPFP::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ + return slvr.check(n, assumptions, core_size, core); + } + + check_result RPFP_caching::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ + slvr_push(); + if(n && assumptions) + std::copy(assumptions,assumptions+n,std::inserter(alit_stack,alit_stack.end())); + check_result res; + if(core_size && core){ + std::vector full_core(alit_stack.size()), core1(n); + std::copy(assumptions,assumptions+n,core1.begin()); + res = slvr.check(alit_stack.size(), &alit_stack[0], core_size, &full_core[0]); + full_core.resize(*core_size); + if(res == unsat){ + FilterCore(core1,full_core); + *core_size = core1.size(); + std::copy(core1.begin(),core1.end(),core); + } + } + else + res = slvr.check(alit_stack.size(), &alit_stack[0]); + slvr_pop(1); + return res; + } + + lbool RPFP::ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals, + bool weak){ + return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); + } + + lbool RPFP_caching::ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals, + bool weak){ + GetTermTreeAssertionLiterals(assumptions); + return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); + } + + void RPFP_caching::GetTermTreeAssertionLiterals(TermTree *assumptions){ + std::vector alits; + hash_map map; + GetAssumptionLits(assumptions->getTerm(),alits,&map); + std::vector &ts = assumptions->getTerms(); + for(unsigned i = 0; i < ts.size(); i++) + GetAssumptionLits(ts[i],alits,&map); + assumptions->setTerm(ctx.bool_val(true)); + ts = alits; + for(unsigned i = 0; i < alits.size(); i++) + ts.push_back(ctx.make(Implies,alits[i],map[alits[i]])); + for(unsigned i = 0; i < assumptions->getChildren().size(); i++) + GetTermTreeAssertionLiterals(assumptions->getChildren()[i]); + return; + } + void RPFP_caching::GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map){ std::vector conjs; CollectConjuncts(fmla,conjs); @@ -817,6 +903,10 @@ namespace Duality { ConstrainEdgeLocalized(parent,GetAnnotation(child)); } + void RPFP_caching::ConstrainParentCache(Edge *parent, Node *child, std::vector &lits){ + ConstrainEdgeLocalizedCache(parent,GetAnnotation(child),lits); + } + /** For incremental solving, asserts the negation of the upper bound associated * with a node. @@ -828,7 +918,7 @@ namespace Duality { { n->dual = GetUpperBound(n); stack.back().nodes.push_back(n); - slvr.add(n->dual); + slvr_add(n->dual); } } @@ -892,9 +982,15 @@ namespace Duality { { e->constraints.push_back(tl); stack.back().constraints.push_back(std::pair(e,tl)); - slvr.add(tl); + slvr_add(tl); } + void RPFP_caching::ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits) + { + e->constraints.push_back(tl); + stack.back().constraints.push_back(std::pair(e,tl)); + GetAssumptionLits(tl,lits); + } /** Declare a constant in the background theory. */ @@ -971,7 +1067,7 @@ namespace Duality { // if (dualLabels != null) dualLabels.Dispose(); timer_start("interpolate_tree"); - lbool res = ls->interpolate_tree(tree, interpolant, dualModel,goals,true); + lbool res = ls_interpolate_tree(tree, interpolant, dualModel,goals,true); timer_stop("interpolate_tree"); if (res == l_false) { @@ -1017,7 +1113,7 @@ namespace Duality { ClearProofCore(); timer_start("interpolate_tree"); - lbool res = ls->interpolate_tree(tree, interpolant, dualModel,0,true); + lbool res = ls_interpolate_tree(tree, interpolant, dualModel,0,true); timer_stop("interpolate_tree"); if (res == l_false) { @@ -1068,22 +1164,22 @@ namespace Duality { // if (dualModel != null) dualModel.Dispose(); check_result res; if(!underapproxes.size()) - res = slvr.check(); + res = slvr_check(); else { std::vector us(underapproxes.size()); for(unsigned i = 0; i < underapproxes.size(); i++) us[i] = UnderapproxFlag(underapproxes[i]); - slvr.check(); // TODO: no idea why I need to do this + slvr_check(); // TODO: no idea why I need to do this if(underapprox_core){ std::vector unsat_core(us.size()); unsigned core_size = 0; - res = slvr.check(us.size(),&us[0],&core_size,&unsat_core[0]); + res = slvr_check(us.size(),&us[0],&core_size,&unsat_core[0]); underapprox_core->resize(core_size); for(unsigned i = 0; i < core_size; i++) (*underapprox_core)[i] = UnderapproxFlagRev(unsat_core[i]); } else { - res = slvr.check(us.size(),&us[0]); + res = slvr_check(us.size(),&us[0]); bool dump = false; if(dump){ std::vector cnsts; @@ -1093,7 +1189,7 @@ namespace Duality { ls->write_interpolation_problem("temp.smt",cnsts,std::vector()); } } - // check_result temp = slvr.check(); + // check_result temp = slvr_check(); } dualModel = slvr.get_model(); timer_stop("Check"); @@ -1101,10 +1197,12 @@ namespace Duality { } check_result RPFP::CheckUpdateModel(Node *root, std::vector assumps){ - // check_result temp1 = slvr.check(); // no idea why I need to do this + // check_result temp1 = slvr_check(); // no idea why I need to do this ClearProofCore(); - check_result res = slvr.check_keep_model(assumps.size(),&assumps[0]); - dualModel = slvr.get_model(); + check_result res = slvr_check(assumps.size(),&assumps[0]); + model mod = slvr.get_model(); + if(!mod.null()) + dualModel = mod;; return res; } @@ -1117,8 +1215,6 @@ namespace Duality { return dualModel.eval(tl); } - - /** Returns true if the given node is empty in the primal solution. For proecudure summaries, this means that the procedure is not called in the current counter-model. */ @@ -2609,14 +2705,14 @@ namespace Duality { void RPFP::Push() { stack.push_back(stack_entry()); - slvr.push(); + slvr_push(); } /** Pop a scope (see Push). Note, you cannot pop axioms. */ void RPFP::Pop(int num_scopes) { - slvr.pop(num_scopes); + slvr_pop(num_scopes); for (int i = 0; i < num_scopes; i++) { stack_entry &back = stack.back(); @@ -2634,15 +2730,15 @@ namespace Duality { all the popped constraints */ void RPFP::PopPush(){ - slvr.pop(1); - slvr.push(); + slvr_pop(1); + slvr_push(); stack_entry &back = stack.back(); for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) - slvr.add((*it)->dual); + slvr_add((*it)->dual); for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) - slvr.add((*it)->dual); + slvr_add((*it)->dual); for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) - slvr.add((*it).second); + slvr_add((*it).second); } @@ -3121,12 +3217,25 @@ namespace Duality { } } + bool RPFP::proof_core_contains(const expr &e){ + return proof_core->find(e) != proof_core->end(); + } + + bool RPFP_caching::proof_core_contains(const expr &e){ + std::vector foo; + GetAssumptionLits(e,foo); + for(unsigned i = 0; i < foo.size(); i++) + if(proof_core->find(foo[i]) != proof_core->end()) + return true; + return false; + } + bool RPFP::EdgeUsedInProof(Edge *edge){ ComputeProofCore(); - if(!edge->dual.null() && proof_core->find(edge->dual) != proof_core->end()) + if(!edge->dual.null() && proof_core_contains(edge->dual)) return true; for(unsigned i = 0; i < edge->constraints.size(); i++) - if(proof_core->find(edge->constraints[i]) != proof_core->end()) + if(proof_core_contains(edge->constraints[i])) return true; return false; } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 7318b020d..1faf838b0 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -37,7 +37,7 @@ Revision History: #define MINIMIZE_CANDIDATES // #define MINIMIZE_CANDIDATES_HARDER #define BOUNDED -#define CHECK_CANDS_FROM_IND_SET +// #define CHECK_CANDS_FROM_IND_SET #define UNDERAPPROX_NODES #define NEW_EXPAND #define EARLY_EXPAND @@ -45,6 +45,10 @@ Revision History: // #define EFFORT_BOUNDED_STRAT #define SKIP_UNDERAPPROX_NODES #define USE_RPFP_CLONE +#define KEEP_EXPANSIONS +#define USE_CACHING_RPFP +// #define PROPAGATE_BEFORE_CHECK +#define USE_NEW_GEN_CANDS namespace Duality { @@ -115,11 +119,19 @@ namespace Duality { Report = false; StratifiedInlining = false; RecursionBound = -1; + { + scoped_no_proof no_proofs_please(ctx.m()); #ifdef USE_RPFP_CLONE clone_ls = new RPFP::iZ3LogicSolver(ctx); clone_rpfp = new RPFP_caching(clone_ls); clone_rpfp->Clone(rpfp); #endif +#ifdef USE_NEW_GEN_CANDS + gen_cands_ls = new RPFP::iZ3LogicSolver(ctx); + gen_cands_rpfp = new RPFP_caching(gen_cands_ls); + gen_cands_rpfp->Clone(rpfp); +#endif + } } ~Duality(){ @@ -127,12 +139,21 @@ namespace Duality { delete clone_rpfp; delete clone_ls; #endif +#ifdef USE_NEW_GEN_CANDS + delete gen_cands_rpfp; + delete gen_cands_ls; +#endif } #ifdef USE_RPFP_CLONE RPFP::LogicSolver *clone_ls; RPFP_caching *clone_rpfp; #endif +#ifdef USE_NEW_GEN_CANDS + RPFP::LogicSolver *gen_cands_ls; + RPFP_caching *gen_cands_rpfp; +#endif + typedef RPFP::Node Node; typedef RPFP::Edge Edge; @@ -1102,7 +1123,8 @@ namespace Duality { void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ candidate.edge = edge; for(unsigned j = 0; j < edge->Children.size(); j++){ - Edge *lb = root->Outgoing->Children[j]->Outgoing; + Node *node = root->Outgoing->Children[j]; + Edge *lb = node->Outgoing; std::vector &insts = insts_of_node[edge->Children[j]]; #ifndef MINIMIZE_CANDIDATES for(int k = insts.size()-1; k >= 0; k--) @@ -1112,8 +1134,8 @@ namespace Duality { { Node *inst = insts[k]; if(indset->Contains(inst)){ - if(checker->Empty(lb->Parent) || - eq(checker->Eval(lb,NodeMarker(inst)),ctx.bool_val(true))){ + if(checker->Empty(node) || + eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst)),ctx.bool_val(true))){ candidate.Children.push_back(inst); goto next_child; } @@ -1183,6 +1205,25 @@ namespace Duality { #endif + Node *CheckerForEdgeClone(Edge *edge, RPFP_caching *checker){ + Edge *gen_cands_edge = gen_cands_rpfp->GetEdgeClone(edge); + Node *root = gen_cands_edge->Parent; + root->Outgoing = gen_cands_edge; + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); +#if 0 + if(root->Bound.IsFull()) + return = 0; +#endif + checker->AssertNode(root); + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = gen_cands_edge->Children[j]; + GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + } + checker->AssertEdge(gen_cands_edge,1,true); + return root; + } + /** If the current proposed solution is not inductive, use the induction failure to generate candidates for extension. */ void GenCandidatesFromInductionFailure(bool full_scan = false){ @@ -1192,6 +1233,7 @@ namespace Duality { Edge *edge = edges[i]; if(!full_scan && updated_nodes.find(edge->Parent) == updated_nodes.end()) continue; +#ifndef USE_RPFP_CLONE slvr.push(); RPFP *checker = new RPFP(rpfp->ls); Node *root = CheckerForEdge(edge,checker); @@ -1203,6 +1245,17 @@ namespace Duality { } slvr.pop(1); delete checker; +#else + clone_rpfp->Push(); + Node *root = CheckerForEdgeClone(edge,clone_rpfp); + if(clone_rpfp->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,clone_rpfp,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + clone_rpfp->Pop(1); +#endif } updated_nodes.clear(); timer_stop("GenCandIndFail"); @@ -1326,7 +1379,9 @@ namespace Duality { node. */ bool SatisfyUpperBound(Node *node){ if(node->Bound.IsFull()) return true; +#ifdef PROPAGATE_BEFORE_CHECK Propagate(); +#endif reporter->Bound(node); int start_decs = rpfp->CumulativeDecisions(); DerivationTree *dtp = new DerivationTreeSlow(this,unwinding,reporter,heuristic,FullExpand); @@ -1544,7 +1599,13 @@ namespace Duality { constrained = _constrained; false_approx = true; timer_start("Derive"); +#ifndef USE_CACHING_RPFP tree = _tree ? _tree : new RPFP(rpfp->ls); +#else + RPFP::LogicSolver *cache_ls = new RPFP::iZ3LogicSolver(ctx); + cache_ls->slvr->push(); + tree = _tree ? _tree : new RPFP_caching(cache_ls); +#endif tree->HornClauses = rpfp->HornClauses; tree->Push(); // so we can clear out the solver later when finished top = CreateApproximatedInstance(root); @@ -1556,19 +1617,27 @@ namespace Duality { timer_start("Pop"); tree->Pop(1); timer_stop("Pop"); +#ifdef USE_CACHING_RPFP + cache_ls->slvr->pop(1); + delete cache_ls; +#endif timer_stop("Derive"); return res; } #define WITH_CHILDREN - Node *CreateApproximatedInstance(RPFP::Node *from){ - Node *to = tree->CloneNode(from); - to->Annotation = from->Annotation; + void InitializeApproximatedInstance(RPFP::Node *to){ + to->Annotation = to->map->Annotation; #ifndef WITH_CHILDREN tree->CreateLowerBoundEdge(to); #endif leaves.push_back(to); + } + + Node *CreateApproximatedInstance(RPFP::Node *from){ + Node *to = tree->CloneNode(from); + InitializeApproximatedInstance(to); return to; } @@ -1637,13 +1706,23 @@ namespace Duality { virtual void ExpandNode(RPFP::Node *p){ // tree->RemoveEdge(p->Outgoing); - Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); - std::vector &cs = edge->Children; - std::vector children(cs.size()); - for(unsigned i = 0; i < cs.size(); i++) - children[i] = CreateApproximatedInstance(cs[i]); - Edge *ne = tree->CreateEdge(p, p->map->Outgoing->F, children); - ne->map = p->map->Outgoing->map; + Edge *ne = p->Outgoing; + if(ne) { + reporter->Message("Recycling edge..."); + std::vector &cs = ne->Children; + for(unsigned i = 0; i < cs.size(); i++) + InitializeApproximatedInstance(cs[i]); + // ne->dual = expr(); + } + else { + Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); + std::vector &cs = edge->Children; + std::vector children(cs.size()); + for(unsigned i = 0; i < cs.size(); i++) + children[i] = CreateApproximatedInstance(cs[i]); + ne = tree->CreateEdge(p, p->map->Outgoing->F, children); + ne->map = p->map->Outgoing->map; + } #ifndef WITH_CHILDREN tree->AssertEdge(ne); // assert the edge in the solver #else @@ -1785,12 +1864,25 @@ namespace Duality { void RemoveExpansion(RPFP::Node *p){ Edge *edge = p->Outgoing; Node *parent = edge->Parent; +#ifndef KEEP_EXPANSIONS std::vector cs = edge->Children; tree->DeleteEdge(edge); for(unsigned i = 0; i < cs.size(); i++) tree->DeleteNode(cs[i]); +#endif leaves.push_back(parent); } + + // remove all the descendants of tree root (but not root itself) + void RemoveTree(RPFP *tree, RPFP::Node *root){ + Edge *edge = root->Outgoing; + std::vector cs = edge->Children; + tree->DeleteEdge(edge); + for(unsigned i = 0; i < cs.size(); i++){ + RemoveTree(tree,cs[i]); + tree->DeleteNode(cs[i]); + } + } }; class DerivationTreeSlow : public DerivationTree { @@ -1852,6 +1944,7 @@ namespace Duality { } } tree->ComputeProofCore(); // need to compute the proof core before popping solver + bool propagated = false; while(1) { std::vector &expansions = stack.back().expansions; bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop @@ -1881,16 +1974,21 @@ namespace Duality { if(!Propagate(node)) break; if(!RecordUpdate(node)) break; // shouldn't happen! RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list + propagated = true; continue; } + if(propagated) break; // propagation invalidates the proof core, so disable non-chron backtrack RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list std::vector &unused_ex = stack.back().expansions; for(unsigned i = 0; i < unused_ex.size(); i++) heuristic->Update(unused_ex[i]->map); // make it less likely to expand this node in future } HandleUpdatedNodes(); - if(stack.size() == 1) + if(stack.size() == 1){ + if(top->Outgoing) + tree->DeleteEdge(top->Outgoing); // in case we kept the tree return false; + } was_sat = false; } else { diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 26bdf52d7..51f357653 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -44,6 +44,7 @@ namespace Duality { m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); m_solver->updt_params(p); // why do we have to do this? canceled = false; + m_mode = m().proof_mode(); } expr context::constant(const std::string &name, const sort &ty){ diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index fa4eca77b..76b28a426 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -50,6 +50,7 @@ Revision History: #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"scoped_timer.h" +#include"scoped_proof.h" namespace Duality { @@ -718,6 +719,7 @@ namespace Duality { m_model = s; return *this; } + bool null() const {return !m_model;} expr eval(expr const & n, bool model_completion=true) const { ::model * _m = m_model.get(); @@ -811,6 +813,7 @@ namespace Duality { ::solver *m_solver; model the_model; bool canceled; + proof_gen_mode m_mode; public: solver(context & c, bool extensional = false); solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} @@ -824,6 +827,7 @@ namespace Duality { m_ctx = s.m_ctx; m_solver = s.m_solver; the_model = s.the_model; + m_mode = s.m_mode; return *this; } struct cancel_exception {}; @@ -832,11 +836,12 @@ namespace Duality { throw(cancel_exception()); } // void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } - void push() { m_solver->push(); } - void pop(unsigned n = 1) { m_solver->pop(n); } + void push() { scoped_proof_mode spm(m(),m_mode); m_solver->push(); } + void pop(unsigned n = 1) { scoped_proof_mode spm(m(),m_mode); m_solver->pop(n); } // void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } - void add(expr const & e) { m_solver->assert_expr(e); } + void add(expr const & e) { scoped_proof_mode spm(m(),m_mode); m_solver->assert_expr(e); } check_result check() { + scoped_proof_mode spm(m(),m_mode); checkpoint(); lbool r = m_solver->check_sat(0,0); model_ref m; @@ -845,6 +850,7 @@ namespace Duality { return to_check_result(r); } check_result check_keep_model(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { + scoped_proof_mode spm(m(),m_mode); model old_model(the_model); check_result res = check(n,assumptions,core_size,core); if(the_model == 0) @@ -852,6 +858,7 @@ namespace Duality { return res; } check_result check(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { + scoped_proof_mode spm(m(),m_mode); checkpoint(); std::vector< ::expr *> _assumptions(n); for (unsigned i = 0; i < n; i++) { @@ -876,6 +883,7 @@ namespace Duality { } #if 0 check_result check(expr_vector assumptions) { + scoped_proof_mode spm(m(),m_mode); unsigned n = assumptions.size(); z3array _assumptions(n); for (unsigned i = 0; i < n; i++) { @@ -900,17 +908,19 @@ namespace Duality { int get_num_decisions(); void cancel(){ + scoped_proof_mode spm(m(),m_mode); canceled = true; if(m_solver) m_solver->cancel(); } - unsigned get_scope_level(){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_assertion_ids(); proof get_proof(){ + scoped_proof_mode spm(m(),m_mode); return proof(ctx(),m_solver->get_proof()); } diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 1409212d4..397d50655 100644 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -35,7 +35,6 @@ Revision History: #include "model_smt2_pp.h" #include "model_v2_pp.h" #include "fixedpoint_params.hpp" -#include "scoped_proof.h" // template class symbol_table; From 673ba137e55716c4e137ec38ea2bc03219da9b3c Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 23 Dec 2013 11:17:38 -0800 Subject: [PATCH 251/509] added qe_lite preprocessing pass to duality --- src/duality/duality_rpfp.cpp | 45 ++++++++++++++++++++++++++++++++- src/duality/duality_wrapper.cpp | 11 ++++++++ src/duality/duality_wrapper.h | 3 +++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 04b4d075e..036a0c86a 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2802,7 +2802,7 @@ namespace Duality { bool Z3User::is_variable(const Term &t){ if(!t.is_app()) - return false; + return t.is_var(); return t.decl().get_decl_kind() == Uninterpreted && t.num_args() == 0; } @@ -2951,6 +2951,8 @@ namespace Duality { arg.decl().get_decl_kind() == Uninterpreted); } +#define USE_QE_LITE + void RPFP::FromClauses(const std::vector &unskolemized_clauses){ hash_map pmap; func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); @@ -2958,6 +2960,7 @@ namespace Duality { std::vector clauses(unskolemized_clauses); // first, skolemize the clauses +#ifndef USE_QE_LITE for(unsigned i = 0; i < clauses.size(); i++){ expr &t = clauses[i]; if (t.is_quantifier() && t.is_quantifier_forall()) { @@ -2974,11 +2977,32 @@ namespace Duality { t = SubstBound(subst,t.body()); } } +#else + std::vector > substs(clauses.size()); +#endif // create the nodes from the heads of the clauses for(unsigned i = 0; i < clauses.size(); i++){ Term &clause = clauses[i]; + +#ifdef USE_QE_LITE + Term &t = clause; + if (t.is_quantifier() && t.is_quantifier_forall()) { + int bound = t.get_quantifier_num_bound(); + std::vector sorts; + std::vector names; + for(int j = 0; j < bound; j++){ + 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)); + substs[i][bound-1-j] = skolem; + } + clause = t.body(); + } + +#endif + if(clause.is_app() && clause.decl().get_decl_kind() == Uninterpreted) clause = implies(ctx.bool_val(true),clause); if(!canonical_clause(clause)) @@ -3010,6 +3034,13 @@ namespace Duality { seen.insert(arg); Indparams.push_back(arg); } +#ifdef USE_QE_LITE + { + hash_map > sb_memo; + for(unsigned j = 0; j < Indparams.size(); j++) + Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); + } +#endif Node *node = CreateNode(R(Indparams.size(),&Indparams[0])); //nodes.push_back(node); pmap[R] = node; @@ -3055,6 +3086,18 @@ namespace Duality { body = RemoveLabels(body,lbls); body = body.simplify(); +#ifdef USE_QE_LITE + std::set idxs; + for(unsigned j = 0; j < Indparams.size(); j++) + if(Indparams[j].is_var()) + idxs.insert(Indparams[j].get_index_value()); + body = body.qe_lite(idxs,false); + hash_map > sb_memo; + body = SubstBoundRec(sb_memo,substs[i],0,body); + for(unsigned j = 0; j < Indparams.size(); j++) + Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); +#endif + // Create the edge Transformer T = CreateTransformer(Relparams,Indparams,body); Edge *edge = CreateEdge(Parent,T,Children); diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 51f357653..dd1828214 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -339,6 +339,17 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st return ctx().cook(result); } + expr expr::qe_lite(const std::set &idxs, bool index_of_bound) const { + ::qe_lite qe(m()); + expr_ref result(to_expr(raw()),m()); + proof_ref pf(m()); + uint_set uis; + for(std::set::const_iterator it=idxs.begin(), en = idxs.end(); it != en; ++it) + uis.insert(*it); + qe(uis,index_of_bound,result); + return ctx().cook(result); + } + expr clone_quantifier(const expr &q, const expr &b){ return q.ctx().cook(q.m().update_quantifier(to_quantifier(q.raw()), to_expr(b.raw()))); } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 76b28a426..0b20cb08d 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -26,6 +26,7 @@ Revision History: #include #include #include +#include #include"version.h" #include @@ -561,6 +562,8 @@ namespace Duality { expr qe_lite() const; + expr qe_lite(const std::set &idxs, bool index_of_bound) const; + friend expr clone_quantifier(const expr &, const expr &); friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); From 1b9f1ea6b32713078e9eff0ea60836be4a295364 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 23 Dec 2013 11:47:24 -0800 Subject: [PATCH 252/509] remove assert on failed label compuation in duality --- src/duality/duality_rpfp.cpp | 2 +- src/duality/duality_solver.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 036a0c86a..ec3e9164f 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -1492,7 +1492,7 @@ namespace Duality { } } /* Unreachable! */ - throw "error in RPFP::GetLabelsRec"; + // throw "error in RPFP::GetLabelsRec"; goto done; } else if(k == Not) { diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 1faf838b0..f264d5569 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1620,6 +1620,7 @@ namespace Duality { #ifdef USE_CACHING_RPFP cache_ls->slvr->pop(1); delete cache_ls; + tree->ls = rpfp->ls; #endif timer_stop("Derive"); return res; From 11ba2178a9c99bec2518dc1349a55e4f1306463e Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 24 Dec 2013 17:20:12 -0800 Subject: [PATCH 253/509] speeding up interpolation in RPFP_caching --- src/duality/duality.h | 1 + src/duality/duality_rpfp.cpp | 74 +++++++++++++++++++++++++--------- src/duality/duality_solver.cpp | 5 +++ src/duality/duality_wrapper.h | 1 + 4 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index b88e5011f..b587c519d 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1139,6 +1139,7 @@ namespace Duality { void GetTermTreeAssertionLiterals(TermTree *assumptions); + void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); }; } diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index ec3e9164f..46902e9a5 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -863,7 +863,7 @@ namespace Duality { return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); } - void RPFP_caching::GetTermTreeAssertionLiterals(TermTree *assumptions){ + void RPFP_caching::GetTermTreeAssertionLiteralsRec(TermTree *assumptions){ std::vector alits; hash_map map; GetAssumptionLits(assumptions->getTerm(),alits,&map); @@ -879,6 +879,51 @@ namespace Duality { return; } + void RPFP_caching::GetTermTreeAssertionLiterals(TermTree *assumptions){ + // optimize binary case + if(assumptions->getChildren().size() == 1 + && assumptions->getChildren()[0]->getChildren().size() == 0){ + hash_map map; + TermTree *child = assumptions->getChildren()[0]; + std::vector dummy; + GetAssumptionLits(child->getTerm(),dummy,&map); + std::vector &ts = child->getTerms(); + for(unsigned i = 0; i < ts.size(); i++) + GetAssumptionLits(ts[i],dummy,&map); + std::vector assumps; + slvr.get_proof().get_assumptions(assumps); + if(!proof_core){ // save the proof core for later use + proof_core = new hash_set; + for(unsigned i = 0; i < assumps.size(); i++) + proof_core->insert(assumps[i]); + } + std::vector *cnsts[2] = {&child->getTerms(),&assumptions->getTerms()}; + for(unsigned i = 0; i < assumps.size(); i++){ + expr &ass = assumps[i]; + expr alit = (ass.is_app() && ass.decl().get_decl_kind() == Implies) ? ass.arg(0) : ass; + bool isA = map.find(alit) != map.end(); + cnsts[isA ? 0 : 1]->push_back(ass); + } + } + else + GetTermTreeAssertionLiteralsRec(assumptions); + } + + void RPFP::AddToProofCore(hash_set &core){ + std::vector assumps; + slvr.get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++) + core.insert(assumps[i]); + } + + void RPFP::ComputeProofCore(){ + if(!proof_core){ + proof_core = new hash_set; + AddToProofCore(*proof_core); + } + } + + void RPFP_caching::GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map){ std::vector conjs; CollectConjuncts(fmla,conjs); @@ -2616,13 +2661,19 @@ namespace Duality { for(unsigned i = 0; i < edge->Children.size(); i++){ expr ass = GetAnnotation(edge->Children[i]); std::vector clauses; - CollectConjuncts(ass.arg(1),clauses); - for(unsigned j = 0; j < clauses.size(); j++) - GetAssumptionLits(ass.arg(0) || clauses[j],assumps); + if(!ass.is_true()){ + CollectConjuncts(ass.arg(1),clauses); + for(unsigned j = 0; j < clauses.size(); j++) + GetAssumptionLits(ass.arg(0) || clauses[j],assumps); + } } expr fmla = GetAnnotation(node); - assumps.push_back(fmla.arg(0).arg(0)); std::vector lits; + if(fmla.is_true()){ + timer_stop("Generalize"); + return; + } + assumps.push_back(fmla.arg(0).arg(0)); CollectConjuncts(!fmla.arg(1),lits); #if 0 for(unsigned i = 0; i < lits.size(); i++){ @@ -3246,19 +3297,6 @@ namespace Duality { } - void RPFP::AddToProofCore(hash_set &core){ - std::vector assumps; - slvr.get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++) - core.insert(assumps[i]); - } - - void RPFP::ComputeProofCore(){ - if(!proof_core){ - proof_core = new hash_set; - AddToProofCore(*proof_core); - } - } bool RPFP::proof_core_contains(const expr &e){ return proof_core->find(e) != proof_core->end(); diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index f264d5569..33318b207 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1937,6 +1937,8 @@ namespace Duality { Generalize(node); if(RecordUpdate(node)) update_count++; + else + heuristic->Update(node->map); // make it less likely to expand this node in future } if(update_count == 0){ if(was_sat) @@ -2018,6 +2020,9 @@ namespace Duality { } bool NodeTooComplicated(Node *node){ + int ops = tree->CountOperators(node->Annotation.Formula); + if(ops > 10) return true; + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); return tree->CountOperators(node->Annotation.Formula) > 3; } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 0b20cb08d..0de3a7d49 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -451,6 +451,7 @@ namespace Duality { bool is_datatype() const { return get_sort().is_datatype(); } bool is_relation() const { return get_sort().is_relation(); } bool is_finite_domain() const { return get_sort().is_finite_domain(); } + bool is_true() const {return is_app() && decl().get_decl_kind() == True; } bool is_numeral() const { return is_app() && decl().get_decl_kind() == OtherArith && m().is_unique_value(to_expr(raw())); From 81f1f7690df331065f775aa15b9abea6441fa5be Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 31 Dec 2013 15:59:56 -0800 Subject: [PATCH 254/509] fix bug in rational.is_int32, it recognized rationals; fix bug reported by Anvesh for integer arithmetic Signed-off-by: Nikolaj Bjorner --- src/tactic/arith/lia2pb_tactic.cpp | 1 + src/util/rational.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tactic/arith/lia2pb_tactic.cpp b/src/tactic/arith/lia2pb_tactic.cpp index 6fee55e4b..9ee8f6728 100644 --- a/src/tactic/arith/lia2pb_tactic.cpp +++ b/src/tactic/arith/lia2pb_tactic.cpp @@ -71,6 +71,7 @@ class lia2pb_tactic : public tactic { if (m_bm.has_lower(n, l, s) && m_bm.has_upper(n, u, s) && l.is_zero() && + !u.is_neg() && u.get_num_bits() <= m_max_bits) { return true; diff --git a/src/util/rational.h b/src/util/rational.h index fc03228bf..c02a5a2c3 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -104,7 +104,7 @@ public: } bool is_int32() const { - if (is_small()) return true; + if (is_small() && is_int()) return true; // we don't assume that if it is small, then it is int32. if (!is_int64()) return false; int64 v = get_int64(); From 084a6f35eb2f1ad7db7b6c89bf580e36f4052a75 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Jan 2014 17:37:35 -0800 Subject: [PATCH 255/509] fix bug reported by Nuno Lopes: inlining is unsound for negated predicates Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_rule_inliner.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/muz/transforms/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp index 3e933b099..d9dad5c56 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -527,6 +527,9 @@ namespace datalog { bool mk_rule_inliner::do_eager_inlining(rule * r, rule_set const& rules, rule_ref& res) { + if (r->has_negation()) { + return false; + } SASSERT(rules.is_closed()); const rule_stratifier& strat = rules.get_stratifier(); From f380e31a6b6f15322ac6ff4f112e6f2db3822a0f Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 9 Jan 2014 17:16:10 -0800 Subject: [PATCH 256/509] runs the integer/cntrol svcomp examples from the Horn repo --- src/duality/duality.h | 130 ++++++- src/duality/duality_rpfp.cpp | 609 +++++++++++++++++++++++++++++++- src/duality/duality_solver.cpp | 74 +++- src/duality/duality_wrapper.cpp | 17 +- src/duality/duality_wrapper.h | 21 +- src/interp/iz3mgr.cpp | 19 +- src/interp/iz3mgr.h | 4 +- src/interp/iz3translate.cpp | 4 +- 8 files changed, 814 insertions(+), 64 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index b587c519d..8c469b9e4 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -36,12 +36,11 @@ namespace Duality { struct Z3User { context &ctx; - solver &slvr; typedef func_decl FuncDecl; typedef expr Term; - Z3User(context &_ctx, solver &_slvr) : ctx(_ctx), slvr(_slvr){} + Z3User(context &_ctx) : ctx(_ctx){} const char *string_of_int(int n); @@ -53,6 +52,8 @@ namespace Duality { Term SubstRec(hash_map &memo, const Term &t); + Term SubstRec(hash_map &memo, hash_map &map, const Term &t); + void Strengthen(Term &x, const Term &y); // return the func_del of an app if it is uninterpreted @@ -77,14 +78,14 @@ namespace Duality { void Summarize(const Term &t); - int CumulativeDecisions(); - int CountOperators(const Term &t); Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); Term RemoveRedundancy(const Term &t); + Term IneqToEq(const Term &t); + bool IsLiteral(const expr &lit, expr &atom, expr &val); expr Negate(const expr &f); @@ -98,6 +99,9 @@ namespace Duality { bool IsClosedFormula(const Term &t); Term AdjustQuantifiers(const Term &t); + + FuncDecl RenumberPred(const FuncDecl &f, int n); + protected: void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); @@ -108,6 +112,7 @@ protected: expr ReduceAndOr(const std::vector &args, bool is_and, std::vector &res); expr FinishAndOr(const std::vector &args, bool is_and); expr PullCommonFactors(std::vector &args, bool is_and); + Term IneqToEqRec(hash_map &memo, const Term &t); }; @@ -256,9 +261,9 @@ protected: } #endif - iZ3LogicSolver(context &c) : LogicSolver(c) { + iZ3LogicSolver(context &c, bool models = true) : LogicSolver(c) { ctx = ictx = &c; - slvr = islvr = new interpolating_solver(*ictx); + slvr = islvr = new interpolating_solver(*ictx, models); need_goals = false; islvr->SetWeakInterpolants(true); } @@ -308,7 +313,7 @@ protected: } LogicSolver *ls; - + protected: int nodeCount; int edgeCount; @@ -340,7 +345,7 @@ protected: inherit the axioms. */ - RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx), *(_ls->slvr)), dualModel(*(_ls->ctx)), aux_solver(_ls->aux_solver) + RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx)), dualModel(*(_ls->ctx)), aux_solver(_ls->aux_solver) { ls = _ls; nodeCount = 0; @@ -574,7 +579,7 @@ protected: * you must pop the context accordingly. The second argument is * the number of pushes we are inside. */ - void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); + virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); /* Constrain an edge by the annotation of one of its children. */ @@ -808,9 +813,31 @@ protected: /** Edges of the graph. */ std::vector edges; + /** Fuse a vector of transformers. If the total number of inputs of the transformers + is N, then the result is an N-ary transfomer whose output is the union of + the outputs of the given transformers. The is, suppose we have a vetor of transfoermers + {T_i(r_i1,...,r_iN(i) : i=1..M}. The the result is a transformer + + F(r_11,...,r_iN(1),...,r_M1,...,r_MN(M)) = + T_1(r_11,...,r_iN(1)) U ... U T_M(r_M1,...,r_MN(M)) + */ + + Transformer Fuse(const std::vector &trs); + + /** Fuse edges so that each node is the output of at most one edge. This + transformation is solution-preserving, but changes the numbering of edges in + counterexamples. + */ + void FuseEdges(); + + void RemoveDeadNodes(); + Term SubstParams(const std::vector &from, const std::vector &to, const Term &t); + Term SubstParamsNoCapture(const std::vector &from, + const std::vector &to, const Term &t); + Term Localize(Edge *e, const Term &t); void EvalNodeAsConstraint(Node *p, Transformer &res); @@ -829,6 +856,12 @@ protected: */ void ComputeProofCore(); + int CumulativeDecisions(); + + solver &slvr(){ + return *ls->slvr; + } + protected: void ClearProofCore(){ @@ -962,7 +995,37 @@ protected: expr NegateLit(const expr &f); expr GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox); + + bool IsVar(const expr &t); + + void GetVarsRec(hash_set &memo, const expr &cnst, std::vector &vars); + + expr UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params); + + void AddParamsToTransformer(Transformer &trans, const std::vector ¶ms); + + expr AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms); + + expr GetRelRec(hash_set &memo, const expr &t, const func_decl &rel); + + expr GetRel(Edge *edge, int child_idx); + + void GetDefs(const expr &cnst, hash_map &defs); + + void GetDefsRec(const expr &cnst, hash_map &defs); + + void AddParamsToNode(Node *node, const std::vector ¶ms); + + void UnhoistLoop(Edge *loop_edge, Edge *init_edge); + + void Unhoist(); + Term ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts); + + Term ElimIte(const Term &t); + + void MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node); + virtual void slvr_add(const expr &e); virtual void slvr_pop(int i); @@ -978,6 +1041,7 @@ protected: bool weak = false); virtual bool proof_core_contains(const expr &e); + }; @@ -1065,6 +1129,9 @@ namespace std { }; } +// #define LIMIT_STACK_WEIGHT 5 + + namespace Duality { /** Caching version of RPFP. Instead of asserting constraints, returns assumption literals */ @@ -1105,6 +1172,10 @@ namespace Duality { assumption lits. */ void ConstrainParentCache(Edge *parent, Node *child, std::vector &lits); +#ifdef LIMIT_STACK_WEIGHT + virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); +#endif + virtual ~RPFP_caching(){} protected: @@ -1113,7 +1184,31 @@ namespace Duality { hash_map EdgeCloneMap; std::vector alit_stack; std::vector alit_stack_sizes; + hash_map > edge_solvers; +#ifdef LIMIT_STACK_WEIGHT + struct weight_counter { + int val; + weight_counter(){val = 0;} + void swap(weight_counter &other){ + std::swap(val,other.val); + } + }; + + struct big_stack_entry { + weight_counter weight_added; + std::vector new_alits; + std::vector alit_stack; + std::vector alit_stack_sizes; + }; + + std::vector new_alits; + weight_counter weight_added; + std::vector big_stack; +#endif + + + void GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map = 0); void GreedyReduceCache(std::vector &assumps, std::vector &core); @@ -1140,6 +1235,23 @@ namespace Duality { void GetTermTreeAssertionLiterals(TermTree *assumptions); void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); + + LogicSolver *SolverForEdge(Edge *edge, bool models); + + public: + struct scoped_solver_for_edge { + LogicSolver *orig_ls; + RPFP_caching *rpfp; + scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false){ + rpfp = _rpfp; + orig_ls = rpfp->ls; + rpfp->ls = rpfp->SolverForEdge(edge,models); + } + ~scoped_solver_for_edge(){ + rpfp->ls = orig_ls; + } + }; + }; } diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 46902e9a5..ad89c1314 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -104,7 +104,7 @@ namespace Duality { lits.push_back(t); } - int Z3User::CumulativeDecisions(){ + int RPFP::CumulativeDecisions(){ #if 0 std::string stats = Z3_statistics_to_string(ctx); int pos = stats.find("decisions:"); @@ -113,7 +113,7 @@ namespace Duality { std::string val = stats.substr(pos,end-pos); return atoi(val.c_str()); #endif - return slvr.get_num_decisions(); + return slvr().get_num_decisions(); } @@ -370,6 +370,38 @@ namespace Duality { return res; } + Z3User::Term Z3User::SubstRec(hash_map &memo, hash_map &map, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(SubstRec(memo, map, t.arg(i))); + hash_map::iterator it = map.find(f); + if(it != map.end()) + f = it->second; + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + { + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = SubstRec(memo, map, pats[i]); + Term body = SubstRec(memo, map, t.body()); + res = clone_quantifier(t, body, pats); + } + // res = CloneQuantifier(t,SubstRec(memo, t.body())); + else res = t; + return res; + } + bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ if(!(lit.is_quantifier() && IsClosedFormula(lit))){ if(!lit.is_app()) @@ -594,6 +626,50 @@ namespace Duality { return t; } + Z3User::Term Z3User::IneqToEqRec(hash_map &memo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(IneqToEqRec(memo, t.arg(i))); + + decl_kind k = f.get_decl_kind(); + if(k == And){ + for(int i = 0; i < nargs-1; i++){ + if((args[i].is_app() && args[i].decl().get_decl_kind() == Geq && + args[i+1].is_app() && args[i+1].decl().get_decl_kind() == Leq) + || + (args[i].is_app() && args[i].decl().get_decl_kind() == Leq && + args[i+1].is_app() && args[i+1].decl().get_decl_kind() == Geq)) + if(eq(args[i].arg(0),args[i+1].arg(0)) && eq(args[i].arg(1),args[i+1].arg(1))){ + args[i] = ctx.make(Equal,args[i].arg(0),args[i].arg(1)); + args[i+1] = ctx.bool_val(true); + } + } + } + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + { + Term body = IneqToEqRec(memo,t.body()); + res = clone_quantifier(t, body); + } + else res = t; + return res; + } + + Z3User::Term Z3User::IneqToEq(const Term &t){ + hash_map memo; + return IneqToEqRec(memo,t); + } + Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) { std::pair foo(t,expr(ctx)); @@ -632,6 +708,51 @@ namespace Duality { return some_diff ? SubstRec(memo,t) : t; } + RPFP::Term RPFP::SubstParamsNoCapture(const std::vector &from, + const std::vector &to, const Term &t){ + hash_map memo; + bool some_diff = false; + for(unsigned i = 0; i < from.size(); i++) + if(i < to.size() && !eq(from[i],to[i])){ + memo[from[i]] = to[i]; + // if the new param is not being mapped to anything else, we need to rename it to prevent capture + // note, if the new param *is* mapped later in the list, it will override this substitution + const expr &w = to[i]; + if(memo.find(w) == memo.end()){ + std::string old_name = w.decl().name().str(); + func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); + expr y = fresh(); + memo[w] = y; + } + some_diff = true; + } + return some_diff ? SubstRec(memo,t) : t; + } + + + + RPFP::Transformer RPFP::Fuse(const std::vector &trs){ + assert(!trs.empty()); + const std::vector ¶ms = trs[0]->IndParams; + std::vector fmlas(trs.size()); + fmlas[0] = trs[0]->Formula; + for(unsigned i = 1; i < trs.size(); i++) + fmlas[i] = SubstParamsNoCapture(trs[i]->IndParams,params,trs[i]->Formula); + std::vector rel_params = trs[0]->RelParams; + for(unsigned i = 1; i < trs.size(); i++){ + const std::vector ¶ms2 = trs[i]->RelParams; + hash_map map; + for(unsigned j = 0; j < params2.size(); j++){ + func_decl rel = RenumberPred(params2[j],rel_params.size()); + rel_params.push_back(rel); + map[params2[j]] = rel; + } + hash_map memo; + fmlas[i] = SubstRec(memo,map,fmlas[i]); + } + return Transformer(rel_params,params,ctx.make(Or,fmlas),trs[0]->owner); + } + void Z3User::Strengthen(Term &x, const Term &y) { @@ -782,6 +903,25 @@ namespace Duality { ConstrainParent(e,e->Children[i]); } + +#ifdef LIMIT_STACK_WEIGHT + void RPFP_caching::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) + { + unsigned old_new_alits = new_alits.size(); + if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) + return; + expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); + timer_start("solver add"); + slvr_add(e->dual); + timer_stop("solver add"); + if(old_new_alits < new_alits.size()) + weight_added.val++; + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + ConstrainParent(e,e->Children[i]); + } +#endif + // caching verion of above void RPFP_caching::AssertEdgeCache(Edge *e, std::vector &lits, bool with_children){ if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) @@ -794,7 +934,7 @@ namespace Duality { } void RPFP::slvr_add(const expr &e){ - slvr.add(e); + slvr().add(e); } void RPFP_caching::slvr_add(const expr &e){ @@ -802,37 +942,70 @@ namespace Duality { } void RPFP::slvr_pop(int i){ - slvr.pop(i); + slvr().pop(i); } void RPFP::slvr_push(){ - slvr.push(); + slvr().push(); } void RPFP_caching::slvr_pop(int i){ for(int j = 0; j < i; j++){ +#ifdef LIMIT_STACK_WEIGHT + if(alit_stack_sizes.empty()){ + if(big_stack.empty()) + throw "stack underflow"; + for(unsigned k = 0; k < new_alits.size(); k++){ + if(AssumptionLits.find(new_alits[k]) == AssumptionLits.end()) + throw "foo!"; + AssumptionLits.erase(new_alits[k]); + } + big_stack_entry &bsb = big_stack.back(); + bsb.alit_stack_sizes.swap(alit_stack_sizes); + bsb.alit_stack.swap(alit_stack); + bsb.new_alits.swap(new_alits); + bsb.weight_added.swap(weight_added); + big_stack.pop_back(); + slvr().pop(1); + continue; + } +#endif alit_stack.resize(alit_stack_sizes.back()); alit_stack_sizes.pop_back(); } } void RPFP_caching::slvr_push(){ +#ifdef LIMIT_STACK_WEIGHT + if(weight_added.val > LIMIT_STACK_WEIGHT){ + big_stack.resize(big_stack.size()+1); + big_stack_entry &bsb = big_stack.back(); + bsb.alit_stack_sizes.swap(alit_stack_sizes); + bsb.alit_stack.swap(alit_stack); + bsb.new_alits.swap(new_alits); + bsb.weight_added.swap(weight_added); + slvr().push(); + for(unsigned i = 0; i < bsb.alit_stack.size(); i++) + slvr().add(bsb.alit_stack[i]); + return; + } +#endif alit_stack_sizes.push_back(alit_stack.size()); } check_result RPFP::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ - return slvr.check(n, assumptions, core_size, core); + return slvr().check(n, assumptions, core_size, core); } check_result RPFP_caching::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ - slvr_push(); + unsigned oldsiz = alit_stack.size(); if(n && assumptions) std::copy(assumptions,assumptions+n,std::inserter(alit_stack,alit_stack.end())); check_result res; if(core_size && core){ std::vector full_core(alit_stack.size()), core1(n); std::copy(assumptions,assumptions+n,core1.begin()); - res = slvr.check(alit_stack.size(), &alit_stack[0], core_size, &full_core[0]); + res = slvr().check(alit_stack.size(), &alit_stack[0], core_size, &full_core[0]); full_core.resize(*core_size); if(res == unsat){ FilterCore(core1,full_core); @@ -841,8 +1014,8 @@ namespace Duality { } } else - res = slvr.check(alit_stack.size(), &alit_stack[0]); - slvr_pop(1); + res = slvr().check(alit_stack.size(), &alit_stack[0]); + alit_stack.resize(oldsiz); return res; } @@ -891,7 +1064,7 @@ namespace Duality { for(unsigned i = 0; i < ts.size(); i++) GetAssumptionLits(ts[i],dummy,&map); std::vector assumps; - slvr.get_proof().get_assumptions(assumps); + slvr().get_proof().get_assumptions(assumps); if(!proof_core){ // save the proof core for later use proof_core = new hash_set; for(unsigned i = 0; i < assumps.size(); i++) @@ -911,7 +1084,7 @@ namespace Duality { void RPFP::AddToProofCore(hash_set &core){ std::vector assumps; - slvr.get_proof().get_assumptions(assumps); + slvr().get_proof().get_assumptions(assumps); for(unsigned i = 0; i < assumps.size(); i++) core.insert(assumps[i]); } @@ -935,7 +1108,10 @@ namespace Duality { if(bar.second){ func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); res = pred(); - slvr.add(ctx.make(Implies,res,conj)); +#ifdef LIMIT_STACK_WEIGHT + new_alits.push_back(conj); +#endif + slvr().add(ctx.make(Implies,res,conj)); // std::cout << res << ": " << conj << "\n"; } if(opt_map) @@ -979,15 +1155,18 @@ namespace Duality { /** Clone another RPFP into this one, keeping a map */ void RPFP_caching::Clone(RPFP *other){ +#if 0 for(unsigned i = 0; i < other->nodes.size(); i++) NodeCloneMap[other->nodes[i]] = CloneNode(other->nodes[i]); +#endif for(unsigned i = 0; i < other->edges.size(); i++){ Edge *edge = other->edges[i]; + Node *parent = CloneNode(edge->Parent); std::vector cs; for(unsigned j = 0; j < edge->Children.size(); j++) // cs.push_back(NodeCloneMap[edge->Children[j]]); cs.push_back(CloneNode(edge->Children[j])); - EdgeCloneMap[edge] = CreateEdge(NodeCloneMap[edge->Parent],edge->F,cs); + EdgeCloneMap[edge] = CreateEdge(parent,edge->F,cs); } } @@ -1005,7 +1184,7 @@ namespace Duality { check_result RPFP_caching::CheckCore(const std::vector &assumps, std::vector &core){ core.resize(assumps.size()); unsigned core_size; - check_result res = slvr.check(assumps.size(),(expr *)&assumps[0],&core_size,&core[0]); + check_result res = slvr().check(assumps.size(),(expr *)&assumps[0],&core_size,&core[0]); if(res == unsat) core.resize(core_size); else @@ -1063,7 +1242,7 @@ namespace Duality { void RPFP::RemoveAxiom(const Term &t) { - slvr.RemoveInterpolationAxiom(t); + slvr().RemoveInterpolationAxiom(t); } #endif @@ -1236,7 +1415,7 @@ namespace Duality { } // check_result temp = slvr_check(); } - dualModel = slvr.get_model(); + dualModel = slvr().get_model(); timer_stop("Check"); return res; } @@ -1245,7 +1424,7 @@ namespace Duality { // check_result temp1 = slvr_check(); // no idea why I need to do this ClearProofCore(); check_result res = slvr_check(assumps.size(),&assumps[0]); - model mod = slvr.get_model(); + model mod = slvr().get_model(); if(!mod.null()) dualModel = mod;; return res; @@ -1706,6 +1885,57 @@ namespace Duality { return res; } + RPFP::Term RPFP::ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts){ + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(bar.second){ + if(t.is_app()){ + int nargs = t.num_args(); + std::vector args; + if(t.decl().get_decl_kind() == Equal){ + expr lhs = t.arg(0); + expr rhs = t.arg(1); + if(rhs.decl().get_decl_kind() == Ite){ + expr rhs_args[3]; + lhs = ElimIteRec(memo,lhs,cnsts); + for(int i = 0; i < 3; i++) + rhs_args[i] = ElimIteRec(memo,rhs.arg(i),cnsts); + res = (rhs_args[0] && (lhs == rhs_args[1])) || ((!rhs_args[0]) && (lhs == rhs_args[2])); + goto done; + } + } + if(t.decl().get_decl_kind() == Ite){ + func_decl sym = ctx.fresh_func_decl("@ite", t.get_sort()); + res = sym(); + cnsts.push_back(ElimIteRec(memo,ctx.make(Equal,res,t),cnsts)); + } + else { + for(int i = 0; i < nargs; i++) + args.push_back(ElimIteRec(memo,t.arg(i),cnsts)); + res = t.decl()(args.size(),&args[0]); + } + } + else if(t.is_quantifier()) + res = clone_quantifier(t,ElimIteRec(memo,t.body(),cnsts)); + else + res = t; + } + done: + return res; + } + + RPFP::Term RPFP::ElimIte(const Term &t){ + hash_map memo; + std::vector cnsts; + expr res = ElimIteRec(memo,t,cnsts); + if(!cnsts.empty()){ + cnsts.push_back(res); + res = ctx.make(And,cnsts); + } + return res; + } + void RPFP::Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares){ hash_set done[2]; ImplicantRed(memo,f,lits,done,true, dont_cares); @@ -2622,7 +2852,7 @@ namespace Duality { else { std::cout << "bad in InterpolateByCase -- core:\n"; std::vector assumps; - slvr.get_proof().get_assumptions(assumps); + slvr().get_proof().get_assumptions(assumps); for(unsigned i = 0; i < assumps.size(); i++) assumps[i].show(); throw "ack!"; @@ -2651,10 +2881,20 @@ namespace Duality { timer_stop("Generalize"); } + RPFP::LogicSolver *RPFP_caching::SolverForEdge(Edge *edge, bool models){ + uptr &p = edge_solvers[edge]; + if(!p.get()){ + scoped_no_proof no_proofs_please(ctx.m()); // no proofs + p.set(new iZ3LogicSolver(ctx,models)); // no models + } + return p.get(); + } + // caching version of above void RPFP_caching::GeneralizeCache(Edge *edge){ timer_start("Generalize"); + scoped_solver_for_edge ssfe(this,edge); Node *node = edge->Parent; std::vector assumps, core, conjuncts; AssertEdgeCache(edge,assumps); @@ -2698,6 +2938,7 @@ namespace Duality { // caching version of above bool RPFP_caching::PropagateCache(Edge *edge){ timer_start("PropagateCache"); + scoped_solver_for_edge ssfe(this,edge); bool some = false; { std::vector candidates, skip; @@ -2723,6 +2964,8 @@ namespace Duality { AssertEdgeCache(edge,assumps); for(unsigned i = 0; i < edge->Children.size(); i++){ expr ass = GetAnnotation(edge->Children[i]); + if(eq(ass,ctx.bool_val(true))) + continue; std::vector clauses; CollectConjuncts(ass.arg(1),clauses); for(unsigned j = 0; j < clauses.size(); j++) @@ -2807,6 +3050,17 @@ namespace Duality { return ctx.function(name.c_str(), nargs, &sorts[0], t.get_sort()); } + Z3User::FuncDecl Z3User::RenumberPred(const FuncDecl &f, int n) + { + std::string name = f.name().str(); + name = name.substr(0,name.rfind('_')) + "_" + string_of_int(n); + int arity = f.arity(); + std::vector domain; + for(int i = 0; i < arity; i++) + domain.push_back(f.domain(i)); + return ctx.function(name.c_str(), arity, &domain[0], f.range()); + } + // Scan the clause body for occurrences of the predicate unknowns RPFP::Term RPFP::ScanBody(hash_map &memo, @@ -3135,6 +3389,7 @@ namespace Duality { Term labeled = body; std::vector lbls; // TODO: throw this away for now body = RemoveLabels(body,lbls); + // body = IneqToEq(body); // UFO converts x=y to (x<=y & x >= y). Undo this. body = body.simplify(); #ifdef USE_QE_LITE @@ -3155,9 +3410,325 @@ namespace Duality { edge->labeled = labeled;; // remember for label extraction // edges.push_back(edge); } + + // undo hoisting of expressions out of loops + RemoveDeadNodes(); + Unhoist(); + // FuseEdges(); } + // The following mess is used to undo hoisting of expressions outside loops by compilers + + expr RPFP::UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params){ + if(memo.find(w) != memo.end()) + return memo[w]; + expr res; + if(init_defs.find(w) != init_defs.end()){ + expr d = init_defs[w]; + std::vector vars; + hash_set get_vars_memo; + GetVarsRec(get_vars_memo,d,vars); + hash_map map; + for(unsigned j = 0; j < vars.size(); j++){ + expr x = vars[j]; + map[x] = UnhoistPullRec(memo,x,init_defs,const_params,const_params_inv,new_params); + } + expr defn = SubstRec(map,d); + res = defn; + } + else if(const_params_inv.find(w) == const_params_inv.end()){ + std::string old_name = w.decl().name().str(); + func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); + expr y = fresh(); + const_params[y] = w; + const_params_inv[w] = y; + new_params.push_back(y); + res = y; + } + else + res = const_params_inv[w]; + memo[w] = res; + return res; + } + + void RPFP::AddParamsToTransformer(Transformer &trans, const std::vector ¶ms){ + std::copy(params.begin(),params.end(),std::inserter(trans.IndParams,trans.IndParams.end())); + } + + expr RPFP::AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms){ + int n = app.num_args(); + std::vector args(n); + for (int i = 0; i < n; i++) + args[i] = app.arg(i); + std::copy(params.begin(),params.end(),std::inserter(args,args.end())); + return new_decl(args); + } + + expr RPFP::GetRelRec(hash_set &memo, const expr &t, const func_decl &rel){ + if(memo.find(t) != memo.end()) + return expr(); + memo.insert(t); + if (t.is_app()) + { + func_decl f = t.decl(); + if(f == rel) + return t; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++){ + expr res = GetRelRec(memo,t.arg(i),rel); + if(!res.null()) + return res; + } + } + else if (t.is_quantifier()) + return GetRelRec(memo,t.body(),rel); + return expr(); + } + + expr RPFP::GetRel(Edge *edge, int child_idx){ + func_decl &rel = edge->F.RelParams[child_idx]; + hash_set memo; + return GetRelRec(memo,edge->F.Formula,rel); + } + + void RPFP::GetDefsRec(const expr &cnst, hash_map &defs){ + if(cnst.is_app()){ + switch(cnst.decl().get_decl_kind()){ + case And: { + int n = cnst.num_args(); + for(int i = 0; i < n; i++) + GetDefsRec(cnst.arg(i),defs); + break; + } + case Equal: { + expr lhs = cnst.arg(0); + expr rhs = cnst.arg(1); + if(IsVar(lhs)) + defs[lhs] = rhs; + break; + } + default: + break; + } + } + } + + void RPFP::GetDefs(const expr &cnst, hash_map &defs){ + // GetDefsRec(IneqToEq(cnst),defs); + GetDefsRec(cnst,defs); + } + + bool RPFP::IsVar(const expr &t){ + return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; + } + + void RPFP::GetVarsRec(hash_set &memo, const expr &t, std::vector &vars){ + if(memo.find(t) != memo.end()) + return; + memo.insert(t); + if (t.is_app()) + { + if(IsVar(t)){ + vars.push_back(t); + return; + } + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++){ + GetVarsRec(memo,t.arg(i),vars); + } + } + else if (t.is_quantifier()) + GetVarsRec(memo,t.body(),vars); + } + + void RPFP::AddParamsToNode(Node *node, const std::vector ¶ms){ + int arity = node->Annotation.IndParams.size(); + std::vector domain; + for(int i = 0; i < arity; i++) + domain.push_back(node->Annotation.IndParams[i].get_sort()); + for(unsigned i = 0; i < params.size(); i++) + domain.push_back(params[i].get_sort()); + std::string old_name = node->Name.name().str(); + func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), domain, ctx.bool_sort()); + node->Name = fresh; + AddParamsToTransformer(node->Annotation,params); + AddParamsToTransformer(node->Bound,params); + AddParamsToTransformer(node->Underapprox,params); + } + + void RPFP::UnhoistLoop(Edge *loop_edge, Edge *init_edge){ + loop_edge->F.Formula = IneqToEq(loop_edge->F.Formula); + init_edge->F.Formula = IneqToEq(init_edge->F.Formula); + expr pre = GetRel(loop_edge,0); + if(pre.null()) + return; // this means the loop got simplified away + int nparams = loop_edge->F.IndParams.size(); + hash_map const_params, const_params_inv; + std::vector work_list; + // find the parameters that are constant in the loop + for(int i = 0; i < nparams; i++){ + if(eq(pre.arg(i),loop_edge->F.IndParams[i])){ + const_params[pre.arg(i)] = init_edge->F.IndParams[i]; + const_params_inv[init_edge->F.IndParams[i]] = pre.arg(i); + work_list.push_back(pre.arg(i)); + } + } + // get the definitions in the initialization + hash_map defs,memo,subst; + GetDefs(init_edge->F.Formula,defs); + // try to pull them inside the loop + std::vector new_params; + for(unsigned i = 0; i < work_list.size(); i++){ + expr v = work_list[i]; + expr w = const_params[v]; + expr def = UnhoistPullRec(memo,w,defs,const_params,const_params_inv,new_params); + if(!eq(def,v)) + subst[v] = def; + } + // do the substitutions + if(subst.empty()) + return; + subst[pre] = pre; // don't substitute inside the precondition itself + loop_edge->F.Formula = SubstRec(subst,loop_edge->F.Formula); + loop_edge->F.Formula = ElimIte(loop_edge->F.Formula); + init_edge->F.Formula = ElimIte(init_edge->F.Formula); + // add the new parameters + if(new_params.empty()) + return; + Node *parent = loop_edge->Parent; + AddParamsToNode(parent,new_params); + AddParamsToTransformer(loop_edge->F,new_params); + AddParamsToTransformer(init_edge->F,new_params); + std::vector &incoming = parent->Incoming; + for(unsigned i = 0; i < incoming.size(); i++){ + Edge *in_edge = incoming[i]; + std::vector &chs = in_edge->Children; + for(unsigned j = 0; j < chs.size(); j++) + if(chs[j] == parent){ + expr lit = GetRel(in_edge,j); + expr new_lit = AddParamsToApp(lit,parent->Name,new_params); + func_decl fd = SuffixFuncDecl(new_lit,j); + int nargs = new_lit.num_args(); + std::vector args; + for(int k = 0; k < nargs; k++) + args.push_back(new_lit.arg(k)); + new_lit = fd(nargs,&args[0]); + in_edge->F.RelParams[j] = fd; + hash_map map; + map[lit] = new_lit; + in_edge->F.Formula = SubstRec(map,in_edge->F.Formula); + } + } + } + + void RPFP::Unhoist(){ + hash_map > outgoing; + for(unsigned i = 0; i < edges.size(); i++) + outgoing[edges[i]->Parent].push_back(edges[i]); + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + std::vector &outs = outgoing[node]; + // if we're not a simple loop with one entry, give up + if(outs.size() == 2){ + for(int j = 0; j < 2; j++){ + Edge *loop_edge = outs[j]; + Edge *init_edge = outs[1-j]; + if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent){ + UnhoistLoop(loop_edge,init_edge); + break; + } + } + } + } + } + + void RPFP::FuseEdges(){ + hash_map > outgoing; + for(unsigned i = 0; i < edges.size(); i++){ + outgoing[edges[i]->Parent].push_back(edges[i]); + } + hash_set edges_to_delete; + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + std::vector &outs = outgoing[node]; + if(outs.size() > 1 && outs.size() <= 16){ + std::vector trs(outs.size()); + for(unsigned j = 0; j < outs.size(); j++) + trs[j] = &outs[j]->F; + Transformer tr = Fuse(trs); + std::vector chs; + for(unsigned j = 0; j < outs.size(); j++) + for(unsigned k = 0; k < outs[j]->Children.size(); k++) + chs.push_back(outs[j]->Children[k]); + Edge *fedge = CreateEdge(node,tr,chs); + for(unsigned j = 0; j < outs.size(); j++) + edges_to_delete.insert(outs[j]); + } + } + std::vector new_edges; + hash_set all_nodes; + for(unsigned j = 0; j < edges.size(); j++){ + if(edges_to_delete.find(edges[j]) == edges_to_delete.end()){ +#if 0 + if(all_nodes.find(edges[j]->Parent) != all_nodes.end()) + throw "help!"; + all_nodes.insert(edges[j]->Parent); +#endif + new_edges.push_back(edges[j]); + } + else + delete edges[j]; + } + edges.swap(new_edges); + } + + void RPFP::MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node){ + if(live_nodes.find(node) != live_nodes.end()) + return; + live_nodes.insert(node); + std::vector &outs = outgoing[node]; + for(unsigned i = 0; i < outs.size(); i++) + for(unsigned j = 0; j < outs[i]->Children.size(); j++) + MarkLiveNodes(outgoing, live_nodes,outs[i]->Children[j]); + } + + void RPFP::RemoveDeadNodes(){ + hash_map > outgoing; + for(unsigned i = 0; i < edges.size(); i++) + outgoing[edges[i]->Parent].push_back(edges[i]); + hash_set live_nodes; + for(unsigned i = 0; i < nodes.size(); i++) + if(!nodes[i]->Bound.IsFull()) + MarkLiveNodes(outgoing,live_nodes,nodes[i]); + std::vector new_edges; + for(unsigned j = 0; j < edges.size(); j++){ + if(live_nodes.find(edges[j]->Parent) != live_nodes.end()) + new_edges.push_back(edges[j]); + else { + Edge *edge = edges[j]; + for(unsigned int i = 0; i < edge->Children.size(); i++){ + std::vector &ic = edge->Children[i]->Incoming; + for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ + if(*it == edge){ + ic.erase(it); + break; + } + } + } + delete edge; + } + } + edges.swap(new_edges); + std::vector new_nodes; + for(unsigned j = 0; j < nodes.size(); j++){ + if(live_nodes.find(nodes[j]) != live_nodes.end()) + new_nodes.push_back(nodes[j]); + else + delete nodes[j]; + } + nodes.swap(new_nodes); + } void RPFP::WriteSolution(std::ostream &s){ for(unsigned i = 0; i < nodes.size(); i++){ diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 33318b207..91532960b 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -45,8 +45,8 @@ Revision History: // #define EFFORT_BOUNDED_STRAT #define SKIP_UNDERAPPROX_NODES #define USE_RPFP_CLONE -#define KEEP_EXPANSIONS -#define USE_CACHING_RPFP +// #define KEEP_EXPANSIONS +// #define USE_CACHING_RPFP // #define PROPAGATE_BEFORE_CHECK #define USE_NEW_GEN_CANDS @@ -105,7 +105,7 @@ namespace Duality { public: Duality(RPFP *_rpfp) : ctx(_rpfp->ctx), - slvr(_rpfp->slvr), + slvr(_rpfp->slvr()), nodes(_rpfp->nodes), edges(_rpfp->edges) { @@ -122,7 +122,7 @@ namespace Duality { { scoped_no_proof no_proofs_please(ctx.m()); #ifdef USE_RPFP_CLONE - clone_ls = new RPFP::iZ3LogicSolver(ctx); + clone_ls = new RPFP::iZ3LogicSolver(ctx, false); // no models needed for this one clone_rpfp = new RPFP_caching(clone_ls); clone_rpfp->Clone(rpfp); #endif @@ -842,8 +842,10 @@ namespace Duality { Node *child = chs[i]; if(TopoSort[child] < TopoSort[node->map]){ Node *leaf = LeafMap[child]; - if(!indset->Contains(leaf)) + if(!indset->Contains(leaf)){ + node->Outgoing->F.Formula = ctx.bool_val(false); // make this a proper leaf, else bogus cex return node->Outgoing; + } } } @@ -1206,7 +1208,7 @@ namespace Duality { Node *CheckerForEdgeClone(Edge *edge, RPFP_caching *checker){ - Edge *gen_cands_edge = gen_cands_rpfp->GetEdgeClone(edge); + Edge *gen_cands_edge = checker->GetEdgeClone(edge); Node *root = gen_cands_edge->Parent; root->Outgoing = gen_cands_edge; GenNodeSolutionFromIndSet(edge->Parent, root->Bound); @@ -1233,7 +1235,7 @@ namespace Duality { Edge *edge = edges[i]; if(!full_scan && updated_nodes.find(edge->Parent) == updated_nodes.end()) continue; -#ifndef USE_RPFP_CLONE +#ifndef USE_NEW_GEN_CANDS slvr.push(); RPFP *checker = new RPFP(rpfp->ls); Node *root = CheckerForEdge(edge,checker); @@ -1246,15 +1248,16 @@ namespace Duality { slvr.pop(1); delete checker; #else - clone_rpfp->Push(); - Node *root = CheckerForEdgeClone(edge,clone_rpfp); - if(clone_rpfp->Check(root) != unsat){ + RPFP_caching::scoped_solver_for_edge(gen_cands_rpfp,edge,true /* models */); + gen_cands_rpfp->Push(); + Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); + if(gen_cands_rpfp->Check(root) != unsat){ Candidate candidate; - ExtractCandidateFromCex(edge,clone_rpfp,root,candidate); + ExtractCandidateFromCex(edge,gen_cands_rpfp,root,candidate); reporter->InductionFailure(edge,candidate.Children); candidates.push_back(candidate); } - clone_rpfp->Pop(1); + gen_cands_rpfp->Pop(1); #endif } updated_nodes.clear(); @@ -1555,7 +1558,7 @@ namespace Duality { public: DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) - : slvr(rpfp->slvr), + : slvr(rpfp->slvr()), ctx(rpfp->ctx) { duality = _duality; @@ -1709,7 +1712,7 @@ namespace Duality { // tree->RemoveEdge(p->Outgoing); Edge *ne = p->Outgoing; if(ne) { - reporter->Message("Recycling edge..."); + // reporter->Message("Recycling edge..."); std::vector &cs = ne->Children; for(unsigned i = 0; i < cs.size(); i++) InitializeApproximatedInstance(cs[i]); @@ -1905,14 +1908,14 @@ namespace Duality { virtual bool Build(){ - stack.back().level = tree->slvr.get_scope_level(); + stack.back().level = tree->slvr().get_scope_level(); bool was_sat = true; while (true) { lbool res; - unsigned slvr_level = tree->slvr.get_scope_level(); + unsigned slvr_level = tree->slvr().get_scope_level(); if(slvr_level != stack.back().level) throw "stacks out of sync!"; @@ -2002,11 +2005,11 @@ namespace Duality { tree->FixCurrentState(expansions[i]->Outgoing); } #if 0 - if(tree->slvr.check() == unsat) + if(tree->slvr().check() == unsat) throw "help!"; #endif stack.push_back(stack_entry()); - stack.back().level = tree->slvr.get_scope_level(); + stack.back().level = tree->slvr().get_scope_level(); if(ExpandSomeNodes(false,1)){ continue; } @@ -2135,6 +2138,7 @@ namespace Duality { tree->Generalize(top,node); #else RPFP_caching *clone_rpfp = duality->clone_rpfp; + if(!node->Outgoing->map) return; Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); Node *clone_node = clone_edge->Parent; clone_node->Annotation = node->Annotation; @@ -2142,10 +2146,11 @@ namespace Duality { clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; clone_rpfp->GeneralizeCache(clone_edge); node->Annotation = clone_node->Annotation; - } #endif + } bool Propagate(Node *node){ +#ifdef USE_RPFP_CLONE RPFP_caching *clone_rpfp = duality->clone_rpfp; Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); Node *clone_node = clone_edge->Parent; @@ -2156,6 +2161,9 @@ namespace Duality { if(res) node->Annotation = clone_node->Annotation; return res; +#else + return false; +#endif } }; @@ -2179,6 +2187,11 @@ namespace Duality { Duality *parent; bool some_updates; +#define NO_CONJ_ON_SIMPLE_LOOPS +#ifdef NO_CONJ_ON_SIMPLE_LOOPS + hash_set simple_loops; +#endif + Node *&covered_by(Node *node){ return cm[node].covered_by; } @@ -2213,6 +2226,24 @@ namespace Duality { Covering(Duality *_parent){ parent = _parent; some_updates = false; + +#ifdef NO_CONJ_ON_SIMPLE_LOOPS + hash_map > outgoing; + for(unsigned i = 0; i < parent->rpfp->edges.size(); i++) + outgoing[parent->rpfp->edges[i]->Parent].push_back(parent->rpfp->edges[i]); + for(unsigned i = 0; i < parent->rpfp->nodes.size(); i++){ + Node * node = parent->rpfp->nodes[i]; + std::vector &outs = outgoing[node]; + if(outs.size() == 2){ + for(int j = 0; j < 2; j++){ + Edge *loop_edge = outs[j]; + if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent) + simple_loops.insert(node); + } + } + } +#endif + } bool IsCoveredRec(hash_set &memo, Node *node){ @@ -2375,6 +2406,11 @@ namespace Duality { } bool CouldCover(Node *covered, Node *covering){ +#ifdef NO_CONJ_ON_SIMPLE_LOOPS + // Forsimple loops, we rely on propagation, not covering + if(simple_loops.find(covered->map) != simple_loops.end()) + return false; +#endif #ifdef UNDERAPPROX_NODES // if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) // return parent->underapprox_map[covering] == covered; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index dd1828214..24f6a5e20 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -30,10 +30,11 @@ Revision History: namespace Duality { - solver::solver(Duality::context& c, bool extensional) : object(c), the_model(c) { + solver::solver(Duality::context& c, bool extensional, bool models) : object(c), the_model(c) { params_ref p; p.set_bool("proof", true); // this is currently useless - p.set_bool("model", true); + if(models) + p.set_bool("model", true); p.set_bool("unsat_core", true); p.set_bool("mbqi",true); p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants @@ -374,6 +375,18 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st } + unsigned func_decl::arity() const { + return (to_func_decl(raw())->get_arity()); + } + + sort func_decl::domain(unsigned i) const { + return sort(ctx(),(to_func_decl(raw())->get_domain(i))); + } + + sort func_decl::range() const { + return sort(ctx(),(to_func_decl(raw())->get_range())); + } + func_decl context::fresh_func_decl(char const * prefix, const std::vector &domain, sort const & range){ std::vector < ::sort * > _domain(domain.size()); for(unsigned i = 0; i < domain.size(); i++) diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 0de3a7d49..94e8d2642 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -819,7 +819,7 @@ namespace Duality { bool canceled; proof_gen_mode m_mode; public: - solver(context & c, bool extensional = false); + 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(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver; canceled = false;} ~solver() { @@ -1308,8 +1308,8 @@ namespace Duality { class interpolating_solver : public solver { public: - interpolating_solver(context &ctx) - : solver(ctx) + interpolating_solver(context &ctx, bool models = true) + : solver(ctx, true, models) { weak_mode = false; } @@ -1373,6 +1373,21 @@ namespace Duality { typedef double clock_t; clock_t current_time(); inline void output_time(std::ostream &os, clock_t time){os << time;} + + template class uptr { + public: + X *ptr; + uptr(){ptr = 0;} + void set(X *_ptr){ + if(ptr) delete ptr; + ptr = _ptr; + } + X *get(){ return ptr;} + ~uptr(){ + if(ptr) delete ptr; + } + }; + }; // to make Duality::ast hashable diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index f35dae93f..994d10e06 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -640,9 +640,9 @@ void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector &coeffs, const std::vector &ineqs){ +iz3mgr::ast iz3mgr::sum_inequalities(const std::vector &coeffs, const std::vector &ineqs, bool round_off){ ast zero = make_int("0"); ast thing = make(Leq,zero,zero); for(unsigned i = 0; i < ineqs.size(); i++){ - linear_comb(thing,coeffs[i],ineqs[i]); + linear_comb(thing,coeffs[i],ineqs[i], round_off); } thing = simplify_ineq(thing); return thing; diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 645c72ccb..c3bde91c5 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -606,9 +606,9 @@ class iz3mgr { return d; } - void linear_comb(ast &P, const ast &c, const ast &Q); + void linear_comb(ast &P, const ast &c, const ast &Q, bool round_off = false); - ast sum_inequalities(const std::vector &coeffs, const std::vector &ineqs); + ast sum_inequalities(const std::vector &coeffs, const std::vector &ineqs, bool round_off = false); ast simplify_ineq(const ast &ineq){ ast res = make(op(ineq),arg(ineq,0),z3_simplify(arg(ineq,1))); diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 2fbc173a9..abddb4bda 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1079,7 +1079,7 @@ public: my_cons.push_back(mk_not(arg(con,i))); my_coeffs.push_back(farkas_coeffs[i]); } - ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons)); + ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons,true /* round_off */)); my_cons.push_back(mk_not(farkas_con)); my_coeffs.push_back(make_int("1")); std::vector my_hyps; @@ -1103,7 +1103,7 @@ public: my_cons.push_back(conc(prem(proof,i-1))); my_coeffs.push_back(farkas_coeffs[i]); } - ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons)); + ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons,true /* round_off */)); std::vector my_hyps; for(int i = 1; i < nargs; i++) my_hyps.push_back(prems[i-1]); From da4793de76a5e0a34bb00c333f8c2902d1c96ec3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 9 Jan 2014 21:14:30 -0800 Subject: [PATCH 257/509] fix type checking bug reported by Nate Signed-off-by: Nikolaj Bjorner --- src/ast/ast.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index b000201b7..68f7596ee 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1850,6 +1850,7 @@ func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * const * args) const { ast_manager& m = const_cast(*this); + if (decl->is_associative()) { sort * expected = decl->get_domain(0); for (unsigned i = 0; i < num_args; i++) { @@ -1894,7 +1895,18 @@ void ast_manager::check_sorts_core(ast const * n) const { if (n->get_kind() != AST_APP) return; // nothing else to check... app const * a = to_app(n); - check_sort(a->get_decl(), a->get_num_args(), a->get_args()); + func_decl* d = a->get_decl(); + check_sort(d, a->get_num_args(), a->get_args()); + if (a->get_num_args() == 2 && + !d->is_flat_associative() && + d->is_right_associative()) { + check_sorts_core(a->get_arg(1)); + } + if (a->get_num_args() == 2 && + !d->is_flat_associative() && + d->is_left_associative()) { + check_sorts_core(a->get_arg(0)); + } } bool ast_manager::check_sorts(ast const * n) const { From b80302cfb0a49efd42eeb45436c934d3c6ff0835 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 Jan 2014 13:41:47 -0800 Subject: [PATCH 258/509] generalize guard in conflict resolution to handle non-equality binary predicates Signed-off-by: Nikolaj Bjorner --- src/smt/smt_conflict_resolution.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index d34aa2ec8..ec62efa1c 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -759,7 +759,8 @@ namespace smt { app * fact = to_app(m_manager.get_fact(pr)); app * n1_owner = n1->get_owner(); app * n2_owner = n2->get_owner(); - if (fact->get_num_args() != 2 || (fact->get_arg(0) != n2_owner && fact->get_arg(1) != n2_owner)) { + bool is_eq = m_manager.is_eq(fact) || m_manager.is_iff(fact); + if (!is_eq || (fact->get_arg(0) != n2_owner && fact->get_arg(1) != n2_owner)) { CTRACE("norm_eq_proof_bug", !m_ctx.is_true(n2) && !m_ctx.is_false(n2), tout << "n1: #" << n1->get_owner_id() << ", n2: #" << n2->get_owner_id() << "\n"; if (fact->get_num_args() == 2) { From 73a1dddc45c5d75a0a428800fddbf799a9e31f9b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 21 Jan 2014 17:06:13 +0000 Subject: [PATCH 259/509] Bugfixes for the build on new OSX machines (XCode 5.0 on). --- scripts/mk_util.py | 12 ++++++++++++ src/util/hwf.cpp | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 9295c949f..0b1dd47a2 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -638,7 +638,19 @@ def is_compiler(given, expected): def is_CXX_gpp(): return is_compiler(CXX, 'g++') +def is_clang_in_gpp_form(cc): + outf = open('clang_version', 'rw') + subprocess.call([cc, '--version'], stdout=outf, stderr=outf) + outf.seek(0) + version_string = outf.read() + contains_clang = version_string.find('clang') != -1 + outf.close() + os.remove('clang_version') + return contains_clang + def is_CXX_clangpp(): + if is_compiler(CXX, 'g++'): + return is_clang_in_gpp_form(CXX) return is_compiler(CXX, 'clang++') def get_cpp_files(path): diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index 8585e7eda..40d20b644 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -49,8 +49,11 @@ Revision History: // clear to the compiler what instructions should be used. E.g., for sqrt(), the Windows compiler selects // the x87 FPU, even when /arch:SSE2 is on. // Luckily, these are kind of standardized, at least for Windows/Linux/OSX. +#ifdef __clang__ +#undef USE_INTRINSICS +#else #include - +#endif hwf_manager::hwf_manager() : m_mpz_manager(m_mpq_manager) From b2be81fd4d3d6399f15bb1ae70f88978c6b82700 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 22 Jan 2014 13:41:48 +0000 Subject: [PATCH 260/509] bugfix for OSX build configuration --- scripts/mk_util.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 0b1dd47a2..2fff61778 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -639,14 +639,8 @@ def is_CXX_gpp(): return is_compiler(CXX, 'g++') def is_clang_in_gpp_form(cc): - outf = open('clang_version', 'rw') - subprocess.call([cc, '--version'], stdout=outf, stderr=outf) - outf.seek(0) - version_string = outf.read() - contains_clang = version_string.find('clang') != -1 - outf.close() - os.remove('clang_version') - return contains_clang + version_string = subprocess.check_output([cc, '--version']) + return version_string.find('clang') != -1 def is_CXX_clangpp(): if is_compiler(CXX, 'g++'): From 0e74362ecbde4c137d393ded081d30604df9d4a1 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Jan 2014 15:36:23 +0000 Subject: [PATCH 261/509] Added support for the final draft of the FPA standard (and fpa2bv conversion). Signed-off-by: Christoph M. Wintersteiger --- src/ast/float_decl_plugin.cpp | 96 ++++++++++++++++++++++--- src/ast/float_decl_plugin.h | 18 ++++- src/ast/rewriter/float_rewriter.cpp | 44 ++++++++++++ src/ast/rewriter/float_rewriter.h | 6 ++ src/tactic/fpa/fpa2bv_converter.cpp | 36 ++++++++++ src/tactic/fpa/fpa2bv_converter.h | 8 ++- src/tactic/fpa/fpa2bv_rewriter.h | 5 ++ src/tactic/fpa/qffpa_tactic.cpp | 74 +++++++++++++++++++ src/tactic/fpa/qffpa_tactic.h | 7 ++ src/tactic/portfolio/default_tactic.cpp | 4 +- src/util/mpf.cpp | 4 ++ src/util/mpf.h | 2 + 12 files changed, 290 insertions(+), 14 deletions(-) diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index eb8aba9ae..1adedb2d7 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -213,6 +213,9 @@ func_decl * float_decl_plugin::mk_float_const_decl(decl_kind k, unsigned num_par if (num_parameters == 1 && parameters[0].is_ast() && is_sort(parameters[0].get_ast()) && is_float_sort(to_sort(parameters[0].get_ast()))) { s = to_sort(parameters[0].get_ast()); } + else if (num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int()) { + s = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); + } else if (range != 0 && is_float_sort(range)) { s = range; } @@ -376,7 +379,19 @@ func_decl * float_decl_plugin::mk_to_float(decl_kind k, unsigned num_parameters, sort * fp = mk_float_sort(domain[2]->get_parameter(0).get_int(), domain[1]->get_parameter(0).get_int()+1); symbol name("asFloat"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); - } + } + else if (m_bv_plugin && arity == 1 && is_sort_of(domain[0], m_bv_fid, BV_SORT)) { + if (num_parameters != 2) + m_manager->raise_exception("invalid number of parameters to to_fp"); + if (!parameters[0].is_int() || !parameters[1].is_int()) + m_manager->raise_exception("invalid parameter type to to_fp"); + int ebits = parameters[0].get_int(); + int sbits = parameters[1].get_int(); + + sort * fp = mk_float_sort(ebits, sbits); + symbol name("asFloat"); + return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); + } else { // .. Otherwise we only know how to convert rationals/reals. if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) @@ -412,6 +427,53 @@ func_decl * float_decl_plugin::mk_to_ieee_bv(decl_kind k, unsigned num_parameter return m_manager->mk_func_decl(name, 1, domain, bv_srt, func_decl_info(m_family_id, k, num_parameters, parameters)); } +func_decl * float_decl_plugin::mk_from3bv(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) { + if (!m_bv_plugin) + m_manager->raise_exception("fp unsupported; use a logic with BV support"); + if (arity != 3) + m_manager->raise_exception("invalid number of arguments to fp"); + if (!is_sort_of(domain[0], m_bv_fid, BV_SORT) || + !is_sort_of(domain[1], m_bv_fid, BV_SORT) || + !is_sort_of(domain[2], m_bv_fid, BV_SORT)) + m_manager->raise_exception("sort mismtach"); + + sort * fp = mk_float_sort(domain[1]->get_parameter(0).get_int(), domain[2]->get_parameter(0).get_int() + 1); + symbol name("fp"); + return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k)); +} + +func_decl * float_decl_plugin::mk_to_fp_unsigned(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) { + if (!m_bv_plugin) + m_manager->raise_exception("to_fp_unsigned unsupported; use a logic with BV support"); + if (arity != 2) + m_manager->raise_exception("invalid number of arguments to to_fp_unsigned"); + if (is_rm_sort(domain[0])) + m_manager->raise_exception("sort mismtach"); + if (!is_sort_of(domain[1], m_bv_fid, BV_SORT)) + m_manager->raise_exception("sort mismtach"); + + sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); + symbol name("to_fp_unsigned"); + return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k)); +} + +func_decl * float_decl_plugin::mk_to_ubv(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) { + NOT_IMPLEMENTED_YET(); +} + +func_decl * float_decl_plugin::mk_to_sbv(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) { + NOT_IMPLEMENTED_YET(); +} + +func_decl * float_decl_plugin::mk_to_real(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) { + NOT_IMPLEMENTED_YET(); +} + func_decl * float_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { switch (k) { @@ -465,6 +527,16 @@ func_decl * float_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters return mk_fused_ma(k, num_parameters, parameters, arity, domain, range); case OP_TO_IEEE_BV: return mk_to_ieee_bv(k, num_parameters, parameters, arity, domain, range); + case OP_FLOAT_FP: + return mk_from3bv(k, num_parameters, parameters, arity, domain, range); + case OP_FLOAT_TO_FP_UNSIGNED: + return mk_to_fp_unsigned(k, num_parameters, parameters, arity, domain, range); + case OP_FLOAT_TO_UBV: + return mk_to_ubv(k, num_parameters, parameters, arity, domain, range); + case OP_FLOAT_TO_SBV: + return mk_to_sbv(k, num_parameters, parameters, arity, domain, range); + case OP_FLOAT_TO_REAL: + return mk_to_real(k, num_parameters, parameters, arity, domain, range); default: m_manager->raise_exception("unsupported floating point operator"); return 0; @@ -517,8 +589,9 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co if (m_bv_plugin) op_names.push_back(builtin_name("asIEEEBV", OP_TO_IEEE_BV)); - // We also support draft version 3 - op_names.push_back(builtin_name("fp", OP_TO_FLOAT)); + // These are the operators from the final draft of the SMT FloatingPoints standard + op_names.push_back(builtin_name("+oo", OP_FLOAT_PLUS_INF)); + op_names.push_back(builtin_name("-oo", OP_FLOAT_MINUS_INF)); op_names.push_back(builtin_name("RNE", OP_RM_NEAREST_TIES_TO_EVEN)); op_names.push_back(builtin_name("RNA", OP_RM_NEAREST_TIES_TO_AWAY)); @@ -547,23 +620,24 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("fp.isNaN", OP_FLOAT_IS_NAN)); op_names.push_back(builtin_name("fp.min", OP_FLOAT_MIN)); op_names.push_back(builtin_name("fp.max", OP_FLOAT_MAX)); - op_names.push_back(builtin_name("fp.convert", OP_TO_FLOAT)); + op_names.push_back(builtin_name("to_fp", OP_TO_FLOAT)); if (m_bv_plugin) { - // op_names.push_back(builtin_name("fp.fromBv", OP_TO_FLOAT)); - // op_names.push_back(builtin_name("fp.fromUBv", OP_TO_FLOAT)); - // op_names.push_back(builtin_name("fp.fromSBv", OP_TO_FLOAT)); - // op_names.push_back(builtin_name("fp.toUBv", OP_TO_IEEE_BV)); - // op_names.push_back(builtin_name("fp.toSBv", OP_TO_IEEE_BV)); + op_names.push_back(builtin_name("fp", OP_FLOAT_FP)); + op_names.push_back(builtin_name("to_fp_unsigned", OP_FLOAT_TO_FP_UNSIGNED)); + op_names.push_back(builtin_name("fp.to_ubv", OP_FLOAT_TO_UBV)); + op_names.push_back(builtin_name("fp.to_sbv", OP_FLOAT_TO_SBV)); } - - op_names.push_back(builtin_name("fp.fromReal", OP_TO_FLOAT)); + // op_names.push_back(builtin_name("fp.toReal", ?)); } void float_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { sort_names.push_back(builtin_name("FP", FLOAT_SORT)); sort_names.push_back(builtin_name("RoundingMode", ROUNDING_MODE_SORT)); + + // In the SMT FPA final draft, FP is called FloatingPoint + sort_names.push_back(builtin_name("FloatingPoint", FLOAT_SORT)); } expr * float_decl_plugin::get_some_value(sort * s) { diff --git a/src/ast/float_decl_plugin.h b/src/ast/float_decl_plugin.h index f1b60a91b..3786b76ee 100644 --- a/src/ast/float_decl_plugin.h +++ b/src/ast/float_decl_plugin.h @@ -72,6 +72,12 @@ enum float_op_kind { OP_TO_FLOAT, OP_TO_IEEE_BV, + + OP_FLOAT_FP, + OP_FLOAT_TO_FP_UNSIGNED, + OP_FLOAT_TO_UBV, + OP_FLOAT_TO_SBV, + OP_FLOAT_TO_REAL, LAST_FLOAT_OP }; @@ -125,7 +131,17 @@ class float_decl_plugin : public decl_plugin { unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); - + func_decl * mk_from3bv(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); + func_decl * mk_to_fp_unsigned(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); + func_decl * mk_to_ubv(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); + func_decl * mk_to_sbv(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); + func_decl * mk_to_real(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); + virtual void set_manager(ast_manager * m, family_id id); unsigned mk_id(mpf const & v); void recycled_id(unsigned id); diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index 6ef147f1c..ecd06ff38 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -64,6 +64,11 @@ br_status float_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c case OP_FLOAT_IS_SUBNORMAL: SASSERT(num_args == 1); st = mk_is_subnormal(args[0], result); break; case OP_FLOAT_IS_SIGN_MINUS: SASSERT(num_args == 1); st = mk_is_sign_minus(args[0], result); break; case OP_TO_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(args[0], result); break; + case OP_FLOAT_FP: SASSERT(num_args == 3); st = mk_fp(args[0], args[1], args[2], result); break; + case OP_FLOAT_TO_FP_UNSIGNED: SASSERT(num_args == 2); st = mk_to_fp_unsigned(args[0], args[1], result); break; + case OP_FLOAT_TO_UBV: SASSERT(num_args == 2); st = mk_to_ubv(args[0], args[1], result); break; + case OP_FLOAT_TO_SBV: SASSERT(num_args == 2); st = mk_to_sbv(args[0], args[1], result); break; + case OP_FLOAT_TO_REAL: SASSERT(num_args == 1); st = mk_to_real(args[0], result); break; } return st; } @@ -504,3 +509,42 @@ br_status float_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result br_status float_rewriter::mk_to_ieee_bv(expr * arg1, expr_ref & result) { return BR_FAILED; } + +br_status float_rewriter::mk_fp(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { + bv_util bu(m()); + rational r1, r2, r3; + unsigned bvs1, bvs2, bvs3; + + if (bu.is_numeral(arg1, r1, bvs1) && bu.is_numeral(arg2, r2, bvs2) && bu.is_numeral(arg3, r3, bvs3)) { + SASSERT(m_util.fm().mpz_manager().is_one(r2.to_mpq().denominator())); + SASSERT(m_util.fm().mpz_manager().is_one(r3.to_mpq().denominator())); + SASSERT(m_util.fm().mpz_manager().is_int64(r3.to_mpq().numerator())); + scoped_mpf v(m_util.fm()); + mpf_exp_t biased_exp = m_util.fm().mpz_manager().get_int64(r2.to_mpq().numerator()); + m_util.fm().set(v, bvs2, bvs3 + 1, + r1.is_one(), + r3.to_mpq().numerator(), + m_util.fm().unbias_exp(bvs2, biased_exp)); + TRACE("fp_rewriter", tout << "v = " << m_util.fm().to_string(v) << std::endl;); + result = m_util.mk_value(v); + return BR_DONE; + } + + return BR_FAILED; +} + +br_status float_rewriter::mk_to_fp_unsigned(expr * arg1, expr * arg2, expr_ref & result) { + return BR_FAILED; +} + +br_status float_rewriter::mk_to_ubv(expr * arg1, expr * arg2, expr_ref & result) { + return BR_FAILED; +} + +br_status float_rewriter::mk_to_sbv(expr * arg1, expr * arg2, expr_ref & result) { + return BR_FAILED; +} + +br_status float_rewriter::mk_to_real(expr * arg1, expr_ref & result) { + return BR_FAILED; +} \ No newline at end of file diff --git a/src/ast/rewriter/float_rewriter.h b/src/ast/rewriter/float_rewriter.h index 4a0879d4b..0f44d227c 100644 --- a/src/ast/rewriter/float_rewriter.h +++ b/src/ast/rewriter/float_rewriter.h @@ -73,6 +73,12 @@ public: br_status mk_is_sign_minus(expr * arg1, expr_ref & result); br_status mk_to_ieee_bv(expr * arg1, expr_ref & result); + + br_status mk_fp(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); + br_status mk_to_fp_unsigned(expr * arg1, expr * arg2, expr_ref & result); + br_status mk_to_ubv(expr * arg1, expr * arg2, expr_ref & result); + br_status mk_to_sbv(expr * arg1, expr * arg2, expr_ref & result); + br_status mk_to_real(expr * arg1, expr_ref & result); }; #endif diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index ebccf4d52..144925164 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1836,6 +1836,21 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a // Just keep it here, as there will be something else that uses it. mk_triple(args[0], args[1], args[2], result); } + else if (num == 1 && m_bv_util.is_bv(args[0])) { + sort * s = f->get_range(); + unsigned to_sbits = m_util.get_sbits(s); + unsigned to_ebits = m_util.get_ebits(s); + + expr * bv = args[0]; + int sz = m_bv_util.get_bv_size(bv); + SASSERT((unsigned)sz == to_sbits + to_ebits); + + m_bv_util.mk_extract(sz - 1, sz - 1, bv); + mk_triple(m_bv_util.mk_extract(sz - 1, sz - 1, bv), + m_bv_util.mk_extract(sz - to_ebits - 2, 0, bv), + m_bv_util.mk_extract(sz - 2, sz - to_ebits - 1, bv), + result); + } else if (num == 2 && is_app(args[1]) && m_util.is_float(m.get_sort(args[1]))) { // We also support float to float conversion sort * s = f->get_range(); @@ -2043,6 +2058,27 @@ void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * result = m_bv_util.mk_concat(m_bv_util.mk_concat(sgn, e), s); } +void fpa2bv_converter::mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 3); + mk_triple(args[0], args[2], args[1], result); +} + +void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + NOT_IMPLEMENTED_YET(); +} + +void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + NOT_IMPLEMENTED_YET(); +} + +void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + NOT_IMPLEMENTED_YET(); +} + +void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + NOT_IMPLEMENTED_YET(); +} + void fpa2bv_converter::split(expr * e, expr * & sgn, expr * & sig, expr * & exp) const { SASSERT(is_app_of(e, m_plugin->get_family_id(), OP_TO_FLOAT)); SASSERT(to_app(e)->get_num_args() == 3); diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/tactic/fpa/fpa2bv_converter.h index 821618bae..79c8039ef 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/tactic/fpa/fpa2bv_converter.h @@ -122,7 +122,13 @@ public: void mk_is_subnormal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_float(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + + void mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_to_fp_unsigned(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_to_sbv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result); obj_map const & const2bv() const { return m_const2bv; } obj_map const & rm_const2bv() const { return m_rm_const2bv; } diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index d737683a8..b2e3da939 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -139,6 +139,11 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { case OP_FLOAT_IS_SIGN_MINUS: m_conv.mk_is_sign_minus(f, num, args, result); return BR_DONE; case OP_TO_FLOAT: m_conv.mk_to_float(f, num, args, result); return BR_DONE; case OP_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE; + case OP_FLOAT_FP: m_conv.mk_fp(f, num, args, result); return BR_DONE; + case OP_FLOAT_TO_FP_UNSIGNED: m_conv.mk_to_fp_unsigned(f, num, args, result); return BR_DONE; + case OP_FLOAT_TO_UBV: m_conv.mk_to_ubv(f, num, args, result); return BR_DONE; + case OP_FLOAT_TO_SBV: m_conv.mk_to_sbv(f, num, args, result); return BR_DONE; + case OP_FLOAT_TO_REAL: m_conv.mk_to_real(f, num, args, result); return BR_DONE; default: TRACE("fpa2bv", tout << "unsupported operator: " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); diff --git a/src/tactic/fpa/qffpa_tactic.cpp b/src/tactic/fpa/qffpa_tactic.cpp index f3c913ccc..90cfc8c92 100644 --- a/src/tactic/fpa/qffpa_tactic.cpp +++ b/src/tactic/fpa/qffpa_tactic.cpp @@ -36,3 +36,77 @@ tactic * mk_qffpa_tactic(ast_manager & m, params_ref const & p) { mk_sat_tactic(m, p), mk_fail_if_undecided_tactic()); } + +struct is_non_qffpa_predicate { + struct found {}; + ast_manager & m; + float_util u; + + is_non_qffpa_predicate(ast_manager & _m) :m(_m), u(m) {} + + void operator()(var *) { throw found(); } + + void operator()(quantifier *) { throw found(); } + + void operator()(app * n) { + sort * s = get_sort(n); + if (!m.is_bool(s) && !(u.is_float(s) || u.is_rm(s))) + throw found(); + family_id fid = s->get_family_id(); + if (fid == m.get_basic_family_id()) + return; + if (fid == u.get_family_id()) + return; + + throw found(); + } +}; + +struct is_non_qffpabv_predicate { + struct found {}; + ast_manager & m; + bv_util bu; + float_util fu; + + is_non_qffpabv_predicate(ast_manager & _m) :m(_m), bu(m), fu(m) {} + + void operator()(var *) { throw found(); } + + void operator()(quantifier *) { throw found(); } + + void operator()(app * n) { + sort * s = get_sort(n); + if (!m.is_bool(s) && !(fu.is_float(s) || fu.is_rm(s) || bu.is_bv_sort(s))) + throw found(); + family_id fid = s->get_family_id(); + if (fid == m.get_basic_family_id()) + return; + if (fid == fu.get_family_id() || fid == bu.get_family_id()) + return; + + throw found(); + } +}; + +class is_qffpa_probe : public probe { +public: + virtual result operator()(goal const & g) { + return !test(g); + } +}; + +class is_qffpabv_probe : public probe { +public: + virtual result operator()(goal const & g) { + return !test(g); + } +}; + +probe * mk_is_qffpa_probe() { + return alloc(is_qffpa_probe); +} + +probe * mk_is_qffpabv_probe() { + return alloc(is_qffpabv_probe); +} + \ No newline at end of file diff --git a/src/tactic/fpa/qffpa_tactic.h b/src/tactic/fpa/qffpa_tactic.h index 8ca2183c1..cd16c5817 100644 --- a/src/tactic/fpa/qffpa_tactic.h +++ b/src/tactic/fpa/qffpa_tactic.h @@ -30,4 +30,11 @@ tactic * mk_qffpa_tactic(ast_manager & m, params_ref const & p = params_ref()); ADD_TACTIC("qffpabv", "(try to) solve goal using the tactic for QF_FPABV (floats+bit-vectors).", "mk_qffpa_tactic(m, p)") */ +probe * mk_is_qffpa_probe(); +probe * mk_is_qffpabv_probe(); +/* + ADD_PROBE("is-qffpa", "true if the goal is in QF_FPA (FloatingPoints).", "mk_is_qffpa_probe()") + ADD_PROBE("is-qffpabv", "true if the goal is in QF_FPABV (FloatingPoints+Bitvectors).", "mk_is_qffpabv_probe()") +*/ + #endif diff --git a/src/tactic/portfolio/default_tactic.cpp b/src/tactic/portfolio/default_tactic.cpp index d65ba8d35..9ecc16ecf 100644 --- a/src/tactic/portfolio/default_tactic.cpp +++ b/src/tactic/portfolio/default_tactic.cpp @@ -27,6 +27,7 @@ Notes: #include"nra_tactic.h" #include"probe_arith.h" #include"quant_tactics.h" +#include"qffpa_tactic.h" tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { tactic * st = using_params(and_then(mk_simplify_tactic(m), @@ -37,7 +38,8 @@ tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { cond(mk_is_qfnia_probe(), mk_qfnia_tactic(m), cond(mk_is_nra_probe(), mk_nra_tactic(m), cond(mk_is_lira_probe(), mk_lira_tactic(m, p), - mk_smt_tactic())))))))), + cond(mk_is_qffpabv_probe(), mk_qffpa_tactic(m, p), + mk_smt_tactic()))))))))), p); return st; } diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 5910f55c9..8cb6ed4fc 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -1400,6 +1400,10 @@ mpf_exp_t mpf_manager::mk_max_exp(unsigned ebits) { return m_mpz_manager.get_int64(m_powers2.m1(ebits-1, false)); } +mpf_exp_t mpf_manager::unbias_exp(unsigned ebits, mpf_exp_t biased_exponent) { + return biased_exponent - m_mpz_manager.get_int64(m_powers2.m1(ebits - 1, false)); +} + void mpf_manager::mk_nzero(unsigned ebits, unsigned sbits, mpf & o) { o.sbits = sbits; o.ebits = ebits; diff --git a/src/util/mpf.h b/src/util/mpf.h index 284b0ba7f..83646f9f3 100644 --- a/src/util/mpf.h +++ b/src/util/mpf.h @@ -182,6 +182,8 @@ public: mpf_exp_t mk_max_exp(unsigned ebits); mpf_exp_t mk_min_exp(unsigned ebits); + mpf_exp_t unbias_exp(unsigned ebits, mpf_exp_t biased_exponent); + /** \brief Return the biggest k s.t. 2^k <= a. From f111dd4e612ba2b0edfc9476f94aec2a5c093b94 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 28 Jan 2014 14:00:42 +0000 Subject: [PATCH 262/509] Fixes for the build on OS X 10.9 --- src/api/api_interp.cpp | 1 + src/interp/iz3hash.h | 2 +- src/interp/iz3mgr.cpp | 4 ++-- src/interp/iz3mgr.h | 1 + src/interp/iz3pp.cpp | 10 +++++----- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 560ee5885..98722022c 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -17,6 +17,7 @@ Revision History: --*/ #include #include +#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index 75d9aa604..94f282265 100755 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -66,7 +66,7 @@ Revision History: namespace stl_ext { template <> class hash { - stl_ext::hash H; + stl_ext::hash H; public: size_t operator()(const std::string &s) const { return H(s.c_str()); diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index f35dae93f..b85df81be 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -172,7 +172,7 @@ iz3mgr::ast iz3mgr::make_quant(opr op, const std::vector &bvs, ast &body){ std::vector names; - std::vector types; + std::vector types; std::vector bound_asts; unsigned num_bound = bvs.size(); @@ -485,7 +485,7 @@ void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& coeffs){ get_farkas_coeffs(proof,rats); coeffs.resize(rats.size()); for(unsigned i = 0; i < rats.size(); i++){ - sort *is = m().mk_sort(m_arith_fid, INT_SORT); + class sort *is = m().mk_sort(m_arith_fid, INT_SORT); ast coeff = cook(m_arith_util.mk_numeral(rats[i],is)); coeffs[i] = coeff; } diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 645c72ccb..7da247287 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -22,6 +22,7 @@ Revision History: #include +#include #include "iz3hash.h" #include"well_sorted.h" diff --git a/src/interp/iz3pp.cpp b/src/interp/iz3pp.cpp index df6fcaf53..31b375c0f 100644 --- a/src/interp/iz3pp.cpp +++ b/src/interp/iz3pp.cpp @@ -58,20 +58,20 @@ namespace stl_ext { class free_func_visitor { ast_manager& m; func_decl_set m_funcs; - obj_hashtable m_sorts; + obj_hashtable m_sorts; public: free_func_visitor(ast_manager& m): m(m) {} void operator()(var * n) { } void operator()(app * n) { m_funcs.insert(n->get_decl()); - sort* s = m.get_sort(n); + class sort* s = m.get_sort(n); if (s->get_family_id() == null_family_id) { m_sorts.insert(s); } } void operator()(quantifier * n) { } func_decl_set& funcs() { return m_funcs; } - obj_hashtable& sorts() { return m_sorts; } + obj_hashtable& sorts() { return m_sorts; } }; class iz3pp_helper : public iz3mgr { @@ -146,8 +146,8 @@ void iz3pp(ast_manager &m, func_decl_set &funcs = visitor.funcs(); func_decl_set::iterator it = funcs.begin(), end = funcs.end(); - obj_hashtable& sorts = visitor.sorts(); - obj_hashtable::iterator sit = sorts.begin(), send = sorts.end(); + obj_hashtable& sorts = visitor.sorts(); + obj_hashtable::iterator sit = sorts.begin(), send = sorts.end(); From 19830bcd333e904aa26485b1e2d932bbe0f79ca7 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 28 Jan 2014 11:43:00 -0800 Subject: [PATCH 263/509] fix a few warnings --- src/cmd_context/interpolant_cmds.cpp | 3 ++- src/duality/duality_rpfp.cpp | 2 +- src/interp/iz3translate_direct.cpp | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index 2a7de3176..318569cd5 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -139,10 +139,11 @@ static void get_interpolant(cmd_context & ctx, expr * t, params_ref &m_params) { get_interpolant_and_maybe_check(ctx,t,m_params,false); } +#if 0 static void get_and_check_interpolant(cmd_context & ctx, params_ref &m_params, expr * t) { get_interpolant_and_maybe_check(ctx,t,m_params,true); } - +#endif static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check){ diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index ad89c1314..17b93d35b 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -3661,7 +3661,7 @@ namespace Duality { for(unsigned j = 0; j < outs.size(); j++) for(unsigned k = 0; k < outs[j]->Children.size(); k++) chs.push_back(outs[j]->Children[k]); - Edge *fedge = CreateEdge(node,tr,chs); + CreateEdge(node,tr,chs); for(unsigned j = 0; j < outs.size(); j++) edges_to_delete.insert(outs[j]); } diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index 4b7d47e0f..0ce3297af 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -58,7 +58,9 @@ namespace stl_ext { static int lemma_count = 0; +#if 0 static int nll_lemma_count = 0; +#endif #define SHOW_LEMMA_COUNT -1 // One half of a resolution. We need this to distinguish From ba193a59b18328a9ae318ab052e24c522b6d61d6 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 9 Feb 2014 16:04:02 -0800 Subject: [PATCH 264/509] some interpolation fixes, plus some options to remove features for testing in duality --- src/duality/duality.h | 0 src/duality/duality_rpfp.cpp | 0 src/duality/duality_solver.cpp | 15 +++- src/duality/duality_wrapper.cpp | 0 src/interp/iz3mgr.cpp | 9 +- src/interp/iz3mgr.h | 0 src/interp/iz3proof_itp.cpp | 64 +++++++++++++- src/interp/iz3translate.cpp | 102 +++++++++++++++++++++-- src/muz/duality/duality_dl_interface.cpp | 0 9 files changed, 176 insertions(+), 14 deletions(-) mode change 100644 => 100755 src/duality/duality.h mode change 100644 => 100755 src/duality/duality_rpfp.cpp mode change 100644 => 100755 src/duality/duality_solver.cpp mode change 100644 => 100755 src/duality/duality_wrapper.cpp mode change 100644 => 100755 src/interp/iz3mgr.cpp mode change 100644 => 100755 src/interp/iz3mgr.h mode change 100644 => 100755 src/interp/iz3proof_itp.cpp mode change 100644 => 100755 src/muz/duality/duality_dl_interface.cpp diff --git a/src/duality/duality.h b/src/duality/duality.h old mode 100644 new mode 100755 diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp old mode 100644 new mode 100755 diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp old mode 100644 new mode 100755 index 91532960b..6d39a4fe2 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -44,12 +44,17 @@ Revision History: // #define TOP_DOWN // #define EFFORT_BOUNDED_STRAT #define SKIP_UNDERAPPROX_NODES -#define USE_RPFP_CLONE // #define KEEP_EXPANSIONS // #define USE_CACHING_RPFP // #define PROPAGATE_BEFORE_CHECK + +#define USE_RPFP_CLONE #define USE_NEW_GEN_CANDS +//#define NO_PROPAGATE +//#define NO_GENERALIZE +//#define NO_DECISIONS + namespace Duality { // TODO: must be a better place for this... @@ -1933,11 +1938,15 @@ namespace Duality { for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; tree->SolveSingleNode(top,node); +#ifdef NO_GENERALIZE + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); +#else if(expansions.size() == 1 && NodeTooComplicated(node)) SimplifyNode(node); else node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); Generalize(node); +#endif if(RecordUpdate(node)) update_count++; else @@ -1977,7 +1986,9 @@ namespace Duality { if(stack.size() == 1)break; if(prev_level_used){ Node *node = stack.back().expansions[0]; +#ifndef NO_PROPAGATE if(!Propagate(node)) break; +#endif if(!RecordUpdate(node)) break; // shouldn't happen! RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list propagated = true; @@ -2001,9 +2012,11 @@ namespace Duality { was_sat = true; tree->Push(); std::vector &expansions = stack.back().expansions; +#ifndef NO_DECISIONS for(unsigned i = 0; i < expansions.size(); i++){ tree->FixCurrentState(expansions[i]->Outgoing); } +#endif #if 0 if(tree->slvr().check() == unsat) throw "help!"; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp old mode 100644 new mode 100755 diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp old mode 100644 new mode 100755 index 994d10e06..329fcb305 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -684,10 +684,13 @@ void iz3mgr::linear_comb(ast &P, const ast &c, const ast &Q, bool round_off){ throw "not an inequality"; } } - Qrhs = make(Times,c,Qrhs); - bool pstrict = op(P) == Lt, strict = pstrict || qstrict; - if(pstrict && qstrict && round_off) + bool pstrict = op(P) == Lt; + if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ Qrhs = make(Sub,Qrhs,make_int(rational(1))); + qstrict = false; + } + Qrhs = make(Times,c,Qrhs); + bool strict = pstrict || qstrict; if(strict) P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); else diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h old mode 100644 new mode 100755 diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp old mode 100644 new mode 100755 index 8b78a7135..2d79e8151 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -677,8 +677,11 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast rotate_sum_rec(const ast &pl, const ast &pf, ast &Bproves, ast &ineq){ - if(pf == pl) + if(pf == pl){ + if(sym(ineq) == normal) + return my_implies(Bproves,ineq); return my_implies(Bproves,simplify_ineq(ineq)); + } if(op(pf) == Uninterpreted && sym(pf) == sum){ if(arg(pf,2) == pl){ ast Aproves = mk_true(); @@ -829,6 +832,8 @@ class iz3proof_itp_impl : public iz3proof_itp { ast equa = sep_cond(args[0],cond); if(is_equivrel_chain(equa)) return my_implies(cond,reverse_chain(equa)); + if(is_negation_chain(equa)) + return commute_negation_chain(equa); throw cannot_simplify(); } @@ -1443,6 +1448,50 @@ class iz3proof_itp_impl : public iz3proof_itp { return is_negation_chain(rest); } + ast commute_negation_chain(const ast &chain){ + if(is_true(chain)) + return chain; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + if(is_true(rest)){ + ast old = rewrite_rhs(last); + if(!(op(old) == Not)) + throw "bad negative equality chain"; + ast equ = arg(old,0); + if(!is_equivrel(equ)) + throw "bad negative equality chain"; + last = rewrite_update_rhs(last,top_pos,make(Not,make(op(equ),arg(equ,1),arg(equ,0))),make(True)); + return chain_cons(rest,last); + } + ast pos = rewrite_pos(last); + if(pos == top_pos) + throw "bad negative equality chain"; + int idx = pos_arg(pos); + if(idx != 0) + throw "bad negative equality chain"; + pos = arg(pos,1); + if(pos == top_pos){ + ast lhs = rewrite_lhs(last); + ast rhs = rewrite_rhs(last); + if(op(lhs) != Equal || op(rhs) != Equal) + throw "bad negative equality chain"; + last = make_rewrite(rewrite_side(last),rewrite_pos(last),rewrite_cond(last), + make(Iff,make(Equal,arg(lhs,1),arg(lhs,0)),make(Equal,arg(rhs,1),arg(rhs,0)))); + } + else { + idx = pos_arg(pos); + if(idx == 0) + idx = 1; + else if(idx == 1) + idx = 0; + else + throw "bad negative equality chain"; + pos = pos_add(0,pos_add(idx,arg(pos,1))); + last = make_rewrite(rewrite_side(last),pos,rewrite_cond(last),rewrite_equ(last)); + } + return chain_cons(commute_negation_chain(rest),last); + } + // split a rewrite chain into head and tail at last top-level rewrite ast get_head_chain(const ast &chain, ast &tail, bool is_not = true){ ast last = chain_last(chain); @@ -2240,10 +2289,19 @@ class iz3proof_itp_impl : public iz3proof_itp { throw proof_error(); } } - Qrhs = make(Times,c,Qrhs); +#if 0 bool pstrict = op(P) == Lt, strict = pstrict || qstrict; if(pstrict && qstrict && round_off) Qrhs = make(Sub,Qrhs,make_int(rational(1))); +#else + bool pstrict = op(P) == Lt; + if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ + Qrhs = make(Sub,Qrhs,make_int(rational(1))); + qstrict = false; + } + Qrhs = make(Times,c,Qrhs); + bool strict = pstrict || qstrict; +#endif if(strict) P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); else @@ -2427,7 +2485,7 @@ class iz3proof_itp_impl : public iz3proof_itp { return e; // this term occurs in range, so it's O.K. if(is_array_type(get_type(e))) - throw "help!"; + std::cerr << "WARNING: array quantifier\n"; // choose a frame for the constraint that is close to range int frame = pv->range_near(pv->ast_scope(e),rng); diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index abddb4bda..bba7b4d0d 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -181,6 +181,13 @@ public: get_Z3_lits(con, lits); iproof->make_axiom(lits); } + else if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST + && get_locality_rec(prem(proof,1)) == INT_MAX){ + std::vector lits; + ast con = conc(proof); + get_Z3_lits(con, lits); + iproof->make_axiom(lits); + } else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ @@ -1264,6 +1271,84 @@ public: return make(Plus,args); } + + ast replace_summands_with_fresh_vars(const ast &t, hash_map &map){ + if(op(t) == Plus){ + int nargs = num_args(t); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = replace_summands_with_fresh_vars(arg(t,i),map); + return make(Plus,args); + } + if(op(t) == Times) + return make(Times,arg(t,0),replace_summands_with_fresh_vars(arg(t,1),map)); + if(map.find(t) == map.end()) + map[t] = mk_fresh_constant("@s",get_type(t)); + return map[t]; + } + + ast painfully_normalize_ineq(const ast &ineq, hash_map &map){ + ast res = normalize_inequality(ineq); + ast lhs = arg(res,0); + lhs = replace_summands_with_fresh_vars(lhs,map); + res = make(op(res),SortSum(lhs),arg(res,1)); + return res; + } + + Iproof::node painfully_reconstruct_farkas(const std::vector &prems, const std::vector &pfs, const ast &con){ + int nprems = prems.size(); + std::vector pcons(nprems),npcons(nprems); + hash_map pcon_to_pf, npcon_to_pcon, pain_map; + for(int i = 0; i < nprems; i++){ + pcons[i] = conc(prems[i]); + npcons[i] = painfully_normalize_ineq(pcons[i],pain_map); + pcon_to_pf[npcons[i]] = pfs[i]; + npcon_to_pcon[npcons[i]] = pcons[i]; + } + // ast leq = make(Leq,arg(con,0),arg(con,1)); + ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); + pcons.push_back(mk_not(con)); + npcons.push_back(ncon); + // ast assumps = make(And,pcons); + ast new_proof; + if(is_sat(npcons,new_proof)) + throw "Proof error!"; + pfrule dk = pr(new_proof); + int nnp = num_prems(new_proof); + std::vector my_prems; + std::vector farkas_coeffs, my_pcons; + + if(dk == PR_TH_LEMMA + && get_theory_lemma_theory(new_proof) == ArithTheory + && get_theory_lemma_kind(new_proof) == FarkasKind) + get_farkas_coeffs(new_proof,farkas_coeffs); + else if(dk == PR_UNIT_RESOLUTION && nnp == 2){ + for(int i = 0; i < nprems; i++) + farkas_coeffs.push_back(make_int(rational(1))); + } + else + throw "cannot reconstruct farkas proof"; + + for(int i = 0; i < nnp; i++){ + ast p = conc(prem(new_proof,i)); + p = really_normalize_ineq(p); + if(pcon_to_pf.find(p) != pcon_to_pf.end()){ + my_prems.push_back(pcon_to_pf[p]); + my_pcons.push_back(npcon_to_pcon[p]); + } + else if(p == ncon){ + my_prems.push_back(iproof->make_hypothesis(mk_not(con))); + my_pcons.push_back(mk_not(con)); + } + else + throw "cannot reconstruct farkas proof"; + } + Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); + return res; + } + + + ast really_normalize_ineq(const ast &ineq){ ast res = normalize_inequality(ineq); res = make(op(res),SortSum(arg(res,0)),arg(res,1)); @@ -1302,7 +1387,7 @@ public: farkas_coeffs.push_back(make_int(rational(1))); } else - throw "cannot reconstruct farkas proof"; + return painfully_reconstruct_farkas(prems,pfs,con); for(int i = 0; i < nnp; i++){ ast p = conc(prem(new_proof,i)); @@ -1445,9 +1530,11 @@ public: lits.push_back(from_ast(con)); // pattern match some idioms - if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST && pr(prem(proof,1)) == PR_REWRITE ) { - res = iproof->make_axiom(lits); - return res; + if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST){ + if(get_locality_rec(prem(proof,1)) == INT_MAX) { + res = iproof->make_axiom(lits); + return res; + } } if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or){ Iproof::node clause = translate_main(prem(proof,0),true); @@ -1620,9 +1707,10 @@ public: break; case ArrayTheory: {// nothing fancy for this ast store_array; - if(!get_store_array(con,store_array)) - throw unsupported(); - res = iproof->make_axiom(lits,ast_scope(store_array)); + if(get_store_array(con,store_array)) + res = iproof->make_axiom(lits,ast_scope(store_array)); + else + res = iproof->make_axiom(lits); // for array extensionality axiom break; } default: diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp old mode 100644 new mode 100755 From f45ad4bdc01835774d6445fc7421b30259f4a3a6 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 10 Feb 2014 12:56:39 -0800 Subject: [PATCH 265/509] disable silly warnings and add needed header for VS --- src/cmd_context/interpolant_cmds.cpp | 2 +- src/duality/duality_profiling.cpp | 6 ++++++ src/duality/duality_rpfp.cpp | 12 ++++++++++-- src/duality/duality_solver.cpp | 7 +++++++ src/duality/duality_wrapper.cpp | 7 +++++++ src/duality/duality_wrapper.h | 1 - src/interp/iz3base.cpp | 6 ++++++ src/interp/iz3checker.cpp | 7 +++++++ src/interp/iz3interp.cpp | 8 ++++++++ src/interp/iz3mgr.cpp | 9 +++++++++ src/interp/iz3mgr.h | 1 + src/interp/iz3profiling.cpp | 7 +++++++ src/interp/iz3proof.cpp | 6 ++++++ src/interp/iz3proof_itp.cpp | 7 +++++++ src/interp/iz3translate.cpp | 7 +++++++ src/interp/iz3translate_direct.cpp | 8 ++++++++ src/muz/duality/duality_dl_interface.cpp | 6 ++++++ 17 files changed, 103 insertions(+), 4 deletions(-) diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index 318569cd5..7fd44c088 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -114,7 +114,7 @@ static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); - ptr_vector cnsts(end - it); + ptr_vector cnsts((unsigned)(end - it)); for (int i = 0; it != end; ++it, ++i) cnsts[i] = *it; diff --git a/src/duality/duality_profiling.cpp b/src/duality/duality_profiling.cpp index 3b392a91a..a4e9e0240 100755 --- a/src/duality/duality_profiling.cpp +++ b/src/duality/duality_profiling.cpp @@ -25,6 +25,12 @@ Revision History: #include #include +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#endif + #include "duality_wrapper.h" #include "iz3profiling.h" diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 17b93d35b..2d52ab283 100644 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -21,13 +21,21 @@ Revision History: -#include "duality.h" -#include "duality_profiling.h" +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#endif + #include #include #include #include + +#include "duality.h" +#include "duality_profiling.h" + #ifndef WIN32 // #define Z3OPS #endif diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 91532960b..973ad769c 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -19,6 +19,12 @@ Revision History: --*/ +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#endif + #include "duality.h" #include "duality_profiling.h" @@ -26,6 +32,7 @@ Revision History: #include #include #include +#include // TODO: make these official options or get rid of them diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 24f6a5e20..cf2c803cb 100644 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -18,6 +18,13 @@ Revision History: --*/ +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#endif + #include "duality_wrapper.h" #include #include "smt_solver.h" diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 94e8d2642..2b0045023 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -1466,6 +1466,5 @@ namespace std { }; } - #endif diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index f316c22cf..26146bfa3 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -18,6 +18,12 @@ Revision History: --*/ +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#endif #include "iz3base.h" #include diff --git a/src/interp/iz3checker.cpp b/src/interp/iz3checker.cpp index b4e55af20..d423ba48f 100755 --- a/src/interp/iz3checker.cpp +++ b/src/interp/iz3checker.cpp @@ -17,6 +17,13 @@ Revision History: --*/ +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#endif + #include "iz3base.h" #include "iz3checker.h" diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 56dc1ccec..c204cc35d 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -18,6 +18,14 @@ Revision History: --*/ /* Copyright 2011 Microsoft Research. */ + +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#endif + #include #include #include diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index aa8e8abe3..6664cd2fe 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -18,6 +18,15 @@ Revision History: --*/ +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#pragma warning(disable:4805) +#pragma warning(disable:4800) +#endif + #include "iz3mgr.h" #include diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 51a9a8697..e974a199b 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -263,6 +263,7 @@ class iz3mgr { default:; } assert(0); + return 0; } ast arg(const ast &t, int i){ diff --git a/src/interp/iz3profiling.cpp b/src/interp/iz3profiling.cpp index 262254271..2c4a31d58 100755 --- a/src/interp/iz3profiling.cpp +++ b/src/interp/iz3profiling.cpp @@ -17,6 +17,13 @@ Revision History: --*/ +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#endif + #include "iz3profiling.h" #include diff --git a/src/interp/iz3proof.cpp b/src/interp/iz3proof.cpp index 968a4b25a..3fefea73f 100755 --- a/src/interp/iz3proof.cpp +++ b/src/interp/iz3proof.cpp @@ -18,6 +18,12 @@ Revision History: --*/ +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#endif #include "iz3proof.h" #include "iz3profiling.h" diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 8b78a7135..61ed78463 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -17,6 +17,13 @@ Revision History: --*/ +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#endif + #include "iz3proof_itp.h" #ifndef WIN32 diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index abddb4bda..6a1b665cf 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -17,6 +17,13 @@ Revision History: --*/ +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#endif + #include "iz3translate.h" #include "iz3proof.h" #include "iz3profiling.h" diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index 0ce3297af..a85c9a1c5 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -20,6 +20,14 @@ Revision History: --*/ +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#pragma warning(disable:4390) +#endif + #include "iz3translate.h" #include "iz3proof.h" #include "iz3profiling.h" diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 397d50655..248aca163 100644 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -38,6 +38,12 @@ Revision History: // template class symbol_table; +#ifdef WIN32 +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#pragma warning(disable:4101) +#endif #include "duality.h" #include "duality_profiling.h" From e860c6556714994a84669e6bc1240bc4e799957c Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 13 Feb 2014 19:33:51 +0000 Subject: [PATCH 266/509] bugfix for sign computation in floating-point FMA Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 144925164..2d1d6705f 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1368,12 +1368,12 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar not_e_sgn = m_bv_util.mk_bv_not(e_sgn); not_f_sgn = m_bv_util.mk_bv_not(f_sgn); not_sign_bv = m_bv_util.mk_bv_not(sign_bv); - res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_e_sgn, e_sgn, sign_bv); + res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_e_sgn, f_sgn, sign_bv); res_sgn_c2 = m.mk_app(bvfid, OP_BAND, e_sgn, not_f_sgn, not_sign_bv); res_sgn_c3 = m.mk_app(bvfid, OP_BAND, e_sgn, f_sgn); expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); - + sticky_raw = m_bv_util.mk_extract(sbits-5, 0, sig_abs); sticky = m_bv_util.mk_zero_extend(sbits+3, m.mk_app(bvfid, OP_BREDOR, sticky_raw.get())); dbg_decouple("fpa2bv_fma_add_sum_sticky", sticky); From 88ff20a0fb7a858a3b5516fcb7bf15c7f832cf27 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 12 Feb 2014 14:48:39 -0800 Subject: [PATCH 267/509] fixed multiple interpolation bugs --- src/interp/iz3proof_itp.cpp | 93 +++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 2d79e8151..8662f34d5 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -745,6 +745,26 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } + ast unmixed_eq2ineq(const ast &lhs, const ast &rhs, opr comp_op, const ast &equa, ast &cond){ + ast ineqs= chain_ineqs(comp_op,LitA,equa,lhs,rhs); // chain must be from lhs to rhs + cond = my_and(cond,chain_conditions(LitA,equa)); + ast Bconds = z3_simplify(chain_conditions(LitB,equa)); + if(is_true(Bconds) && op(ineqs) != And) + return my_implies(cond,ineqs); + throw cannot_simplify(); + } + + ast add_mixed_eq2ineq(const ast &lhs, const ast &rhs, const ast &equa, const ast &itp){ + if(is_true(equa)) + return itp; + std::vector args(3); + args[0] = itp; + args[1] = make_int("1"); + ast ineq = make(Leq,make_int(rational(0)),make_int(rational(0))); + args[2] = make_normal(ineq,cons_normal(fix_normal(lhs,rhs,equa),mk_true())); + return simplify_sum(args); + } + ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){ if(pl == arg(pf,1)){ ast cond = mk_true(); @@ -752,16 +772,17 @@ class iz3proof_itp_impl : public iz3proof_itp { if(is_equivrel_chain(equa)){ ast lhs,rhs; eq_from_ineq(arg(neg_equality,0),lhs,rhs); // get inequality we need to prove LitType lhst = get_term_type(lhs), rhst = get_term_type(rhs); - if(lhst != LitMixed && rhst != LitMixed){ - ast ineqs= chain_ineqs(op(arg(neg_equality,0)),LitA,equa,lhs,rhs); // chain must be from lhs to rhs - cond = my_and(cond,chain_conditions(LitA,equa)); - ast Bconds = z3_simplify(chain_conditions(LitB,equa)); - if(is_true(Bconds) && op(ineqs) != And) - return my_implies(cond,ineqs); - } + if(lhst != LitMixed && rhst != LitMixed) + return unmixed_eq2ineq(lhs, rhs, op(arg(neg_equality,0)), equa, cond); else { - ast itp = make(Leq,make_int(rational(0)),make_int(rational(0))); - return make_normal(itp,cons_normal(fix_normal(lhs,rhs,equa),mk_true())); + ast left, left_term, middle, right_term, right; + left = get_left_movers(equa,lhs,middle,left_term); + middle = get_right_movers(middle,rhs,right,right_term); + ast itp = unmixed_eq2ineq(left_term, right_term, op(arg(neg_equality,0)), middle, cond); + // itp = my_implies(cond,itp); + itp = add_mixed_eq2ineq(lhs, left_term, left, itp); + itp = add_mixed_eq2ineq(right_term, rhs, right, itp); + return itp; } } } @@ -907,7 +928,9 @@ class iz3proof_itp_impl : public iz3proof_itp { get_subterm_normals(ineq1,ineq2,tail,nc,top_pos,memo, Aproves, Bproves); ast itp; if(is_rewrite_side(LitA,head)){ - itp = ineq1; + itp = make(Leq,make_int("0"),make_int("0")); + linear_comb(itp,make_int("1"),ineq1); // make sure it is normal form + //itp = ineq1; ast mc = z3_simplify(chain_side_proves(LitB,pref)); Bproves = my_and(Bproves,mc); } @@ -1508,6 +1531,43 @@ class iz3proof_itp_impl : public iz3proof_itp { return head; } + // split a rewrite chain into head and tail at last non-mixed term + ast get_right_movers(const ast &chain, const ast &rhs, ast &tail, ast &mid){ + if(is_true(chain) || get_term_type(rhs) != LitMixed){ + mid = rhs; + tail = mk_true(); + return chain; + } + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast mm = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); + ast res = get_right_movers(rest,mm,tail,mid); + tail = chain_cons(tail,last); + return chain; + } + + // split a rewrite chain into head and tail at first non-mixed term + ast get_left_movers(const ast &chain, const ast &lhs, ast &tail, ast &mid){ + if(is_true(chain)){ + mid = lhs; + return ast(); + } + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast res = get_left_movers(rest,lhs,tail,mid); + if(res.null()){ + mid = subst_in_pos(mid,rewrite_pos(last),rewrite_rhs(last)); + if(get_term_type(mid) != LitMixed){ + tail = mk_true(); + return chain; + } + return ast(); + } + tail = chain_cons(tail,last); + return res; + } + + struct cannot_split {}; /** Split a chain of rewrites two chains, operating on positions 0 and 1. @@ -2320,8 +2380,14 @@ class iz3proof_itp_impl : public iz3proof_itp { itp = mk_true(); break; default: { // mixed equality - if(get_term_type(x) == LitMixed || get_term_type(y) == LitMixed) - std::cerr << "WARNING: mixed term in leq2eq\n"; + if(get_term_type(x) == LitMixed || get_term_type(y) == LitMixed){ + // std::cerr << "WARNING: mixed term in leq2eq\n"; + std::vector lits; + lits.push_back(con); + lits.push_back(make(Not,xleqy)); + lits.push_back(make(Not,yleqx)); + return make_axiom(lits); + } std::vector conjs; conjs.resize(3); conjs[0] = mk_not(con); conjs[1] = xleqy; @@ -2456,7 +2522,8 @@ class iz3proof_itp_impl : public iz3proof_itp { frng = srng; // this term will be localized } else if(o == Plus || o == Times){ // don't want bound variables inside arith ops - frng = erng; // this term will be localized + // std::cout << "WARNING: non-local arithmetic\n"; + // frng = erng; // this term will be localized } std::vector largs(nargs); std::vector eqs; From cb3dc63e68ff6db81e3bbee5ad07791bf59eead6 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 14 Feb 2014 14:03:54 -0800 Subject: [PATCH 268/509] some interpolation fixes; make duality a bit more persistent in checking interpolant --- src/duality/duality_rpfp.cpp | 8 +-- src/interp/iz3mgr.cpp | 14 ++++++ src/interp/iz3proof_itp.cpp | 96 +++++++++++++++++++++++++----------- 3 files changed, 87 insertions(+), 31 deletions(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index ad89c1314..9813bce6a 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2703,10 +2703,12 @@ namespace Duality { const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) s.add(theory[i]); - if(s.check(lits.size(),&lits[0]) != unsat) - throw "should be unsat"; + for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! + if(s.check(lits.size(),&lits[0]) == unsat) + goto is_unsat; + throw "should be unsat"; } - + is_unsat: for(unsigned i = 0; i < conjuncts.size(); ){ std::swap(conjuncts[i],conjuncts.back()); std::swap(lits[i],lits.back()); diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 329fcb305..0a93d0e73 100755 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -240,6 +240,9 @@ iz3mgr::ast iz3mgr::clone(const ast &t, const std::vector &_args){ void iz3mgr::show(ast t){ + if(t.null()){ + std::cout << "(null)" << std::endl; + } params_ref p; p.set_bool("flat_assoc",false); std::cout << mk_pp(t.raw(), m(), p) << std::endl; @@ -875,3 +878,14 @@ iz3mgr::ast iz3mgr::apply_quant(opr quantifier, ast var, ast e){ std::vector bvs; bvs.push_back(var); return make_quant(quantifier,bvs,e); } + +#if 0 +void iz3mgr::get_bound_substitutes(stl_ext::hash_map &memo, const ast &e, const ast &var, std::vector &substs){ + std::pair foo(e,false); + std::pair::iterator,bool> bar = memo.insert(foo); + if(bar.second){ + if(op(e) == + } + +} +#endif diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 8662f34d5..cf86e9914 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -572,18 +572,36 @@ class iz3proof_itp_impl : public iz3proof_itp { return is_ineq(ineq); } + ast destruct_cond_ineq(const ast &ineq, ast &Aproves, ast &Bproves){ + ast res = ineq; + opr o = op(res); + if(o == And){ + Aproves = my_and(Aproves,arg(res,0)); + res = arg(res,1); + o = op(res); + } + if(o == Implies){ + Bproves = my_and(Bproves,arg(res,0)); + res = arg(res,1); + } + return res; + } + ast simplify_sum(std::vector &args){ ast Aproves = mk_true(), Bproves = mk_true(); - ast ineq = args[0]; + ast ineq = destruct_cond_ineq(args[0],Aproves,Bproves); if(!is_normal_ineq(ineq)) throw cannot_simplify(); sum_cond_ineq(ineq,args[1],args[2],Aproves,Bproves); return my_and(Aproves,my_implies(Bproves,ineq)); } ast simplify_rotate_sum(const ast &pl, const ast &pf){ - ast cond = mk_true(); + ast Aproves = mk_true(), Bproves = mk_true(); ast ineq = make(Leq,make_int("0"),make_int("0")); - return rotate_sum_rec(pl,pf,cond,ineq); + ineq = rotate_sum_rec(pl,pf,Aproves,Bproves,ineq); + if(is_true(Aproves) && is_true(Bproves)) + return ineq; + return my_and(Aproves,my_implies(Bproves,ineq)); } bool is_rewrite_chain(const ast &chain){ @@ -616,7 +634,11 @@ class iz3proof_itp_impl : public iz3proof_itp { void sum_cond_ineq(ast &ineq, const ast &coeff2, const ast &ineq2, ast &Aproves, ast &Bproves){ opr o = op(ineq2); - if(o == Implies){ + if(o == And){ + sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); + Aproves = my_and(Aproves,arg(ineq2,0)); + } + else if(o == Implies){ sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); Bproves = my_and(Bproves,arg(ineq2,0)); } @@ -676,26 +698,20 @@ class iz3proof_itp_impl : public iz3proof_itp { return make(op(ineq),mk_idiv(arg(ineq,0),divisor),mk_idiv(arg(ineq,1),divisor)); } - ast rotate_sum_rec(const ast &pl, const ast &pf, ast &Bproves, ast &ineq){ + ast rotate_sum_rec(const ast &pl, const ast &pf, ast &Aproves, ast &Bproves, ast &ineq){ if(pf == pl){ if(sym(ineq) == normal) - return my_implies(Bproves,ineq); - return my_implies(Bproves,simplify_ineq(ineq)); + return ineq; + return simplify_ineq(ineq); } if(op(pf) == Uninterpreted && sym(pf) == sum){ if(arg(pf,2) == pl){ - ast Aproves = mk_true(); sum_cond_ineq(ineq,make_int("1"),arg(pf,0),Aproves,Bproves); - if(!is_true(Aproves)) - throw "help!"; ineq = idiv_ineq(ineq,arg(pf,1)); - return my_implies(Bproves,ineq); + return ineq; } - ast Aproves = mk_true(); sum_cond_ineq(ineq,arg(pf,1),arg(pf,2),Aproves,Bproves); - if(!is_true(Aproves)) - throw "help!"; - return rotate_sum_rec(pl,arg(pf,0),Bproves,ineq); + return rotate_sum_rec(pl,arg(pf,0),Aproves,Bproves,ineq); } throw cannot_simplify(); } @@ -751,7 +767,9 @@ class iz3proof_itp_impl : public iz3proof_itp { ast Bconds = z3_simplify(chain_conditions(LitB,equa)); if(is_true(Bconds) && op(ineqs) != And) return my_implies(cond,ineqs); - throw cannot_simplify(); + if(op(ineqs) != And) + return my_and(Bconds,my_implies(cond,ineqs)); + throw "help!"; } ast add_mixed_eq2ineq(const ast &lhs, const ast &rhs, const ast &equa, const ast &itp){ @@ -786,7 +804,7 @@ class iz3proof_itp_impl : public iz3proof_itp { } } } - throw cannot_simplify(); + throw "help!"; } void reverse_modpon(std::vector &args){ @@ -972,7 +990,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast simplify_modpon(const std::vector &args){ ast Aproves = mk_true(), Bproves = mk_true(); ast chain = simplify_modpon_fwd(args,Bproves); - ast Q2 = sep_cond(args[2],Bproves); + ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); ast interp; if(is_normal_ineq(Q2)){ // inequalities are special ast nQ2 = rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); @@ -1543,13 +1561,17 @@ class iz3proof_itp_impl : public iz3proof_itp { ast mm = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); ast res = get_right_movers(rest,mm,tail,mid); tail = chain_cons(tail,last); - return chain; + return res; } // split a rewrite chain into head and tail at first non-mixed term ast get_left_movers(const ast &chain, const ast &lhs, ast &tail, ast &mid){ if(is_true(chain)){ - mid = lhs; + mid = lhs; + if(get_term_type(lhs) != LitMixed){ + tail = mk_true(); + return chain; + } return ast(); } ast last = chain_last(chain); @@ -1770,11 +1792,13 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast fix_normal(const ast &lhs, const ast &rhs, const ast &proof){ + LitType lhst = get_term_type(lhs); LitType rhst = get_term_type(rhs); - if(rhst != LitMixed || ast_id(lhs) < ast_id(rhs)) + if(lhst == LitMixed && (rhst != LitMixed || ast_id(lhs) < ast_id(rhs))) return make_normal_step(lhs,rhs,proof); - else + if(rhst == LitMixed && (lhst != LitMixed || ast_id(rhs) < ast_id(lhs))) return make_normal_step(rhs,lhs,reverse_chain(proof)); + throw "help!"; } ast chain_side_proves(LitType side, const ast &chain){ @@ -1794,8 +1818,10 @@ class iz3proof_itp_impl : public iz3proof_itp { ast lhs2 = normal_lhs(f2); int id1 = ast_id(lhs1); int id2 = ast_id(lhs2); - if(id1 < id2) return cons_normal(f1,merge_normal_chains_rec(normal_rest(chain1),chain2,trans,Aproves,Bproves)); - if(id2 < id1) return cons_normal(f2,merge_normal_chains_rec(chain1,normal_rest(chain2),trans,Aproves,Bproves)); + if(id1 < id2) + return cons_normal(f1,merge_normal_chains_rec(normal_rest(chain1),chain2,trans,Aproves,Bproves)); + if(id2 < id1) + return cons_normal(f2,merge_normal_chains_rec(chain1,normal_rest(chain2),trans,Aproves,Bproves)); ast rhs1 = normal_rhs(f1); ast rhs2 = normal_rhs(f2); LitType t1 = get_term_type(rhs1); @@ -1821,9 +1847,13 @@ class iz3proof_itp_impl : public iz3proof_itp { Aproves = my_and(Aproves,mcB); ast rep = apply_rewrite_chain(rhs1,Aproof); new_proof = concat_rewrite_chain(pf1,Aproof); - new_normal = make_normal_step(rhs1,rep,new_proof); + new_normal = make_normal_step(lhs1,rep,new_proof); + ast A_normal = make_normal_step(rhs1,rep,Aproof); + ast res = cons_normal(new_normal,merge_normal_chains_rec(normal_rest(chain1),normal_rest(chain2),trans,Aproves,Bproves)); + res = merge_normal_chains_rec(res,cons_normal(A_normal,make(True)),trans,Aproves,Bproves); + return res; } - else if(t1 == LitA && t2 == LitB) + else if(t1 == LitB && t2 == LitA) return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); else if(t1 == LitA) { ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); @@ -1845,17 +1875,20 @@ class iz3proof_itp_impl : public iz3proof_itp { return chain; ast f = normal_first(chain); ast r = normal_rest(chain); + r = trans_normal_chain(r,trans); ast rhs = normal_rhs(f); hash_map::iterator it = trans.find(rhs); ast new_normal; - if(it != trans.end()){ + if(it != trans.end() && get_term_type(normal_lhs(f)) == LitMixed){ const ast &f2 = it->second; ast pf = concat_rewrite_chain(normal_proof(f),normal_proof(f2)); new_normal = make_normal_step(normal_lhs(f),normal_rhs(f2),pf); } else new_normal = f; - return cons_normal(new_normal,trans_normal_chain(r,trans)); + if(get_term_type(normal_lhs(f)) == LitMixed) + trans[normal_lhs(f)] = new_normal; + return cons_normal(new_normal,r); } ast merge_normal_chains(const ast &chain1, const ast &chain2, ast &Aproves, ast &Bproves){ @@ -2525,6 +2558,13 @@ class iz3proof_itp_impl : public iz3proof_itp { // std::cout << "WARNING: non-local arithmetic\n"; // frng = erng; // this term will be localized } + else if(o == Select){ // treat the array term like a function symbol + prover::range srng = pv->ast_scope(arg(e,0)); + if(!(srng.lo > srng.hi) && pv->ranges_intersect(srng,rng)) // localize to desired range if possible + frng = pv->range_glb(srng,rng); + else + frng = srng; // this term will be localized + } std::vector largs(nargs); std::vector eqs; std::vector pfs; From 928d419138ebb463d613b9f164078d52f359f9b9 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 17 Feb 2014 12:15:11 -0800 Subject: [PATCH 269/509] duality fixes --- src/duality/duality.h | 22 +++++++++++++----- src/duality/duality_rpfp.cpp | 41 +++++++++++++++++++++++++--------- src/duality/duality_solver.cpp | 10 ++------- src/interp/iz3translate.cpp | 2 ++ 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 8c469b9e4..aacc04f24 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1184,7 +1184,13 @@ namespace Duality { hash_map EdgeCloneMap; std::vector alit_stack; std::vector alit_stack_sizes; - hash_map > edge_solvers; + + // to let us use one solver per edge + struct edge_solver { + hash_map AssumptionLits; + uptr slvr; + }; + hash_map edge_solvers; #ifdef LIMIT_STACK_WEIGHT struct weight_counter { @@ -1236,19 +1242,23 @@ namespace Duality { void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); - LogicSolver *SolverForEdge(Edge *edge, bool models); + edge_solver &SolverForEdge(Edge *edge, bool models); public: struct scoped_solver_for_edge { - LogicSolver *orig_ls; + solver *orig_slvr; RPFP_caching *rpfp; + edge_solver *es; scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false){ rpfp = _rpfp; - orig_ls = rpfp->ls; - rpfp->ls = rpfp->SolverForEdge(edge,models); + orig_slvr = rpfp->ls->slvr; + es = &(rpfp->SolverForEdge(edge,models)); + rpfp->ls->slvr = es->slvr.get(); + rpfp->AssumptionLits.swap(es->AssumptionLits); } ~scoped_solver_for_edge(){ - rpfp->ls = orig_ls; + rpfp->ls->slvr = orig_slvr; + rpfp->AssumptionLits.swap(es->AssumptionLits); } }; diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 9813bce6a..173959567 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2741,8 +2741,20 @@ namespace Duality { // verify check_result res = CheckCore(lits,full_core); - if(res != unsat) + if(res != unsat){ + // add the axioms in the off chance they are useful + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + GetAssumptionLits(theory[i],assumps); + lits = assumps; + std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); + + for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! + if((res = CheckCore(lits,full_core)) == unsat) + goto is_unsat; throw "should be unsat"; + } + is_unsat: FilterCore(core,full_core); std::vector dummy; @@ -2883,13 +2895,14 @@ namespace Duality { timer_stop("Generalize"); } - RPFP::LogicSolver *RPFP_caching::SolverForEdge(Edge *edge, bool models){ - uptr &p = edge_solvers[edge]; + RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models){ + edge_solver &es = edge_solvers[edge]; + uptr &p = es.slvr; if(!p.get()){ scoped_no_proof no_proofs_please(ctx.m()); // no proofs - p.set(new iZ3LogicSolver(ctx,models)); // no models + p.set(new solver(ctx,true,models)); // no models } - return p.get(); + return es; } @@ -3356,6 +3369,8 @@ namespace Duality { } } + bool some_labels = false; + // create the edges for(unsigned i = 0; i < clauses.size(); i++){ @@ -3391,17 +3406,23 @@ namespace Duality { Term labeled = body; std::vector lbls; // TODO: throw this away for now body = RemoveLabels(body,lbls); + if(!eq(labeled,body)) + some_labels = true; // remember if there are labels, as we then can't do qe_lite // body = IneqToEq(body); // UFO converts x=y to (x<=y & x >= y). Undo this. body = body.simplify(); #ifdef USE_QE_LITE std::set idxs; - for(unsigned j = 0; j < Indparams.size(); j++) - if(Indparams[j].is_var()) - idxs.insert(Indparams[j].get_index_value()); - body = body.qe_lite(idxs,false); + if(!some_labels){ // can't do qe_lite if we have to reconstruct labels + for(unsigned j = 0; j < Indparams.size(); j++) + if(Indparams[j].is_var()) + idxs.insert(Indparams[j].get_index_value()); + body = body.qe_lite(idxs,false); + } hash_map > sb_memo; body = SubstBoundRec(sb_memo,substs[i],0,body); + if(some_labels) + labeled = SubstBoundRec(sb_memo,substs[i],0,labeled); for(unsigned j = 0; j < Indparams.size(); j++) Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); #endif @@ -3663,7 +3684,7 @@ namespace Duality { for(unsigned j = 0; j < outs.size(); j++) for(unsigned k = 0; k < outs[j]->Children.size(); k++) chs.push_back(outs[j]->Children[k]); - Edge *fedge = CreateEdge(node,tr,chs); + CreateEdge(node,tr,chs); for(unsigned j = 0; j < outs.size(); j++) edges_to_delete.insert(outs[j]); } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 6d39a4fe2..e3a294550 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -127,13 +127,11 @@ namespace Duality { { scoped_no_proof no_proofs_please(ctx.m()); #ifdef USE_RPFP_CLONE - clone_ls = new RPFP::iZ3LogicSolver(ctx, false); // no models needed for this one - clone_rpfp = new RPFP_caching(clone_ls); + clone_rpfp = new RPFP_caching(rpfp->ls); clone_rpfp->Clone(rpfp); #endif #ifdef USE_NEW_GEN_CANDS - gen_cands_ls = new RPFP::iZ3LogicSolver(ctx); - gen_cands_rpfp = new RPFP_caching(gen_cands_ls); + gen_cands_rpfp = new RPFP_caching(rpfp->ls); gen_cands_rpfp->Clone(rpfp); #endif } @@ -142,20 +140,16 @@ namespace Duality { ~Duality(){ #ifdef USE_RPFP_CLONE delete clone_rpfp; - delete clone_ls; #endif #ifdef USE_NEW_GEN_CANDS delete gen_cands_rpfp; - delete gen_cands_ls; #endif } #ifdef USE_RPFP_CLONE - RPFP::LogicSolver *clone_ls; RPFP_caching *clone_rpfp; #endif #ifdef USE_NEW_GEN_CANDS - RPFP::LogicSolver *gen_cands_ls; RPFP_caching *gen_cands_rpfp; #endif diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index bba7b4d0d..ef0c6f5ec 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -181,6 +181,7 @@ public: get_Z3_lits(con, lits); iproof->make_axiom(lits); } +#ifdef LOCALIZATION_KLUDGE else if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST && get_locality_rec(prem(proof,1)) == INT_MAX){ std::vector lits; @@ -188,6 +189,7 @@ public: get_Z3_lits(con, lits); iproof->make_axiom(lits); } +#endif else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ From 76fb66314b12d38c423c77001b05c9b1d7dbfe53 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 19 Feb 2014 13:56:16 -0800 Subject: [PATCH 270/509] interpolation fix for commutativity --- src/interp/iz3translate.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index ef0c6f5ec..fd4cbb32b 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1547,12 +1547,20 @@ public: if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or) std::cout << "foo!\n"; +#if 0 if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,1)) == PR_COMMUTATIVITY){ Iproof::node clause = translate_main(prem(proof,0),true); res = make(commute,clause,conc(prem(proof,0))); // HACK -- we depend on Iproof::node being same as ast. return res; } + if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,0)) == PR_COMMUTATIVITY){ + Iproof::node clause = translate_main(prem(proof,1),true); + res = make(commute,clause,conc(prem(proof,1))); // HACK -- we depend on Iproof::node being same as ast. + return res; + } +#endif + if(dk == PR_TRANSITIVITY && is_eq_propagate(prem(proof,1))){ try { res = CombineEqPropagate(proof); @@ -1736,6 +1744,12 @@ public: res = args[0]; break; } + case PR_COMMUTATIVITY: { + ast comm_equiv = make(op(con),arg(con,0),arg(con,0)); + ast pf = iproof->make_reflexivity(comm_equiv); + res = make(commute,pf,comm_equiv); + break; + } default: assert(0 && "translate_main: unsupported proof rule"); throw unsupported(); From c9cf658e7ccc19f470789e923e10f84ffa7d54f2 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 19 Feb 2014 13:56:55 -0800 Subject: [PATCH 271/509] interpolation fix for commutativity --- src/interp/iz3proof_itp.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index cf86e9914..fac7849ec 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2146,8 +2146,14 @@ class iz3proof_itp_impl : public iz3proof_itp { /** Make a Reflexivity node. This rule produces |- x = x */ virtual node make_reflexivity(ast con){ - throw proof_error(); -} + if(get_term_type(con) == LitA) + return mk_false(); + if(get_term_type(con) == LitB) + return mk_true(); + ast itp = make(And,make(contra,no_proof,mk_false()), + make(contra,mk_true(),mk_not(con))); + return itp; + } /** Make a Symmetry node. This takes a derivation of |- x = y and produces | y = x. Ditto for ~(x=y) */ From 65c54b87d09495c711f9a0b140c875f814b9ec07 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 19 Feb 2014 13:57:27 -0800 Subject: [PATCH 272/509] duality fix --- src/duality/duality.h | 6 +++--- src/duality/duality_rpfp.cpp | 8 +++++++- src/duality/duality_solver.cpp | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index aacc04f24..e1d058f10 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1242,17 +1242,17 @@ namespace Duality { void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); - edge_solver &SolverForEdge(Edge *edge, bool models); + edge_solver &SolverForEdge(Edge *edge, bool models, bool axioms); public: struct scoped_solver_for_edge { solver *orig_slvr; RPFP_caching *rpfp; edge_solver *es; - scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false){ + scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false, bool axioms = false){ rpfp = _rpfp; orig_slvr = rpfp->ls->slvr; - es = &(rpfp->SolverForEdge(edge,models)); + es = &(rpfp->SolverForEdge(edge,models,axioms)); rpfp->ls->slvr = es->slvr.get(); rpfp->AssumptionLits.swap(es->AssumptionLits); } diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 173959567..6b9f3ba00 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2895,12 +2895,18 @@ namespace Duality { timer_stop("Generalize"); } - RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models){ + RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models, bool axioms){ edge_solver &es = edge_solvers[edge]; uptr &p = es.slvr; if(!p.get()){ scoped_no_proof no_proofs_please(ctx.m()); // no proofs p.set(new solver(ctx,true,models)); // no models + if(axioms){ + RPFP::LogicSolver *ls = edge->owner->ls; + const std::vector &axs = ls->get_axioms(); + for(unsigned i = 0; i < axs.size(); i++) + p.get()->add(axs[i]); + } } return es; } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index e3a294550..277789ce9 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1247,7 +1247,7 @@ namespace Duality { slvr.pop(1); delete checker; #else - RPFP_caching::scoped_solver_for_edge(gen_cands_rpfp,edge,true /* models */); + RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); gen_cands_rpfp->Push(); Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); if(gen_cands_rpfp->Check(root) != unsat){ @@ -2004,7 +2004,7 @@ namespace Duality { } else { was_sat = true; - tree->Push(); + tree->Push(); std::vector &expansions = stack.back().expansions; #ifndef NO_DECISIONS for(unsigned i = 0; i < expansions.size(); i++){ From e077fc5cb41de38b6ef56b071d71fc4fc1a0a442 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 20 Feb 2014 14:09:55 -0800 Subject: [PATCH 273/509] fix(api/python): make sure Z3 compiles using Python3 Signed-off-by: Leonardo de Moura --- scripts/mk_util.py | 16 ++++++++-------- src/api/python/z3.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 2fff61778..906c6b2c8 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -640,7 +640,7 @@ def is_CXX_gpp(): def is_clang_in_gpp_form(cc): version_string = subprocess.check_output([cc, '--version']) - return version_string.find('clang') != -1 + return str(version_string).find('clang') != -1 def is_CXX_clangpp(): if is_compiler(CXX, 'g++'): @@ -1437,7 +1437,7 @@ def mk_config(): 'SO_EXT=.dll\n' 'SLINK=cl\n' 'SLINK_OUT_FLAG=/Fe\n' - 'OS_DEFINES=/D _WINDOWS\n') + 'OS_DEFINES=/D _WINDOWS\n') extra_opt = '' if GIT_HASH: extra_opt = '%s /D Z3GITHASH=%s' % (extra_opt, GIT_HASH) @@ -1485,7 +1485,7 @@ def mk_config(): print('Java Compiler: %s' % JAVAC) else: global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG - OS_DEFINES = "" + OS_DEFINES = "" ARITH = "internal" check_ar() CXX = find_cxx_compiler() @@ -1508,7 +1508,7 @@ def mk_config(): SLIBEXTRAFLAGS = '%s %s' % (SLIBEXTRAFLAGS,FOCI2LIB) CPPFLAGS = '%s -D_FOCI2' % CPPFLAGS else: - print "FAILED\n" + print("FAILED\n") FOCI2 = False if GIT_HASH: CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH) @@ -1536,21 +1536,21 @@ def mk_config(): SLIBFLAGS = '-dynamiclib' elif sysname == 'Linux': CXXFLAGS = '%s -fno-strict-aliasing -D_LINUX_' % CXXFLAGS - OS_DEFINES = '-D_LINUX' + OS_DEFINES = '-D_LINUX' SO_EXT = '.so' LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS elif sysname == 'FreeBSD': CXXFLAGS = '%s -fno-strict-aliasing -D_FREEBSD_' % CXXFLAGS - OS_DEFINES = '-D_FREEBSD_' + OS_DEFINES = '-D_FREEBSD_' SO_EXT = '.so' LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS elif sysname[:6] == 'CYGWIN': CXXFLAGS = '%s -D_CYGWIN -fno-strict-aliasing' % CXXFLAGS - OS_DEFINES = '-D_CYGWIN' + OS_DEFINES = '-D_CYGWIN' SO_EXT = '.dll' SLIBFLAGS = '-shared' else: @@ -1586,7 +1586,7 @@ def mk_config(): config.write('SLINK_FLAGS=%s\n' % SLIBFLAGS) config.write('SLINK_EXTRA_FLAGS=%s\n' % SLIBEXTRAFLAGS) config.write('SLINK_OUT_FLAG=-o \n') - config.write('OS_DEFINES=%s\n' % OS_DEFINES) + config.write('OS_DEFINES=%s\n' % OS_DEFINES) if is_verbose(): print('Host platform: %s' % sysname) print('C++ Compiler: %s' % CXX) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 959566619..9233a41dc 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -586,7 +586,7 @@ class FuncDeclRef(AstRef): return Z3_func_decl_to_ast(self.ctx_ref(), self.ast) def as_func_decl(self): - return self.ast + return self.ast def name(self): """Return the name of the function declaration `self`. From d51d9b18f944acc436c0acead229eae0ae8e9625 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 21 Feb 2014 13:00:02 +0000 Subject: [PATCH 274/509] Bugfixes for compilation in Cygwin (WIN32 -> _WINDOWS) Signed-off-by: Christoph M. Wintersteiger --- src/duality/duality.h | 4 ++-- src/duality/duality_wrapper.h | 4 ++-- src/interp/iz3hash.h | 12 ++++++------ src/interp/iz3mgr.h | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index e1d058f10..7db9a4479 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -25,7 +25,7 @@ Revision History: #include // make hash_map and hash_set available -#ifndef WIN32 +#ifndef _WINDOWS using namespace stl_ext; #endif @@ -782,7 +782,7 @@ protected: }; -#ifdef WIN32 +#ifdef _WINDOWS __declspec(dllexport) #endif void FromClauses(const std::vector &clauses); diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 2b0045023..8a77a69da 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -1402,7 +1402,7 @@ namespace hash_space { } // to make Duality::ast hashable in windows -#ifdef WIN32 +#ifdef _WINDOWS template <> inline size_t stdext::hash_value(const Duality::ast& s) { @@ -1446,7 +1446,7 @@ namespace hash_space { } // to make Duality::func_decl hashable in windows -#ifdef WIN32 +#ifdef _WINDOWS template <> inline size_t stdext::hash_value(const Duality::func_decl& s) { diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index 94f282265..a564026bd 100755 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -42,7 +42,7 @@ Revision History: #include #include #else -#ifdef WIN32 +#ifdef _WINDOWS #define stl_ext stdext #define hash_space std #include @@ -61,7 +61,7 @@ Revision History: // stupid STL doesn't include hash function for class string -#ifndef WIN32 +#ifndef _WINDOWS namespace stl_ext { template <> @@ -86,7 +86,7 @@ namespace hash_space { }; } -#ifdef WIN32 +#ifdef _WINDOWS template <> inline size_t stdext::hash_value >(const std::pair& p) { // hash _Keyval to size_t value one-to-one @@ -112,7 +112,7 @@ size_t stdext::hash_value >(const std::pair& p) } #endif -#ifdef WIN32 +#ifdef _WINDOWS namespace std { template <> @@ -139,7 +139,7 @@ namespace std { #endif -#ifndef WIN32 +#ifndef _WINDOWS #if 0 namespace stl_ext { @@ -155,7 +155,7 @@ namespace stl_ext { #endif -#ifdef WIN32 +#ifdef _WINDOWS diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index e974a199b..d9593ced5 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -127,7 +127,7 @@ namespace hash_space { } // to make ast_r hashable in windows -#ifdef WIN32 +#ifdef _WINDOWS template <> inline size_t stdext::hash_value(const ast_r& s) { From 07d56bdc705cc5caf7a6fa5776095f9d7289918a Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 21 Feb 2014 13:44:39 +0000 Subject: [PATCH 275/509] Java API bugfixes for cygwin compilation Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_util.py | 6 +++--- scripts/update_api.py | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 906c6b2c8..d425f6576 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1198,9 +1198,9 @@ class JavaDLLComponent(Component): deps += '%s ' % os.path.join(self.to_src_dir, 'enumerations', jfile) out.write(deps) out.write('\n') - if IS_WINDOWS: - JAVAC = '"%s"' % JAVAC - JAR = '"%s"' % JAR + #if IS_WINDOWS: + JAVAC = '"%s"' % JAVAC + JAR = '"%s"' % JAR t = ('\t%s %s.java -d %s\n' % (JAVAC, os.path.join(self.to_src_dir, 'enumerations', '*'), os.path.join('api', 'java', 'classes'))) out.write(t) t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC, diff --git a/scripts/update_api.py b/scripts/update_api.py index fa6111482..f88e0393f 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -523,7 +523,7 @@ def mk_java(): java_native.write(' public static class LongPtr { public long value; }\n') java_native.write(' public static class StringPtr { public String value; }\n') java_native.write(' public static native void setInternalErrorHandler(long ctx);\n\n') - if IS_WINDOWS: + if IS_WINDOWS or os.uname()[0]=="CYGWIN": java_native.write(' static { System.loadLibrary("%s"); }\n' % get_component('java').dll_name) else: java_native.write(' static { System.loadLibrary("%s"); }\n' % get_component('java').dll_name[3:]) # We need 3: to extract the prexi 'lib' form the dll_name @@ -588,6 +588,9 @@ def mk_java(): java_wrapper = open(java_wrapperf, 'w') pkg_str = get_component('java').package_name.replace('.', '_') java_wrapper.write('// Automatically generated file\n') + java_wrapper.write('#ifdef _CYGWIN\n') + java_wrapper.write('typedef long long __int64;\n') + java_wrapper.write('#endif\n') java_wrapper.write('#include\n') java_wrapper.write('#include\n') java_wrapper.write('#include"z3.h"\n') From 4a9f12dd3431e9df0ae9b8578df73ccfca943c69 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 24 Feb 2014 13:57:15 +0000 Subject: [PATCH 276/509] bugfix for FPA --- src/tactic/fpa/qffpa_tactic.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tactic/fpa/qffpa_tactic.cpp b/src/tactic/fpa/qffpa_tactic.cpp index 90cfc8c92..73faa5b50 100644 --- a/src/tactic/fpa/qffpa_tactic.cpp +++ b/src/tactic/fpa/qffpa_tactic.cpp @@ -52,6 +52,8 @@ struct is_non_qffpa_predicate { sort * s = get_sort(n); if (!m.is_bool(s) && !(u.is_float(s) || u.is_rm(s))) throw found(); + if (is_uninterp(n)) + throw found(); family_id fid = s->get_family_id(); if (fid == m.get_basic_family_id()) return; @@ -78,6 +80,8 @@ struct is_non_qffpabv_predicate { sort * s = get_sort(n); if (!m.is_bool(s) && !(fu.is_float(s) || fu.is_rm(s) || bu.is_bv_sort(s))) throw found(); + if (is_uninterp(n)) + throw found(); family_id fid = s->get_family_id(); if (fid == m.get_basic_family_id()) return; From efd0cdc740e15d6095ba57aa9b22b97cea488981 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 24 Feb 2014 14:01:51 +0000 Subject: [PATCH 277/509] bugfix for FPA --- src/tactic/fpa/qffpa_tactic.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tactic/fpa/qffpa_tactic.cpp b/src/tactic/fpa/qffpa_tactic.cpp index 73faa5b50..772fd3996 100644 --- a/src/tactic/fpa/qffpa_tactic.cpp +++ b/src/tactic/fpa/qffpa_tactic.cpp @@ -52,7 +52,7 @@ struct is_non_qffpa_predicate { sort * s = get_sort(n); if (!m.is_bool(s) && !(u.is_float(s) || u.is_rm(s))) throw found(); - if (is_uninterp(n)) + if (is_uninterp(n) && n->get_num_args() != 0) throw found(); family_id fid = s->get_family_id(); if (fid == m.get_basic_family_id()) @@ -80,7 +80,7 @@ struct is_non_qffpabv_predicate { sort * s = get_sort(n); if (!m.is_bool(s) && !(fu.is_float(s) || fu.is_rm(s) || bu.is_bv_sort(s))) throw found(); - if (is_uninterp(n)) + if (is_uninterp(n) && n->get_num_args() != 0) throw found(); family_id fid = s->get_family_id(); if (fid == m.get_basic_family_id()) From 7fc011dbb81242d03fb8009bc25b5dbd24b3379d Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 24 Feb 2014 11:59:02 -0800 Subject: [PATCH 278/509] interpolation fixes --- src/interp/iz3interp.cpp | 2 +- src/interp/iz3proof_itp.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index c204cc35d..73eaab841 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -64,7 +64,7 @@ struct frame_reducer : public iz3mgr { : iz3mgr(other) {} void get_proof_assumptions_rec(z3pf proof, hash_set &memo, std::vector &used_frames){ - if(memo.count(proof))return; + if(memo.find(proof) != memo.end())return; memo.insert(proof); pfrule dk = pr(proof); if(dk == PR_ASSERTED){ diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 303d90b1b..9d241398c 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -680,7 +680,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast dummy1, dummy2; sum_cond_ineq(in1,coeff2,in2,dummy1,dummy2); n1 = merge_normal_chains(n1,n2, Aproves, Bproves); - ineq = make_normal(in1,n1); + ineq = is_true(n1) ? in1 : make_normal(in1,n1); } bool is_ineq(const ast &ineq){ @@ -760,6 +760,8 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast round_ineq(const ast &ineq){ + if(sym(ineq) == normal) + return make_normal(round_ineq(arg(ineq,0)),arg(ineq,1)); if(!is_ineq(ineq)) throw cannot_simplify(); ast res = simplify_ineq(ineq); @@ -1689,7 +1691,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast diff; if(comp_op == Leq) diff = make(Sub,rhs,mid); else diff = make(Sub,mid,rhs); - ast foo = z3_simplify(make(Leq,make_int("0"),diff)); + ast foo = make(Leq,make_int("0"),z3_simplify(diff)); if(is_true(cond)) cond = foo; else { From b968eb2b8cf75385523955e51f99e689b31a19b8 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 25 Feb 2014 18:13:16 +0000 Subject: [PATCH 279/509] FPA probe bugfixes Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/qffpa_tactic.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/tactic/fpa/qffpa_tactic.cpp b/src/tactic/fpa/qffpa_tactic.cpp index 772fd3996..04ffc281a 100644 --- a/src/tactic/fpa/qffpa_tactic.cpp +++ b/src/tactic/fpa/qffpa_tactic.cpp @@ -42,7 +42,7 @@ struct is_non_qffpa_predicate { ast_manager & m; float_util u; - is_non_qffpa_predicate(ast_manager & _m) :m(_m), u(m) {} + is_non_qffpa_predicate(ast_manager & _m) : m(_m), u(m) {} void operator()(var *) { throw found(); } @@ -50,11 +50,9 @@ struct is_non_qffpa_predicate { void operator()(app * n) { sort * s = get_sort(n); - if (!m.is_bool(s) && !(u.is_float(s) || u.is_rm(s))) + if (!m.is_bool(s) && !u.is_float(s) && !u.is_rm(s)) throw found(); - if (is_uninterp(n) && n->get_num_args() != 0) - throw found(); - family_id fid = s->get_family_id(); + family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) @@ -70,7 +68,7 @@ struct is_non_qffpabv_predicate { bv_util bu; float_util fu; - is_non_qffpabv_predicate(ast_manager & _m) :m(_m), bu(m), fu(m) {} + is_non_qffpabv_predicate(ast_manager & _m) : m(_m), bu(m), fu(m) {} void operator()(var *) { throw found(); } @@ -78,11 +76,9 @@ struct is_non_qffpabv_predicate { void operator()(app * n) { sort * s = get_sort(n); - if (!m.is_bool(s) && !(fu.is_float(s) || fu.is_rm(s) || bu.is_bv_sort(s))) + if (!m.is_bool(s) && !fu.is_float(s) && !fu.is_rm(s) && !bu.is_bv_sort(s)) throw found(); - if (is_uninterp(n) && n->get_num_args() != 0) - throw found(); - family_id fid = s->get_family_id(); + family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == fu.get_family_id() || fid == bu.get_family_id()) From 4c8bbad8d67bca51930788392792352df209564b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 25 Feb 2014 18:16:28 +0000 Subject: [PATCH 280/509] FPA probe bugfix Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/qffpa_tactic.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tactic/fpa/qffpa_tactic.cpp b/src/tactic/fpa/qffpa_tactic.cpp index 04ffc281a..f9b7f88a1 100644 --- a/src/tactic/fpa/qffpa_tactic.cpp +++ b/src/tactic/fpa/qffpa_tactic.cpp @@ -57,6 +57,8 @@ struct is_non_qffpa_predicate { return; if (fid == u.get_family_id()) return; + if (is_uninterp_const(n)) + return; throw found(); } @@ -83,6 +85,8 @@ struct is_non_qffpabv_predicate { return; if (fid == fu.get_family_id() || fid == bu.get_family_id()) return; + if (is_uninterp_const(n)) + return; throw found(); } From ee9907a700903cbbc86cd1e2669b019f529cb908 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 25 Feb 2014 18:19:50 -0800 Subject: [PATCH 281/509] two interpolation fixes --- src/interp/iz3proof_itp.cpp | 112 +++++++++++++++++++++++++++++------- 1 file changed, 91 insertions(+), 21 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 9d241398c..1eb4fcc84 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -729,29 +729,31 @@ class iz3proof_itp_impl : public iz3proof_itp { ast x = arg(equality,0); ast y = arg(equality,1); ast Aproves1 = mk_true(), Bproves1 = mk_true(); - ast xleqy = round_ineq(ineq_from_chain(arg(pf,1),Aproves1,Bproves1)); - ast yleqx = round_ineq(ineq_from_chain(arg(pf,2),Aproves1,Bproves1)); + ast pf1 = destruct_cond_ineq(arg(pf,1), Aproves1, Bproves1); + ast pf2 = destruct_cond_ineq(arg(pf,2), Aproves1, Bproves1); + ast xleqy = round_ineq(ineq_from_chain(pf1,Aproves1,Bproves1)); + ast yleqx = round_ineq(ineq_from_chain(pf2,Aproves1,Bproves1)); ast ineq1 = make(Leq,make_int("0"),make_int("0")); sum_cond_ineq(ineq1,make_int("-1"),xleqy,Aproves1,Bproves1); sum_cond_ineq(ineq1,make_int("-1"),yleqx,Aproves1,Bproves1); - Bproves1 = my_and(Bproves1,z3_simplify(ineq1)); + ast Acond = my_implies(Aproves1,my_and(Bproves1,z3_simplify(ineq1))); ast Aproves2 = mk_true(), Bproves2 = mk_true(); ast ineq2 = make(Leq,make_int("0"),make_int("0")); sum_cond_ineq(ineq2,make_int("1"),xleqy,Aproves2,Bproves2); sum_cond_ineq(ineq2,make_int("1"),yleqx,Aproves2,Bproves2); - Bproves2 = z3_simplify(ineq2); - if(!is_true(Aproves1) || !is_true(Aproves2)) - throw "help!"; + ast Bcond = my_implies(Bproves1,my_and(Aproves1,z3_simplify(ineq2))); + // if(!is_true(Aproves1) || !is_true(Bproves1)) + // std::cout << "foo!\n";; if(get_term_type(x) == LitA){ ast iter = z3_simplify(make(Plus,x,get_ineq_rhs(xleqy))); - ast rewrite1 = make_rewrite(LitA,top_pos,Bproves1,make(Equal,x,iter)); - ast rewrite2 = make_rewrite(LitB,top_pos,Bproves2,make(Equal,iter,y)); + ast rewrite1 = make_rewrite(LitA,top_pos,Acond,make(Equal,x,iter)); + ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); } if(get_term_type(y) == LitA){ ast iter = z3_simplify(make(Plus,y,get_ineq_rhs(yleqx))); - ast rewrite2 = make_rewrite(LitA,top_pos,Bproves1,make(Equal,iter,y)); - ast rewrite1 = make_rewrite(LitB,top_pos,Bproves2,make(Equal,x,iter)); + ast rewrite2 = make_rewrite(LitA,top_pos,Acond,make(Equal,iter,y)); + ast rewrite1 = make_rewrite(LitB,top_pos,Bcond,make(Equal,x,iter)); return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); } throw cannot_simplify(); @@ -922,6 +924,8 @@ class iz3proof_itp_impl : public iz3proof_itp { return chain; } + struct subterm_normals_failed {}; + void get_subterm_normals(const ast &ineq1, const ast &ineq2, const ast &chain, ast &normals, const ast &pos, hash_set &memo, ast &Aproves, ast &Bproves){ opr o1 = op(ineq1); @@ -935,14 +939,77 @@ class iz3proof_itp_impl : public iz3proof_itp { get_subterm_normals(arg(ineq1,i), arg(ineq2,i), chain, normals, new_pos, memo, Aproves, Bproves); } } - else if(get_term_type(ineq2) == LitMixed && memo.find(ineq2) == memo.end()){ - memo.insert(ineq2); - ast sub_chain = extract_rewrites(chain,pos); - if(is_true(sub_chain)) - throw "bad inequality rewriting"; - ast new_normal = make_normal_step(ineq2,ineq1,reverse_chain(sub_chain)); - normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); + else if(get_term_type(ineq2) == LitMixed){ + if(memo.find(ineq2) == memo.end()){ + memo.insert(ineq2); + ast sub_chain = extract_rewrites(chain,pos); + if(is_true(sub_chain)) + throw "bad inequality rewriting"; + ast new_normal = make_normal_step(ineq2,ineq1,reverse_chain(sub_chain)); + normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); + } } + else if(!(ineq1 == ineq2)) + throw subterm_normals_failed(); + } + + ast rewrites_to_normals(const ast &ineq1, const ast &chain, ast &normals, ast &Aproves, ast &Bproves, ast &Aineqs){ + if(is_true(chain)) + return ineq1; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast new_ineq1 = rewrites_to_normals(ineq1, rest, normals, Aproves, Bproves, Aineqs); + ast p1 = rewrite_pos(last); + ast term1; + ast coeff = arith_rewrite_coeff(new_ineq1,p1,term1); + ast res = subst_in_pos(new_ineq1,rewrite_pos(last),rewrite_rhs(last)); + ast rpos; + pos_diff(p1,rewrite_pos(last),rpos); + ast term2 = subst_in_pos(term1,rpos,rewrite_rhs(last)); + if(get_term_type(term1) != LitMixed && get_term_type(term2) != LitMixed){ + if(is_rewrite_side(LitA,last)) + linear_comb(Aineqs,coeff,make(Leq,make_int(rational(0)),make(Sub,term2,term1))); + } + else { + ast pf = extract_rewrites(make(concat,mk_true(),rest),p1); + ast new_normal = fix_normal(term1,term2,pf); + normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); + } + return res; + } + + ast arith_rewrite_coeff(const ast &ineq, ast &p1, ast &term){ + ast coeff = make_int(rational(1)); + if(p1 == top_pos){ + term = ineq; + return coeff; + } + int argpos = pos_arg(p1); + opr o = op(ineq); + switch(o){ + case Leq: + case Lt: + coeff = argpos ? make_int(rational(1)) : make_int(rational(-1)); + break; + case Geq: + case Gt: + coeff = argpos ? make_int(rational(-1)) : make_int(rational(1)); + break; + case Not: + case Plus: + break; + case Times: + coeff = arg(ineq,0); + break; + default: + p1 = top_pos; + term = ineq; + return coeff; + } + p1 = arg(p1,1); + ast res = arith_rewrite_coeff(arg(ineq,argpos),p1,term); + p1 = pos_add(argpos,p1); + return coeff == make_int(rational(1)) ? res : make(Times,coeff,res); } ast rewrite_chain_to_normal_ineq(const ast &chain, ast &Aproves, ast &Bproves){ @@ -952,17 +1019,20 @@ class iz3proof_itp_impl : public iz3proof_itp { ast ineq2 = apply_rewrite_chain(ineq1,tail); ast nc = mk_true(); hash_set memo; - get_subterm_normals(ineq1,ineq2,tail,nc,top_pos,memo, Aproves, Bproves); - ast itp; + ast itp = make(Leq,make_int(rational(0)),make_int(rational(0))); + ast Aproves_save = Aproves, Bproves_save = Bproves; try { + get_subterm_normals(ineq1,ineq2,tail,nc,top_pos,memo, Aproves, Bproves); + } + catch (const subterm_normals_failed &){ Aproves = Aproves_save; Bproves = Bproves_save; nc = mk_true(); + rewrites_to_normals(ineq1, tail, nc, Aproves, Bproves, itp); + } if(is_rewrite_side(LitA,head)){ - itp = make(Leq,make_int("0"),make_int("0")); linear_comb(itp,make_int("1"),ineq1); // make sure it is normal form //itp = ineq1; ast mc = z3_simplify(chain_side_proves(LitB,pref)); Bproves = my_and(Bproves,mc); } else { - itp = make(Leq,make_int(rational(0)),make_int(rational(0))); ast mc = z3_simplify(chain_side_proves(LitA,pref)); Aproves = my_and(Aproves,mc); } From 4f06b347b340c1c6308795e434283b25952fd23c Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 25 Feb 2014 18:21:06 -0800 Subject: [PATCH 282/509] new hastable implementation for interp/duality --- src/interp/iz3hash.h | 542 +++++++++++++++++++++++++++++++++---------- 1 file changed, 420 insertions(+), 122 deletions(-) mode change 100755 => 100644 src/interp/iz3hash.h diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h old mode 100755 new mode 100644 index 94f282265..c628ef32b --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -7,7 +7,16 @@ Module Name: Abstract: - Wrapper for stl hash tables + Simple implementation of bucket-list hash tables conforming to SGI + hash_map and hash_set interfaces. Just enough members are + implemented to support iz3 and duality. + + iz3 and duality need this package because they assume that insert + preserves iterators and references to elements, which is not true + of the hashtable packages in util. + + This package lives in namespace hash_space. Specializations of + class "hash" should be made in this namespace. Author: @@ -17,66 +26,36 @@ Revision History: --*/ -// pull in the headers for has_map and hash_set -// these live in non-standard places - #ifndef IZ3_HASH_H #define IZ3_HASH_H -//#define USE_UNORDERED_MAP -#ifdef USE_UNORDERED_MAP - -#define stl_ext std -#define hash_space std -#include -#include -#define hash_map unordered_map -#define hash_set unordered_set - -#else - -#if __GNUC__ >= 3 -#undef __DEPRECATED -#define stl_ext __gnu_cxx -#define hash_space stl_ext -#include -#include -#else -#ifdef WIN32 -#define stl_ext stdext -#define hash_space std -#include -#include -#else -#define stl_ext std -#define hash_space std -#include -#include -#endif -#endif - -#endif - #include +#include +#include +#include "hash.h" -// stupid STL doesn't include hash function for class string - -#ifndef WIN32 - -namespace stl_ext { - template <> - class hash { - stl_ext::hash H; - public: - size_t operator()(const std::string &s) const { - return H(s.c_str()); - } - }; -} - -#endif +#define stl_ext hash_space namespace hash_space { + + template class hash {}; + + template <> + class hash { + public: + size_t operator()(const int &s) const { + return s; + } + }; + + template <> + class hash { + public: + size_t operator()(const std::string &s) const { + return string_hash(s.c_str(), s.size(), 0); + } + }; + template <> class hash > { public: @@ -84,17 +63,7 @@ namespace hash_space { return p.first + p.second; } }; -} -#ifdef WIN32 -template <> inline -size_t stdext::hash_value >(const std::pair& p) -{ // hash _Keyval to size_t value one-to-one - return p.first + p.second; -} -#endif - -namespace hash_space { template class hash > { public: @@ -102,70 +71,399 @@ namespace hash_space { return (size_t)p.first + (size_t)p.second; } }; -} -#if 0 -template inline -size_t stdext::hash_value >(const std::pair& p) -{ // hash _Keyval to size_t value one-to-one - return (size_t)p.first + (size_t)p.second; -} -#endif + enum { num_primes = 29 }; -#ifdef WIN32 + static const unsigned long primes[num_primes] = + { + 7ul, + 53ul, + 97ul, + 193ul, + 389ul, + 769ul, + 1543ul, + 3079ul, + 6151ul, + 12289ul, + 24593ul, + 49157ul, + 98317ul, + 196613ul, + 393241ul, + 786433ul, + 1572869ul, + 3145739ul, + 6291469ul, + 12582917ul, + 25165843ul, + 50331653ul, + 100663319ul, + 201326611ul, + 402653189ul, + 805306457ul, + 1610612741ul, + 3221225473ul, + 4294967291ul + }; -namespace std { - template <> - class less > { - public: - bool operator()(const pair &x, const pair &y) const { - return x.first < y.first || x.first == y.first && x.second < y.second; - } - }; - -} - -namespace std { - template - class less > { - public: - bool operator()(const pair &x, const pair &y) const { - return (size_t)x.first < (size_t)y.first || (size_t)x.first == (size_t)y.first && (size_t)x.second < (size_t)y.second; - } - }; - -} + inline unsigned long next_prime(unsigned long n) { + const unsigned long* to = primes + (int)num_primes; + for(const unsigned long* p = primes; p < to; p++) + if(*p >= n) return *p; + return primes[num_primes-1]; + } -#endif - - -#ifndef WIN32 - -#if 0 -namespace stl_ext { - template - class hash { + template + class hashtable + { public: - size_t operator()(const T *p) const { - return (size_t) p; + + typedef Value &reference; + typedef const Value &const_reference; + + struct Entry + { + Entry* next; + Value val; + + Entry(const Value &_val) : val(_val) {next = 0;} + }; + + + struct iterator + { + Entry* ent; + hashtable* tab; + + typedef std::forward_iterator_tag iterator_category; + typedef Value value_type; + typedef std::ptrdiff_t difference_type; + typedef size_t size_type; + typedef Value& reference; + typedef Value* pointer; + + iterator(Entry* _ent, hashtable* _tab) : ent(_ent), tab(_tab) { } + + iterator() { } + + Value &operator*() const { return ent->val; } + + Value *operator->() const { return &(operator*()); } + + iterator &operator++() { + Entry *old = ent; + ent = ent->next; + if (!ent) { + size_t bucket = tab->get_bucket(old->val); + while (!ent && ++bucket < tab->buckets.size()) + ent = tab->buckets[bucket]; + } + return *this; + } + + iterator operator++(int) { + iterator tmp = *this; + operator++(); + return tmp; + } + + + bool operator==(const iterator& it) const { + return ent == it.ent; + } + + bool operator!=(const iterator& it) const { + return ent != it.ent; + } + }; + + struct const_iterator + { + const Entry* ent; + const hashtable* tab; + + typedef std::forward_iterator_tag iterator_category; + typedef Value value_type; + typedef std::ptrdiff_t difference_type; + typedef size_t size_type; + typedef const Value& reference; + typedef const Value* pointer; + + const_iterator(const Entry* _ent, const hashtable* _tab) : ent(_ent), tab(_tab) { } + + const_iterator() { } + + const Value &operator*() const { return ent->val; } + + const Value *operator->() const { return &(operator*()); } + + const_iterator &operator++() { + Entry *old = ent; + ent = ent->next; + if (!ent) { + size_t bucket = tab->get_bucket(old->val); + while (!ent && ++bucket < tab->buckets.size()) + ent = tab->buckets[bucket]; + } + return *this; + } + + const_iterator operator++(int) { + const_iterator tmp = *this; + operator++(); + return tmp; + } + + + bool operator==(const const_iterator& it) const { + return ent == it.ent; + } + + bool operator!=(const const_iterator& it) const { + return ent != it.ent; + } + }; + + private: + + typedef std::vector Table; + + Table buckets; + size_t entries; + HashFun hash_fun ; + GetKey get_key; + KeyEqFun key_eq_fun; + + public: + + hashtable(size_t init_size) : buckets(init_size,(Entry *)0) { + entries = 0; + } + + hashtable(const hashtable& other) { + dup(other); + } + + hashtable& operator= (const hashtable& other) { + if (&other != this) + dup(other); + return *this; + } + + ~hashtable() { + clear(); + } + + size_t size() const { + return entries; + } + + bool empty() const { + return size() == 0; + } + + void swap(hashtable& other) { + buckets.swap(other.buckets); + std::swap(entries, other.entries); + } + + iterator begin() { + for (size_t i = 0; i < buckets.size(); ++i) + if (buckets[i]) + return iterator(buckets[i], this); + return end(); + } + + iterator end() { + return iterator(0, this); + } + + const_iterator begin() const { + for (size_t i = 0; i < buckets.size(); ++i) + if (buckets[i]) + return const_iterator(buckets[i], this); + return end(); + } + + const_iterator end() const { + return const_iterator(0, this); + } + + size_t get_bucket(const Value& val, size_t n) const { + return hash_fun(get_key(val)) % n; + } + + size_t get_key_bucket(const Key& key) const { + return hash_fun(key) % buckets.size(); + } + + size_t get_bucket(const Value& val) const { + return get_bucket(val,buckets.size()); + } + + Entry *lookup(const Value& val, bool ins = false) + { + resize(entries + 1); + + size_t n = get_bucket(val); + Entry* from = buckets[n]; + + for (Entry* ent = from; ent; ent = ent->next) + if (key_eq_fun(get_key(ent->val), get_key(val))) + return ent; + + if(!ins) return 0; + + Entry* tmp = new Entry(val); + tmp->next = from; + buckets[n] = tmp; + ++entries; + return tmp; + } + + Entry *lookup_key(const Key& key) const + { + size_t n = get_key_bucket(key); + Entry* from = buckets[n]; + + for (Entry* ent = from; ent; ent = ent->next) + if (key_eq_fun(get_key(ent->val), key)) + return ent; + + return 0; + } + + const_iterator find(const Key& key) const { + return const_iterator(lookup_key(key),this); + } + + iterator find(const Key& key) { + return iterator(lookup_key(key),this); + } + + std::pair insert(const Value& val){ + size_t old_entries = entries; + Entry *ent = lookup(val,true); + return std::pair(iterator(ent,this),entries > old_entries); + } + + iterator insert(const iterator &it, const Value& val){ + Entry *ent = lookup(val,true); + return iterator(ent,this); + } + + size_t erase(const Key& key) + { + Entry** p = &(buckets[get_key_bucket(key)]); + size_t count = 0; + while(*p){ + Entry *q = *p; + if (key_eq_fun(get_key(q->val), key)) { + ++count; + *p = q->next; + delete q; + } + else + p = &(q->next); + } + entries -= count; + return count; + } + + void resize(size_t new_size) { + const size_t old_n = buckets.size(); + if (new_size <= old_n) return; + const size_t n = next_prime(new_size); + if (n <= old_n) return; + Table tmp(n, (Entry*)(0)); + for (size_t i = 0; i < old_n; ++i) { + Entry* ent = buckets[i]; + while (ent) { + size_t new_bucket = get_bucket(ent->val, n); + buckets[i] = ent->next; + ent->next = tmp[new_bucket]; + tmp[new_bucket] = ent; + ent = buckets[i]; + } + } + buckets.swap(tmp); + } + + void clear() + { + for (size_t i = 0; i < buckets.size(); ++i) { + for (Entry* ent = buckets[i]; ent != 0;) { + Entry* next = ent->next; + delete ent; + ent = next; + } + buckets[i] = 0; + } + entries = 0; + } + + void dup(const hashtable& other) + { + buckets.resize(other.buckets.size()); + for (size_t i = 0; i < other.buckets.size(); ++i) { + Entry** to = &buckets[i]; + for (Entry* from = other.buckets[i]; from; from = from->next) + to = &((*to = new Entry(from->val))->next); + } + entries = other.entries; } }; + + template + class equal { + public: + bool operator()(const T& x, const T &y) const { + return x == y; + } + }; + + template + class identity { + public: + const T &operator()(const T &x) const { + return x; + } + }; + + template + class proj1 { + public: + const T &operator()(const std::pair &x) const { + return x.first; + } + }; + + template , + class EqFun = equal > + class hash_set + : public hashtable,EqFun> { + + public: + hash_set() + : hashtable,EqFun>(7) {} + }; + + template , + class EqFun = equal > + class hash_map + : public hashtable,Key,HashFun,proj1,EqFun> { + + public: + + hash_map() + : hashtable,Key,HashFun,proj1,EqFun>(7) {} + + Value &operator[](const Key& key) { + std::pair kvp(key,Value()); + return lookup(kvp,true)->val.second; + } + }; + } #endif - -#endif - -#ifdef WIN32 - - - - -template -class hash_map : public stl_ext::hash_map > > {}; - -template -class hash_set : public stl_ext::hash_set > > {}; - -#endif - -#endif From 891d0d07f84ba2c7c1001bca64e6b1b3d08b382e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 27 Feb 2014 13:42:40 -0800 Subject: [PATCH 283/509] removig unsound simplification in ctx-solver-simplify tactic Signed-off-by: Nikolaj Bjorner --- src/smt/tactic/ctx_solver_simplify_tactic.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.cpp b/src/smt/tactic/ctx_solver_simplify_tactic.cpp index 8cefee8da..dcd5cd9e1 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.cpp +++ b/src/smt/tactic/ctx_solver_simplify_tactic.cpp @@ -211,11 +211,7 @@ protected: // found.push_back(arg); - if (path_r.first == self_pos) { - TRACE("ctx_solver_simplify_tactic", tout << "cached " << mk_pp(arg, m) << " |-> " << mk_pp(path_r.second, m) << "\n";); - args.push_back(path_r.second); - } - else if (m.is_bool(arg)) { + if (m.is_bool(arg)) { res = local_simplify(a, n, id, i); TRACE("ctx_solver_simplify_tactic", tout << "Already cached: " << path_r.first << " " << mk_pp(arg, m) << " |-> " << mk_pp(res, m) << "\n";); From 08d892bbdbf1f40d0ebca0d7e9c7479b75b1bd37 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 27 Feb 2014 17:21:47 -0800 Subject: [PATCH 284/509] interpolation fixes --- src/duality/duality.h | 2 ++ src/duality/duality_rpfp.cpp | 19 ++++++++++++ src/duality/duality_solver.cpp | 9 ++++-- src/interp/iz3proof_itp.cpp | 55 ++++++++++++++++++++++------------ 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index e1d058f10..12ba21516 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -82,6 +82,8 @@ namespace Duality { Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); + Term CloneQuantAndSimp(const expr &t, const expr &body); + Term RemoveRedundancy(const Term &t); Term IneqToEq(const Term &t); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 5751fa890..e09278dd4 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -523,6 +523,25 @@ namespace Duality { return foo; } + Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ + if(t.is_quantifier_forall() && body.is_app() && body.decl().get_decl_kind() == And){ + int nargs = body.num_args(); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = CloneQuantAndSimp(t, body.arg(i)); + return ctx.make(And,args); + } + if(!t.is_quantifier_forall() && body.is_app() && body.decl().get_decl_kind() == Or){ + int nargs = body.num_args(); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = CloneQuantAndSimp(t, body.arg(i)); + return ctx.make(Or,args); + } + return clone_quantifier(t,body); + } + + Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val){ std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 1c2f2c927..c7eec7e9f 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1916,6 +1916,7 @@ namespace Duality { stack.back().level = tree->slvr().get_scope_level(); bool was_sat = true; + int update_failures = 0; while (true) { @@ -1954,10 +1955,14 @@ namespace Duality { heuristic->Update(node->map); // make it less likely to expand this node in future } if(update_count == 0){ - if(was_sat) - throw Incompleteness(); + if(was_sat){ + update_failures++; + if(update_failures > 10) + throw Incompleteness(); + } reporter->Message("backtracked without learning"); } + else update_failures = 0; } tree->ComputeProofCore(); // need to compute the proof core before popping solver bool propagated = false; diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 1eb4fcc84..a1a332005 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -369,11 +369,17 @@ class iz3proof_itp_impl : public iz3proof_itp { } default: { - symb s = sym(itp2); - if(s == sforall || s == sexists) - res = make(s,arg(itp2,0),resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,1))); - else + opr o = op(itp2); + if(o == Uninterpreted){ + symb s = sym(itp2); + if(s == sforall || s == sexists) + res = make(s,arg(itp2,0),resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,1))); + else + res = itp2; + } + else { res = itp2; + } } } } @@ -405,11 +411,17 @@ class iz3proof_itp_impl : public iz3proof_itp { } default: { - symb s = sym(itp1); - if(s == sforall || s == sexists) - res = make(s,arg(itp1,0),resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,1), itp2)); - else + opr o = op(itp1); + if(o == Uninterpreted){ + symb s = sym(itp1); + if(s == sforall || s == sexists) + res = make(s,arg(itp1,0),resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,1), itp2)); + else + res = itp1; + } + else { res = itp1; + } } } } @@ -464,18 +476,20 @@ class iz3proof_itp_impl : public iz3proof_itp { std::pair::iterator,bool> bar = subst_memo.insert(foo); ast &res = bar.first->second; if(bar.second){ - symb g = sym(e); - if(g == rotate_sum){ - if(var == get_placeholder(arg(e,0))){ - res = e; + if(op(e) == Uninterpreted){ + symb g = sym(e); + if(g == rotate_sum){ + if(var == get_placeholder(arg(e,0))){ + res = e; + } + else + res = make(rotate_sum,arg(e,0),subst_term_and_simp_rec(var,t,arg(e,1))); + return res; + } + if(g == concat){ + res = e; + return res; } - else - res = make(rotate_sum,arg(e,0),subst_term_and_simp_rec(var,t,arg(e,1))); - return res; - } - if(g == concat){ - res = e; - return res; } int nargs = num_args(e); std::vector args(nargs); @@ -794,6 +808,7 @@ class iz3proof_itp_impl : public iz3proof_itp { return simplify_sum(args); } + ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){ if(pl == arg(pf,1)){ ast cond = mk_true(); @@ -1036,6 +1051,8 @@ class iz3proof_itp_impl : public iz3proof_itp { ast mc = z3_simplify(chain_side_proves(LitA,pref)); Aproves = my_and(Aproves,mc); } + if(is_true(nc)) + return itp; return make_normal(itp,nc); } From acf4ad0ab670d1714e5a82aee7b4ac2c84ecd74a Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 27 Feb 2014 17:23:19 -0800 Subject: [PATCH 285/509] use new hashtable implementation in windows --- src/api/api_interp.cpp | 4 -- src/duality/duality.h | 2 - src/duality/duality_solver.cpp | 2 +- src/duality/duality_wrapper.h | 16 -------- src/interp/iz3base.cpp | 2 - src/interp/iz3checker.cpp | 2 - src/interp/iz3foci.cpp | 2 - src/interp/iz3hash.h | 3 ++ src/interp/iz3interp.cpp | 2 - src/interp/iz3mgr.cpp | 2 - src/interp/iz3mgr.h | 8 ---- src/interp/iz3pp.cpp | 4 -- src/interp/iz3proof_itp.cpp | 2 - src/interp/iz3translate.cpp | 2 - src/interp/iz3translate_direct.cpp | 65 ------------------------------ 15 files changed, 4 insertions(+), 114 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 98722022c..2c88a1978 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -39,11 +39,8 @@ Revision History: #include"iz3pp.h" #include"iz3checker.h" -#ifndef WIN32 using namespace stl_ext; -#endif -#ifndef WIN32 // WARNING: don't make a hash_map with this if the range type // has a destructor: you'll get an address dependency!!! namespace stl_ext { @@ -55,7 +52,6 @@ namespace stl_ext { } }; } -#endif typedef interpolation_options_struct *Z3_interpolation_options; diff --git a/src/duality/duality.h b/src/duality/duality.h index e1d058f10..479c90a29 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -25,9 +25,7 @@ Revision History: #include // make hash_map and hash_set available -#ifndef WIN32 using namespace stl_ext; -#endif namespace Duality { diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 1c2f2c927..6079e17d9 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1955,7 +1955,7 @@ namespace Duality { } if(update_count == 0){ if(was_sat) - throw Incompleteness(); + throw "Help!"; reporter->Message("backtracked without learning"); } } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 2b0045023..90f1799a6 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -1401,14 +1401,6 @@ namespace hash_space { }; } -// to make Duality::ast hashable in windows -#ifdef WIN32 -template <> inline -size_t stdext::hash_value(const Duality::ast& s) -{ - return s.raw()->get_id(); -} -#endif // to make Duality::ast usable in ordered collections namespace std { @@ -1445,14 +1437,6 @@ namespace hash_space { }; } -// to make Duality::func_decl hashable in windows -#ifdef WIN32 -template <> inline -size_t stdext::hash_value(const Duality::func_decl& s) -{ - return s.raw()->get_id(); -} -#endif // to make Duality::func_decl usable in ordered collections namespace std { diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 26146bfa3..054c8a811 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -34,9 +34,7 @@ Revision History: #include "../smt/smt_solver.h" -#ifndef WIN32 using namespace stl_ext; -#endif iz3base::range &iz3base::ast_range(ast t){ diff --git a/src/interp/iz3checker.cpp b/src/interp/iz3checker.cpp index d423ba48f..02acaa5c1 100755 --- a/src/interp/iz3checker.cpp +++ b/src/interp/iz3checker.cpp @@ -36,9 +36,7 @@ Revision History: #include -#ifndef WIN32 using namespace stl_ext; -#endif struct iz3checker : iz3base { diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp index 1d81a1b15..527e73ae4 100755 --- a/src/interp/iz3foci.cpp +++ b/src/interp/iz3foci.cpp @@ -25,9 +25,7 @@ Revision History: #include "foci2.h" #include "iz3foci.h" -#ifndef WIN32 using namespace stl_ext; -#endif class iz3foci_impl : public iz3secondary { diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index c628ef32b..355c03817 100644 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -445,6 +445,9 @@ namespace hash_space { : public hashtable,EqFun> { public: + + typedef Element value_type; + hash_set() : hashtable,EqFun>(7) {} }; diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 73eaab841..667d962d0 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -43,9 +43,7 @@ Revision History: -#ifndef WIN32 using namespace stl_ext; -#endif diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 35dc67bcd..0259f1b52 100755 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -38,9 +38,7 @@ Revision History: #include "params.h" -#ifndef WIN32 using namespace stl_ext; -#endif std::ostream &operator <<(std::ostream &s, const iz3mgr::ast &a){ diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index e974a199b..4ef594cee 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -126,14 +126,6 @@ namespace hash_space { }; } -// to make ast_r hashable in windows -#ifdef WIN32 -template <> inline -size_t stdext::hash_value(const ast_r& s) -{ - return s.raw()->get_id(); -} -#endif // to make ast_r usable in ordered collections namespace std { diff --git a/src/interp/iz3pp.cpp b/src/interp/iz3pp.cpp index 31b375c0f..d530a3bc6 100644 --- a/src/interp/iz3pp.cpp +++ b/src/interp/iz3pp.cpp @@ -36,11 +36,8 @@ Revision History: #include"expr_abstract.h" -#ifndef WIN32 using namespace stl_ext; -#endif -#ifndef WIN32 // We promise not to use this for hash_map with range destructor namespace stl_ext { template <> @@ -51,7 +48,6 @@ namespace stl_ext { } }; } -#endif // TBD: algebraic data-types declarations will not be printed. diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 1eb4fcc84..6a150ad38 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -26,9 +26,7 @@ Revision History: #include "iz3proof_itp.h" -#ifndef WIN32 using namespace stl_ext; -#endif // #define INVARIANT_CHECKING diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 9eac5f040..7e0fa65d6 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -39,9 +39,7 @@ Revision History: #include //using std::vector; -#ifndef WIN32 using namespace stl_ext; -#endif diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index a85c9a1c5..a42c7034d 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -42,11 +42,7 @@ Revision History: #include //using std::vector; -#ifndef WIN32 using namespace stl_ext; -#endif - -#ifndef WIN32 /* This can introduce an address dependency if the range type of hash_map has a destructor. Since the code in this file is not used and only here for @@ -62,9 +58,6 @@ namespace stl_ext { }; } -#endif - - static int lemma_count = 0; #if 0 static int nll_lemma_count = 0; @@ -96,38 +89,12 @@ namespace hash_space { }; } -#ifdef WIN32 - -template <> inline -size_t stdext::hash_value(const Z3_resolvent& p) -{ - std::hash h; - return h(p); -} - - -namespace std { - template <> - class less { - public: - bool operator()(const Z3_resolvent &x, const Z3_resolvent &y) const { - size_t ixproof = (size_t) x.proof.raw(); - size_t iyproof = (size_t) y.proof.raw(); - if(ixproof < iyproof) return true; - if(ixproof > iyproof) return false; - return x.pivot < y.pivot; - } - }; -} - -#else bool operator==(const Z3_resolvent &x, const Z3_resolvent &y) { return x.proof == y.proof && x.pivot == y.pivot; } -#endif typedef std::vector ResolventAppSet; @@ -151,36 +118,6 @@ namespace hash_space { }; } -#ifdef WIN32 - -template <> inline -size_t stdext::hash_value(const non_local_lits& p) -{ - std::hash h; - return h(p); -} - -namespace std { - template <> - class less { - public: - bool operator()(const non_local_lits &x, const non_local_lits &y) const { - ResolventAppSet::const_iterator itx = x.proofs.begin(); - ResolventAppSet::const_iterator ity = y.proofs.begin(); - while(true){ - if(ity == y.proofs.end()) return false; - if(itx == x.proofs.end()) return true; - size_t xi = (size_t) *itx; - size_t yi = (size_t) *ity; - if(xi < yi) return true; - if(xi > yi) return false; - ++itx; ++ity; - } - } - }; -} - -#else bool operator==(const non_local_lits &x, const non_local_lits &y) { ResolventAppSet::const_iterator itx = x.proofs.begin(); @@ -194,8 +131,6 @@ bool operator==(const non_local_lits &x, const non_local_lits &y) { } -#endif - /* This translator goes directly from Z3 proofs to interpolatable proofs without an intermediate representation as an iz3proof. */ From 19dbd02e1344c22552da196e52e9d0b0ba413d0a Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 28 Feb 2014 10:38:35 -0800 Subject: [PATCH 286/509] interpolation fix --- src/interp/iz3proof_itp.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index a1a332005..e66bda5f3 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2635,6 +2635,11 @@ class iz3proof_itp_impl : public iz3proof_itp { } hash_map::iterator it = localization_map.find(e); + + if(it != localization_map.end() && is_bool_type(get_type(e)) + && !pv->ranges_intersect(pv->ast_scope(it->second),rng)) + it = localization_map.end(); // prevent quantifiers over booleans + if(it != localization_map.end()){ pf = localization_pf_map[e]; e = it->second; From f2ecd70e654c072b86860d9642935c0beb0c525b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 1 Mar 2014 11:02:18 -0800 Subject: [PATCH 287/509] fix ctx solver simplify, remove horn inequalities Signed-off-by: Nikolaj Bjorner --- src/smt/params/theory_arith_params.h | 3 +- src/smt/smt_setup.cpp | 7 - src/smt/tactic/ctx_solver_simplify_tactic.cpp | 107 +- src/smt/theory_horn_ineq.cpp | 236 ---- src/smt/theory_horn_ineq.h | 328 ----- src/smt/theory_horn_ineq_def.h | 1166 ----------------- 6 files changed, 36 insertions(+), 1811 deletions(-) delete mode 100644 src/smt/theory_horn_ineq.cpp delete mode 100644 src/smt/theory_horn_ineq.h delete mode 100644 src/smt/theory_horn_ineq_def.h diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index 30bc65b6d..911c75d36 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -27,8 +27,7 @@ enum arith_solver_id { AS_DIFF_LOGIC, AS_ARITH, AS_DENSE_DIFF_LOGIC, - AS_UTVPI, - AS_HORN + AS_UTVPI }; enum bound_prop_mode { diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index f91a58f87..51b83d61c 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -22,7 +22,6 @@ Revision History: #include"theory_arith.h" #include"theory_dense_diff_logic.h" #include"theory_diff_logic.h" -#include"theory_horn_ineq.h" #include"theory_utvpi.h" #include"theory_array.h" #include"theory_array_full.h" @@ -725,12 +724,6 @@ namespace smt { m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params)); } break; - case AS_HORN: - if (m_params.m_arith_int_only) - m_context.register_plugin(alloc(smt::theory_ihi, m_manager)); - else - m_context.register_plugin(alloc(smt::theory_rhi, m_manager)); - break; case AS_UTVPI: if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_iutvpi, m_manager)); diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.cpp b/src/smt/tactic/ctx_solver_simplify_tactic.cpp index dcd5cd9e1..98b7592c8 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.cpp +++ b/src/smt/tactic/ctx_solver_simplify_tactic.cpp @@ -139,22 +139,32 @@ protected: SASSERT(g.is_well_sorted()); } + struct expr_pos { + unsigned m_parent; + unsigned m_self; + unsigned m_idx; + expr* m_expr; + expr_pos(unsigned p, unsigned s, unsigned i, expr* e): + m_parent(p), m_self(s), m_idx(i), m_expr(e) + {} + expr_pos(): + m_parent(0), m_self(0), m_idx(0), m_expr(0) + {} + }; + void reduce(expr_ref& result){ SASSERT(m.is_bool(result)); - ptr_vector todo; ptr_vector names; - svector is_checked; - svector parent_ids, self_ids; + svector todo; expr_ref_vector fresh_vars(m), trail(m); expr_ref res(m), tmp(m); - obj_map > cache; - unsigned id = 1; + obj_map cache; + unsigned id = 1, child_id = 0; expr_ref n2(m), fml(m); - unsigned path_id = 0, self_pos = 0; + unsigned parent_pos = 0, self_pos = 0, self_idx = 0; app * a; unsigned sz; - std::pair path_r; - ptr_vector found; + expr_pos path_r; expr_ref_vector args(m); expr_ref n = mk_fresh(id, m.mk_bool_sort()); trail.push_back(n); @@ -163,26 +173,25 @@ protected: tmp = m.mk_not(m.mk_iff(fml, n)); m_solver.assert_expr(tmp); - todo.push_back(fml); + todo.push_back(expr_pos(0,0,0,fml)); names.push_back(n); - is_checked.push_back(false); - parent_ids.push_back(0); - self_ids.push_back(0); m_solver.push(); while (!todo.empty() && !m_cancel) { expr_ref res(m); args.reset(); - expr* e = todo.back(); - unsigned pos = parent_ids.back(); + expr* e = todo.back().m_expr; + self_pos = todo.back().m_self; + parent_pos = todo.back().m_parent; + self_idx = todo.back().m_idx; n = names.back(); - bool checked = is_checked.back(); if (cache.contains(e)) { goto done; } - if (m.is_bool(e) && !checked && simplify_bool(n, res)) { - TRACE("ctx_solver_simplify_tactic", tout << "simplified: " << mk_pp(e, m) << " |-> " << mk_pp(res, m) << "\n";); + if (m.is_bool(e) && simplify_bool(n, res)) { + TRACE("ctx_solver_simplify_tactic", + tout << "simplified: " << mk_pp(e, m) << " |-> " << mk_pp(res, m) << "\n";); goto done; } if (!is_app(e)) { @@ -191,45 +200,31 @@ protected: } a = to_app(e); - if (!is_checked.back()) { - self_ids.back() = ++path_id; - is_checked.back() = true; - } - self_pos = self_ids.back(); - sz = a->get_num_args(); - + sz = a->get_num_args(); n2 = 0; - found.reset(); // arguments already simplified. for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); - if (cache.find(arg, path_r) && !found.contains(arg)) { + if (cache.find(arg, path_r)) { // // This is a single traversal version of the context // simplifier. It simplifies only the first occurrence of // a sub-term with respect to the context. // - found.push_back(arg); - if (m.is_bool(arg)) { - res = local_simplify(a, n, id, i); - TRACE("ctx_solver_simplify_tactic", - tout << "Already cached: " << path_r.first << " " << mk_pp(arg, m) << " |-> " << mk_pp(res, m) << "\n";); - args.push_back(res); + if (path_r.m_parent == self_pos && path_r.m_idx == i) { + args.push_back(path_r.m_expr); } else { args.push_back(arg); } } - else if (!n2 && !found.contains(arg)) { + else if (!n2) { n2 = mk_fresh(id, m.get_sort(arg)); trail.push_back(n2); - todo.push_back(arg); - parent_ids.push_back(self_pos); - self_ids.push_back(0); + todo.push_back(expr_pos(self_pos, child_id++, i, arg)); names.push_back(n2); args.push_back(n2); - is_checked.push_back(false); } else { args.push_back(arg); @@ -247,22 +242,16 @@ protected: done: if (res) { - cache.insert(e, std::make_pair(pos, res)); - } - - TRACE("ctx_solver_simplify_tactic", - tout << mk_pp(e, m) << " checked: " << checked << " cached: " << mk_pp(res?res.get():e, m) << "\n";); + cache.insert(e, expr_pos(parent_pos, self_pos, self_idx, res)); + } todo.pop_back(); - parent_ids.pop_back(); - self_ids.pop_back(); names.pop_back(); - is_checked.pop_back(); m_solver.pop(1); } if (!m_cancel) { VERIFY(cache.find(fml, path_r)); - result = path_r.second; + result = path_r.m_expr; } } @@ -302,32 +291,6 @@ protected: } return expr_ref(m.mk_app(fn, m_arith.mk_numeral(rational(id++), true)), m); } - - - expr_ref local_simplify(app* a, expr* n, unsigned& id, unsigned index) { - SASSERT(index < a->get_num_args()); - SASSERT(m.is_bool(a->get_arg(index))); - expr_ref n2(m), result(m), tmp(m); - n2 = mk_fresh(id, m.get_sort(a->get_arg(index))); - ptr_buffer args; - for (unsigned i = 0; i < a->get_num_args(); ++i) { - if (i == index) { - args.push_back(n2); - } - else { - args.push_back(a->get_arg(i)); - } - } - m_mk_app(a->get_decl(), args.size(), args.c_ptr(), result); - m_solver.push(); - tmp = m.mk_eq(result, n); - m_solver.assert_expr(tmp); - if (!simplify_bool(n2, result)) { - result = a->get_arg(index); - } - m_solver.pop(1); - return result; - } }; diff --git a/src/smt/theory_horn_ineq.cpp b/src/smt/theory_horn_ineq.cpp deleted file mode 100644 index 978b5b003..000000000 --- a/src/smt/theory_horn_ineq.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - theory_horn_ineq.h - -Author: - - Nikolaj Bjorner (nbjorner) 2013-04-18 - -Revision History: - - The implementaton is derived from theory_diff_logic. - ---*/ -#include "theory_horn_ineq.h" -#include "theory_horn_ineq_def.h" - -namespace smt { - - template class theory_horn_ineq; - template class theory_horn_ineq; - - // similar to test_diff_logic: - - horn_ineq_tester::horn_ineq_tester(ast_manager& m): m(m), a(m) {} - - bool horn_ineq_tester::operator()(expr* e) { - m_todo.reset(); - m_pols.reset(); - pos_mark.reset(); - neg_mark.reset(); - m_todo.push_back(e); - m_pols.push_back(l_true); - while (!m_todo.empty()) { - expr* e = m_todo.back(); - lbool p = m_pols.back(); - m_todo.pop_back(); - m_pols.pop_back(); - switch (p) { - case l_true: - if (pos_mark.is_marked(e)) { - continue; - } - pos_mark.mark(e, true); - break; - case l_false: - if (neg_mark.is_marked(e)) { - continue; - } - neg_mark.mark(e, true); - break; - case l_undef: - if (pos_mark.is_marked(e) && neg_mark.is_marked(e)) { - continue; - } - pos_mark.mark(e, true); - neg_mark.mark(e, true); - break; - } - if (!test_expr(p, e)) { - return false; - } - } - return true; - } - - vector > const& horn_ineq_tester::get_linearization() const { - return m_terms; - } - - bool horn_ineq_tester::test_expr(lbool p, expr* e) { - expr* e1, *e2, *e3; - if (is_var(e)) { - return true; - } - if (!is_app(e)) { - return false; - } - app* ap = to_app(e); - if (m.is_and(ap) || m.is_or(ap)) { - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - m_todo.push_back(ap->get_arg(i)); - m_pols.push_back(p); - } - } - else if (m.is_not(e, e1)) { - m_todo.push_back(e); - m_pols.push_back(~p); - } - else if (m.is_ite(e, e1, e2, e3)) { - m_todo.push_back(e1); - m_pols.push_back(l_undef); - m_todo.push_back(e2); - m_pols.push_back(p); - m_todo.push_back(e3); - m_pols.push_back(p); - } - else if (m.is_iff(e, e1, e2)) { - m_todo.push_back(e1); - m_pols.push_back(l_undef); - m_todo.push_back(e2); - m_pols.push_back(l_undef); - m_todo.push_back(e2); - } - else if (m.is_implies(e, e1, e2)) { - m_todo.push_back(e1); - m_pols.push_back(~p); - m_todo.push_back(e2); - m_pols.push_back(p); - } - else if (m.is_eq(e, e1, e2)) { - return linearize(e1, e2) == diff; - } - else if (m.is_true(e) || m.is_false(e)) { - // no-op - } - else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) || - a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { - if (p == l_false) { - std::swap(e2, e1); - } - classify_t cl = linearize(e1, e2); - switch(p) { - case l_undef: - return cl == diff; - case l_true: - case l_false: - return cl == horn || cl == diff; - } - } - else if (!is_uninterp_const(e)) { - return false; - } - return true; - } - - bool horn_ineq_tester::operator()(unsigned num_fmls, expr* const* fmls) { - for (unsigned i = 0; i < num_fmls; ++i) { - if (!(*this)(fmls[i])) { - return false; - } - } - return true; - } - - horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e) { - m_terms.reset(); - m_terms.push_back(std::make_pair(e, rational(1))); - return linearize(); - } - - horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e1, expr* e2) { - m_terms.reset(); - m_terms.push_back(std::make_pair(e1, rational(1))); - m_terms.push_back(std::make_pair(e2, rational(-1))); - return linearize(); - } - - horn_ineq_tester::classify_t horn_ineq_tester::linearize() { - - m_weight.reset(); - m_coeff_map.reset(); - - while (!m_terms.empty()) { - expr* e1, *e2; - rational num; - rational mul = m_terms.back().second; - expr* e = m_terms.back().first; - m_terms.pop_back(); - if (a.is_add(e)) { - for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { - m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul)); - } - } - else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) { - m_terms.push_back(std::make_pair(e2, mul*num)); - } - else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) { - m_terms.push_back(std::make_pair(e2, mul*num)); - } - else if (a.is_sub(e, e1, e2)) { - m_terms.push_back(std::make_pair(e1, mul)); - m_terms.push_back(std::make_pair(e2, -mul)); - } - else if (a.is_uminus(e, e1)) { - m_terms.push_back(std::make_pair(e1, -mul)); - } - else if (a.is_numeral(e, num)) { - m_weight += num*mul; - } - else if (a.is_to_real(e, e1)) { - m_terms.push_back(std::make_pair(e1, mul)); - } - else if (!is_uninterp_const(e)) { - return non_horn; - } - else { - m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul; - } - } - unsigned num_negative = 0; - unsigned num_positive = 0; - bool is_unit_pos = true, is_unit_neg = true; - obj_map::iterator it = m_coeff_map.begin(); - obj_map::iterator end = m_coeff_map.end(); - for (; it != end; ++it) { - rational r = it->m_value; - if (r.is_zero()) { - continue; - } - m_terms.push_back(std::make_pair(it->m_key, r)); - if (r.is_pos()) { - is_unit_pos = is_unit_pos && r.is_one(); - num_positive++; - } - else { - is_unit_neg = is_unit_neg && r.is_minus_one(); - num_negative++; - } - } - if (num_negative <= 1 && is_unit_pos && is_unit_neg && num_positive <= 1) { - return diff; - } - if (num_positive <= 1 && is_unit_pos) { - return horn; - } - if (num_negative <= 1 && is_unit_neg) { - return co_horn; - } - return non_horn; - } - - -} diff --git a/src/smt/theory_horn_ineq.h b/src/smt/theory_horn_ineq.h deleted file mode 100644 index ed96f25f6..000000000 --- a/src/smt/theory_horn_ineq.h +++ /dev/null @@ -1,328 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - theory_horn_ineq.h - -Abstract: - - A*x <= weight + D*x, coefficients to A and D are non-negative, - D is a diagonal matrix. - Coefficients to weight may have both signs. - - Label variables by weight. - Select inequality that is not satisfied. - Set delta(LHS) := 0 - Set delta(RHS(x)) := weight(x) - b - Propagate weight increment through inequalities. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-04-18 - -Revision History: - - The implementaton is derived from theory_diff_logic. - ---*/ - -#ifndef _THEORY_HORN_INEQ_H_ -#define _THEORY_HORN_INEQ_H_ - -#include"rational.h" -#include"inf_rational.h" -#include"inf_int_rational.h" -#include"inf_eps_rational.h" -#include"smt_theory.h" -#include"arith_decl_plugin.h" -#include"smt_justification.h" -#include"map.h" -#include"smt_params.h" -#include"arith_eq_adapter.h" -#include"smt_model_generator.h" -#include"numeral_factory.h" -#include"smt_clause.h" - -namespace smt { - - class horn_ineq_tester { - ast_manager& m; - arith_util a; - ptr_vector m_todo; - svector m_pols; - ast_mark pos_mark, neg_mark; - obj_map m_coeff_map; - rational m_weight; - vector > m_terms; - - public: - enum classify_t { - co_horn, - horn, - diff, - non_horn - }; - horn_ineq_tester(ast_manager& m); - - // test if formula is in the Horn inequality fragment: - bool operator()(expr* fml); - bool operator()(unsigned num_fmls, expr* const* fmls); - - // linearize inequality/equality - classify_t linearize(expr* e); - classify_t linearize(expr* e1, expr* e2); - - // retrieve linearization - vector > const& get_linearization() const; - rational const& get_weight() const { return m_weight; } - private: - bool test_expr(lbool p, expr* e); - classify_t linearize(); - }; - - template - class theory_horn_ineq : public theory, private Ext { - - typedef typename Ext::numeral numeral; - typedef typename Ext::inf_numeral inf_numeral; - typedef literal explanation; - typedef theory_var th_var; - typedef svector th_var_vector; - typedef unsigned clause_id; - typedef vector > coeffs; - - class clause; - class graph; - class assignment_trail; - class parent_trail; - - class atom { - protected: - bool_var m_bvar; - bool m_true; - int m_pos; - int m_neg; - public: - atom(bool_var bv, int pos, int neg) : - m_bvar(bv), m_true(false), - m_pos(pos), m_neg(neg) {} - ~atom() {} - bool_var get_bool_var() const { return m_bvar; } - bool is_true() const { return m_true; } - void assign_eh(bool is_true) { m_true = is_true; } - int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } - int get_pos() const { return m_pos; } - int get_neg() const { return m_neg; } - std::ostream& display(theory_horn_ineq const& th, std::ostream& out) const; - }; - typedef svector atoms; - - struct scope { - unsigned m_atoms_lim; - unsigned m_asserted_atoms_lim; - unsigned m_asserted_qhead_old; - }; - - struct stats { - unsigned m_num_conflicts; - unsigned m_num_assertions; - unsigned m_num_core2th_eqs; - unsigned m_num_core2th_diseqs; - - void reset() { - memset(this, 0, sizeof(*this)); - } - - stats() { - reset(); - } - }; - - stats m_stats; - smt_params m_params; - arith_util a; - arith_eq_adapter m_arith_eq_adapter; - th_var m_zero_int; // cache the variable representing the zero variable. - th_var m_zero_real; // cache the variable representing the zero variable. - - graph* m_graph; - atoms m_atoms; - unsigned_vector m_asserted_atoms; // set of asserted atoms - unsigned m_asserted_qhead; - u_map m_bool_var2atom; - svector m_scopes; - - double m_agility; - bool m_lia; - bool m_lra; - bool m_non_horn_ineq_exprs; - - horn_ineq_tester m_test; - - - arith_factory * m_factory; - rational m_delta; - rational m_lambda; - - - // Set a conflict due to a negative cycle. - void set_neg_cycle_conflict(); - - // Create a new theory variable. - virtual th_var mk_var(enode* n); - - virtual th_var mk_var(expr* n); - - void compute_delta(); - - void found_non_horn_ineq_expr(expr * n); - - bool is_interpreted(app* n) const { - return n->get_family_id() == get_family_id(); - } - - public: - theory_horn_ineq(ast_manager& m); - - virtual ~theory_horn_ineq(); - - virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_horn_ineq, get_manager()); } - - virtual char const * get_name() const { return "horn-inequality-logic"; } - - /** - \brief See comment in theory::mk_eq_atom - */ - virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); } - - virtual void init(context * ctx); - - virtual bool internalize_atom(app * atom, bool gate_ctx); - - virtual bool internalize_term(app * term); - - virtual void internalize_eq_eh(app * atom, bool_var v); - - virtual void assign_eh(bool_var v, bool is_true); - - virtual void new_eq_eh(th_var v1, th_var v2) { - m_arith_eq_adapter.new_eq_eh(v1, v2); - } - - virtual bool use_diseqs() const { return true; } - - virtual void new_diseq_eh(th_var v1, th_var v2) { - m_arith_eq_adapter.new_diseq_eh(v1, v2); - } - - virtual void push_scope_eh(); - - virtual void pop_scope_eh(unsigned num_scopes); - - virtual void restart_eh() { - m_arith_eq_adapter.restart_eh(); - } - - virtual void relevant_eh(app* e) {} - - virtual void init_search_eh() { - m_arith_eq_adapter.init_search_eh(); - } - - virtual final_check_status final_check_eh(); - - virtual bool is_shared(th_var v) const { - return false; - } - - virtual bool can_propagate() { - SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); - return m_asserted_qhead != m_asserted_atoms.size(); - } - - virtual void propagate(); - - virtual justification * why_is_diseq(th_var v1, th_var v2) { - UNREACHABLE(); - return 0; - } - - virtual void reset_eh(); - - virtual void init_model(model_generator & m); - - virtual model_value_proc * mk_value(enode * n, model_generator & mg); - - virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const { - return true; - } - - virtual void display(std::ostream & out) const; - - virtual void collect_statistics(::statistics & st) const; - - private: - - virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { - m_stats.m_num_core2th_eqs++; - new_eq_or_diseq(true, v1, v2, j); - } - - virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { - m_stats.m_num_core2th_diseqs++; - new_eq_or_diseq(false, v1, v2, j); - } - - void negate(coeffs& coeffs, rational& weight); - numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; - void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); - - void del_atoms(unsigned old_size); - - void propagate_core(); - - bool propagate_atom(atom const& a); - - th_var mk_term(app* n); - - th_var mk_num(app* n, rational const& r); - - bool is_consistent() const; - - th_var expand(bool pos, th_var v, rational & k); - - void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); - - th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; } - - th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } - - void inc_conflicts(); - - }; - - struct rhi_ext { - typedef inf_rational inf_numeral; - typedef inf_eps_rational numeral; - numeral m_epsilon; - numeral m_minus_infty; - rhi_ext() : m_epsilon(inf_rational(rational(), true)), m_minus_infty(rational(-1),inf_rational()) {} - }; - - struct ihi_ext { - typedef rational inf_numeral; - typedef inf_eps_rational numeral; - numeral m_epsilon; - numeral m_minus_infty; - ihi_ext() : m_epsilon(rational(1)), m_minus_infty(rational(-1),rational(0)) {} - }; - - typedef theory_horn_ineq theory_rhi; - typedef theory_horn_ineq theory_ihi; -}; - - - - -#endif /* _THEORY_HORN_INEQ_H_ */ diff --git a/src/smt/theory_horn_ineq_def.h b/src/smt/theory_horn_ineq_def.h deleted file mode 100644 index f4fa7e8d7..000000000 --- a/src/smt/theory_horn_ineq_def.h +++ /dev/null @@ -1,1166 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - theory_horn_ineq_def.h - -Abstract: - - A*x <= b + D*x, coefficients to A and D are non-negative, - D is a diagonal matrix. - Coefficients to b may have both signs. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-04-18 - -Revision History: - ---*/ - -#ifndef _THEORY_HORN_INEQ_DEF_H_ -#define _THEORY_HORN_INEQ_DEF_H_ -#include "theory_horn_ineq.h" -#include "ast_pp.h" -#include "smt_context.h" - -namespace smt { - - static const unsigned null_clause_id = UINT_MAX; - - /** - A clause represents an inequality of the form - - v1*c1 + v2*c2 + .. + v_n*c_n + w <= v*c - - where - - m_vars: [v1,v2,...,v_n] - - m_coeffs: [c1,c2,...,c_n] - - m_var: v - - m_coeff: c - - m_weight: w - - */ - template - class theory_horn_ineq::clause { - vector m_coeffs; // coefficients of body. - svector m_vars; // variables of body. - rational m_coeff; // coefficient of head. - th_var m_var; // head variable. - numeral m_weight; // constant to add - literal m_explanation; - bool m_enabled; - public: - clause(unsigned sz, rational const* coeffs, th_var const* vars, - rational const& coeff, th_var var, numeral const& w, - const literal& ex): - m_coeffs(sz, coeffs), - m_vars(sz, vars), - m_coeff(coeff), - m_var(var), - m_weight(w), - m_explanation(ex), - m_enabled(false) { - DEBUG_CODE( - { - for (unsigned i = 0; i < size(); ++i) { - SASSERT(coeffs[i].is_pos()); - } - SASSERT(coeff.is_pos()); - }); - } - - th_var vars(unsigned i) const { return m_vars[i]; } - rational const& coeffs(unsigned i) const { return m_coeffs[i]; } - th_var var() const { return m_var; } - rational const& coeff() const { return m_coeff; } - const numeral & get_weight() const { return m_weight; } - const literal & get_explanation() const { return m_explanation; } - bool is_enabled() const { return m_enabled; } - unsigned size() const { return m_vars.size(); } - - void enable() { m_enabled = true; } - void disable() { m_enabled = false; } - - void display(std::ostream& out) const { - out << (is_enabled()?"+ ":"- "); - for (unsigned i = 0; i < size(); ++i) { - if (i > 0 && coeffs(i).is_pos()) { - out << " + "; - } - display(out, coeffs(i), vars(i)); - } - if (!get_weight().is_zero()) { - out << " + " << get_weight(); - } - display(out << " <= ", coeff(), var()); - out << "\n"; - } - - private: - - void display(std::ostream& out, rational const& c, th_var v) const { - if (!c.is_one()) { - out << c << "*"; - } - out << "v" << v; - } - }; - - template - class theory_horn_ineq::assignment_trail { - th_var m_var; - numeral m_old_value; - public: - assignment_trail(th_var v, const numeral & val): - m_var(v), - m_old_value(val) {} - th_var get_var() const { return m_var; } - const numeral & get_old_value() const { return m_old_value; } - }; - - template - class theory_horn_ineq::parent_trail { - th_var m_var; - clause_id m_old_value; - public: - parent_trail(th_var v, clause_id val): - m_var(v), - m_old_value(val) {} - th_var get_var() const { return m_var; } - clause_id get_old_value() const { return m_old_value; } - }; - - - template - class theory_horn_ineq::graph : private Ext { - - typedef vector assignment_stack; - typedef vector parent_stack; - typedef unsigned_vector clause_id_vector; - - struct stats { - unsigned m_propagation_cost; - - void reset() { - memset(this, 0, sizeof(*this)); - } - }; - - struct scope { - unsigned m_clauses_lim; - unsigned m_enabled_clauses_lim; - unsigned m_assignment_lim; - unsigned m_parent_lim; - scope(unsigned e, unsigned enabled, unsigned alim, unsigned plim): - m_clauses_lim(e), - m_enabled_clauses_lim(enabled), - m_assignment_lim(alim), - m_parent_lim(plim) { - } - }; - - stats m_stats; - vector m_clauses; - vector m_assignment; // per var - clause_id_vector m_parent; // per var - assignment_stack m_assignment_stack; // stack for restoring the assignment - parent_stack m_parent_stack; // stack for restoring parents - clause_id_vector m_enabled_clauses; - vector m_out_clauses; // use-list for clauses. - vector m_in_clauses; // clauses that have variable in head. - // forward reachability - unsigned_vector m_onstack; - unsigned m_ts; - unsigned_vector m_todo; - literal_vector m_lits; - vector m_coeffs; - th_var m_zero; - clause_id m_unsat_clause; - svector m_trail_stack; - - - public: - - graph(): m_ts(0), m_zero(null_theory_var), m_unsat_clause(null_clause_id) {} - - void reset() { - m_clauses .reset(); - m_assignment .reset(); - m_parent .reset(); - m_assignment_stack .reset(); - m_parent_stack .reset(); - m_out_clauses .reset(); - m_in_clauses .reset(); - m_enabled_clauses .reset(); - m_onstack .reset(); - m_ts = 0; - m_lits .reset(); - m_trail_stack .reset(); - m_unsat_clause = null_clause_id; - } - - - void traverse_neg_cycle1(bool /*stronger_lemmas*/) { - TRACE("horn_ineq", display(tout);); - SASSERT(!m_enabled_clauses.empty()); - clause_id id = m_unsat_clause; - SASSERT(id != null_clause_id); - SASSERT(!is_feasible(m_clauses[id])); - clause_id_vector todo; - vector muls; - todo.push_back(id); - muls.push_back(rational(1)); - u_map lits; - while (!todo.empty()) { - id = todo.back(); - rational mul = muls.back(); - todo.pop_back(); - muls.pop_back(); - clause const& cl = m_clauses[id]; - literal lit = cl.get_explanation(); - if (lit != null_literal) { - lits.insert_if_not_there2(id, rational(0))->get_data().m_value += mul; - } - for (unsigned i = 0; i < cl.size(); ++i) { - id = m_parent[cl.vars(i)]; - if (id != null_clause_id) { - todo.push_back(id); - muls.push_back(mul*cl.coeffs(i)); - } - } - } - u_map::iterator it = lits.begin(), end = lits.end(); - m_lits.reset(); - m_coeffs.reset(); - for (; it != end; ++it) { - m_lits.push_back(m_clauses[it->m_key].get_explanation()); - m_coeffs.push_back(it->m_value); - } - - // TODO: use topological sort to tune traversal of parents to linear. - // (current traversal can be exponential). - // TODO: negative cycle traversal with inline resolution to find - // stronger conflict clauses. - // follow heuristic used in theory_diff_logic_def.h: - } - - unsigned get_num_clauses() const { - return m_clauses.size(); - } - - literal_vector const& get_lits() const { - return m_lits; - } - - vector const& get_coeffs() const { - return m_coeffs; - } - - numeral get_assignment(th_var v) const { - return m_assignment[v]; - } - - numeral eval_body(clause const& cl) const { - numeral v(0); - for (unsigned i = 0; i < cl.size(); ++i) { - v += cl.coeffs(i)*m_assignment[cl.vars(i)]; - } - v += cl.get_weight(); - return v; - } - - numeral eval_body(clause_id id) const { - return eval_body(m_clauses[id]); - } - - numeral eval_head(clause_id id) const { - return eval_head(m_clauses[id]); - } - - numeral eval_head(clause const& cl) const { - return cl.coeff()*m_assignment[cl.var()]; - } - - clause const& get_clause(clause_id id) const { - return m_clauses[id]; - } - - void display_clause(std::ostream& out, clause_id id) const { - if (id == null_clause_id) { - out << "null\n"; - } - else { - m_clauses[id].display(out); - } - } - - void display(std::ostream& out) const { - for (unsigned i = 0; i < m_clauses.size(); ++i) { - display_clause(out, i); - } - for (unsigned i = 0; i < m_assignment.size(); ++i) { - out << m_assignment[i] << "\n"; - } - } - - void collect_statistics(::statistics& st) const { - st.update("hi_propagation_cst", m_stats.m_propagation_cost); - } - - void push() { - m_trail_stack.push_back(scope(m_clauses.size(), m_enabled_clauses.size(), - m_assignment_stack.size(), m_parent_stack.size())); - } - - void pop(unsigned num_scopes) { - unsigned lvl = m_trail_stack.size(); - SASSERT(num_scopes <= lvl); - unsigned new_lvl = lvl - num_scopes; - scope & s = m_trail_stack[new_lvl]; - // restore enabled clauses - for (unsigned i = m_enabled_clauses.size(); i > s.m_enabled_clauses_lim; ) { - --i; - m_clauses[m_enabled_clauses[i]].disable(); - } - m_enabled_clauses.shrink(s.m_enabled_clauses_lim); - - // restore assignment stack - for (unsigned i = m_assignment_stack.size(); i > s.m_assignment_lim; ) { - --i; - m_assignment[m_assignment_stack[i].get_var()] = m_assignment_stack[i].get_old_value(); - } - m_assignment_stack.shrink(s.m_assignment_lim); - - // restore parent stack - for (unsigned i = m_parent_stack.size(); i > s.m_parent_lim; ) { - --i; - m_parent[m_parent_stack[i].get_var()] = m_parent_stack[i].get_old_value(); - } - m_assignment_stack.shrink(s.m_assignment_lim); - - // restore clauses - unsigned old_num_clauses = s.m_clauses_lim; - unsigned num_clauses = m_clauses.size(); - SASSERT(old_num_clauses <= num_clauses); - unsigned to_delete = num_clauses - old_num_clauses; - for (unsigned i = 0; i < to_delete; i++) { - const clause & cl = m_clauses.back(); - TRACE("horn_ineq", tout << "deleting clause:\n"; cl.display(tout);); - for (unsigned j = 0; j < cl.size(); ++j) { - m_out_clauses[cl.vars(j)].pop_back(); - } - m_in_clauses[cl.var()].pop_back(); - m_clauses.pop_back(); - } - m_trail_stack.shrink(new_lvl); - SASSERT(check_invariant()); - } - - /** - \brief Add clause z <= z and the assignment z := 0 - Then z cannot be incremented without causing a loop (and therefore a contradiction). - */ - void set_to_zero(th_var z) { - m_zero = z; - } - - bool enable_clause(clause_id id) { - if (id == null_clause_id) { - return true; - } - clause& cl = m_clauses[id]; - bool r = true; - if (!cl.is_enabled()) { - cl.enable(); - if (!is_feasible(cl)) { - r = make_feasible(id); - } - m_enabled_clauses.push_back(id); - } - return r; - } - - void init_var(th_var v) { - unsigned sz = static_cast(v); - while (m_assignment.size() <= sz) { - m_assignment.push_back(Ext::m_minus_infty); - m_out_clauses.push_back(clause_id_vector()); - m_in_clauses.push_back(clause_id_vector()); - m_parent.push_back(null_clause_id); - m_onstack.push_back(0); - } - m_assignment[v] = Ext::m_minus_infty; - SASSERT(m_out_clauses[v].empty()); - SASSERT(m_in_clauses[v].empty()); - SASSERT(check_invariant()); - } - - clause_id add_ineq(vector > const& terms, numeral const& weight, literal l) { - vector coeffs; - svector vars; - rational coeff(1); - th_var var = null_theory_var; - bool found_negative = false; - for (unsigned i = 0; i < terms.size(); ++i) { - rational const& r = terms[i].second; - if (r.is_pos()) { - coeffs.push_back(terms[i].second); - vars.push_back(terms[i].first); - } - else if (found_negative) { - return null_clause_id; - } - else { - SASSERT(r.is_neg()); - found_negative = true; - coeff = -r; - var = terms[i].first; - } - } - if (!found_negative) { - coeff = rational(1); - var = m_zero; - } - if (!coeff.is_one()) { - // so far just support unit coefficients on right. - return null_clause_id; - } - clause_id id = m_clauses.size(); - m_clauses.push_back(clause(coeffs.size(), coeffs.c_ptr(), vars.c_ptr(), coeff, var, weight, l)); - for (unsigned i = 0; i < vars.size(); ++i) { - m_out_clauses[vars[i]].push_back(id); - } - m_in_clauses[var].push_back(id); - - return id; - } - - bool is_feasible() const { - for (unsigned i = 0; i < m_clauses.size(); ++i) { - if (!is_feasible(m_clauses[i])) { - return false; - } - } - return true; - } - - private: - - bool check_invariant() { - return true; - } - - /** - assignments are fully retraced on backtracking. - This is not always necessary. - */ - - void acc_assignment(th_var v, const numeral & inc) { - m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); - m_assignment[v] += inc; - } - - void acc_parent(th_var v, clause_id parent) { - m_parent[v] = parent; - m_parent_stack.push_back(parent_trail(v, parent)); - } - - numeral get_delta(const clause & cl) const { - SASSERT(cl.coeff().is_one() && "Not yet support for non-units"); - return eval_body(cl) - eval_head(cl); - } - - void set_onstack(th_var v) { - SASSERT(m_ts != 0); - m_onstack[v] = m_ts; - } - - void reset_onstack(th_var v) { - m_onstack[v] = 0; - } - - bool is_onstack(th_var v) const { - return m_onstack[v] == m_ts; - } - - void inc_ts() { - m_ts++; - if (m_ts == 0) { - m_ts++; - m_onstack.reset(); - m_onstack.resize(m_assignment.size(), 0); - } - } - - // Make the assignment feasible. An assignment is feasible if - // Forall clause cl. eval_body(cl) <= eval_head(cl) - // - // This method assumes that if the assignment is not feasible, - // then the only infeasible clause is the last added clause. - // - // Traversal is by naive DFS search. - // - bool make_feasible(clause_id id) { - SASSERT(is_almost_feasible(id)); - SASSERT(!m_clauses.empty()); - SASSERT(!is_feasible(m_clauses[id])); - const clause & cl0 = m_clauses[id]; - inc_ts(); - for (unsigned i = 0; i < cl0.size(); ++i) { - set_onstack(cl0.vars(i)); - } - th_var source = cl0.var(); - numeral delta = get_delta(cl0); - acc_parent(source, id); - SASSERT(delta.is_pos()); - acc_assignment(source, delta); - m_todo.reset(); - m_todo.push_back(source); - - TRACE("horn_ineq", cl0.display(tout);); - - do { - ++m_stats.m_propagation_cost; - - typename clause_id_vector::iterator it = m_out_clauses[source].begin(); - typename clause_id_vector::iterator end = m_out_clauses[source].end(); - for (; it != end; ++it) { - clause & cl = m_clauses[*it]; - if (!cl.is_enabled()) { - continue; - } - delta = get_delta(cl); - - if (delta.is_pos()) { - TRACE("horn_ineq", cl.display(tout);); - th_var target = cl.var(); - if (is_onstack(target)) { - m_unsat_clause = *it; - return false; - } - else { - acc_assignment(target, delta); - acc_parent(target, *it); - m_todo.push_back(target); - } - } - } - set_onstack(source); - source = m_todo.back(); - // pop stack until there is a new variable to process. - while (is_onstack(source)) { - m_todo.pop_back(); - reset_onstack(source); - if (m_todo.empty()) { - break; - } - source = m_todo.back(); - } - } - while (!m_todo.empty()); - return true; - } - - bool is_almost_feasible(clause_id id) const { - for (unsigned i = 0; i < m_clauses.size(); ++i) { - if (id != static_cast(i) && !is_feasible(m_clauses[i])) { - return false; - } - } - return true; - } - - bool is_feasible(const clause & cl) const { - return !cl.is_enabled() || get_delta(cl).is_nonpos(); - } - - }; - - template - theory_horn_ineq::theory_horn_ineq(ast_manager& m): - theory(m.mk_family_id("arith")), - a(m), - m_arith_eq_adapter(*this, m_params, a), - m_zero_int(null_theory_var), - m_zero_real(null_theory_var), - m_graph(0), - m_asserted_qhead(0), - m_agility(0.5), - m_lia(false), - m_lra(false), - m_non_horn_ineq_exprs(false), - m_test(m), - m_factory(0) { - m_graph = alloc(graph); - } - - template - theory_horn_ineq::~theory_horn_ineq() { - reset_eh(); - dealloc(m_graph); - } - - template - std::ostream& theory_horn_ineq::atom::display(theory_horn_ineq const& th, std::ostream& out) const { - context& ctx = th.get_context(); - lbool asgn = ctx.get_assignment(m_bvar); - bool sign = (l_undef == asgn) || m_true; - return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; - if (l_undef == asgn) { - out << "unassigned\n"; - } - else { - th.m_graph->display_clause(out, get_asserted_edge()); - } - return out; - } - - template - theory_var theory_horn_ineq::mk_var(enode* n) { - th_var v = theory::mk_var(n); - m_graph->init_var(v); - get_context().attach_th_var(n, this, v); - return v; - } - - template - theory_var theory_horn_ineq::mk_var(expr* n) { - context & ctx = get_context(); - enode* e = 0; - th_var v = null_theory_var; - m_lia |= a.is_int(n); - m_lra |= a.is_real(n); - if (!is_app(n)) { - return v; - } - if (ctx.e_internalized(n)) { - e = ctx.get_enode(n); - v = e->get_th_var(get_id()); - } - else { - ctx.internalize(n, false); - e = ctx.get_enode(n); - } - if (v == null_theory_var) { - v = mk_var(e); - } - if (is_interpreted(to_app(n))) { - found_non_horn_ineq_expr(n); - } - return v; - } - - template - void theory_horn_ineq::reset_eh() { - m_graph ->reset(); - m_zero_int = null_theory_var; - m_zero_real = null_theory_var; - m_atoms .reset(); - m_asserted_atoms .reset(); - m_stats .reset(); - m_scopes .reset(); - m_asserted_qhead = 0; - m_agility = 0.5; - m_lia = false; - m_lra = false; - m_non_horn_ineq_exprs = false; - theory::reset_eh(); - } - - - - template - void theory_horn_ineq::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) { - rational k; - th_var s = expand(true, v1, k); - th_var t = expand(false, v2, k); - context& ctx = get_context(); - ast_manager& m = get_manager(); - - if (s == t) { - if (is_eq != k.is_zero()) { - // conflict 0 /= k; - inc_conflicts(); - ctx.set_conflict(&eq_just); - } - } - else { - // - // Create equality ast, internalize_atom - // assign the corresponding equality literal. - // - - app_ref eq(m), s2(m), t2(m); - app* s1 = get_enode(s)->get_owner(); - app* t1 = get_enode(t)->get_owner(); - s2 = a.mk_sub(t1, s1); - t2 = a.mk_numeral(k, m.get_sort(s2.get())); - // t1 - s1 = k - eq = m.mk_eq(s2.get(), t2.get()); - - TRACE("horn_ineq", - tout << v1 << " .. " << v2 << "\n"; - tout << mk_pp(eq.get(), m) <<"\n";); - - if (!internalize_atom(eq.get(), false)) { - UNREACHABLE(); - } - - literal l(ctx.get_literal(eq.get())); - if (!is_eq) { - l = ~l; - } - - ctx.assign(l, b_justification(&eq_just), false); - } - } - - template - void theory_horn_ineq::inc_conflicts() { - m_stats.m_num_conflicts++; - if (m_params.m_arith_adaptive) { - double g = m_params.m_arith_adaptive_propagation_threshold; - m_agility = m_agility*g + 1 - g; - } - } - - template - void theory_horn_ineq::set_neg_cycle_conflict() { - m_graph->traverse_neg_cycle1(m_params.m_arith_stronger_lemmas); - inc_conflicts(); - literal_vector const& lits = m_graph->get_lits(); - context & ctx = get_context(); - TRACE("horn_ineq", - tout << "conflict: "; - for (unsigned i = 0; i < lits.size(); ++i) { - ctx.display_literal_info(tout, lits[i]); - } - tout << "\n"; - ); - - if (m_params.m_arith_dump_lemmas) { - char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"; - ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); - } - - vector params; - if (get_manager().proofs_enabled()) { - params.push_back(parameter(symbol("farkas"))); - vector const& coeffs = m_graph->get_coeffs(); - for (unsigned i = 0; i < coeffs.size(); ++i) { - params.push_back(parameter(coeffs[i])); - } - } - - ctx.set_conflict( - ctx.mk_justification( - ext_theory_conflict_justification( - get_id(), ctx.get_region(), - lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); - } - - template - void theory_horn_ineq::found_non_horn_ineq_expr(expr* n) { - if (!m_non_horn_ineq_exprs) { - TRACE("horn_ineq", tout << "found non horn logic expression:\n" << mk_pp(n, get_manager()) << "\n";); - get_context().push_trail(value_trail(m_non_horn_ineq_exprs)); - m_non_horn_ineq_exprs = true; - } - } - - template - void theory_horn_ineq::init(context* ctx) { - theory::init(ctx); - m_zero_int = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); - m_zero_real = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), false), false, false, true)); - m_graph->set_to_zero(m_zero_int); - m_graph->set_to_zero(m_zero_real); - } - - /** - \brief Create negated literal. - - The negation of: E <= 0 - - -E + epsilon <= 0 - or - -E + 1 <= 0 - */ - template - void theory_horn_ineq::negate(coeffs& coeffs, rational& weight) { - for (unsigned i = 0; i < coeffs.size(); ++i) { - coeffs[i].second.neg(); - } - weight.neg(); - } - - template - typename theory_horn_ineq::numeral theory_horn_ineq::mk_weight(bool is_real, bool is_strict, rational const& w) const { - if (is_strict) { - return numeral(inf_numeral(w)) + (is_real?Ext::m_epsilon:numeral(1)); - } - else { - return numeral(inf_numeral(w)); - } - } - - template - void theory_horn_ineq::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) { - coeffs.reset(); - w = m_test.get_weight(); - for (unsigned i = 0; i < terms.size(); ++i) { - coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second)); - } - } - - template - bool theory_horn_ineq::internalize_atom(app * n, bool) { - context & ctx = get_context(); - if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) { - found_non_horn_ineq_expr(n); - return false; - } - SASSERT(!ctx.b_internalized(n)); - expr* e1 = n->get_arg(0), *e2 = n->get_arg(1); - if (a.is_ge(n) || a.is_gt(n)) { - std::swap(e1, e2); - } - bool is_strict = a.is_gt(n) || a.is_lt(n); - - horn_ineq_tester::classify_t cl = m_test.linearize(e1, e2); - if (cl == horn_ineq_tester::non_horn) { - found_non_horn_ineq_expr(n); - return false; - } - - rational w; - coeffs coeffs; - mk_coeffs(m_test.get_linearization(), coeffs, w); - bool_var bv = ctx.mk_bool_var(n); - ctx.set_var_theory(bv, get_id()); - literal l(bv); - numeral w1 = mk_weight(a.is_real(e1), is_strict, w); - clause_id pos = m_graph->add_ineq(coeffs, w1, l); - negate(coeffs, w); - numeral w2 = mk_weight(a.is_real(e1), !is_strict, w); - clause_id neg = m_graph->add_ineq(coeffs, w2, ~l); - m_bool_var2atom.insert(bv, m_atoms.size()); - m_atoms.push_back(atom(bv, pos, neg)); - - TRACE("horn_ineq", - tout << mk_pp(n, get_manager()) << "\n"; - m_graph->display_clause(tout << "pos: ", pos); - m_graph->display_clause(tout << "neg: ", neg); - ); - - return true; - } - - template - bool theory_horn_ineq::internalize_term(app * term) { - bool result = null_theory_var != mk_term(term); - CTRACE("horn_ineq", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); - TRACE("horn_ineq", tout << "Terms may not be internalized " << mk_pp(term, get_manager()) << "\n";); - found_non_horn_ineq_expr(term); - return result; - } - - template - void theory_horn_ineq::internalize_eq_eh(app * atom, bool_var) { - // noop - } - - template - void theory_horn_ineq::assign_eh(bool_var v, bool is_true) { - m_stats.m_num_assertions++; - unsigned idx = m_bool_var2atom.find(v); - SASSERT(get_context().get_assignment(v) != l_undef); - SASSERT((get_context().get_assignment(v) == l_true) == is_true); - m_atoms[idx].assign_eh(is_true); - m_asserted_atoms.push_back(idx); - } - - template - void theory_horn_ineq::push_scope_eh() { - theory::push_scope_eh(); - m_graph->push(); - m_scopes.push_back(scope()); - scope & s = m_scopes.back(); - s.m_atoms_lim = m_atoms.size(); - s.m_asserted_atoms_lim = m_asserted_atoms.size(); - s.m_asserted_qhead_old = m_asserted_qhead; - } - - template - void theory_horn_ineq::pop_scope_eh(unsigned num_scopes) { - unsigned lvl = m_scopes.size(); - SASSERT(num_scopes <= lvl); - unsigned new_lvl = lvl - num_scopes; - scope & s = m_scopes[new_lvl]; - del_atoms(s.m_atoms_lim); - m_asserted_atoms.shrink(s.m_asserted_atoms_lim); - m_asserted_qhead = s.m_asserted_qhead_old; - m_scopes.shrink(new_lvl); - m_graph->pop(num_scopes); - theory::pop_scope_eh(num_scopes); - } - - template - final_check_status theory_horn_ineq::final_check_eh() { - SASSERT(is_consistent()); - TRACE("horn_ineq", display(tout);); - if (can_propagate()) { - propagate_core(); - return FC_CONTINUE; - } - else if (m_non_horn_ineq_exprs) { - return FC_GIVEUP; - } - else { - return FC_DONE; - } - } - - template - void theory_horn_ineq::propagate() { - propagate_core(); - } - - template - void theory_horn_ineq::display(std::ostream& out) const { - for (unsigned i = 0; i < m_atoms.size(); ++i) { - m_atoms[i].display(*this, out); - } - out << "\n"; - m_graph->display(out); - } - - template - void theory_horn_ineq::collect_statistics(::statistics& st) const { - st.update("horn ineq conflicts", m_stats.m_num_conflicts); - st.update("horn ineq assertions", m_stats.m_num_assertions); - m_arith_eq_adapter.collect_statistics(st); - m_graph->collect_statistics(st); - } - - template - void theory_horn_ineq::del_atoms(unsigned old_size) { - typename atoms::iterator begin = m_atoms.begin() + old_size; - typename atoms::iterator it = m_atoms.end(); - while (it != begin) { - --it; - m_bool_var2atom.erase(it->get_bool_var()); - } - m_atoms.shrink(old_size); - } - - template - void theory_horn_ineq::propagate_core() { - bool consistent = true; - while (consistent && can_propagate()) { - unsigned idx = m_asserted_atoms[m_asserted_qhead]; - m_asserted_qhead++; - consistent = propagate_atom(m_atoms[idx]); - } - } - - template - bool theory_horn_ineq::propagate_atom(atom const& a) { - context& ctx = get_context(); - TRACE("horn_ineq", a.display(*this, tout); tout << "\n";); - if (ctx.inconsistent()) { - return false; - } - int clause_id = a.get_asserted_edge(); - if (!m_graph->enable_clause(clause_id)) { - set_neg_cycle_conflict(); - return false; - } - return true; - } - - template - theory_var theory_horn_ineq::mk_term(app* n) { - context& ctx = get_context(); - - horn_ineq_tester::classify_t cl = m_test.linearize(n); - if (cl == horn_ineq_tester::non_horn) { - found_non_horn_ineq_expr(n); - return null_theory_var; - } - - coeffs coeffs; - rational w; - mk_coeffs(m_test.get_linearization(), coeffs, w); - if (coeffs.empty()) { - return mk_num(n, w); - } - if (coeffs.size() == 1 && coeffs[0].second.is_one()) { - return coeffs[0].first; - } - th_var target = mk_var(ctx.mk_enode(n, false, false, true)); - coeffs.push_back(std::make_pair(target, rational(-1))); - - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); - negate(coeffs, w); - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); - return target; - } - - template - theory_var theory_horn_ineq::mk_num(app* n, rational const& r) { - theory_var v = null_theory_var; - context& ctx = get_context(); - if (r.is_zero()) { - v = a.is_int(n)?m_zero_int:m_zero_real; - } - else if (ctx.e_internalized(n)) { - enode* e = ctx.get_enode(n); - v = e->get_th_var(get_id()); - SASSERT(v != null_theory_var); - } - else { - v = mk_var(ctx.mk_enode(n, false, false, true)); - // v = k: v <= k k <= v - coeffs coeffs; - coeffs.push_back(std::make_pair(v, rational(1))); - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(r)), null_literal))); - coeffs.back().second.neg(); - VERIFY (m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(-r)), null_literal))); - } - return v; - } - - template - theory_var theory_horn_ineq::expand(bool pos, th_var v, rational & k) { - context& ctx = get_context(); - enode* e = get_enode(v); - expr* x, *y; - rational r; - for (;;) { - app* n = e->get_owner(); - if (a.is_add(n, x, y)) { - if (a.is_numeral(x, r)) { - e = ctx.get_enode(y); - } - else if (a.is_numeral(y, r)) { - e = ctx.get_enode(x); - } - v = e->get_th_var(get_id()); - SASSERT(v != null_theory_var); - if (v == null_theory_var) { - break; - } - if (pos) { - k += r; - } - else { - k -= r; - } - } - else { - break; - } - } - return v; - } - - template - bool theory_horn_ineq::is_consistent() const { - return m_graph->is_feasible(); - } - - // models: - template - void theory_horn_ineq::init_model(model_generator & m) { - m_factory = alloc(arith_factory, get_manager()); - m.register_factory(m_factory); - compute_delta(); - } - - template - model_value_proc * theory_horn_ineq::mk_value(enode * n, model_generator & mg) { - theory_var v = n->get_th_var(get_id()); - SASSERT(v != null_theory_var); - numeral val = m_graph->get_assignment(v); - rational num = val.get_infinity()*m_lambda + val.get_rational() + val.get_infinitesimal()*m_delta; - TRACE("horn_ineq", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); - return alloc(expr_wrapper_proc, m_factory->mk_value(num, a.is_int(n->get_owner()))); - } - - /** - \brief Compute numeral values for the infinitesimals to satisfy the inequalities. - */ - - template - void theory_horn_ineq::compute_delta() { - m_delta = rational(1); - m_lambda = rational(0); - unsigned sz = m_graph->get_num_clauses(); - - for (unsigned i = 0; i < sz; ++i) { - if (!m_graph->get_clause(i).is_enabled()) { - continue; - } - numeral b = m_graph->eval_body(i); - numeral h = m_graph->eval_head(i); - - if (b.get_infinity() < h.get_infinity()) { - continue; - } - SASSERT(b.get_infinity() == h.get_infinity()); - - // b <= h - // suppose that h.eps < b.eps - // then we have h.num > b.num - // but also h.num + delta*h.eps >= b.num + delta*b.eps - // <=> - // (h.num - b.num)/(b.eps - h.eps) >= delta - rational num_r = h.get_rational() - b.get_rational(); - rational eps_r = b.get_infinitesimal() - h.get_infinitesimal(); - if (eps_r.is_pos()) { - SASSERT(num_r.is_pos()); - rational new_delta = num_r/eps_r; - if (new_delta < m_delta) { - m_delta = new_delta; - } - } - } - - for (unsigned i = 0; i < sz; ++i) { - if (!m_graph->get_clause(i).is_enabled()) { - continue; - } - numeral b = m_graph->eval_body(i); - numeral h = m_graph->eval_head(i); - - rational ir = h.get_infinity() - b.get_infinity(); - rational hr = b.get_rational() - h.get_rational(); - rational num_r = hr + m_delta*(b.get_infinitesimal() - h.get_infinitesimal()); - - SASSERT(b.get_infinity() <= h.get_infinity()); - - // b <= h - // suppose that h.finite < b.finite - // then we have h.infinite > b.infinite - // but also - // h.infinite*lambda + h.finite >= b.infinite*lambda + b.finite - // <=> - // lambda >= (b.finite - h.finite) / (h.infinite - b.infinite) - if (num_r.is_pos()) { - SASSERT(ir.is_pos()); - rational new_lambda = num_r/ir; - if (new_lambda > m_lambda) { - m_lambda = new_lambda; - } - } - } - } - - - -}; - -#endif From e27b7e30381a66ff033a0afa80fdd82c749447fd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 1 Mar 2014 11:09:57 -0800 Subject: [PATCH 288/509] use size_t for return values from strlen Signed-off-by: Nikolaj Bjorner --- src/smt/smt_quantifier.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index 8fd0e08bc..e9b0e069a 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -439,12 +439,12 @@ namespace smt { virtual bool model_based() const { return m_fparams->m_mbqi; } virtual bool mbqi_enabled(quantifier *q) const { - if(!m_fparams->m_mbqi_id) return true; - const symbol &s = q->get_qid(); - unsigned len = strlen(m_fparams->m_mbqi_id); - if(s == symbol::null || s.is_numerical()) - return len == 0; - return strncmp(s.bare_str(),m_fparams->m_mbqi_id,len) == 0; + if(!m_fparams->m_mbqi_id) return true; + const symbol &s = q->get_qid(); + size_t len = strlen(m_fparams->m_mbqi_id); + if(s == symbol::null || s.is_numerical()) + return len == 0; + return strncmp(s.bare_str(),m_fparams->m_mbqi_id,len) == 0; } /* Quantifier id's must begin with the prefix specified by From a00a9fbdfd1e9545c26d8cb5114832dc13849114 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Mar 2014 17:10:48 -0800 Subject: [PATCH 289/509] generate error on duplicated data-type accessors. Issue 85 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/pdecl.cpp | 19 +++++++++++++++++++ src/cmd_context/pdecl.h | 2 ++ src/parsers/smt2/smt2parser.cpp | 7 +++++++ 3 files changed, 28 insertions(+) diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp index 44ba2b4d2..4a51e4943 100644 --- a/src/cmd_context/pdecl.cpp +++ b/src/cmd_context/pdecl.cpp @@ -515,6 +515,25 @@ bool pdatatype_decl::has_missing_refs(symbol & missing) const { return false; } +bool pdatatype_decl::has_duplicate_accessors(symbol & duplicated) const { + hashtable names; + ptr_vector::const_iterator it = m_constructors.begin(); + ptr_vector::const_iterator end = m_constructors.end(); + for (; it != end; ++it) { + ptr_vector const& acc = (*it)->m_accessors; + for (unsigned i = 0; i < acc.size(); ++i) { + symbol const& name = acc[i]->get_name(); + if (names.contains(name)) { + duplicated = name; + return true; + } + names.insert(name); + } + } + return false; +} + + bool pdatatype_decl::fix_missing_refs(dictionary const & symbol2idx, symbol & missing) { ptr_vector::iterator it = m_constructors.begin(); ptr_vector::iterator end = m_constructors.end(); diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h index 76ad2a020..83d881f57 100644 --- a/src/cmd_context/pdecl.h +++ b/src/cmd_context/pdecl.h @@ -169,6 +169,7 @@ public: class paccessor_decl : public pdecl { friend class pdecl_manager; friend class pconstructor_decl; + friend class pdatatype_decl; symbol m_name; ptype m_type; paccessor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, ptype const & r); @@ -222,6 +223,7 @@ public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s); virtual void display(std::ostream & out) const; bool has_missing_refs(symbol & missing) const; + bool has_duplicate_accessors(symbol & repeated) const; }; /** diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index b5af6353e..546fd3819 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -850,6 +850,13 @@ namespace smt2 { for (unsigned i = 0; i < sz; i++) { pdatatype_decl * d = new_dt_decls[i]; SASSERT(d != 0); + symbol duplicated; + if (d->has_duplicate_accessors(duplicated)) { + std::string err_msg = "invalid datatype declaration, repeated accessor identifier '"; + err_msg += duplicated.str(); + err_msg += "'"; + throw parser_exception(err_msg, line, pos); + } m_ctx.insert(d); if (d->get_num_params() == 0) { // if datatype is not parametric... then force instantiation to register accessor, recognizers and constructors... From 4f20216677199792f784ed4aa43f6927bbafb417 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Mar 2014 17:14:26 -0800 Subject: [PATCH 290/509] fix documnetation to say milli-seconds. Issue 84 Signed-off-by: Nikolaj Bjorner --- src/shell/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell/main.cpp b/src/shell/main.cpp index e0fa4a1f2..f23bc470c 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -87,7 +87,7 @@ void display_usage() { std::cout << "\nResources:\n"; // timeout and memout are now available on Linux and OSX too. std::cout << " -T:timeout set the timeout (in seconds).\n"; - std::cout << " -t:timeout set the soft timeout (in seconds). It only kills the current query.\n"; + std::cout << " -t:timeout set the soft timeout (in milli seconds). It only kills the current query.\n"; std::cout << " -memory:Megabytes set a limit for virtual memory consumption.\n"; // std::cout << "\nOutput:\n"; From 23313e5bdca1bde6aee38c61eb4c46ffb4f2f8ff Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Mar 2014 17:24:40 -0800 Subject: [PATCH 291/509] remove unsound simplification for rem. Codeplex Issue 76 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/arith_rewriter.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 0856ab5b8..b040efc0c 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -753,12 +753,7 @@ br_status arith_rewriter::mk_rem_core(expr * arg1, expr * arg2, expr_ref & resul } else if (m_util.is_numeral(arg2, v2, is_int) && is_int && !v2.is_zero()) { if (is_add(arg1) || is_mul(arg1)) { - ptr_buffer new_args; - unsigned num_args = to_app(arg1)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) - new_args.push_back(m_util.mk_rem(to_app(arg1)->get_arg(i), arg2)); - result = m().mk_app(to_app(arg1)->get_decl(), new_args.size(), new_args.c_ptr()); - return BR_REWRITE2; + return BR_FAILED; } else { if (v2.is_neg()) { From b1b349f49662d16c87060f82ddcf8204ce4f7b74 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Mar 2014 17:50:29 -0800 Subject: [PATCH 292/509] modify offset check to accept linear expressions over numerals. Codeplex issue 81 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/check_logic.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cmd_context/check_logic.cpp b/src/cmd_context/check_logic.cpp index dfee148b5..99b0f2521 100644 --- a/src/cmd_context/check_logic.cpp +++ b/src/cmd_context/check_logic.cpp @@ -310,6 +310,8 @@ struct check_logic::imp { return false; non_numeral = arg; } + if (non_numeral == 0) + return true; if (is_diff_var(non_numeral)) return true; if (!m_a_util.is_add(non_numeral) && !m_a_util.is_sub(non_numeral)) From 601cb43f7898f108ae34aa6755d74f1ac2693ff6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 4 Mar 2014 17:18:49 -0800 Subject: [PATCH 293/509] fix quotation bug reported by Arie Gurfinkel Signed-off-by: Nikolaj Bjorner --- src/ast/ast_smt2_pp.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 1ad2b8222..77c8ac58f 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -472,6 +472,25 @@ class smt2_printer { ast_manager & m() const { return m_manager; } ast_manager & fm() const { return format_ns::fm(m()); } + std::string ensure_quote(symbol const& s) { + std::string str; + if (is_smt2_quoted_symbol(s)) + str = mk_smt2_quoted_symbol(s); + else + str = s.str(); + return str; + } + + symbol ensure_quote_sym(symbol const& s) { + if (is_smt2_quoted_symbol(s)) { + std::string str; + str = mk_smt2_quoted_symbol(s); + return symbol(str.c_str()); + } + else + return s; + } + void pp_var(var * v) { format * f; if (v->get_idx() < m_var_names.size()) { @@ -501,11 +520,7 @@ class smt2_printer { } format * pp_simple_attribute(char const * attr, symbol const & s) { - std::string str; - if (is_smt2_quoted_symbol(s)) - str = mk_smt2_quoted_symbol(s); - else - str = s.str(); + std::string str = ensure_quote(s); return mk_compose(m(), mk_string(m(), attr), mk_string(m(), str.c_str())); } @@ -773,7 +788,7 @@ class smt2_printer { void register_var_names(quantifier * q) { unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; i++) { - symbol name = q->get_decl_name(i); + symbol name = ensure_quote_sym(q->get_decl_name(i)); if (name.is_numerical()) { unsigned idx = 1; name = next_name("x", idx); @@ -997,6 +1012,7 @@ public: unsigned idx = 1; for (unsigned i = 0; i < num; i++) { symbol name = next_name(var_prefix, idx); + name = ensure_quote_sym(name); var_names.push_back(name); m_var_names_set.insert(name); m_var_names.push_back(name); From 83a774ac7934f40940c7f23424fced2c1a0ba590 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 4 Mar 2014 18:38:08 -0800 Subject: [PATCH 294/509] duality fix plus mbqi option --- src/duality/duality_rpfp.cpp | 21 ++++++++++++++++++++- src/duality/duality_wrapper.cpp | 3 ++- src/duality/duality_wrapper.h | 1 + src/muz/base/fixedpoint_params.pyg | 1 + src/muz/duality/duality_dl_interface.cpp | 1 + 5 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index e09278dd4..550f94f2d 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2847,7 +2847,12 @@ namespace Duality { } void RPFP::InterpolateByCases(Node *root, Node *node){ + timer_start("InterpolateByCases"); bool axioms_added = false; + hash_set axioms_needed; + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + axioms_needed.insert(theory[i]); aux_solver.push(); AddEdgeToSolver(node->Outgoing); node->Annotation.SetEmpty(); @@ -2869,7 +2874,17 @@ namespace Duality { check_result foo = Check(root); if(foo != unsat) throw "should be unsat"; - AddToProofCore(*core); + + std::vector assumps, axioms_to_add; + slvr().get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++){ + (*core).insert(assumps[i]); + if(axioms_needed.find(assumps[i]) != axioms_needed.end()){ + axioms_to_add.push_back(assumps[i]); + axioms_needed.erase(assumps[i]); + } + } + // AddToProofCore(*core); Transformer old_annot = node->Annotation; SolveSingleNode(root,node); @@ -2882,6 +2897,9 @@ namespace Duality { node->Annotation.Formula = node->Annotation.Formula.simplify(); } + for(unsigned i = 0; i < axioms_to_add.size(); i++) + aux_solver.add(axioms_to_add[i]); + if(node->Annotation.IsEmpty()){ if(!axioms_added){ // add the axioms in the off chance they are useful @@ -2906,6 +2924,7 @@ namespace Duality { delete proof_core; // shouldn't happen proof_core = core; aux_solver.pop(1); + timer_stop("InterpolateByCases"); } void RPFP::Generalize(Node *root, Node *node){ diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index cf2c803cb..345fef562 100755 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -43,7 +43,8 @@ namespace Duality { if(models) p.set_bool("model", true); p.set_bool("unsat_core", true); - p.set_bool("mbqi",true); + bool mbqi = c.get_config().get().get_bool("mbqi",true); + p.set_bool("mbqi",mbqi); // just to test p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants p.set_uint("mbqi.max_iterations",1); // use mbqi for quantifiers in interpolants if(true || extensional) diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 2b0045023..febc3f1f1 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -182,6 +182,7 @@ namespace Duality { void set(char const * param, char const * value) { m_config.set(param,value); } void set(char const * param, bool value) { m_config.set(param,value); } void set(char const * param, int value) { m_config.set(param,value); } + config &get_config() {return m_config;} symbol str_symbol(char const * s); symbol int_symbol(int n); diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index e09e1307b..5eec77f39 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -74,6 +74,7 @@ def_module_params('fixedpoint', ('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'), ('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'), ('profile', BOOL, False, 'DUALITY: profile run time'), + ('mbqi', BOOL, True, 'DUALITY: use model-based quantifier instantion'), ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), )) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 248aca163..e7c7976f6 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -141,6 +141,7 @@ lbool dl_interface::query(::expr * query) { // make a new problem and solver _d = alloc(duality_data,m_ctx.get_manager()); + _d->ctx.set("mbqi",m_ctx.get_params().mbqi()); _d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx); _d->rpfp = alloc(RPFP,_d->ls); From 4732e03259487da4a45391a6acc45b6adb8a4a3e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 7 Mar 2014 08:59:27 -0800 Subject: [PATCH 295/509] filter fresh constants from models Signed-off-by: Nikolaj Bjorner --- src/tactic/bv/bv_size_reduction_tactic.cpp | 34 ++++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/tactic/bv/bv_size_reduction_tactic.cpp b/src/tactic/bv/bv_size_reduction_tactic.cpp index d24f506a1..bbbc00783 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -25,6 +25,7 @@ Notes: #include"bv_decl_plugin.h" #include"expr_replacer.h" #include"extension_model_converter.h" +#include"filter_model_converter.h" #include"ast_smt2_pp.h" class bv_size_reduction_tactic : public tactic { @@ -60,6 +61,7 @@ struct bv_size_reduction_tactic::imp { obj_map m_unsigned_lowers; obj_map m_unsigned_uppers; ref m_mc; + ref m_fmc; scoped_ptr m_replacer; bool m_produce_models; volatile bool m_cancel; @@ -196,6 +198,7 @@ struct bv_size_reduction_tactic::imp { numeral u = m_util.norm(entry->get_data().m_value, bv_sz, true); TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";); expr * new_def = 0; + app * new_const = 0; if (l > u) { g.assert_expr(m.mk_false()); return; @@ -208,15 +211,19 @@ struct bv_size_reduction_tactic::imp { if (l.is_neg()) { unsigned i_nb = (u - l).get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); - if (i_nb < v_nb) - new_def = m_util.mk_sign_extend(v_nb - i_nb, m.mk_fresh_const(0, m_util.mk_sort(i_nb))); + if (i_nb < v_nb) { + new_const = m.mk_fresh_const(0, m_util.mk_sort(i_nb)); + new_def = m_util.mk_sign_extend(v_nb - i_nb, new_const); + } } else { // 0 <= l <= v <= u unsigned u_nb = u.get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); - if (u_nb < v_nb) - new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), m.mk_fresh_const(0, m_util.mk_sort(u_nb))); + if (u_nb < v_nb) { + new_const = m.mk_fresh_const(0, m_util.mk_sort(u_nb)); + new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), new_const); + } } } @@ -226,6 +233,10 @@ struct bv_size_reduction_tactic::imp { if (!m_mc) m_mc = alloc(bv_size_reduction_mc, m); m_mc->insert(v->get_decl(), new_def); + if (!m_fmc && new_const) + m_fmc = alloc(filter_model_converter, m); + if (new_const) + m_fmc->insert(new_const->get_decl()); } num_reduced++; } @@ -264,6 +275,7 @@ struct bv_size_reduction_tactic::imp { TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";); expr * new_def = 0; + app * new_const = 0; if (l > u) { g.assert_expr(m.mk_false()); return; @@ -275,8 +287,10 @@ struct bv_size_reduction_tactic::imp { // 0 <= l <= v <= u unsigned u_nb = u.get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); - if (u_nb < v_nb) - new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), m.mk_fresh_const(0, m_util.mk_sort(u_nb))); + if (u_nb < v_nb) { + new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), new_const); + new_const = m.mk_fresh_const(0, m_util.mk_sort(u_nb)); + } } if (new_def) { @@ -285,6 +299,10 @@ struct bv_size_reduction_tactic::imp { if (!m_mc) m_mc = alloc(bv_size_reduction_mc, m); m_mc->insert(v->get_decl(), new_def); + if (!m_fmc && new_const) + m_fmc = alloc(filter_model_converter, m); + if (new_const) + m_fmc->insert(new_const->get_decl()); } num_reduced++; TRACE("bv_size_reduction", tout << "New definition = " << mk_ismt2_pp(new_def, m) << "\n";); @@ -309,7 +327,11 @@ struct bv_size_reduction_tactic::imp { g.update(i, new_f); } mc = m_mc.get(); + if (m_fmc) { + mc = concat(m_fmc.get(), mc.get()); + } m_mc = 0; + m_fmc = 0; } report_tactic_progress(":bv-reduced", num_reduced); TRACE("after_bv_size_reduction", g.display(tout); if (m_mc) m_mc->display(tout);); From 180f55bbdaaec6f2eee07c6c973cef2af2558add Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 11 Mar 2014 18:20:42 -0700 Subject: [PATCH 296/509] adding support for non-extensional arrays in duality --- src/duality/duality.h | 15 +- src/duality/duality_rpfp.cpp | 183 ++++++++++++++++++----- src/duality/duality_solver.cpp | 22 ++- src/duality/duality_wrapper.cpp | 17 ++- src/duality/duality_wrapper.h | 3 + src/muz/base/fixedpoint_params.pyg | 1 + src/muz/duality/duality_dl_interface.cpp | 1 + 7 files changed, 196 insertions(+), 46 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 12ba21516..137279899 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -31,6 +31,8 @@ using namespace stl_ext; namespace Duality { + class implicant_solver; + /* Generic operations on Z3 formulas */ struct Z3User { @@ -104,6 +106,9 @@ namespace Duality { FuncDecl RenumberPred(const FuncDecl &f, int n); + Term ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming); + + protected: void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); @@ -602,6 +607,8 @@ protected: void FixCurrentState(Edge *root); void FixCurrentStateFull(Edge *edge, const expr &extra); + + void FixCurrentStateFull(Edge *edge, const std::vector &assumps, const hash_map &renaming); /** Declare a constant in the background theory. */ @@ -944,10 +951,12 @@ protected: Term UnderapproxFormula(const Term &f, hash_set &dont_cares); void ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares); + hash_set &done, hash_set &dont_cares, bool extensional = true); - Term UnderapproxFullFormula(const Term &f, hash_set &dont_cares); + public: + Term UnderapproxFullFormula(const Term &f, bool extensional = true); + protected: Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); hash_map resolve_ite_memo; @@ -988,6 +997,8 @@ protected: void AddEdgeToSolver(Edge *edge); + void AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge); + void AddToProofCore(hash_set &core); void GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 550f94f2d..2d871c324 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -410,6 +410,33 @@ namespace Duality { return res; } + Z3User::Term Z3User::ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(ExtractStores(memo, t.arg(i),cnstrs,renaming)); + res = f(args.size(),&args[0]); + if(f.get_decl_kind() == Store){ + func_decl fresh = ctx.fresh_func_decl("@arr", res.get_sort()); + expr y = fresh(); + expr equ = ctx.make(Equal,y,res); + cnstrs.push_back(equ); + renaming[y] = res; + res = y; + } + } + else res = t; + return res; + } + + bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ if(!(lit.is_quantifier() && IsClosedFormula(lit))){ if(!lit.is_app()) @@ -1851,7 +1878,7 @@ namespace Duality { } void RPFP::ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares){ + hash_set &done, hash_set &dont_cares, bool extensional){ if(done.find(f) != done.end()) return; /* already processed */ if(f.is_app()){ @@ -1859,7 +1886,7 @@ namespace Duality { decl_kind k = f.decl().get_decl_kind(); if(k == Implies || k == Iff || k == And || k == Or || k == Not){ for(int i = 0; i < nargs; i++) - ImplicantFullRed(memo,f.arg(i),lits,done,dont_cares); + ImplicantFullRed(memo,f.arg(i),lits,done,dont_cares, extensional); goto done; } } @@ -1867,6 +1894,15 @@ namespace Duality { if(dont_cares.find(f) == dont_cares.end()){ int b = SubtermTruth(memo,f); if(b != 0 && b != 1) goto done; + if(f.is_app() && f.decl().get_decl_kind() == Equal && f.arg(0).is_array()){ + if(b == 1 && !extensional){ + expr x = dualModel.eval(f.arg(0)); expr y = dualModel.eval(f.arg(1)); + if(!eq(x,y)) + b = 0; + } + if(b == 0) + goto done; + } expr bv = (b==1) ? f : !f; lits.push_back(bv); } @@ -1988,12 +2024,16 @@ namespace Duality { return conjoin(lits); } - RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, hash_set &dont_cares){ + RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, bool extensional){ + hash_set dont_cares; + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); /* first compute truth values of subterms */ hash_map memo; hash_set done; std::vector lits; - ImplicantFullRed(memo,f,lits,done,dont_cares); + ImplicantFullRed(memo,f,lits,done,dont_cares, extensional); + timer_stop("UnderapproxFormula"); /* return conjunction of literals */ return conjoin(lits); } @@ -2538,22 +2578,6 @@ namespace Duality { ConstrainEdgeLocalized(edge,eu); } - void RPFP::FixCurrentStateFull(Edge *edge, const expr &extra){ - hash_set dont_cares; - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; - for(unsigned i = 0; i < edge->constraints.size(); i++) - dual = dual && edge->constraints[i]; - // dual = dual && extra; - Term eu = UnderapproxFullFormula(dual,dont_cares); - timer_stop("UnderapproxFormula"); - ConstrainEdgeLocalized(edge,eu); - } - - - - void RPFP::GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under){ if(memo[under].find(f) != memo[under].end()) return; @@ -2836,7 +2860,91 @@ namespace Duality { return ctx.make(And,lits); } - // set up edge constraint in aux solver + + /* This is a wrapper for a solver that is intended to compute + implicants from models. It works around a problem in Z3 with + models in the non-extensional array theory. It does this by + naming all of the store terms in a formula. That is, (store ...) + is replaced by "name" with an added constraint name = (store + ...). This allows us to determine from the model whether an array + equality is true or false (it is false if the two sides are + mapped to different function symbols, even if they have the same + contents). + */ + + struct implicant_solver { + RPFP *owner; + solver &aux_solver; + std::vector assumps, namings; + std::vector assump_stack, naming_stack; + hash_map renaming, renaming_memo; + + void add(const expr &e){ + expr t = e; + if(!aux_solver.extensional_array_theory()){ + unsigned i = namings.size(); + t = owner->ExtractStores(renaming_memo,t,namings,renaming); + for(; i < namings.size(); i++) + aux_solver.add(namings[i]); + } + assumps.push_back(t); + aux_solver.add(t); + } + + void push() { + assump_stack.push_back(assumps.size()); + naming_stack.push_back(namings.size()); + aux_solver.push(); + } + + // When we pop the solver, we have to re-add any namings that were lost + + void pop(int n) { + aux_solver.pop(n); + int new_assumps = assump_stack[assump_stack.size()-n]; + int new_namings = naming_stack[naming_stack.size()-n]; + for(unsigned i = new_namings; i < namings.size(); i++) + aux_solver.add(namings[i]); + assumps.resize(new_assumps); + namings.resize(new_namings); + assump_stack.resize(assump_stack.size()-1); + naming_stack.resize(naming_stack.size()-1); + } + + check_result check() { + return aux_solver.check(); + } + + model get_model() { + return aux_solver.get_model(); + } + + expr get_implicant() { + owner->dualModel = aux_solver.get_model(); + expr dual = owner->ctx.make(And,assumps); + bool ext = aux_solver.extensional_array_theory(); + expr eu = owner->UnderapproxFullFormula(dual,ext); + // if we renamed store terms, we have to undo + if(!ext) + eu = owner->SubstRec(renaming,eu); + return eu; + } + + implicant_solver(RPFP *_owner, solver &_aux_solver) + : owner(_owner), aux_solver(_aux_solver) + {} + }; + + // set up edge constraint in aux solver + void RPFP::AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge){ + if(!edge->dual.null()) + aux_solver.add(edge->dual); + for(unsigned i = 0; i < edge->constraints.size(); i++){ + expr tl = edge->constraints[i]; + aux_solver.add(tl); + } + } + void RPFP::AddEdgeToSolver(Edge *edge){ if(!edge->dual.null()) aux_solver.add(edge->dual); @@ -2853,28 +2961,29 @@ namespace Duality { const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) axioms_needed.insert(theory[i]); - aux_solver.push(); - AddEdgeToSolver(node->Outgoing); + implicant_solver is(this,aux_solver); + is.push(); + AddEdgeToSolver(is,node->Outgoing); node->Annotation.SetEmpty(); hash_set *core = new hash_set; core->insert(node->Outgoing->dual); while(1){ - aux_solver.push(); + is.push(); expr annot = !GetAnnotation(node); - aux_solver.add(annot); - if(aux_solver.check() == unsat){ - aux_solver.pop(1); + is.add(annot); + if(is.check() == unsat){ + is.pop(1); break; } - dualModel = aux_solver.get_model(); - aux_solver.pop(1); + is.pop(1); Push(); - FixCurrentStateFull(node->Outgoing,annot); - ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); + ConstrainEdgeLocalized(node->Outgoing,is.get_implicant()); + ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this? check_result foo = Check(root); - if(foo != unsat) + if(foo != unsat){ + slvr().print("should_be_unsat.smt2"); throw "should be unsat"; - + } std::vector assumps, axioms_to_add; slvr().get_proof().get_assumptions(assumps); for(unsigned i = 0; i < assumps.size(); i++){ @@ -2890,7 +2999,7 @@ namespace Duality { { expr itp = GetAnnotation(node); - dualModel = aux_solver.get_model(); + dualModel = is.get_model(); // TODO: what does this mean? std::vector case_lits; itp = StrengthenFormulaByCaseSplitting(itp, case_lits); SetAnnotation(node,itp); @@ -2898,14 +3007,14 @@ namespace Duality { } for(unsigned i = 0; i < axioms_to_add.size(); i++) - aux_solver.add(axioms_to_add[i]); + is.add(axioms_to_add[i]); if(node->Annotation.IsEmpty()){ if(!axioms_added){ // add the axioms in the off chance they are useful const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) - aux_solver.add(theory[i]); + is.add(theory[i]); axioms_added = true; } else { @@ -2923,7 +3032,7 @@ namespace Duality { if(proof_core) delete proof_core; // shouldn't happen proof_core = core; - aux_solver.pop(1); + is.pop(1); timer_stop("InterpolateByCases"); } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index c7eec7e9f..3c69d79c0 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -131,6 +131,7 @@ namespace Duality { Report = false; StratifiedInlining = false; RecursionBound = -1; + BatchExpand = false; { scoped_no_proof no_proofs_please(ctx.m()); #ifdef USE_RPFP_CLONE @@ -365,6 +366,7 @@ namespace Duality { bool Report; // spew on stdout bool StratifiedInlining; // Do stratified inlining as preprocessing step int RecursionBound; // Recursion bound for bounded verification + bool BatchExpand; bool SetBoolOption(bool &opt, const std::string &value){ if(value == "0") { @@ -403,6 +405,9 @@ namespace Duality { if(option == "stratified_inlining"){ return SetBoolOption(StratifiedInlining,value); } + if(option == "batch_expand"){ + return SetBoolOption(BatchExpand,value); + } if(option == "recursion_bound"){ return SetIntOption(RecursionBound,value); } @@ -2016,7 +2021,7 @@ namespace Duality { } else { was_sat = true; - tree->Push(); + tree->Push(); std::vector &expansions = stack.back().expansions; #ifndef NO_DECISIONS for(unsigned i = 0; i < expansions.size(); i++){ @@ -2027,11 +2032,16 @@ namespace Duality { if(tree->slvr().check() == unsat) throw "help!"; #endif - stack.push_back(stack_entry()); - stack.back().level = tree->slvr().get_scope_level(); - if(ExpandSomeNodes(false,1)){ - continue; + int expand_max = 1; + if(0&&duality->BatchExpand){ + int thing = stack.size() * 0.1; + expand_max = std::max(1,thing); + if(expand_max > 1) + std::cout << "foo!\n"; } + + if(ExpandSomeNodes(false,expand_max)) + continue; while(stack.size() > 1){ tree->Pop(1); stack.pop_back(); @@ -2090,6 +2100,8 @@ namespace Duality { std::list updated_nodes; virtual void ExpandNode(RPFP::Node *p){ + stack.push_back(stack_entry()); + stack.back().level = tree->slvr().get_scope_level(); stack.back().expansions.push_back(p); DerivationTree::ExpandNode(p); std::vector &new_nodes = p->Outgoing->Children; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 345fef562..0f6198edd 100755 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -37,7 +37,7 @@ Revision History: namespace Duality { - solver::solver(Duality::context& c, bool extensional, bool models) : object(c), the_model(c) { + solver::solver(Duality::context& c, bool _extensional, bool models) : object(c), the_model(c) { params_ref p; p.set_bool("proof", true); // this is currently useless if(models) @@ -47,7 +47,8 @@ namespace Duality { p.set_bool("mbqi",mbqi); // just to test p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants p.set_uint("mbqi.max_iterations",1); // use mbqi for quantifiers in interpolants - if(true || extensional) + extensional = mbqi && (true || _extensional); + if(extensional) p.set_bool("array.extensional",true); scoped_ptr sf = mk_smt_solver_factory(); m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); @@ -657,6 +658,18 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st pp.display_smt2(std::cout, m_solver->get_assertion(n-1)); } + void solver::print(const char *filename) { + std::ofstream f(filename); + unsigned n = m_solver->get_num_assertions(); + if(!n) + return; + ast_smt_pp pp(m()); + for (unsigned i = 0; i < n-1; ++i) + pp.add_assumption(m_solver->get_assertion(i)); + pp.display_smt2(f, m_solver->get_assertion(n-1)); + } + + void solver::show_assertion_ids() { #if 0 unsigned n = m_solver->get_num_assertions(); diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index febc3f1f1..ccc0800b2 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -819,6 +819,7 @@ namespace Duality { model the_model; bool canceled; proof_gen_mode m_mode; + bool extensional; public: solver(context & c, bool extensional = false, bool models = true); solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} @@ -922,6 +923,7 @@ namespace Duality { unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();} void show(); + void print(const char *filename); void show_assertion_ids(); proof get_proof(){ @@ -929,6 +931,7 @@ namespace Duality { return proof(ctx(),m_solver->get_proof()); } + bool extensional_array_theory() {return extensional;} }; #if 0 diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 5eec77f39..e201c2a19 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -75,6 +75,7 @@ def_module_params('fixedpoint', ('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'), ('profile', BOOL, False, 'DUALITY: profile run time'), ('mbqi', BOOL, True, 'DUALITY: use model-based quantifier instantion'), + ('batch_expand', BOOL, False, 'DUALITY: use batch expansion'), ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), )) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index e7c7976f6..0c2a9e48a 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -205,6 +205,7 @@ lbool dl_interface::query(::expr * query) { rs->SetOption("feasible_edges",m_ctx.get_params().feasible_edges() ? "1" : "0"); rs->SetOption("use_underapprox",m_ctx.get_params().use_underapprox() ? "1" : "0"); rs->SetOption("stratified_inlining",m_ctx.get_params().stratified_inlining() ? "1" : "0"); + rs->SetOption("batch_expand",m_ctx.get_params().batch_expand() ? "1" : "0"); unsigned rb = m_ctx.get_params().recursion_bound(); if(rb != UINT_MAX){ std::ostringstream os; os << rb; From bbab6be280c687fce57eee84bd1161356dd76853 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 14 Mar 2014 13:40:31 -0700 Subject: [PATCH 297/509] duality: eager deduction and history-based conjectures --- src/duality/duality.h | 41 ++- src/duality/duality_rpfp.cpp | 2 +- src/duality/duality_solver.cpp | 418 +++++++++++++++++++---- src/duality/duality_wrapper.h | 17 + src/muz/duality/duality_dl_interface.cpp | 70 ++-- 5 files changed, 440 insertions(+), 108 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 137279899..b1171a698 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -204,6 +204,9 @@ protected: /** Is this a background constant? */ virtual bool is_constant(const func_decl &f) = 0; + /** Get the constants in the background vocabulary */ + virtual hash_set &get_constants() = 0; + /** Assert a background axiom. */ virtual void assert_axiom(const expr &axiom) = 0; @@ -297,6 +300,11 @@ protected: return bckg.find(f) != bckg.end(); } + /** Get the constants in the background vocabulary */ + virtual hash_set &get_constants(){ + return bckg; + } + ~iZ3LogicSolver(){ // delete ictx; delete islvr; @@ -1064,13 +1072,40 @@ protected: public: - struct Counterexample { + class Counterexample { + private: RPFP *tree; RPFP::Node *root; + public: Counterexample(){ tree = 0; root = 0; } + Counterexample(RPFP *_tree, RPFP::Node *_root){ + tree = _tree; + root = _root; + } + ~Counterexample(){ + if(tree) delete tree; + } + void swap(Counterexample &other){ + std::swap(tree,other.tree); + std::swap(root,other.root); + } + void set(RPFP *_tree, RPFP::Node *_root){ + if(tree) delete tree; + tree = _tree; + root = _root; + } + void clear(){ + if(tree) delete tree; + tree = 0; + } + RPFP *get_tree() const {return tree;} + RPFP::Node *get_root() const {return root;} + private: + Counterexample &operator=(const Counterexample &); + Counterexample(const Counterexample &); }; /** Solve the problem. You can optionally give an old @@ -1080,7 +1115,7 @@ protected: virtual bool Solve() = 0; - virtual Counterexample GetCounterexample() = 0; + virtual Counterexample &GetCounterexample() = 0; virtual bool SetOption(const std::string &option, const std::string &value) = 0; @@ -1088,7 +1123,7 @@ protected: is chiefly useful for abstraction refinement, when we want to solve a series of similar problems. */ - virtual void LearnFrom(Counterexample &old_cex) = 0; + virtual void LearnFrom(Solver *old_solver) = 0; virtual ~Solver(){} diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 2d871c324..f3deee225 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -3334,7 +3334,7 @@ namespace Duality { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); - if(nargs == 0) + if(nargs == 0 && f.get_decl_kind() == Uninterpreted) ls->declare_constant(f); // keep track of background constants for(int i = 0; i < nargs; i++) args.push_back(SubstBoundRec(memo, subst, level, t.arg(i))); diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 3c69d79c0..d59309c00 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -54,6 +54,7 @@ Revision History: // #define KEEP_EXPANSIONS // #define USE_CACHING_RPFP // #define PROPAGATE_BEFORE_CHECK +#define NEW_STRATIFIED_INLINING #define USE_RPFP_CLONE #define USE_NEW_GEN_CANDS @@ -82,7 +83,7 @@ namespace Duality { rpfp = _rpfp; } virtual void Extend(RPFP::Node *node){} - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update){} + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){} virtual void Bound(RPFP::Node *node){} virtual void Expand(RPFP::Edge *edge){} virtual void AddCover(RPFP::Node *covered, std::vector &covering){} @@ -94,6 +95,7 @@ namespace Duality { virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} virtual void Message(const std::string &msg){} + virtual void Depth(int){} virtual ~Reporter(){} }; @@ -124,6 +126,7 @@ namespace Duality { rpfp = _rpfp; reporter = 0; heuristic = 0; + unwinding = 0; FullExpand = false; NoConj = false; FeasibleEdges = true; @@ -152,6 +155,7 @@ namespace Duality { #ifdef USE_NEW_GEN_CANDS delete gen_cands_rpfp; #endif + if(unwinding) delete unwinding; } #ifdef USE_RPFP_CLONE @@ -250,6 +254,19 @@ namespace Duality { virtual void Done() {} }; + /** The Proposer class proposes conjectures eagerly. These can come + from any source, including predicate abstraction, templates, or + previous solver runs. The proposed conjectures are checked + with low effort when the unwinding is expanded. + */ + + class Proposer { + public: + /** Given a node in the unwinding, propose some conjectures */ + virtual std::vector &Propose(Node *node) = 0; + virtual ~Proposer(){}; + }; + class Covering; // see below @@ -279,6 +296,7 @@ namespace Duality { hash_map underapprox_map; // maps underapprox nodes to the nodes they approximate int last_decisions; hash_set overapproxes; + std::vector proposers; #ifdef BOUNDED struct Counter { @@ -293,24 +311,22 @@ namespace Duality { virtual bool Solve(){ reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); #ifndef LOCALIZE_CONJECTURES - heuristic = !cex.tree ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); + heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); #else - heuristic = !cex.tree ? (Heuristic *)(new LocalHeuristic(rpfp)) + heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp)) : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); #endif - cex.tree = 0; // heuristic now owns it + cex.clear(); // in case we didn't use it for heuristic + if(unwinding) delete unwinding; unwinding = new RPFP(rpfp->ls); unwinding->HornClauses = rpfp->HornClauses; indset = new Covering(this); last_decisions = 0; CreateEdgesByChildMap(); - CreateLeaves(); #ifndef TOP_DOWN - if(!StratifiedInlining){ - if(FeasibleEdges)NullaryCandidates(); - else InstantiateAllEdges(); - } + void CreateInitialUnwinding(); #else + CreateLeaves(); for(unsigned i = 0; i < leaves.size(); i++) if(!SatisfyUpperBound(leaves[i])) return false; @@ -322,11 +338,29 @@ namespace Duality { // print_profile(std::cout); delete indset; delete heuristic; - delete unwinding; + // delete unwinding; // keep the unwinding for future mining of predicates delete reporter; + for(unsigned i = 0; i < proposers.size(); i++) + delete proposers[i]; return res; } + void CreateInitialUnwinding(){ + if(!StratifiedInlining){ + CreateLeaves(); + if(FeasibleEdges)NullaryCandidates(); + else InstantiateAllEdges(); + } + else { +#ifdef NEW_STRATIFIED_INLINING + +#else + CreateLeaves(); +#endif + } + + } + void Cancel(){ // TODO } @@ -347,15 +381,19 @@ namespace Duality { } #endif - virtual void LearnFrom(Counterexample &old_cex){ - cex = old_cex; + virtual void LearnFrom(Solver *other_solver){ + // get the counterexample as a guide + cex.swap(other_solver->GetCounterexample()); + + // propose conjectures based on old unwinding + Duality *old_duality = dynamic_cast(other_solver); + if(old_duality) + proposers.push_back(new HistoryProposer(old_duality,this)); } - /** Return the counterexample */ - virtual Counterexample GetCounterexample(){ - Counterexample res = cex; - cex.tree = 0; // Cex now belongs to caller - return res; + /** Return a reference to the counterexample */ + virtual Counterexample &GetCounterexample(){ + return cex; } // options @@ -519,7 +557,11 @@ namespace Duality { c.Children.resize(edge->Children.size()); for(unsigned j = 0; j < c.Children.size(); j++) c.Children[j] = leaf_map[edge->Children[j]]; - Extend(c); + Node *new_node; + Extend(c,new_node); +#ifdef EARLY_EXPAND + TryExpandNode(new_node); +#endif } for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it) indset->Add(*it); @@ -771,16 +813,14 @@ namespace Duality { } - /* For stratified inlining, we need a topological sort of the - nodes. */ - hash_map TopoSort; int TopoSortCounter; + std::vector SortedEdges; void DoTopoSortRec(Node *node){ if(TopoSort.find(node) != TopoSort.end()) return; - TopoSort[node] = TopoSortCounter++; // just to break cycles + TopoSort[node] = INT_MAX; // just to break cycles Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge if(edge){ std::vector &chs = edge->Children; @@ -788,22 +828,81 @@ namespace Duality { DoTopoSortRec(chs[i]); } TopoSort[node] = TopoSortCounter++; + SortedEdges.push_back(edge); } void DoTopoSort(){ TopoSort.clear(); + SortedEdges.clear(); TopoSortCounter = 0; for(unsigned i = 0; i < nodes.size(); i++) DoTopoSortRec(nodes[i]); } + + int StratifiedLeafCount; + +#ifdef NEW_STRATIFIED_INLINING + + /** Stratified inlining builds an initial layered unwinding before + switching to the LAWI strategy. Currently the number of layers + is one. Only nodes that are the targets of back edges are + consider to be leaves. This assumes we have already computed a + topological sort. + */ + + bool DoStratifiedInlining(){ + DoTopoSort(); + int depth = 1; // TODO: make this an option + std::vector > unfolding_levels(depth+1); + for(int level = 1; level <= depth; level++) + for(unsigned i = 0; i < SortedEdges.size(); i++){ + Edge *edge = SortedEdges[i]; + Node *parent = edge->Parent; + std::vector &chs = edge->Children; + std::vector my_chs(chs.size()); + for(unsigned j = 0; j < chs.size(); j++){ + Node *child = chs[j]; + int ch_level = TopoSort[child] >= TopoSort[parent] ? level-1 : level; + if(unfolding_levels[ch_level].find(child) == unfolding_levels[ch_level].end()){ + if(ch_level == 0) + unfolding_levels[0][child] = CreateLeaf(child); + else + throw InternalError("in levelized unwinding"); + } + my_chs[j] = unfolding_levels[ch_level][child]; + } + Candidate cand; cand.edge = edge; cand.Children = my_chs; + Node *new_node; + bool ok = Extend(cand,new_node); + MarkExpanded(new_node); // we don't expand here -- just mark it done + if(!ok) return false; // got a counterexample + unfolding_levels[level][parent] = new_node; + } + return true; + } + + Node *CreateLeaf(Node *node){ + RPFP::Node *nchild = CreateNodeInstance(node); + MakeLeaf(nchild, /* do_not_expand = */ true); + nchild->Annotation.SetEmpty(); + return nchild; + } + + void MarkExpanded(Node *node){ + if(unexpanded.find(node) != unexpanded.end()){ + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + } + } + +#else + /** In stratified inlining, we build the unwinding from the bottom down, trying to satisfy the node bounds. We do this as a pre-pass, limiting the expansion. If we get a counterexample, we are done, else we continue as usual expanding the unwinding upward. */ - - int StratifiedLeafCount; bool DoStratifiedInlining(){ timer_start("StratifiedInlining"); @@ -826,6 +925,8 @@ namespace Duality { return true; } +#endif + /** Here, we do the downward expansion for stratified inlining */ hash_map LeafMap, StratifiedLeafMap; @@ -912,9 +1013,14 @@ namespace Duality { } Candidate cand = candidates.front(); candidates.pop_front(); - if(CandidateFeasible(cand)) - if(!Extend(cand)) + if(CandidateFeasible(cand)){ + Node *new_node; + if(!Extend(cand,new_node)) return false; +#ifdef EARLY_EXPAND + TryExpandNode(new_node); +#endif + } } } @@ -934,9 +1040,9 @@ namespace Duality { Node *CreateUnderapproxNode(Node *node){ - // cex.tree->ComputeUnderapprox(cex.root,0); + // cex.get_tree()->ComputeUnderapprox(cex.get_root(),0); RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */); - under_node->Annotation.IntersectWith(cex.root->Underapprox); + under_node->Annotation.IntersectWith(cex.get_root()->Underapprox); AddThing(under_node->Annotation.Formula); Edge *e = unwinding->CreateLowerBoundEdge(under_node); under_node->Annotation.SetFull(); // allow this node to cover others @@ -972,9 +1078,8 @@ namespace Duality { ExpandNodeFromCoverFail(node); } #endif - if(_cex) *_cex = cex; - else delete cex.tree; // delete the cex if not required - cex.tree = 0; + if(_cex) (*_cex).swap(cex); // return the cex if asked + cex.clear(); // throw away the useless cex node->Bound = save; // put back original bound timer_stop("ProveConjecture"); return false; @@ -1354,16 +1459,20 @@ namespace Duality { } } - bool UpdateNodeToNode(Node *node, Node *top){ - if(!node->Annotation.SubsetEq(top->Annotation)){ - reporter->Update(node,top->Annotation); - indset->Update(node,top->Annotation); + bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){ + if(!node->Annotation.SubsetEq(fact)){ + reporter->Update(node,fact,eager); + indset->Update(node,fact); updated_nodes.insert(node->map); - node->Annotation.IntersectWith(top->Annotation); + node->Annotation.IntersectWith(fact); return true; } return false; } + + bool UpdateNodeToNode(Node *node, Node *top){ + return Update(node,top->Annotation); + } /** Update the unwinding solution, using an interpolant for the derivation tree. */ @@ -1405,8 +1514,7 @@ namespace Duality { // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; last_decisions = end_decs - start_decs; if(res){ - cex.tree = dt.tree; - cex.root = dt.top; + cex.set(dt.tree,dt.top); // note tree is now owned by cex if(UseUnderapprox){ UpdateWithCounterexample(node,dt.tree,dt.top); } @@ -1418,6 +1526,64 @@ namespace Duality { delete dtp; return !res; } + + /* For a given nod in the unwinding, get conjectures from the + proposers and check them locally. Update the node with any true + conjectures. + */ + + void DoEagerDeduction(Node *node){ + for(unsigned i = 0; i < proposers.size(); i++){ + const std::vector &conjectures = proposers[i]->Propose(node); + for(unsigned j = 0; j < conjectures.size(); j++){ + const RPFP::Transformer &conjecture = conjectures[j]; + RPFP::Transformer bound(conjecture); + std::vector conj_vec; + unwinding->CollectConjuncts(bound.Formula,conj_vec); + for(unsigned k = 0; k < conj_vec.size(); k++){ + bound.Formula = conj_vec[k]; + if(CheckEdgeCaching(node->Outgoing,bound) == unsat) + Update(node,bound, /* eager = */ true); + //else + //std::cout << "conjecture failed\n"; + } + } + } + } + + + check_result CheckEdge(RPFP *checker, Edge *edge){ + Node *root = edge->Parent; + checker->Push(); + checker->AssertNode(root); + checker->AssertEdge(edge,1,true); + check_result res = checker->Check(root); + checker->Pop(1); + return res; + } + + check_result CheckEdgeCaching(Edge *unwinding_edge, const RPFP::Transformer &bound){ + + // use a dedicated solver for this edge + // TODO: can this mess be hidden somehow? + + RPFP_caching *checker = gen_cands_rpfp; // TODO: a good choice? + Edge *edge = unwinding_edge->map; // get the edge in the original RPFP + RPFP_caching::scoped_solver_for_edge ssfe(checker,edge,true /* models */, true /*axioms*/); + Edge *checker_edge = checker->GetEdgeClone(edge); + + // copy the annotations and bound to the clone + Node *root = checker_edge->Parent; + root->Bound = bound; + for(unsigned j = 0; j < checker_edge->Children.size(); j++){ + Node *oc = unwinding_edge->Children[j]; + Node *nc = checker_edge->Children[j]; + nc->Annotation = oc->Annotation; + } + + return CheckEdge(checker,checker_edge); + } + /* If the counterexample derivation is partial due to use of underapproximations, complete it. */ @@ -1426,10 +1592,7 @@ namespace Duality { DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); bool res = dt.Derive(unwinding,node,UseUnderapprox,true); // build full tree if(!res) throw "Duality internal error in BuildFullCex"; - if(cex.tree) - delete cex.tree; - cex.tree = dt.tree; - cex.root = dt.top; + cex.set(dt.tree,dt.top); } void UpdateBackEdges(Node *node){ @@ -1452,25 +1615,23 @@ namespace Duality { } /** Extend the unwinding, keeping it solved. */ - bool Extend(Candidate &cand){ + bool Extend(Candidate &cand, Node *&node){ timer_start("Extend"); - Node *node = CreateNodeInstance(cand.edge->Parent); + node = CreateNodeInstance(cand.edge->Parent); CreateEdgeInstance(cand.edge,node,cand.Children); UpdateBackEdges(node); reporter->Extend(node); - bool res = SatisfyUpperBound(node); + DoEagerDeduction(node); // first be eager... + bool res = SatisfyUpperBound(node); // then be lazy if(res) indset->CloseDescendants(node); else { #ifdef UNDERAPPROX_NODES - ExpandUnderapproxNodes(cex.tree, cex.root); + ExpandUnderapproxNodes(cex.get_tree(), cex.get_root()); #endif if(UseUnderapprox) BuildFullCex(node); timer_stop("Extend"); return res; } -#ifdef EARLY_EXPAND - TryExpandNode(node); -#endif timer_stop("Extend"); return res; } @@ -1930,6 +2091,7 @@ namespace Duality { unsigned slvr_level = tree->slvr().get_scope_level(); if(slvr_level != stack.back().level) throw "stacks out of sync!"; + reporter->Depth(stack.size()); // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop check_result foo = tree->Check(top); @@ -2459,7 +2621,7 @@ namespace Duality { } bool ContainsCex(Node *node, Counterexample &cex){ - expr val = cex.tree->Eval(cex.root->Outgoing,node->Annotation.Formula); + expr val = cex.get_tree()->Eval(cex.get_root()->Outgoing,node->Annotation.Formula); return eq(val,parent->ctx.bool_val(true)); } @@ -2478,15 +2640,15 @@ namespace Duality { Node *other = insts[i]; if(CouldCover(node,other)){ reporter()->Forcing(node,other); - if(cex.tree && !ContainsCex(other,cex)) + if(cex.get_tree() && !ContainsCex(other,cex)) continue; - if(cex.tree) {delete cex.tree; cex.tree = 0;} + cex.clear(); if(parent->ProveConjecture(node,other->Annotation,other,&cex)) if(CloseDescendants(node)) return true; } } - if(cex.tree) {delete cex.tree; cex.tree = 0;} + cex.clear(); return false; } #else @@ -2585,13 +2747,12 @@ namespace Duality { Counterexample old_cex; public: ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) - : Heuristic(_rpfp), old_cex(_old_cex) + : Heuristic(_rpfp) { + old_cex.swap(_old_cex); // take ownership from caller } ~ReplayHeuristic(){ - if(old_cex.tree) - delete old_cex.tree; } // Maps nodes of derivation tree into old cex @@ -2599,9 +2760,7 @@ namespace Duality { void Done() { cex_map.clear(); - if(old_cex.tree) - delete old_cex.tree; - old_cex.tree = 0; // only replay once! + old_cex.clear(); } void ShowNodeAndChildren(Node *n){ @@ -2623,7 +2782,7 @@ namespace Duality { } virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ - if(!high_priority || !old_cex.tree){ + if(!high_priority || !old_cex.get_tree()){ Heuristic::ChooseExpand(choices,best,false); return; } @@ -2632,7 +2791,7 @@ namespace Duality { for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ Node *node = (*it); if(cex_map.empty()) - cex_map[node] = old_cex.root; // match the root nodes + cex_map[node] = old_cex.get_root(); // match the root nodes if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! if(cex_map.find(parent) == cex_map.end()) @@ -2658,7 +2817,7 @@ namespace Duality { Node *old_node = cex_map[node]; if(!old_node) unmatched.insert(node); - else if(old_cex.tree->Empty(old_node)) + else if(old_cex.get_tree()->Empty(old_node)) unmatched.insert(node); else matched.insert(node); @@ -2732,7 +2891,120 @@ namespace Duality { } }; + /** This proposer class generates conjectures based on the + unwinding generated by a previous solver. The assumption is + that the provious solver was working on a different + abstraction of the same system. The trick is to adapt the + annotations in the old unwinding to the new predicates. We + start by generating a map from predicates and parameters in + the old problem to the new. + HACK: mapping is done by cheesy name comparison. + */ + + class HistoryProposer : public Proposer + { + Duality *old_solver; + Duality *new_solver; + hash_map > conjectures; + + public: + /** Construct a history solver. */ + HistoryProposer(Duality *_old_solver, Duality *_new_solver) + : old_solver(_old_solver), new_solver(_new_solver) { + + // tricky: names in the axioms may have changed -- map them + hash_set &old_constants = old_solver->unwinding->ls->get_constants(); + hash_set &new_constants = new_solver->rpfp->ls->get_constants(); + hash_map cmap; + for(hash_set::iterator it = new_constants.begin(), en = new_constants.end(); it != en; ++it) + cmap[GetKey(*it)] = *it; + hash_map bckg_map; + for(hash_set::iterator it = old_constants.begin(), en = old_constants.end(); it != en; ++it){ + func_decl f = new_solver->ctx.translate(*it); // move to new context + if(cmap.find(GetKey(f)) != cmap.end()) + bckg_map[f] = cmap[GetKey(f)]; + // else + // std::cout << "constant not matched\n"; + } + + RPFP *old_unwinding = old_solver->unwinding; + hash_map > pred_match; + + // index all the predicates in the old unwinding + for(unsigned i = 0; i < old_unwinding->nodes.size(); i++){ + Node *node = old_unwinding->nodes[i]; + std::string key = GetKey(node); + pred_match[key].push_back(node); + } + + // match with predicates in the new RPFP + RPFP *rpfp = new_solver->rpfp; + for(unsigned i = 0; i < rpfp->nodes.size(); i++){ + Node *node = rpfp->nodes[i]; + std::string key = GetKey(node); + std::vector &matches = pred_match[key]; + for(unsigned j = 0; j < matches.size(); j++) + MatchNodes(node,matches[j],bckg_map); + } + } + + virtual std::vector &Propose(Node *node){ + return conjectures[node->map]; + } + + virtual ~HistoryProposer(){ + }; + + private: + void MatchNodes(Node *new_node, Node *old_node, hash_map &bckg_map){ + if(old_node->Annotation.IsFull()) + return; // don't conjecture true! + hash_map var_match; + std::vector &new_params = new_node->Annotation.IndParams; + // Index the new parameters by their keys + for(unsigned i = 0; i < new_params.size(); i++) + var_match[GetKey(new_params[i])] = new_params[i]; + RPFP::Transformer &old = old_node->Annotation; + std::vector from_params = old.IndParams; + for(unsigned j = 0; j < from_params.size(); j++) + from_params[j] = new_solver->ctx.translate(from_params[j]); // get in new context + std::vector to_params = from_params; + for(unsigned j = 0; j < to_params.size(); j++){ + std::string key = GetKey(to_params[j]); + if(var_match.find(key) == var_match.end()){ + // std::cout << "unmatched parameter!\n"; + return; + } + to_params[j] = var_match[key]; + } + expr fmla = new_solver->ctx.translate(old.Formula); // get in new context + fmla = new_solver->rpfp->SubstParams(old.IndParams,to_params,fmla); // substitute parameters + hash_map memo; + fmla = new_solver->rpfp->SubstRec(memo,bckg_map,fmla); // substitute background constants + RPFP::Transformer new_annot = new_node->Annotation; + new_annot.Formula = fmla; + conjectures[new_node].push_back(new_annot); + } + + // We match names by removing suffixes beginning with double at sign + + std::string GetKey(Node *node){ + return GetKey(node->Name); + } + + std::string GetKey(const expr &var){ + return GetKey(var.decl()); + } + + std::string GetKey(const func_decl &f){ + std::string name = f.name().str(); + int idx = name.find("@@"); + if(idx >= 0) + name.erase(idx); + return name; + } + }; }; @@ -2740,8 +3012,9 @@ namespace Duality { std::ostream &s; public: StreamReporter(RPFP *_rpfp, std::ostream &_s) - : Reporter(_rpfp), s(_s) {event = 0;} + : Reporter(_rpfp), s(_s) {event = 0; depth = -1;} int event; + int depth; void ev(){ s << "[" << event++ << "]" ; } @@ -2752,23 +3025,30 @@ namespace Duality { s << " " << rps[i]->number; s << std::endl; } - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update){ + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ ev(); s << "update " << node->number << " " << node->Name.name() << ": "; rpfp->Summarize(update.Formula); - std::cout << std::endl; + if(depth > 0) s << " (depth=" << depth << ")"; + if(eager) s << " (eager)"; + s << std::endl; } virtual void Bound(RPFP::Node *node){ ev(); s << "check " << node->number << std::endl; } virtual void Expand(RPFP::Edge *edge){ RPFP::Node *node = edge->Parent; - ev(); s << "expand " << node->map->number << " " << node->Name.name() << std::endl; + ev(); s << "expand " << node->map->number << " " << node->Name.name(); + if(depth > 0) s << " (depth=" << depth << ")"; + s << std::endl; + } + virtual void Depth(int d){ + depth = d; } virtual void AddCover(RPFP::Node *covered, std::vector &covering){ ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; for(unsigned i = 0; i < covering.size(); i++) - std::cout << covering[i]->number << " "; - std::cout << std::endl; + s << covering[i]->number << " "; + s << std::endl; } virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){ ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; @@ -2779,7 +3059,7 @@ namespace Duality { virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){ ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": "; rpfp->Summarize(t.Formula); - std::cout << std::endl; + s << std::endl; } virtual void Dominates(RPFP::Node *node, RPFP::Node *other){ ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index ccc0800b2..a7497318a 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -244,6 +244,9 @@ namespace Duality { sort_kind get_sort_kind(const sort &s); + expr translate(const expr &e); + func_decl translate(const func_decl &); + void print_expr(std::ostream &s, const ast &e); fixedpoint mk_fixedpoint(); @@ -1374,6 +1377,20 @@ namespace Duality { return to_expr(a.raw()); } + inline expr context::translate(const expr &e) { + ::expr *f = to_expr(e.raw()); + if(&e.ctx().m() != &m()) // same ast manager -> no translation + throw "ast manager mismatch"; + return cook(f); + } + + inline func_decl context::translate(const func_decl &e) { + ::func_decl *f = to_func_decl(e.raw()); + if(&e.ctx().m() != &m()) // same ast manager -> no translation + throw "ast manager mismatch"; + return func_decl(*this,f); + } + typedef double clock_t; clock_t current_time(); inline void output_time(std::ostream &os, clock_t time){os << time;} diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 0c2a9e48a..587a184bc 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -64,20 +64,22 @@ namespace Duality { std::vector clauses; std::vector > clause_labels; hash_map map; // edges to clauses + Solver *old_rs; Solver::Counterexample cex; duality_data(ast_manager &_m) : ctx(_m,config(params_ref())) { ls = 0; rpfp = 0; status = StatusNull; + old_rs = 0; } ~duality_data(){ + if(old_rs) + dealloc(old_rs); if(rpfp) dealloc(rpfp); if(ls) dealloc(ls); - if(cex.tree) - delete cex.tree; } }; @@ -132,10 +134,12 @@ lbool dl_interface::query(::expr * query) { m_ctx.ensure_opened(); // if there is old data, get the cex and dispose (later) - Solver::Counterexample old_cex; duality_data *old_data = _d; - if(old_data) - old_cex = old_data->cex; + Solver *old_rs = 0; + if(old_data){ + old_rs = old_data->old_rs; + old_rs->GetCounterexample().swap(old_data->cex); + } scoped_proof generate_proofs_please(m_ctx.get_manager()); @@ -196,8 +200,9 @@ lbool dl_interface::query(::expr * query) { Solver *rs = Solver::Create("duality", _d->rpfp); - rs->LearnFrom(old_cex); // new solver gets hints from old cex - + if(old_rs) + rs->LearnFrom(old_rs); // new solver gets hints from old solver + // set its options IF_VERBOSE(1, rs->SetOption("report","1");); rs->SetOption("full_expand",m_ctx.get_params().full_expand() ? "1" : "0"); @@ -231,15 +236,14 @@ lbool dl_interface::query(::expr * query) { // save the result and counterexample if there is one _d->status = ans ? StatusModel : StatusRefutation; - _d->cex = rs->GetCounterexample(); + _d->cex.swap(rs->GetCounterexample()); // take ownership of cex + _d->old_rs = rs; // save this for later hints if(old_data){ - old_data->cex.tree = 0; // we own it now - dealloc(old_data); + dealloc(old_data); // this deallocates the old solver if there is one } - - dealloc(rs); + // dealloc(rs); this is now owned by data // true means the RPFP problem is SAT, so the query is UNSAT return ans ? l_false : l_true; @@ -267,18 +271,16 @@ void dl_interface::reset_statistics() { static hash_set *local_func_decls; -static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexample &cex) { + static void print_proof(dl_interface *d, std::ostream& out, RPFP *tree, RPFP::Node *root) { context &ctx = d->dd()->ctx; - RPFP::Node &node = *cex.root; + RPFP::Node &node = *root; RPFP::Edge &edge = *node.Outgoing; // first, prove the children (that are actually used) for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!cex.tree->Empty(edge.Children[i])){ - Solver::Counterexample foo = cex; - foo.root = edge.Children[i]; - print_proof(d,out,foo); + if(!tree->Empty(edge.Children[i])){ + print_proof(d,out,tree,edge.Children[i]); } } @@ -287,7 +289,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp out << "(step s!" << node.number; out << " (" << node.Name.name(); for(unsigned i = 0; i < edge.F.IndParams.size(); i++) - out << " " << cex.tree->Eval(&edge,edge.F.IndParams[i]); + out << " " << tree->Eval(&edge,edge.F.IndParams[i]); out << ")\n"; // print the rule number @@ -309,8 +311,8 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp sort the_sort = t.get_quantifier_bound_sort(j); symbol name = t.get_quantifier_bound_name(j); expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - out << " (= " << skolem << " " << cex.tree->Eval(&edge,skolem) << ")\n"; - expr local_skolem = cex.tree->Localize(&edge,skolem); + out << " (= " << skolem << " " << tree->Eval(&edge,skolem) << ")\n"; + expr local_skolem = tree->Localize(&edge,skolem); (*local_func_decls).insert(local_skolem.decl()); } } @@ -318,7 +320,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp out << " (labels"; std::vector labels; - cex.tree->GetLabels(&edge,labels); + tree->GetLabels(&edge,labels); for(unsigned j = 0; j < labels.size(); j++){ out << " " << labels[j]; } @@ -330,7 +332,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp out << " (ref "; for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!cex.tree->Empty(edge.Children[i])) + if(!tree->Empty(edge.Children[i])) out << " s!" << edge.Children[i]->number; else out << " true"; @@ -355,11 +357,11 @@ void dl_interface::display_certificate_non_const(std::ostream& out) { // negation of the query is the last clause -- prove it hash_set locals; local_func_decls = &locals; - print_proof(this,out,_d->cex); + print_proof(this,out,_d->cex.get_tree(),_d->cex.get_root()); out << ")\n"; out << "(model \n\""; ::model mod(m_ctx.get_manager()); - model orig_model = _d->cex.tree->dualModel; + model orig_model = _d->cex.get_tree()->dualModel; for(unsigned i = 0; i < orig_model.num_consts(); i++){ func_decl cnst = orig_model.get_const_decl(i); if(locals.find(cnst) == locals.end()){ @@ -430,10 +432,10 @@ model_ref dl_interface::get_model() { return md; } -static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) { + static proof_ref extract_proof(dl_interface *d, RPFP *tree, RPFP::Node *root) { context &ctx = d->dd()->ctx; ast_manager &mgr = ctx.m(); - RPFP::Node &node = *cex.root; + RPFP::Node &node = *root; RPFP::Edge &edge = *node.Outgoing; RPFP::Edge *orig_edge = edge.map; @@ -455,21 +457,19 @@ static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) { sort the_sort = t.get_quantifier_bound_sort(j); symbol name = t.get_quantifier_bound_name(j); expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - expr val = cex.tree->Eval(&edge,skolem); + expr val = tree->Eval(&edge,skolem); expr_ref thing(ctx.uncook(val),mgr); substs[0].push_back(thing); - expr local_skolem = cex.tree->Localize(&edge,skolem); + expr local_skolem = tree->Localize(&edge,skolem); (*local_func_decls).insert(local_skolem.decl()); } } svector > pos; for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!cex.tree->Empty(edge.Children[i])){ + if(!tree->Empty(edge.Children[i])){ pos.push_back(std::pair(i+1,0)); - Solver::Counterexample foo = cex; - foo.root = edge.Children[i]; - proof_ref prem = extract_proof(d,foo); + proof_ref prem = extract_proof(d,tree,edge.Children[i]); prems.push_back(prem); substs.push_back(expr_ref_vector(mgr)); } @@ -478,7 +478,7 @@ static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) { func_decl f = node.Name; std::vector args; for(unsigned i = 0; i < edge.F.IndParams.size(); i++) - args.push_back(cex.tree->Eval(&edge,edge.F.IndParams[i])); + args.push_back(tree->Eval(&edge,edge.F.IndParams[i])); expr conc = f(args); @@ -495,7 +495,7 @@ proof_ref dl_interface::get_proof() { if(_d->status == StatusRefutation){ hash_set locals; local_func_decls = &locals; - return extract_proof(this,_d->cex); + return extract_proof(this,_d->cex.get_tree(),_d->cex.get_root()); } else return proof_ref(m_ctx.get_manager()); From 663d110b722d584f54c11fad705027392af00ead Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 16 Mar 2014 12:09:53 -0700 Subject: [PATCH 298/509] interpolation fix --- src/interp/iz3translate.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 7e0fa65d6..345381047 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1590,6 +1590,27 @@ public: return res; } + /* This idiom takes ~P and Q=P, yielding ~Q. It uses a "rewrite" + (Q=false) = ~Q. We eliminate the rewrite by using symmetry, + congruence and modus ponens. */ + + if(dk == PR_MODUS_PONENS && pr(prem(proof,1)) == PR_REWRITE && pr(prem(proof,0)) == PR_TRANSITIVITY && pr(prem(prem(proof,0),1)) == PR_IFF_FALSE){ + if(op(con) == Not && arg(con,0) == arg(conc(prem(proof,0)),0)){ + Iproof::node ante1 = translate_main(prem(prem(proof,0),0),false); + Iproof::node ante2 = translate_main(prem(prem(prem(proof,0),1),0),false); + ast ante1_con = conc(prem(prem(proof,0),0)); + ast eq0 = arg(ante1_con,0); + ast eq1 = arg(ante1_con,1); + ast symm_con = make(Iff,eq1,eq0); + Iproof::node ante1s = iproof->make_symmetry(symm_con,ante1_con,ante1); + ast cong_con = make(Iff,make(Not,eq1),make(Not,eq0)); + Iproof::node ante1sc = iproof->make_congruence(symm_con,cong_con,ante1s); + res = iproof->make_mp(cong_con,ante2,ante1sc); + return res; + } + } + + // translate all the premises std::vector args(nprems); for(unsigned i = 0; i < nprems; i++) @@ -1749,6 +1770,13 @@ public: res = args[0]; break; } + case PR_IFF_FALSE: { // turns ~p into p <-> false, noop for us + if(is_local(con)) + res = args[0]; + else + throw unsupported(); + break; + } case PR_COMMUTATIVITY: { ast comm_equiv = make(op(con),arg(con,0),arg(con,0)); ast pf = iproof->make_reflexivity(comm_equiv); @@ -1756,6 +1784,7 @@ public: break; } default: + pfgoto(proof); assert(0 && "translate_main: unsupported proof rule"); throw unsupported(); } From 2417b75d8d2b2aa9862d75e51532a3f692f3dc7e Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 16 Mar 2014 15:37:19 -0700 Subject: [PATCH 299/509] duality: added restarts --- src/duality/duality.h | 4 ++ src/duality/duality_rpfp.cpp | 56 ++++++++++++++++++++++ src/duality/duality_solver.cpp | 87 +++++++++++++++++++++++----------- 3 files changed, 120 insertions(+), 27 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index e35c97b50..e7516c1db 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -746,6 +746,10 @@ protected: struct bad_format { }; + // thrown on internal error + struct Bad { + }; + /** Pop a scope (see Push). Note, you cannot pop axioms. */ void Pop(int num_scopes); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index f3deee225..7d3ddda7e 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2954,6 +2954,8 @@ namespace Duality { } } + static int by_case_counter = 0; + void RPFP::InterpolateByCases(Node *root, Node *node){ timer_start("InterpolateByCases"); bool axioms_added = false; @@ -2968,6 +2970,7 @@ namespace Duality { hash_set *core = new hash_set; core->insert(node->Outgoing->dual); while(1){ + by_case_counter++; is.push(); expr annot = !GetAnnotation(node); is.add(annot); @@ -3009,6 +3012,23 @@ namespace Duality { for(unsigned i = 0; i < axioms_to_add.size(); i++) is.add(axioms_to_add[i]); +#define TEST_BAD +#ifdef TEST_BAD + { + static int bad_count = 0, num_bads = 1; + if(bad_count >= num_bads){ + bad_count = 0; + num_bads = num_bads * 2; + Pop(1); + is.pop(1); + delete core; + timer_stop("InterpolateByCases"); + throw Bad(); + } + bad_count++; + } +#endif + if(node->Annotation.IsEmpty()){ if(!axioms_added){ // add the axioms in the off chance they are useful @@ -3018,12 +3038,48 @@ namespace Duality { axioms_added = true; } else { +#ifdef KILL_ON_BAD_INTERPOLANT std::cout << "bad in InterpolateByCase -- core:\n"; +#if 0 std::vector assumps; slvr().get_proof().get_assumptions(assumps); for(unsigned i = 0; i < assumps.size(); i++) assumps[i].show(); +#endif + std::cout << "checking for inconsistency\n"; + std::cout << "model:\n"; + is.get_model().show(); + expr impl = is.get_implicant(); + std::vector conjuncts; + CollectConjuncts(impl,conjuncts,true); + std::cout << "impl:\n"; + for(unsigned i = 0; i < conjuncts.size(); i++) + conjuncts[i].show(); + std::cout << "annot:\n"; + annot.show(); + is.add(annot); + for(unsigned i = 0; i < conjuncts.size(); i++) + is.add(conjuncts[i]); + if(is.check() == unsat){ + std::cout << "inconsistent!\n"; + std::vector is_assumps; + is.aux_solver.get_proof().get_assumptions(is_assumps); + std::cout << "core:\n"; + for(unsigned i = 0; i < is_assumps.size(); i++) + is_assumps[i].show(); + } + else { + std::cout << "consistent!\n"; + is.aux_solver.print("should_be_inconsistent.smt2"); + } + std::cout << "by_case_counter = " << by_case_counter << "\n"; throw "ack!"; +#endif + Pop(1); + is.pop(1); + delete core; + timer_stop("InterpolateByCases"); + throw Bad(); } } Pop(1); diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index d59309c00..9c910a2e7 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -2078,7 +2078,24 @@ namespace Duality { stack.push_back(stack_entry()); } + struct DoRestart {}; + virtual bool Build(){ + while (true) { + try { + return BuildMain(); + } + catch (const DoRestart &) { + // clear the statck and try again + updated_nodes.clear(); + while(stack.size() > 1) + PopLevel(); + reporter->Message("restarted"); + } + } + } + + bool BuildMain(){ stack.back().level = tree->slvr().get_scope_level(); bool was_sat = true; @@ -2110,11 +2127,17 @@ namespace Duality { #ifdef NO_GENERALIZE node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); #else - if(expansions.size() == 1 && NodeTooComplicated(node)) - SimplifyNode(node); - else - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); - Generalize(node); + try { + if(expansions.size() == 1 && NodeTooComplicated(node)) + SimplifyNode(node); + else + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); + Generalize(node); + } + catch(const RPFP::Bad &){ + // bad interpolants can get us here + throw DoRestart(); + } #endif if(RecordUpdate(node)) update_count++; @@ -2134,28 +2157,8 @@ namespace Duality { tree->ComputeProofCore(); // need to compute the proof core before popping solver bool propagated = false; while(1) { - std::vector &expansions = stack.back().expansions; bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop - tree->Pop(1); - hash_set leaves_to_remove; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - // if(node != top) - // tree->ConstrainParent(node->Incoming[0],node); - std::vector &cs = node->Outgoing->Children; - for(unsigned i = 0; i < cs.size(); i++){ - leaves_to_remove.insert(cs[i]); - UnmapNode(cs[i]); - if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) - throw "help!"; - } - } - RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - RemoveExpansion(node); - } - stack.pop_back(); + PopLevel(); if(stack.size() == 1)break; if(prev_level_used){ Node *node = stack.back().expansions[0]; @@ -2213,6 +2216,30 @@ namespace Duality { } } + void PopLevel(){ + std::vector &expansions = stack.back().expansions; + tree->Pop(1); + hash_set leaves_to_remove; + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + // if(node != top) + // tree->ConstrainParent(node->Incoming[0],node); + std::vector &cs = node->Outgoing->Children; + for(unsigned i = 0; i < cs.size(); i++){ + leaves_to_remove.insert(cs[i]); + UnmapNode(cs[i]); + if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) + throw "help!"; + } + } + RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + RemoveExpansion(node); + } + stack.pop_back(); + } + bool NodeTooComplicated(Node *node){ int ops = tree->CountOperators(node->Annotation.Formula); if(ops > 10) return true; @@ -2224,7 +2251,13 @@ namespace Duality { // have to destroy the old proof to get a new interpolant timer_start("SimplifyNode"); tree->PopPush(); - tree->InterpolateByCases(top,node); + try { + tree->InterpolateByCases(top,node); + } + catch(const RPFP::Bad&){ + timer_stop("SimplifyNode"); + throw RPFP::Bad(); + } timer_stop("SimplifyNode"); } From 3e91037a4dc43c26c4d6f39629bc08f15d0b93ac Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 19 Mar 2014 12:37:05 -0700 Subject: [PATCH 300/509] duality fixes --- src/duality/duality_solver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 9c910a2e7..d22cfe80f 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1729,6 +1729,8 @@ namespace Duality { class DerivationTree { public: + virtual ~DerivationTree(){} + DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) : slvr(rpfp->slvr()), ctx(rpfp->ctx) @@ -2207,6 +2209,7 @@ namespace Duality { if(ExpandSomeNodes(false,expand_max)) continue; + tree->Pop(1); while(stack.size() > 1){ tree->Pop(1); stack.pop_back(); From a8fb15ce2c89be53f625f495b64373623d6c7d43 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Mar 2014 18:02:05 -0700 Subject: [PATCH 301/509] patch bounds normalization bug found by dvitek Signed-off-by: Nikolaj Bjorner --- src/tactic/bv/bv_size_reduction_tactic.cpp | 26 +++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/tactic/bv/bv_size_reduction_tactic.cpp b/src/tactic/bv/bv_size_reduction_tactic.cpp index bbbc00783..d3fdb6e56 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -123,21 +123,41 @@ struct bv_size_reduction_tactic::imp { negated = true; f = to_app(f)->get_arg(0); } - + if (m_util.is_bv_sle(f, lhs, rhs)) { + bv_sz = m_util.get_bv_size(lhs); if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // v <= k - if (negated) update_signed_lower(to_app(lhs), val+numeral(1)); + val = m_util.norm(val, bv_sz, true); + if (negated) { + val += numeral(1); + if (m_util.norm(val, bv_sz, true) != val) { + // bound is infeasible. + } + else { + update_signed_lower(to_app(lhs), val); + } + } else update_signed_upper(to_app(lhs), val); } else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // k <= v - if (negated) update_signed_upper(to_app(rhs), val-numeral(1)); + val = m_util.norm(val, bv_sz, true); + if (negated) { + val -= numeral(1); + if (m_util.norm(val, bv_sz, true) != val) { + // bound is infeasible. + } + else { + update_signed_upper(to_app(lhs), val); + } + } else update_signed_lower(to_app(rhs), val); } } + #if 0 else if (m_util.is_bv_ule(f, lhs, rhs)) { if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) { From a9e8045071cb5a172cadf111244cbb5f8fb16f36 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Mar 2014 20:23:54 -0700 Subject: [PATCH 302/509] fix bug reported by Nuno Lopes when query gets sliced Signed-off-by: Nikolaj Bjorner --- src/muz/bmc/dl_bmc_engine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 7a88c1188..0dcbf4644 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -1465,6 +1465,10 @@ namespace datalog { if (m_rules.get_num_rules() == 0) { return l_false; } + if (m_rules.get_predicate_rules(m_query_pred).empty()) { + return l_false; + } + if (is_linear()) { if (m_ctx.get_engine() == QBMC_ENGINE) { From 3e0e9c7f3cc22efbd96351885d0da2202dee64f5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Mar 2014 20:30:58 -0700 Subject: [PATCH 303/509] parse also bit-vector constants with set-info. Reported by David Cok Signed-off-by: Nikolaj Bjorner --- src/parsers/smt2/smt2parser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 546fd3819..785f578f3 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -2077,6 +2077,7 @@ namespace smt2 { void parse_option_value() { switch (curr()) { + case scanner::BV_TOKEN: case scanner::INT_TOKEN: case scanner::FLOAT_TOKEN: m_curr_cmd->set_next_arg(m_ctx, m_scanner.get_number()); From 83f88917a82b4e1169d7eb07524e01ded03f5d34 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 20 Mar 2014 17:47:41 +0000 Subject: [PATCH 304/509] bugfix for python 2.6 --- scripts/mk_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index d425f6576..883492614 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -639,7 +639,7 @@ def is_CXX_gpp(): return is_compiler(CXX, 'g++') def is_clang_in_gpp_form(cc): - version_string = subprocess.check_output([cc, '--version']) + version_string = check_output([cc, '--version']) return str(version_string).find('clang') != -1 def is_CXX_clangpp(): From fb2caf99e6809ae450e3f9f8127cca77ada2ed63 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 21 Mar 2014 10:35:33 -0700 Subject: [PATCH 305/509] duality fix --- src/duality/duality_rpfp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 7d3ddda7e..564f31891 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -150,8 +150,10 @@ namespace Duality { } return 0; } - if(t.is_quantifier()) - return CountOperatorsRec(memo,t.body())+2; // count 2 for a quantifier + if(t.is_quantifier()){ + int nbv = t.get_quantifier_num_bound(); + return CountOperatorsRec(memo,t.body()) + 2 * nbv; // count 2 for each quantifier + } return 0; } From d1376343c7e8362fdb5b653077c1f46fbb2c544b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sat, 22 Mar 2014 16:42:11 +0000 Subject: [PATCH 306/509] Compilation fix. gcc 4.3.2 (on debian 5) did not like the definitions of gcd and abs in class rational, so I moved them outside of the class. Signed-off-by: Christoph M. Wintersteiger --- src/util/rational.h | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/util/rational.h b/src/util/rational.h index c02a5a2c3..0d8a23e81 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -331,22 +331,13 @@ public: return target; } - friend inline rational gcd(rational const & r1, rational const & r2) { - rational result; - m().gcd(r1.m_val, r2.m_val, result.m_val); - return result; - } + friend inline rational gcd(rational const & r1, rational const & r2); // // extended Euclid: // r1*a + r2*b = gcd // - friend inline rational gcd(rational const & r1, rational const & r2, rational & a, rational & b) { - rational result; - m().gcd(r1.m_val, r2.m_val, a.m_val, b.m_val, result.m_val); - return result; - } - + friend inline rational gcd(rational const & r1, rational const & r2, rational & a, rational & b); friend inline rational lcm(rational const & r1, rational const & r2) { rational result; @@ -378,11 +369,7 @@ public: return result; } - friend inline rational abs(rational const & r) { - rational result(r); - m().abs(result.m_val); - return result; - } + friend inline rational abs(rational const & r); rational to_rational() const { return *this; } @@ -446,5 +433,24 @@ inline rational power(rational const & r, unsigned p) { return r.expt(p); } +inline rational abs(rational const & r) { + rational result(r); + rational::m().abs(result.m_val); + return result; +} + +inline rational gcd(rational const & r1, rational const & r2) { + rational result; + rational::m().gcd(r1.m_val, r2.m_val, result.m_val); + return result; +} + +inline rational gcd(rational const & r1, rational const & r2, rational & a, rational & b) { + rational result; + rational::m().gcd(r1.m_val, r2.m_val, a.m_val, b.m_val, result.m_val); + return result; +} + + #endif /* _RATIONAL_H_ */ From e3c1cdfe8c8440897c29462ed7f7b03f739803e4 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 24 Mar 2014 11:33:09 -0700 Subject: [PATCH 307/509] interpolation fix --- src/interp/iz3proof_itp.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 9352348f4..92790e5a0 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -550,6 +550,7 @@ class iz3proof_itp_impl : public iz3proof_itp { else if(g == symm) res = simplify_symm(args); else if(g == modpon) res = simplify_modpon(args); else if(g == sum) res = simplify_sum(args); + else if(g == exmid) res = simplify_exmid(args); #if 0 else if(g == cong) res = simplify_cong(args); else if(g == modpon) res = simplify_modpon(args); @@ -1097,6 +1098,17 @@ class iz3proof_itp_impl : public iz3proof_itp { } + ast simplify_exmid(const std::vector &args){ + if(is_equivrel(args[0])){ + ast Aproves = mk_true(), Bproves = mk_true(); + ast chain = destruct_cond_ineq(args[1],Aproves,Bproves); + ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); + ast interp = contra_chain(Q2,chain); + return my_and(Aproves,my_implies(Bproves,interp)); + } + throw "bad exmid"; + } + bool is_equivrel(const ast &p){ opr o = op(p); return o == Equal || o == Iff; @@ -1381,6 +1393,8 @@ class iz3proof_itp_impl : public iz3proof_itp { if(pos == top_pos && op(equality) == Iff && !is_true(arg(equality,0))) throw "bad rewrite"; #endif + if(!is_equivrel(equality)) + throw "bad rewrite"; return make(t == LitA ? rewrite_A : rewrite_B, pos, cond, equality); } From c9fcf7ee96707b9a91b8e9009d2f66fdd9ddafc0 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 24 Mar 2014 17:21:29 -0700 Subject: [PATCH 308/509] interpolation fix (add simplify_cong) --- src/interp/iz3proof_itp.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 92790e5a0..5fa3ee021 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -551,8 +551,8 @@ class iz3proof_itp_impl : public iz3proof_itp { else if(g == modpon) res = simplify_modpon(args); else if(g == sum) res = simplify_sum(args); else if(g == exmid) res = simplify_exmid(args); -#if 0 else if(g == cong) res = simplify_cong(args); +#if 0 else if(g == modpon) res = simplify_modpon(args); else if(g == leq2eq) res = simplify_leq2eq(args); else if(g == eq2leq) res = simplify_eq2leq(args); @@ -1109,6 +1109,20 @@ class iz3proof_itp_impl : public iz3proof_itp { throw "bad exmid"; } + ast simplify_cong(const std::vector &args){ + ast Aproves = mk_true(), Bproves = mk_true(); + ast chain = destruct_cond_ineq(args[0],Aproves,Bproves); + rational pos; + if(is_numeral(args[1],pos)){ + int ipos = pos.get_unsigned(); + chain = chain_pos_add(ipos,chain); + ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); + ast interp = contra_chain(Q2,chain); + return my_and(Aproves,my_implies(Bproves,interp)); + } + throw "bad cong"; + } + bool is_equivrel(const ast &p){ opr o = op(p); return o == Equal || o == Iff; From fcada914d525e97703ce8001e6054bb972eeb017 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 26 Mar 2014 14:10:21 -0700 Subject: [PATCH 309/509] duality fix --- src/duality/duality_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index d22cfe80f..7e36495b1 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -324,7 +324,7 @@ namespace Duality { last_decisions = 0; CreateEdgesByChildMap(); #ifndef TOP_DOWN - void CreateInitialUnwinding(); + CreateInitialUnwinding(); #else CreateLeaves(); for(unsigned i = 0; i < leaves.size(); i++) From 4c95bb4dd901f0ba33d025538233c6598b165554 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 28 Mar 2014 08:51:50 -0700 Subject: [PATCH 310/509] add 'distinct' to C++ API Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 12 ++++++++++++ src/api/dotnet/Context.cs | 2 ++ src/api/dotnet/Expr.cs | 1 + 3 files changed, 15 insertions(+) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index c75acc5e2..8f228cdba 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -652,6 +652,8 @@ namespace z3 { return expr(c.ctx(), r); } + friend expr distinct(expr_vector const& args); + friend expr operator==(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_eq(a.ctx(), a, b); @@ -1065,6 +1067,16 @@ namespace z3 { array vars(xs); Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); } + + + inline expr distinct(expr_vector const& args) { + assert(args.size() > 0); + context& ctx = args[0].ctx(); + array _args(args); + Z3_ast r = Z3_mk_distinct(ctx, _args.size(), _args.ptr()); + ctx.check_error(); + return expr(ctx, r); + } class func_entry : public object { Z3_func_entry m_entry; diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index ae8b233d1..47294244a 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -916,6 +916,8 @@ namespace Microsoft.Z3 CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_or(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } + + #endregion #region Arithmetic diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index b2927e2c3..49b46edeb 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -320,6 +320,7 @@ namespace Microsoft.Z3 /// Indicates whether the term is an implication /// public bool IsImplies { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IMPLIES; } } + #endregion #region Arithmetic Terms From 6c9483c70a17777cf8d44a882729929da70284db Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 1 Apr 2014 17:10:14 -0700 Subject: [PATCH 311/509] interpolation fix and improving duality quantifier handling --- src/duality/duality.h | 2 ++ src/duality/duality_rpfp.cpp | 54 ++++++++++++++++++++++++--------- src/duality/duality_wrapper.cpp | 6 ++++ src/duality/duality_wrapper.h | 7 +++++ src/interp/iz3proof_itp.cpp | 36 +++++++++++++++++++--- 5 files changed, 86 insertions(+), 19 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index fe86ed2ec..a37896af1 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -118,6 +118,8 @@ protected: expr FinishAndOr(const std::vector &args, bool is_and); expr PullCommonFactors(std::vector &args, bool is_and); Term IneqToEqRec(hash_map &memo, const Term &t); + Term CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall); + Term PushQuantifier(const expr &t, const expr &body, bool is_forall); }; diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 25c5f9c7d..3cbeafa77 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -548,24 +548,50 @@ namespace Duality { return foo; } - Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ - if(t.is_quantifier_forall() && body.is_app() && body.decl().get_decl_kind() == And){ - int nargs = body.num_args(); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = CloneQuantAndSimp(t, body.arg(i)); - return ctx.make(And,args); + Z3User::Term Z3User::PushQuantifier(const expr &t, const expr &body, bool is_forall){ + if(t.get_quantifier_num_bound() == 1){ + std::vector fmlas,free,not_free; + CollectConjuncts(body,fmlas, !is_forall); + for(unsigned i = 0; i < fmlas.size(); i++){ + const expr &fmla = fmlas[i]; + if(fmla.has_free(0)) + free.push_back(fmla); + else + not_free.push_back(fmla); + } + decl_kind op = is_forall ? Or : And; + if(free.empty()) + return SimplifyAndOr(not_free,op == And); + expr q = clone_quantifier(is_forall ? Forall : Exists,t, SimplifyAndOr(free, op == And)); + if(!not_free.empty()) + q = ctx.make(op,q,SimplifyAndOr(not_free, op == And)); + return q; } - if(!t.is_quantifier_forall() && body.is_app() && body.decl().get_decl_kind() == Or){ - int nargs = body.num_args(); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = CloneQuantAndSimp(t, body.arg(i)); - return ctx.make(Or,args); + return clone_quantifier(is_forall ? Forall : Exists,t,body); + } + + Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall){ + if(body.is_app()){ + if(body.decl().get_decl_kind() == (is_forall ? And : Or)){ // quantifier distributes + int nargs = body.num_args(); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = CloneQuantAndSimp(t, body.arg(i), is_forall); + return SimplifyAndOr(args, body.decl().get_decl_kind() == And); + } + else if(body.decl().get_decl_kind() == is_forall ? And : Or){ // quantifier distributes + return PushQuantifier(t,body,is_forall); // may distribute partially + } + else if(body.decl().get_decl_kind() == Not){ + return CloneQuantAndSimp(t,body.arg(0),!is_forall); + } } return clone_quantifier(t,body); } + Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ + return CloneQuantAndSimp(t,body,t.is_quantifier_forall()); + } Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val){ std::pair foo(t,expr(ctx)); @@ -659,7 +685,7 @@ namespace Duality { else if (t.is_quantifier()) { Term body = RemoveRedundancyRec(memo,smemo,t.body()); - res = clone_quantifier(t, body); + res = CloneQuantAndSimp(t, body); } else res = t; return res; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 2a699a995..d5c9a9575 100755 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -374,6 +374,12 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st return q.ctx().cook(q.m().update_quantifier(thing, is_forall, num_patterns, &_patterns[0], to_expr(b.raw()))); } + expr clone_quantifier(decl_kind dk, const expr &q, const expr &b){ + quantifier *thing = to_quantifier(q.raw()); + bool is_forall = dk == Forall; + return q.ctx().cook(q.m().update_quantifier(thing, is_forall, to_expr(b.raw()))); + } + void expr::get_patterns(std::vector &pats) const { quantifier *thing = to_quantifier(raw()); unsigned num_patterns = thing->get_num_patterns(); diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 0861ab791..a85b3981d 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -466,6 +466,11 @@ namespace Duality { bool is_label (bool &pos,std::vector &names) const ; bool is_ground() const {return to_app(raw())->is_ground();} bool has_quantifiers() const {return to_app(raw())->has_quantifiers();} + bool has_free(int idx) const { + used_vars proc; + proc.process(to_expr(raw())); + return proc.contains(idx); + } // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} @@ -573,6 +578,8 @@ namespace Duality { friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); + friend expr clone_quantifier(decl_kind, const expr &, const expr &); + friend std::ostream & operator<<(std::ostream & out, expr const & m){ m.ctx().print_expr(out,m); return out; diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 2a4c4bd85..c880d60aa 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -814,6 +814,10 @@ class iz3proof_itp_impl : public iz3proof_itp { ast equa = sep_cond(arg(pf,0),cond); if(is_equivrel_chain(equa)){ ast lhs,rhs; eq_from_ineq(arg(neg_equality,0),lhs,rhs); // get inequality we need to prove + if(!rewrites_from_to(equa,lhs,rhs)){ + lhs = arg(arg(neg_equality,0),0); // the equality proved is ambiguous, sadly + rhs = arg(arg(neg_equality,0),1); + } LitType lhst = get_term_type(lhs), rhst = get_term_type(rhs); if(lhst != LitMixed && rhst != LitMixed) return unmixed_eq2ineq(lhs, rhs, op(arg(neg_equality,0)), equa, cond); @@ -1671,9 +1675,20 @@ class iz3proof_itp_impl : public iz3proof_itp { return head; } - // split a rewrite chain into head and tail at last non-mixed term + bool has_mixed_summands(const ast &e){ + if(op(e) == Plus){ + int nargs = num_args(e); + for(int i = 0; i < nargs; i++) + if(has_mixed_summands(arg(e,i))) + return true; + return false; + } + return get_term_type(e) == LitMixed; + } + + // split a rewrite chain into head and tail at last sum with no mixed sumands ast get_right_movers(const ast &chain, const ast &rhs, ast &tail, ast &mid){ - if(is_true(chain) || get_term_type(rhs) != LitMixed){ + if(is_true(chain) || !has_mixed_summands(rhs)){ mid = rhs; tail = mk_true(); return chain; @@ -1686,11 +1701,11 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } - // split a rewrite chain into head and tail at first non-mixed term + // split a rewrite chain into head and tail at first sum with no mixed sumands ast get_left_movers(const ast &chain, const ast &lhs, ast &tail, ast &mid){ if(is_true(chain)){ mid = lhs; - if(get_term_type(lhs) != LitMixed){ + if(!has_mixed_summands(lhs)){ tail = mk_true(); return chain; } @@ -1790,10 +1805,21 @@ class iz3proof_itp_impl : public iz3proof_itp { } + bool rewrites_from_to(const ast &chain, const ast &lhs, const ast &rhs){ + if(is_true(chain)) + return lhs == rhs; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); + return rewrites_from_to(rest,lhs,mid); + } + + struct bad_ineq_inference {}; + ast chain_ineqs(opr comp_op, LitType t, const ast &chain, const ast &lhs, const ast &rhs){ if(is_true(chain)){ if(lhs != rhs) - throw "bad ineq inference"; + throw bad_ineq_inference(); return make(Leq,make_int(rational(0)),make_int(rational(0))); } ast last = chain_last(chain); From 278d61952177d987063eb2c8951e36c9444823df Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 1 Apr 2014 17:20:37 -0700 Subject: [PATCH 312/509] set text default to auto to try to avoid crlf disasters --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitattributes b/.gitattributes index b86ed5df7..8df0f39d9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,4 @@ +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto + src/api/dotnet/Properties/AssemblyInfo.cs text eol=crlf From 4671c1be4132ea5fb18a0bce28b55ee301c57c99 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 1 Apr 2014 17:50:48 -0700 Subject: [PATCH 313/509] duality fix --- src/duality/duality_rpfp.cpp | 8344 +++++++++++++++++----------------- 1 file changed, 4172 insertions(+), 4172 deletions(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 3cbeafa77..accbec81a 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -1,4172 +1,4172 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - duality_rpfp.h - -Abstract: - - implements relational post-fixedpoint problem - (RPFP) data structure. - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - - ---*/ - - - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include -#include -#include -#include - - -#include "duality.h" -#include "duality_profiling.h" - -// TODO: do we need these? -#ifdef Z3OPS - -class Z3_subterm_truth { - public: - virtual bool eval(Z3_ast f) = 0; - ~Z3_subterm_truth(){} -}; - -Z3_subterm_truth *Z3_mk_subterm_truth(Z3_context ctx, Z3_model model); - -#endif - -#include - -// TODO: use something official for this -int debug_gauss = 0; - -namespace Duality { - - static char string_of_int_buffer[20]; - - const char *Z3User::string_of_int(int n){ - sprintf(string_of_int_buffer,"%d",n); - return string_of_int_buffer; - } - - RPFP::Term RPFP::SuffixVariable(const Term &t, int n) - { - std::string name = t.decl().name().str() + "_" + string_of_int(n); - return ctx.constant(name.c_str(), t.get_sort()); - } - - RPFP::Term RPFP::HideVariable(const Term &t, int n) - { - std::string name = std::string("@p_") + t.decl().name().str() + "_" + string_of_int(n); - return ctx.constant(name.c_str(), t.get_sort()); - } - - void RPFP::RedVars(Node *node, Term &b, std::vector &v) - { - int idx = node->number; - if(HornClauses) - b = ctx.bool_val(true); - else { - std::string name = std::string("@b_") + string_of_int(idx); - symbol sym = ctx.str_symbol(name.c_str()); - b = ctx.constant(sym,ctx.bool_sort()); - } - // ctx.constant(name.c_str(), ctx.bool_sort()); - v = node->Annotation.IndParams; - for(unsigned i = 0; i < v.size(); i++) - v[i] = SuffixVariable(v[i],idx); - } - - void Z3User::SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t){ - if(memo.find(t) != memo.end()) - return; - memo.insert(t); - if(t.is_app()){ - decl_kind k = t.decl().get_decl_kind(); - if(k == And || k == Or || k == Not || k == Implies || k == Iff){ - ops++; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - SummarizeRec(memo,lits,ops,t.arg(i)); - return; - } - } - lits.push_back(t); - } - - int RPFP::CumulativeDecisions(){ -#if 0 - std::string stats = Z3_statistics_to_string(ctx); - int pos = stats.find("decisions:"); - pos += 10; - int end = stats.find('\n',pos); - std::string val = stats.substr(pos,end-pos); - return atoi(val.c_str()); -#endif - return slvr().get_num_decisions(); - } - - - void Z3User::Summarize(const Term &t){ - hash_set memo; std::vector lits; int ops = 0; - SummarizeRec(memo, lits, ops, t); - std::cout << ops << ": "; - for(unsigned i = 0; i < lits.size(); i++){ - if(i > 0) std::cout << ", "; - std::cout << lits[i]; - } - } - - int Z3User::CountOperatorsRec(hash_set &memo, const Term &t){ - if(memo.find(t) != memo.end()) - return 0; - memo.insert(t); - if(t.is_app()){ - decl_kind k = t.decl().get_decl_kind(); - if(k == And || k == Or){ - int count = 1; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - count += CountOperatorsRec(memo,t.arg(i)); - return count; - } - return 0; - } - if(t.is_quantifier()){ - int nbv = t.get_quantifier_num_bound(); - return CountOperatorsRec(memo,t.body()) + 2 * nbv; // count 2 for each quantifier - } - return 0; - } - - int Z3User::CountOperators(const Term &t){ - hash_set memo; - return CountOperatorsRec(memo,t); - } - - - Z3User::Term Z3User::conjoin(const std::vector &args){ - return ctx.make(And,args); - } - - Z3User::Term Z3User::sum(const std::vector &args){ - return ctx.make(Plus,args); - } - - RPFP::Term RPFP::RedDualRela(Edge *e, std::vector &args, int idx){ - Node *child = e->Children[idx]; - Term b(ctx); - std::vector v; - RedVars(child, b, v); - for (unsigned i = 0; i < args.size(); i++) - { - if (eq(args[i].get_sort(),ctx.bool_sort())) - args[i] = ctx.make(Iff,args[i], v[i]); - else - args[i] = args[i] == v[i]; - } - return args.size() > 0 ? (b && conjoin(args)) : b; - } - - Z3User::Term Z3User::CloneQuantifier(const Term &t, const Term &new_body){ -#if 0 - Z3_context c = ctx; - Z3_ast a = t; - std::vector pats; - int num_pats = Z3_get_quantifier_num_patterns(c,a); - for(int i = 0; i < num_pats; i++) - pats.push_back(Z3_get_quantifier_pattern_ast(c,a,i)); - std::vector no_pats; - int num_no_pats = Z3_get_quantifier_num_patterns(c,a); - for(int i = 0; i < num_no_pats; i++) - no_pats.push_back(Z3_get_quantifier_no_pattern_ast(c,a,i)); - int bound = Z3_get_quantifier_num_bound(c,a); - std::vector sorts; - std::vector names; - for(int i = 0; i < bound; i++){ - sorts.push_back(Z3_get_quantifier_bound_sort(c,a,i)); - names.push_back(Z3_get_quantifier_bound_name(c,a,i)); - } - Z3_ast foo = Z3_mk_quantifier_ex(c, - Z3_is_quantifier_forall(c,a), - Z3_get_quantifier_weight(c,a), - 0, - 0, - num_pats, - &pats[0], - num_no_pats, - &no_pats[0], - bound, - &sorts[0], - &names[0], - new_body); - return expr(ctx,foo); -#endif - return clone_quantifier(t,new_body); - } - - - RPFP::Term RPFP::LocalizeRec(Edge *e, hash_map &memo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - hash_map::iterator it = e->varMap.find(t); - if (it != e->varMap.end()){ - res = it->second; - return res; - } - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(LocalizeRec(e, memo, t.arg(i))); - hash_map::iterator rit = e->relMap.find(f); - if(rit != e->relMap.end()) - res = RedDualRela(e,args,(rit->second)); - else { - if (args.size() == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)) - { - res = HideVariable(t,e->number); - } - else - { - res = f(args.size(),&args[0]); - } - } - } - else if (t.is_quantifier()) - { - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = LocalizeRec(e,memo,pats[i]); - Term body = LocalizeRec(e,memo,t.body()); - res = clone_quantifier(t, body, pats); - } - else res = t; - return res; - } - - void RPFP::SetEdgeMaps(Edge *e){ - timer_start("SetEdgeMaps"); - e->relMap.clear(); - e->varMap.clear(); - for(unsigned i = 0; i < e->F.RelParams.size(); i++){ - e->relMap[e->F.RelParams[i]] = i; - } - Term b(ctx); - std::vector v; - RedVars(e->Parent, b, v); - for(unsigned i = 0; i < e->F.IndParams.size(); i++){ - // func_decl parentParam = e->Parent.Annotation.IndParams[i]; - expr oldname = e->F.IndParams[i]; - expr newname = v[i]; - e->varMap[oldname] = newname; - } - timer_stop("SetEdgeMaps"); - - } - - - RPFP::Term RPFP::Localize(Edge *e, const Term &t){ - timer_start("Localize"); - hash_map memo; - if(e->F.IndParams.size() > 0 && e->varMap.empty()) - SetEdgeMaps(e); // TODO: why is this needed? - Term res = LocalizeRec(e,memo,t); - timer_stop("Localize"); - return res; - } - - - RPFP::Term RPFP::ReducedDualEdge(Edge *e) - { - SetEdgeMaps(e); - timer_start("RedVars"); - Term b(ctx); - std::vector v; - RedVars(e->Parent, b, v); - timer_stop("RedVars"); - // ast_to_track = (ast)b; - return implies(b, Localize(e, e->F.Formula)); - } - - TermTree *RPFP::ToTermTree(Node *root, Node *skip_descendant) - { - if(skip_descendant && root == skip_descendant) - return new TermTree(ctx.bool_val(true)); - Edge *e = root->Outgoing; - if(!e) return new TermTree(ctx.bool_val(true), std::vector()); - std::vector children(e->Children.size()); - for(unsigned i = 0; i < children.size(); i++) - children[i] = ToTermTree(e->Children[i],skip_descendant); - // Term top = ReducedDualEdge(e); - Term top = e->dual.null() ? ctx.bool_val(true) : e->dual; - TermTree *res = new TermTree(top, children); - for(unsigned i = 0; i < e->constraints.size(); i++) - res->addTerm(e->constraints[i]); - return res; - } - - TermTree *RPFP::GetGoalTree(Node *root){ - std::vector children(1); - children[0] = ToGoalTree(root); - return new TermTree(ctx.bool_val(false),children); - } - - TermTree *RPFP::ToGoalTree(Node *root) - { - Term b(ctx); - std::vector v; - RedVars(root, b, v); - Term goal = root->Name(v); - Edge *e = root->Outgoing; - if(!e) return new TermTree(goal, std::vector()); - std::vector children(e->Children.size()); - for(unsigned i = 0; i < children.size(); i++) - children[i] = ToGoalTree(e->Children[i]); - // Term top = ReducedDualEdge(e); - return new TermTree(goal, children); - } - - Z3User::Term Z3User::SubstRec(hash_map &memo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, t.arg(i))); - res = f(args.size(),&args[0]); - } - else if (t.is_quantifier()) - { - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstRec(memo,pats[i]); - Term body = SubstRec(memo,t.body()); - res = clone_quantifier(t, body, pats); - } - // res = CloneQuantifier(t,SubstRec(memo, t.body())); - else res = t; - return res; - } - - Z3User::Term Z3User::SubstRec(hash_map &memo, hash_map &map, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, map, t.arg(i))); - hash_map::iterator it = map.find(f); - if(it != map.end()) - f = it->second; - res = f(args.size(),&args[0]); - } - else if (t.is_quantifier()) - { - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstRec(memo, map, pats[i]); - Term body = SubstRec(memo, map, t.body()); - res = clone_quantifier(t, body, pats); - } - // res = CloneQuantifier(t,SubstRec(memo, t.body())); - else res = t; - return res; - } - - Z3User::Term Z3User::ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(ExtractStores(memo, t.arg(i),cnstrs,renaming)); - res = f(args.size(),&args[0]); - if(f.get_decl_kind() == Store){ - func_decl fresh = ctx.fresh_func_decl("@arr", res.get_sort()); - expr y = fresh(); - expr equ = ctx.make(Equal,y,res); - cnstrs.push_back(equ); - renaming[y] = res; - res = y; - } - } - else res = t; - return res; - } - - - bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ - if(!(lit.is_quantifier() && IsClosedFormula(lit))){ - if(!lit.is_app()) - return false; - decl_kind k = lit.decl().get_decl_kind(); - if(k == Not){ - if(IsLiteral(lit.arg(0),atom,val)){ - val = eq(val,ctx.bool_val(true)) ? ctx.bool_val(false) : ctx.bool_val(true); - return true; - } - return false; - } - if(k == And || k == Or || k == Iff || k == Implies) - return false; - } - atom = lit; - val = ctx.bool_val(true); - return true; - } - - expr Z3User::Negate(const expr &f){ - if(f.is_app() && f.decl().get_decl_kind() == Not) - return f.arg(0); - else if(eq(f,ctx.bool_val(true))) - return ctx.bool_val(false); - else if(eq(f,ctx.bool_val(false))) - return ctx.bool_val(true); - return !f; - } - - expr Z3User::ReduceAndOr(const std::vector &args, bool is_and, std::vector &res){ - for(unsigned i = 0; i < args.size(); i++) - if(!eq(args[i],ctx.bool_val(is_and))){ - if(eq(args[i],ctx.bool_val(!is_and))) - return ctx.bool_val(!is_and); - res.push_back(args[i]); - } - return expr(); - } - - expr Z3User::FinishAndOr(const std::vector &args, bool is_and){ - if(args.size() == 0) - return ctx.bool_val(is_and); - if(args.size() == 1) - return args[0]; - return ctx.make(is_and ? And : Or,args); - } - - expr Z3User::SimplifyAndOr(const std::vector &args, bool is_and){ - std::vector sargs; - expr res = ReduceAndOr(args,is_and,sargs); - if(!res.null()) return res; - return FinishAndOr(sargs,is_and); - } - - expr Z3User::PullCommonFactors(std::vector &args, bool is_and){ - - // first check if there's anything to do... - if(args.size() < 2) - return FinishAndOr(args,is_and); - for(unsigned i = 0; i < args.size(); i++){ - const expr &a = args[i]; - if(!(a.is_app() && a.decl().get_decl_kind() == (is_and ? Or : And))) - return FinishAndOr(args,is_and); - } - std::vector common; - for(unsigned i = 0; i < args.size(); i++){ - unsigned n = args[i].num_args(); - std::vector v(n),w; - for(unsigned j = 0; j < n; j++) - v[j] = args[i].arg(j); - std::less comp; - std::sort(v.begin(),v.end(),comp); - if(i == 0) - common.swap(v); - else { - std::set_intersection(common.begin(),common.end(),v.begin(),v.end(),std::inserter(w,w.begin()),comp); - common.swap(w); - } - } - if(common.empty()) - return FinishAndOr(args,is_and); - std::set common_set(common.begin(),common.end()); - for(unsigned i = 0; i < args.size(); i++){ - unsigned n = args[i].num_args(); - std::vector lits; - for(unsigned j = 0; j < n; j++){ - const expr b = args[i].arg(j); - if(common_set.find(b) == common_set.end()) - lits.push_back(b); - } - args[i] = SimplifyAndOr(lits,!is_and); - } - common.push_back(SimplifyAndOr(args,is_and)); - return SimplifyAndOr(common,!is_and); - } - - expr Z3User::ReallySimplifyAndOr(const std::vector &args, bool is_and){ - std::vector sargs; - expr res = ReduceAndOr(args,is_and,sargs); - if(!res.null()) return res; - return PullCommonFactors(sargs,is_and); - } - - Z3User::Term Z3User::SubstAtomTriv(const expr &foo, const expr &atom, const expr &val){ - if(eq(foo,atom)) - return val; - else if(foo.is_app() && foo.decl().get_decl_kind() == Not && eq(foo.arg(0),atom)) - return Negate(val); - else - return foo; - } - - Z3User::Term Z3User::PushQuantifier(const expr &t, const expr &body, bool is_forall){ - if(t.get_quantifier_num_bound() == 1){ - std::vector fmlas,free,not_free; - CollectConjuncts(body,fmlas, !is_forall); - for(unsigned i = 0; i < fmlas.size(); i++){ - const expr &fmla = fmlas[i]; - if(fmla.has_free(0)) - free.push_back(fmla); - else - not_free.push_back(fmla); - } - decl_kind op = is_forall ? Or : And; - if(free.empty()) - return SimplifyAndOr(not_free,op == And); - expr q = clone_quantifier(is_forall ? Forall : Exists,t, SimplifyAndOr(free, op == And)); - if(!not_free.empty()) - q = ctx.make(op,q,SimplifyAndOr(not_free, op == And)); - return q; - } - return clone_quantifier(is_forall ? Forall : Exists,t,body); - } - - Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall){ - if(body.is_app()){ - if(body.decl().get_decl_kind() == (is_forall ? And : Or)){ // quantifier distributes - int nargs = body.num_args(); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = CloneQuantAndSimp(t, body.arg(i), is_forall); - return SimplifyAndOr(args, body.decl().get_decl_kind() == And); - } - else if(body.decl().get_decl_kind() == is_forall ? And : Or){ // quantifier distributes - return PushQuantifier(t,body,is_forall); // may distribute partially - } - else if(body.decl().get_decl_kind() == Not){ - return CloneQuantAndSimp(t,body.arg(0),!is_forall); - } - } - return clone_quantifier(t,body); - } - - Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ - return CloneQuantAndSimp(t,body,t.is_quantifier_forall()); - } - - Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val){ - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()){ - func_decl f = t.decl(); - decl_kind k = f.get_decl_kind(); - - // TODO: recur here, but how much? We don't want to be quadractic in formula size - - if(k == And || k == Or){ - int nargs = t.num_args(); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = SubstAtom(memo,t.arg(i),atom,val); - res = ReallySimplifyAndOr(args, k==And); - return res; - } - } - else if(t.is_quantifier() && atom.is_quantifier()){ - if(eq(t,atom)) - res = val; - else - res = clone_quantifier(t,SubstAtom(memo,t.body(),atom,val)); - return res; - } - res = SubstAtomTriv(t,atom,val); - return res; - } - - void Z3User::RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo){ - for(unsigned i = 0; i < args.size(); i++){ - const expr &lit = args[i]; - expr atom, val; - if(IsLiteral(lit,atom,val)){ - if(atom.is_app() && atom.decl().get_decl_kind() == Equal) - if(pol ? eq(val,ctx.bool_val(true)) : eq(val,ctx.bool_val(false))){ - expr lhs = atom.arg(0), rhs = atom.arg(1); - if(lhs.is_numeral()) - std::swap(lhs,rhs); - if(rhs.is_numeral() && lhs.is_app()){ - for(unsigned j = 0; j < args.size(); j++) - if(j != i){ - smemo.clear(); - smemo[lhs] = rhs; - args[j] = SubstRec(smemo,args[j]); - } - } - } - for(unsigned j = 0; j < args.size(); j++) - if(j != i){ - smemo.clear(); - args[j] = SubstAtom(smemo,args[j],atom,pol ? val : !val); - } - } - } - } - - - Z3User::Term Z3User::RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(RemoveRedundancyRec(memo, smemo, t.arg(i))); - - decl_kind k = f.get_decl_kind(); - if(k == And){ - RemoveRedundancyOp(true,args,smemo); - res = ReallySimplifyAndOr(args, true); - } - else if(k == Or){ - RemoveRedundancyOp(false,args,smemo); - res = ReallySimplifyAndOr(args, false); - } - else { - if(k == Equal && args[0].get_id() > args[1].get_id()) - std::swap(args[0],args[1]); - res = f(args.size(),&args[0]); - } - } - else if (t.is_quantifier()) - { - Term body = RemoveRedundancyRec(memo,smemo,t.body()); - res = CloneQuantAndSimp(t, body); - } - else res = t; - return res; - } - - Z3User::Term Z3User::RemoveRedundancy(const Term &t){ - hash_map memo; - hash_map smemo; - return RemoveRedundancyRec(memo,smemo,t); - } - - Z3User::Term Z3User::AdjustQuantifiers(const Term &t) - { - if(t.is_quantifier() || (t.is_app() && t.has_quantifiers())) - return t.qe_lite(); - return t; - } - - Z3User::Term Z3User::IneqToEqRec(hash_map &memo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(IneqToEqRec(memo, t.arg(i))); - - decl_kind k = f.get_decl_kind(); - if(k == And){ - for(int i = 0; i < nargs-1; i++){ - if((args[i].is_app() && args[i].decl().get_decl_kind() == Geq && - args[i+1].is_app() && args[i+1].decl().get_decl_kind() == Leq) - || - (args[i].is_app() && args[i].decl().get_decl_kind() == Leq && - args[i+1].is_app() && args[i+1].decl().get_decl_kind() == Geq)) - if(eq(args[i].arg(0),args[i+1].arg(0)) && eq(args[i].arg(1),args[i+1].arg(1))){ - args[i] = ctx.make(Equal,args[i].arg(0),args[i].arg(1)); - args[i+1] = ctx.bool_val(true); - } - } - } - res = f(args.size(),&args[0]); - } - else if (t.is_quantifier()) - { - Term body = IneqToEqRec(memo,t.body()); - res = clone_quantifier(t, body); - } - else res = t; - return res; - } - - Z3User::Term Z3User::IneqToEq(const Term &t){ - hash_map memo; - return IneqToEqRec(memo,t); - } - - Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ - std::string name = std::string("@q_") + t.decl().name().str() + "_" + string_of_int(number); - res = ctx.constant(name.c_str(), t.get_sort()); - return res; - } - for(int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, t.arg(i))); - res = f(args.size(),&args[0]); - } - else if (t.is_quantifier()) - res = CloneQuantifier(t,SubstRec(memo, t.body())); - else res = t; - return res; - } - - RPFP::Term RPFP::SubstParams(const std::vector &from, - const std::vector &to, const Term &t){ - hash_map memo; - bool some_diff = false; - for(unsigned i = 0; i < from.size(); i++) - if(i < to.size() && !eq(from[i],to[i])){ - memo[from[i]] = to[i]; - some_diff = true; - } - return some_diff ? SubstRec(memo,t) : t; - } - - RPFP::Term RPFP::SubstParamsNoCapture(const std::vector &from, - const std::vector &to, const Term &t){ - hash_map memo; - bool some_diff = false; - for(unsigned i = 0; i < from.size(); i++) - if(i < to.size() && !eq(from[i],to[i])){ - memo[from[i]] = to[i]; - // if the new param is not being mapped to anything else, we need to rename it to prevent capture - // note, if the new param *is* mapped later in the list, it will override this substitution - const expr &w = to[i]; - if(memo.find(w) == memo.end()){ - std::string old_name = w.decl().name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); - expr y = fresh(); - memo[w] = y; - } - some_diff = true; - } - return some_diff ? SubstRec(memo,t) : t; - } - - - - RPFP::Transformer RPFP::Fuse(const std::vector &trs){ - assert(!trs.empty()); - const std::vector ¶ms = trs[0]->IndParams; - std::vector fmlas(trs.size()); - fmlas[0] = trs[0]->Formula; - for(unsigned i = 1; i < trs.size(); i++) - fmlas[i] = SubstParamsNoCapture(trs[i]->IndParams,params,trs[i]->Formula); - std::vector rel_params = trs[0]->RelParams; - for(unsigned i = 1; i < trs.size(); i++){ - const std::vector ¶ms2 = trs[i]->RelParams; - hash_map map; - for(unsigned j = 0; j < params2.size(); j++){ - func_decl rel = RenumberPred(params2[j],rel_params.size()); - rel_params.push_back(rel); - map[params2[j]] = rel; - } - hash_map memo; - fmlas[i] = SubstRec(memo,map,fmlas[i]); - } - return Transformer(rel_params,params,ctx.make(Or,fmlas),trs[0]->owner); - } - - - void Z3User::Strengthen(Term &x, const Term &y) - { - if (eq(x,ctx.bool_val(true))) - x = y; - else - x = x && y; - } - - void RPFP::SetAnnotation(Node *root, const expr &t){ - hash_map memo; - Term b; - std::vector v; - RedVars(root, b, v); - memo[b] = ctx.bool_val(true); - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = root->Annotation.IndParams[i]; - Term annot = SubstRec(memo, t); - // Strengthen(ref root.Annotation.Formula, annot); - root->Annotation.Formula = annot; - } - - void RPFP::DecodeTree(Node *root, TermTree *interp, int persist) - { - std::vector &ic = interp->getChildren(); - if (ic.size() > 0) - { - std::vector &nc = root->Outgoing->Children; - for (unsigned i = 0; i < nc.size(); i++) - DecodeTree(nc[i], ic[i], persist); - } - SetAnnotation(root,interp->getTerm()); -#if 0 - if(persist != 0) - Z3_persist_ast(ctx,root->Annotation.Formula,persist); -#endif - } - - RPFP::Term RPFP::GetUpperBound(Node *n) - { - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned int i = 0; i < v.size(); i++) - memo[n->Bound.IndParams[i]] = v[i]; - Term cnst = SubstRec(memo, n->Bound.Formula); - return b && !cnst; - } - - RPFP::Term RPFP::GetAnnotation(Node *n) - { - if(eq(n->Annotation.Formula,ctx.bool_val(true))) - return n->Annotation.Formula; - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[n->Annotation.IndParams[i]] = v[i]; - Term cnst = SubstRec(memo, n->Annotation.Formula); - return !b || cnst; - } - - RPFP::Term RPFP::GetUnderapprox(Node *n) - { - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[n->Underapprox.IndParams[i]] = v[i]; - Term cnst = SubstRecHide(memo, n->Underapprox.Formula, n->number); - return !b || cnst; - } - - TermTree *RPFP::AddUpperBound(Node *root, TermTree *t) - { - Term f = !((ast)(root->dual)) ? ctx.bool_val(true) : root->dual; - std::vector c(1); c[0] = t; - return new TermTree(f, c); - } - -#if 0 - void RPFP::WriteInterps(System.IO.StreamWriter f, TermTree t) - { - foreach (var c in t.getChildren()) - WriteInterps(f, c); - f.WriteLine(t.getTerm()); - } -#endif - - - expr RPFP::GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox) - { - if (e->dual.null()) { - timer_start("ReducedDualEdge"); - e->dual = ReducedDualEdge(e); - timer_stop("ReducedDualEdge"); - timer_start("getting children"); - if(underapprox){ - std::vector cus(e->Children.size()); - for(unsigned i = 0; i < e->Children.size(); i++) - cus[i] = !UnderapproxFlag(e->Children[i]) || GetUnderapprox(e->Children[i]); - expr cnst = conjoin(cus); - e->dual = e->dual && cnst; - } - timer_stop("getting children"); - timer_start("Persisting"); - std::list::reverse_iterator it = stack.rbegin(); - for(int i = 0; i < persist && it != stack.rend(); i++) - it++; - if(it != stack.rend()) - it->edges.push_back(e); - timer_stop("Persisting"); - //Console.WriteLine("{0}", cnst); - } - return e->dual; - } - - /** For incremental solving, asserts the constraint associated - * with this edge in the SMT context. If this edge is removed, - * you must pop the context accordingly. The second argument is - * the number of pushes we are inside. The constraint formula - * will survive "persist" pops of the context. If you plan - * to reassert the edge after popping the context once, - * you can save time re-constructing the formula by setting - * "persist" to one. If you set "persist" too high, however, - * you could have a memory leak. - * - * The flag "with children" causes the annotations of the children - * to be asserted. The flag underapprox causes the underapproximations - * of the children to be asserted *conditionally*. See Check() on - * how to actually enforce these constraints. - * - */ - - void RPFP::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) - { - if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); - timer_start("solver add"); - slvr_add(e->dual); - timer_stop("solver add"); - if(with_children) - for(unsigned i = 0; i < e->Children.size(); i++) - ConstrainParent(e,e->Children[i]); - } - - -#ifdef LIMIT_STACK_WEIGHT - void RPFP_caching::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) - { - unsigned old_new_alits = new_alits.size(); - if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); - timer_start("solver add"); - slvr_add(e->dual); - timer_stop("solver add"); - if(old_new_alits < new_alits.size()) - weight_added.val++; - if(with_children) - for(unsigned i = 0; i < e->Children.size(); i++) - ConstrainParent(e,e->Children[i]); - } -#endif - - // caching verion of above - void RPFP_caching::AssertEdgeCache(Edge *e, std::vector &lits, bool with_children){ - if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, 0, with_children, false); - GetAssumptionLits(fmla,lits); - if(with_children) - for(unsigned i = 0; i < e->Children.size(); i++) - ConstrainParentCache(e,e->Children[i],lits); - } - - void RPFP::slvr_add(const expr &e){ - slvr().add(e); - } - - void RPFP_caching::slvr_add(const expr &e){ - GetAssumptionLits(e,alit_stack); - } - - void RPFP::slvr_pop(int i){ - slvr().pop(i); - } - - void RPFP::slvr_push(){ - slvr().push(); - } - - void RPFP_caching::slvr_pop(int i){ - for(int j = 0; j < i; j++){ -#ifdef LIMIT_STACK_WEIGHT - if(alit_stack_sizes.empty()){ - if(big_stack.empty()) - throw "stack underflow"; - for(unsigned k = 0; k < new_alits.size(); k++){ - if(AssumptionLits.find(new_alits[k]) == AssumptionLits.end()) - throw "foo!"; - AssumptionLits.erase(new_alits[k]); - } - big_stack_entry &bsb = big_stack.back(); - bsb.alit_stack_sizes.swap(alit_stack_sizes); - bsb.alit_stack.swap(alit_stack); - bsb.new_alits.swap(new_alits); - bsb.weight_added.swap(weight_added); - big_stack.pop_back(); - slvr().pop(1); - continue; - } -#endif - alit_stack.resize(alit_stack_sizes.back()); - alit_stack_sizes.pop_back(); - } - } - - void RPFP_caching::slvr_push(){ -#ifdef LIMIT_STACK_WEIGHT - if(weight_added.val > LIMIT_STACK_WEIGHT){ - big_stack.resize(big_stack.size()+1); - big_stack_entry &bsb = big_stack.back(); - bsb.alit_stack_sizes.swap(alit_stack_sizes); - bsb.alit_stack.swap(alit_stack); - bsb.new_alits.swap(new_alits); - bsb.weight_added.swap(weight_added); - slvr().push(); - for(unsigned i = 0; i < bsb.alit_stack.size(); i++) - slvr().add(bsb.alit_stack[i]); - return; - } -#endif - alit_stack_sizes.push_back(alit_stack.size()); - } - - check_result RPFP::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ - return slvr().check(n, assumptions, core_size, core); - } - - check_result RPFP_caching::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ - unsigned oldsiz = alit_stack.size(); - if(n && assumptions) - std::copy(assumptions,assumptions+n,std::inserter(alit_stack,alit_stack.end())); - check_result res; - if(core_size && core){ - std::vector full_core(alit_stack.size()), core1(n); - std::copy(assumptions,assumptions+n,core1.begin()); - res = slvr().check(alit_stack.size(), &alit_stack[0], core_size, &full_core[0]); - full_core.resize(*core_size); - if(res == unsat){ - FilterCore(core1,full_core); - *core_size = core1.size(); - std::copy(core1.begin(),core1.end(),core); - } - } - else - res = slvr().check(alit_stack.size(), &alit_stack[0]); - alit_stack.resize(oldsiz); - return res; - } - - lbool RPFP::ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals, - bool weak){ - return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); - } - - lbool RPFP_caching::ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals, - bool weak){ - GetTermTreeAssertionLiterals(assumptions); - return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); - } - - void RPFP_caching::GetTermTreeAssertionLiteralsRec(TermTree *assumptions){ - std::vector alits; - hash_map map; - GetAssumptionLits(assumptions->getTerm(),alits,&map); - std::vector &ts = assumptions->getTerms(); - for(unsigned i = 0; i < ts.size(); i++) - GetAssumptionLits(ts[i],alits,&map); - assumptions->setTerm(ctx.bool_val(true)); - ts = alits; - for(unsigned i = 0; i < alits.size(); i++) - ts.push_back(ctx.make(Implies,alits[i],map[alits[i]])); - for(unsigned i = 0; i < assumptions->getChildren().size(); i++) - GetTermTreeAssertionLiterals(assumptions->getChildren()[i]); - return; - } - - void RPFP_caching::GetTermTreeAssertionLiterals(TermTree *assumptions){ - // optimize binary case - if(assumptions->getChildren().size() == 1 - && assumptions->getChildren()[0]->getChildren().size() == 0){ - hash_map map; - TermTree *child = assumptions->getChildren()[0]; - std::vector dummy; - GetAssumptionLits(child->getTerm(),dummy,&map); - std::vector &ts = child->getTerms(); - for(unsigned i = 0; i < ts.size(); i++) - GetAssumptionLits(ts[i],dummy,&map); - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - if(!proof_core){ // save the proof core for later use - proof_core = new hash_set; - for(unsigned i = 0; i < assumps.size(); i++) - proof_core->insert(assumps[i]); - } - std::vector *cnsts[2] = {&child->getTerms(),&assumptions->getTerms()}; - for(unsigned i = 0; i < assumps.size(); i++){ - expr &ass = assumps[i]; - expr alit = (ass.is_app() && ass.decl().get_decl_kind() == Implies) ? ass.arg(0) : ass; - bool isA = map.find(alit) != map.end(); - cnsts[isA ? 0 : 1]->push_back(ass); - } - } - else - GetTermTreeAssertionLiteralsRec(assumptions); - } - - void RPFP::AddToProofCore(hash_set &core){ - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++) - core.insert(assumps[i]); - } - - void RPFP::ComputeProofCore(){ - if(!proof_core){ - proof_core = new hash_set; - AddToProofCore(*proof_core); - } - } - - - void RPFP_caching::GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map){ - std::vector conjs; - CollectConjuncts(fmla,conjs); - for(unsigned i = 0; i < conjs.size(); i++){ - const expr &conj = conjs[i]; - std::pair foo(conj,expr(ctx)); - std::pair::iterator, bool> bar = AssumptionLits.insert(foo); - Term &res = bar.first->second; - if(bar.second){ - func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); - res = pred(); -#ifdef LIMIT_STACK_WEIGHT - new_alits.push_back(conj); -#endif - slvr().add(ctx.make(Implies,res,conj)); - // std::cout << res << ": " << conj << "\n"; - } - if(opt_map) - (*opt_map)[res] = conj; - lits.push_back(res); - } - } - - void RPFP::ConstrainParent(Edge *parent, Node *child){ - ConstrainEdgeLocalized(parent,GetAnnotation(child)); - } - - void RPFP_caching::ConstrainParentCache(Edge *parent, Node *child, std::vector &lits){ - ConstrainEdgeLocalizedCache(parent,GetAnnotation(child),lits); - } - - - /** For incremental solving, asserts the negation of the upper bound associated - * with a node. - * */ - - void RPFP::AssertNode(Node *n) - { - if (n->dual.null()) - { - n->dual = GetUpperBound(n); - stack.back().nodes.push_back(n); - slvr_add(n->dual); - } - } - - // caching version of above - void RPFP_caching::AssertNodeCache(Node *n, std::vector lits){ - if (n->dual.null()) - { - n->dual = GetUpperBound(n); - stack.back().nodes.push_back(n); - GetAssumptionLits(n->dual,lits); - } - } - - /** Clone another RPFP into this one, keeping a map */ - void RPFP_caching::Clone(RPFP *other){ -#if 0 - for(unsigned i = 0; i < other->nodes.size(); i++) - NodeCloneMap[other->nodes[i]] = CloneNode(other->nodes[i]); -#endif - for(unsigned i = 0; i < other->edges.size(); i++){ - Edge *edge = other->edges[i]; - Node *parent = CloneNode(edge->Parent); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++) - // cs.push_back(NodeCloneMap[edge->Children[j]]); - cs.push_back(CloneNode(edge->Children[j])); - EdgeCloneMap[edge] = CreateEdge(parent,edge->F,cs); - } - } - - /** Get the clone of a node */ - RPFP::Node *RPFP_caching::GetNodeClone(Node *other_node){ - return NodeCloneMap[other_node]; - } - - /** Get the clone of an edge */ - RPFP::Edge *RPFP_caching::GetEdgeClone(Edge *other_edge){ - return EdgeCloneMap[other_edge]; - } - - /** check assumption lits, and return core */ - check_result RPFP_caching::CheckCore(const std::vector &assumps, std::vector &core){ - core.resize(assumps.size()); - unsigned core_size; - check_result res = slvr().check(assumps.size(),(expr *)&assumps[0],&core_size,&core[0]); - if(res == unsat) - core.resize(core_size); - else - core.clear(); - return res; - } - - - /** Assert a constraint on an edge in the SMT context. - */ - - void RPFP::ConstrainEdge(Edge *e, const Term &t) - { - Term tl = Localize(e, t); - ConstrainEdgeLocalized(e,tl); - } - - void RPFP::ConstrainEdgeLocalized(Edge *e, const Term &tl) - { - e->constraints.push_back(tl); - stack.back().constraints.push_back(std::pair(e,tl)); - slvr_add(tl); - } - - void RPFP_caching::ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits) - { - e->constraints.push_back(tl); - stack.back().constraints.push_back(std::pair(e,tl)); - GetAssumptionLits(tl,lits); - } - - - /** Declare a constant in the background theory. */ - - void RPFP::DeclareConstant(const FuncDecl &f){ - ls->declare_constant(f); - } - - /** Assert a background axiom. Background axioms can be used to provide the - * theory of auxilliary functions or relations. All symbols appearing in - * background axioms are considered global, and may appear in both transformer - * and relational solutions. Semantically, a solution to the RPFP gives - * an interpretation of the unknown relations for each interpretation of the - * auxilliary symbols that is consistent with the axioms. Axioms should be - * asserted before any calls to Push. They cannot be de-asserted by Pop. */ - - void RPFP::AssertAxiom(const Term &t) - { - ls->assert_axiom(t); - axioms.push_back(t); // for printing only - } - -#if 0 - /** Do not call this. */ - - void RPFP::RemoveAxiom(const Term &t) - { - slvr().RemoveInterpolationAxiom(t); - } -#endif - - /** Solve an RPFP graph. This means either strengthen the annotation - * so that the bound at the given root node is satisfied, or - * show that this cannot be done by giving a dual solution - * (i.e., a counterexample). - * - * In the current implementation, this only works for graphs that - * are: - * - tree-like - * - * - closed. - * - * In a tree-like graph, every nod has out most one incoming and one out-going edge, - * and there are no cycles. In a closed graph, every node has exactly one out-going - * edge. This means that the leaves of the tree are all hyper-edges with no - * children. Such an edge represents a relation (nullary transformer) and thus - * a lower bound on its parent. The parameter root must be the root of this tree. - * - * If Solve returns LBool.False, this indicates success. The annotation of the tree - * has been updated to satisfy the upper bound at the root. - * - * If Solve returns LBool.True, this indicates a counterexample. For each edge, - * you can then call Eval to determine the values of symbols in the transformer formula. - * You can also call Empty on a node to determine if its value in the counterexample - * is the empty relation. - * - * \param root The root of the tree - * \param persist Number of context pops through which result should persist - * - * - */ - - lbool RPFP::Solve(Node *root, int persist) - { - timer_start("Solve"); - TermTree *tree = GetConstraintTree(root); - TermTree *interpolant = NULL; - TermTree *goals = NULL; - if(ls->need_goals) - goals = GetGoalTree(root); - ClearProofCore(); - - // if (dualModel != null) dualModel.Dispose(); - // if (dualLabels != null) dualLabels.Dispose(); - - timer_start("interpolate_tree"); - lbool res = ls_interpolate_tree(tree, interpolant, dualModel,goals,true); - timer_stop("interpolate_tree"); - if (res == l_false) - { - DecodeTree(root, interpolant->getChildren()[0], persist); - delete interpolant; - } - - delete tree; - if(goals) - delete goals; - - timer_stop("Solve"); - return res; - } - - void RPFP::CollapseTermTreeRec(TermTree *root, TermTree *node){ - root->addTerm(node->getTerm()); - std::vector &cnsts = node->getTerms(); - for(unsigned i = 0; i < cnsts.size(); i++) - root->addTerm(cnsts[i]); - std::vector &chs = node->getChildren(); - for(unsigned i = 0; i < chs.size(); i++){ - CollapseTermTreeRec(root,chs[i]); - } - } - - TermTree *RPFP::CollapseTermTree(TermTree *node){ - std::vector &chs = node->getChildren(); - for(unsigned i = 0; i < chs.size(); i++) - CollapseTermTreeRec(node,chs[i]); - for(unsigned i = 0; i < chs.size(); i++) - delete chs[i]; - chs.clear(); - return node; - } - - lbool RPFP::SolveSingleNode(Node *root, Node *node) - { - timer_start("Solve"); - TermTree *tree = CollapseTermTree(GetConstraintTree(root,node)); - tree->getChildren().push_back(CollapseTermTree(ToTermTree(node))); - TermTree *interpolant = NULL; - ClearProofCore(); - - timer_start("interpolate_tree"); - lbool res = ls_interpolate_tree(tree, interpolant, dualModel,0,true); - timer_stop("interpolate_tree"); - if (res == l_false) - { - DecodeTree(node, interpolant->getChildren()[0], 0); - delete interpolant; - } - - delete tree; - timer_stop("Solve"); - return res; - } - - /** Get the constraint tree (but don't solve it) */ - - TermTree *RPFP::GetConstraintTree(Node *root, Node *skip_descendant) - { - return AddUpperBound(root, ToTermTree(root,skip_descendant)); - } - - /** Dispose of the dual model (counterexample) if there is one. */ - - void RPFP::DisposeDualModel() - { - dualModel = model(ctx,NULL); - } - - RPFP::Term RPFP::UnderapproxFlag(Node *n){ - expr flag = ctx.constant((std::string("@under") + string_of_int(n->number)).c_str(), ctx.bool_sort()); - underapprox_flag_rev[flag] = n; - return flag; - } - - RPFP::Node *RPFP::UnderapproxFlagRev(const Term &flag){ - return underapprox_flag_rev[flag]; - } - - /** Check satisfiability of asserted edges and nodes. Same functionality as - * Solve, except no primal solution (interpolant) is generated in the unsat case. - * The vector underapproxes gives the set of node underapproximations to be enforced - * (assuming these were conditionally asserted by AssertEdge). - * - */ - - check_result RPFP::Check(Node *root, std::vector underapproxes, std::vector *underapprox_core ) - { - timer_start("Check"); - ClearProofCore(); - // if (dualModel != null) dualModel.Dispose(); - check_result res; - if(!underapproxes.size()) - res = slvr_check(); - else { - std::vector us(underapproxes.size()); - for(unsigned i = 0; i < underapproxes.size(); i++) - us[i] = UnderapproxFlag(underapproxes[i]); - slvr_check(); // TODO: no idea why I need to do this - if(underapprox_core){ - std::vector unsat_core(us.size()); - unsigned core_size = 0; - res = slvr_check(us.size(),&us[0],&core_size,&unsat_core[0]); - underapprox_core->resize(core_size); - for(unsigned i = 0; i < core_size; i++) - (*underapprox_core)[i] = UnderapproxFlagRev(unsat_core[i]); - } - else { - res = slvr_check(us.size(),&us[0]); - bool dump = false; - if(dump){ - std::vector cnsts; - // cnsts.push_back(axioms[0]); - cnsts.push_back(root->dual); - cnsts.push_back(root->Outgoing->dual); - ls->write_interpolation_problem("temp.smt",cnsts,std::vector()); - } - } - // check_result temp = slvr_check(); - } - dualModel = slvr().get_model(); - timer_stop("Check"); - return res; - } - - check_result RPFP::CheckUpdateModel(Node *root, std::vector assumps){ - // check_result temp1 = slvr_check(); // no idea why I need to do this - ClearProofCore(); - check_result res = slvr_check(assumps.size(),&assumps[0]); - model mod = slvr().get_model(); - if(!mod.null()) - dualModel = mod;; - return res; - } - - /** Determines the value in the counterexample of a symbol occuring in the transformer formula of - * a given edge. */ - - RPFP::Term RPFP::Eval(Edge *e, Term t) - { - Term tl = Localize(e, t); - return dualModel.eval(tl); - } - - /** Returns true if the given node is empty in the primal solution. For proecudure summaries, - this means that the procedure is not called in the current counter-model. */ - - bool RPFP::Empty(Node *p) - { - Term b; std::vector v; - RedVars(p, b, v); - // dualModel.show_internals(); - // std::cout << "b: " << b << std::endl; - expr bv = dualModel.eval(b); - // std::cout << "bv: " << bv << std::endl; - bool res = !eq(bv,ctx.bool_val(true)); - return res; - } - - RPFP::Term RPFP::EvalNode(Node *p) - { - Term b; std::vector v; - RedVars(p, b, v); - std::vector args; - for(unsigned i = 0; i < v.size(); i++) - args.push_back(dualModel.eval(v[i])); - return (p->Name)(args); - } - - void RPFP::EvalArrayTerm(const RPFP::Term &t, ArrayValue &res){ - if(t.is_app()){ - decl_kind k = t.decl().get_decl_kind(); - if(k == AsArray){ - func_decl fd = t.decl().get_func_decl_parameter(0); - func_interp r = dualModel.get_func_interp(fd); - int num = r.num_entries(); - res.defined = true; - for(int i = 0; i < num; i++){ - expr arg = r.get_arg(i,0); - expr value = r.get_value(i); - res.entries[arg] = value; - } - res.def_val = r.else_value(); - return; - } - else if(k == Store){ - EvalArrayTerm(t.arg(0),res); - if(!res.defined)return; - expr addr = t.arg(1); - expr val = t.arg(2); - if(addr.is_numeral() && val.is_numeral()){ - if(eq(val,res.def_val)) - res.entries.erase(addr); - else - res.entries[addr] = val; - } - else - res.defined = false; - return; - } - } - res.defined = false; - } - - int eae_count = 0; - - RPFP::Term RPFP::EvalArrayEquality(const RPFP::Term &f){ - ArrayValue lhs,rhs; - eae_count++; - EvalArrayTerm(f.arg(0),lhs); - EvalArrayTerm(f.arg(1),rhs); - if(lhs.defined && rhs.defined){ - if(eq(lhs.def_val,rhs.def_val)) - if(lhs.entries == rhs.entries) - return ctx.bool_val(true); - return ctx.bool_val(false); - } - return f; - } - - /** Compute truth values of all boolean subterms in current model. - Assumes formulas has been simplified by Z3, so only boolean ops - ands and, or, not. Returns result in memo. - */ - - int RPFP::SubtermTruth(hash_map &memo, const Term &f){ - if(memo.find(f) != memo.end()){ - return memo[f]; - } - int res; - if(f.is_app()){ - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if(k == Implies){ - res = SubtermTruth(memo,!f.arg(0) || f.arg(1)); - goto done; - } - if(k == And) { - res = 1; - for(int i = 0; i < nargs; i++){ - int ar = SubtermTruth(memo,f.arg(i)); - if(ar == 0){ - res = 0; - goto done; - } - if(ar == 2)res = 2; - } - goto done; - } - else if(k == Or) { - res = 0; - for(int i = 0; i < nargs; i++){ - int ar = SubtermTruth(memo,f.arg(i)); - if(ar == 1){ - res = 1; - goto done; - } - if(ar == 2)res = 2; - } - goto done; - } - else if(k == Not) { - int ar = SubtermTruth(memo,f.arg(0)); - res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); - goto done; - } - } - { - bool pos; std::vector names; - if(f.is_label(pos,names)){ - res = SubtermTruth(memo,f.arg(0)); - goto done; - } - } - { - expr bv = dualModel.eval(f); - if(bv.is_app() && bv.decl().get_decl_kind() == Equal && - bv.arg(0).is_array()){ - bv = EvalArrayEquality(bv); - } - // Hack!!!! array equalities can occur negatively! - if(bv.is_app() && bv.decl().get_decl_kind() == Not && - bv.arg(0).decl().get_decl_kind() == Equal && - bv.arg(0).arg(0).is_array()){ - bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); - } - if(eq(bv,ctx.bool_val(true))) - res = 1; - else if(eq(bv,ctx.bool_val(false))) - res = 0; - else - res = 2; - } - done: - memo[f] = res; - return res; - } - - int RPFP::EvalTruth(hash_map &memo, Edge *e, const Term &f){ - Term tl = Localize(e, f); - return SubtermTruth(memo,tl); - } - - /** Compute truth values of all boolean subterms in current model. - Assumes formulas has been simplified by Z3, so only boolean ops - ands and, or, not. Returns result in memo. - */ - -#if 0 - int RPFP::GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos){ - if(memo[labpos].find(f) != memo[labpos].end()){ - return memo[labpos][f]; - } - int res; - if(f.is_app()){ - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if(k == Implies){ - res = GetLabelsRec(memo,f.arg(1) || !f.arg(0), labels, labpos); - goto done; - } - if(k == And) { - res = 1; - for(int i = 0; i < nargs; i++){ - int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); - if(ar == 0){ - res = 0; - goto done; - } - if(ar == 2)res = 2; - } - goto done; - } - else if(k == Or) { - res = 0; - for(int i = 0; i < nargs; i++){ - int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); - if(ar == 1){ - res = 1; - goto done; - } - if(ar == 2)res = 2; - } - goto done; - } - else if(k == Not) { - int ar = GetLabelsRec(memo,f.arg(0), labels, !labpos); - res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); - goto done; - } - } - { - bool pos; std::vector names; - if(f.is_label(pos,names)){ - res = GetLabelsRec(memo,f.arg(0), labels, labpos); - if(pos == labpos && res == (pos ? 1 : 0)) - for(unsigned i = 0; i < names.size(); i++) - labels.push_back(names[i]); - goto done; - } - } - { - expr bv = dualModel.eval(f); - if(bv.is_app() && bv.decl().get_decl_kind() == Equal && - bv.arg(0).is_array()){ - bv = EvalArrayEquality(bv); - } - // Hack!!!! array equalities can occur negatively! - if(bv.is_app() && bv.decl().get_decl_kind() == Not && - bv.arg(0).decl().get_decl_kind() == Equal && - bv.arg(0).arg(0).is_array()){ - bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); - } - if(eq(bv,ctx.bool_val(true))) - res = 1; - else if(eq(bv,ctx.bool_val(false))) - res = 0; - else - res = 2; - } - done: - memo[labpos][f] = res; - return res; - } -#endif - - void RPFP::GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, - hash_set *done, bool truth){ - if(done[truth].find(f) != done[truth].end()) - return; /* already processed */ - if(f.is_app()){ - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if(k == Implies){ - GetLabelsRec(memo,f.arg(1) || !f.arg(0) ,labels,done,truth); - goto done; - } - if(k == Iff){ - int b = SubtermTruth(memo,f.arg(0)); - if(b == 2) - throw "disaster in GetLabelsRec"; - GetLabelsRec(memo,f.arg(1),labels,done,truth ? b : !b); - goto done; - } - if(truth ? k == And : k == Or) { - for(int i = 0; i < nargs; i++) - GetLabelsRec(memo,f.arg(i),labels,done,truth); - goto done; - } - if(truth ? k == Or : k == And) { - for(int i = 0; i < nargs; i++){ - Term a = f.arg(i); - timer_start("SubtermTruth"); - int b = SubtermTruth(memo,a); - timer_stop("SubtermTruth"); - if(truth ? (b == 1) : (b == 0)){ - GetLabelsRec(memo,a,labels,done,truth); - goto done; - } - } - /* Unreachable! */ - // throw "error in RPFP::GetLabelsRec"; - goto done; - } - else if(k == Not) { - GetLabelsRec(memo,f.arg(0),labels,done,!truth); - goto done; - } - else { - bool pos; std::vector names; - if(f.is_label(pos,names)){ - GetLabelsRec(memo,f.arg(0), labels, done, truth); - if(pos == truth) - for(unsigned i = 0; i < names.size(); i++) - labels.push_back(names[i]); - goto done; - } - } - } - done: - done[truth].insert(f); - } - - void RPFP::GetLabels(Edge *e, std::vector &labels){ - if(!e->map || e->map->labeled.null()) - return; - Term tl = Localize(e, e->map->labeled); - hash_map memo; - hash_set done[2]; - GetLabelsRec(memo,tl,labels,done,true); - } - -#ifdef Z3OPS - static Z3_subterm_truth *stt; -#endif - - int ir_count = 0; - - void RPFP::ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set *done, bool truth, hash_set &dont_cares){ - if(done[truth].find(f) != done[truth].end()) - return; /* already processed */ -#if 0 - int this_count = ir_count++; - if(this_count == 50092) - std::cout << "foo!\n"; -#endif - if(f.is_app()){ - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if(k == Implies){ - ImplicantRed(memo,f.arg(1) || !f.arg(0) ,lits,done,truth,dont_cares); - goto done; - } - if(k == Iff){ - int b = SubtermTruth(memo,f.arg(0)); - if(b == 2) - throw "disaster in ImplicantRed"; - ImplicantRed(memo,f.arg(1),lits,done,truth ? b : !b,dont_cares); - goto done; - } - if(truth ? k == And : k == Or) { - for(int i = 0; i < nargs; i++) - ImplicantRed(memo,f.arg(i),lits,done,truth,dont_cares); - goto done; - } - if(truth ? k == Or : k == And) { - for(int i = 0; i < nargs; i++){ - Term a = f.arg(i); -#if 0 - if(i == nargs - 1){ // last chance! - ImplicantRed(memo,a,lits,done,truth,dont_cares); - goto done; - } -#endif - timer_start("SubtermTruth"); -#ifdef Z3OPS - bool b = stt->eval(a); -#else - int b = SubtermTruth(memo,a); -#endif - timer_stop("SubtermTruth"); - if(truth ? (b == 1) : (b == 0)){ - ImplicantRed(memo,a,lits,done,truth,dont_cares); - goto done; - } - } - /* Unreachable! */ - // TODO: need to indicate this failure to caller - // std::cerr << "error in RPFP::ImplicantRed"; - goto done; - } - else if(k == Not) { - ImplicantRed(memo,f.arg(0),lits,done,!truth,dont_cares); - goto done; - } - } - { - if(dont_cares.find(f) == dont_cares.end()){ - expr rf = ResolveIte(memo,f,lits,done,dont_cares); - expr bv = truth ? rf : !rf; - lits.push_back(bv); - } - } - done: - done[truth].insert(f); - } - - void RPFP::ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares, bool extensional){ - if(done.find(f) != done.end()) - return; /* already processed */ - if(f.is_app()){ - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if(k == Implies || k == Iff || k == And || k == Or || k == Not){ - for(int i = 0; i < nargs; i++) - ImplicantFullRed(memo,f.arg(i),lits,done,dont_cares, extensional); - goto done; - } - } - { - if(dont_cares.find(f) == dont_cares.end()){ - int b = SubtermTruth(memo,f); - if(b != 0 && b != 1) goto done; - if(f.is_app() && f.decl().get_decl_kind() == Equal && f.arg(0).is_array()){ - if(b == 1 && !extensional){ - expr x = dualModel.eval(f.arg(0)); expr y = dualModel.eval(f.arg(1)); - if(!eq(x,y)) - b = 0; - } - if(b == 0) - goto done; - } - expr bv = (b==1) ? f : !f; - lits.push_back(bv); - } - } - done: - done.insert(f); - } - - RPFP::Term RPFP::ResolveIte(hash_map &memo, const Term &t, std::vector &lits, - hash_set *done, hash_set &dont_cares){ - if(resolve_ite_memo.find(t) != resolve_ite_memo.end()) - return resolve_ite_memo[t]; - Term res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if(f.get_decl_kind() == Ite){ - timer_start("SubtermTruth"); -#ifdef Z3OPS - bool sel = stt->eval(t.arg(0)); -#else - int xval = SubtermTruth(memo,t.arg(0)); - bool sel; - if(xval == 0)sel = false; - else if(xval == 1)sel = true; - else - throw "unresolved ite in model"; -#endif - timer_stop("SubtermTruth"); - ImplicantRed(memo,t.arg(0),lits,done,sel,dont_cares); - res = ResolveIte(memo,t.arg(sel?1:2),lits,done,dont_cares); - } - else { - for(int i = 0; i < nargs; i++) - args.push_back(ResolveIte(memo,t.arg(i),lits,done,dont_cares)); - res = f(args.size(),&args[0]); - } - } - else res = t; - resolve_ite_memo[t] = res; - return res; - } - - RPFP::Term RPFP::ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts){ - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(bar.second){ - if(t.is_app()){ - int nargs = t.num_args(); - std::vector args; - if(t.decl().get_decl_kind() == Equal){ - expr lhs = t.arg(0); - expr rhs = t.arg(1); - if(rhs.decl().get_decl_kind() == Ite){ - expr rhs_args[3]; - lhs = ElimIteRec(memo,lhs,cnsts); - for(int i = 0; i < 3; i++) - rhs_args[i] = ElimIteRec(memo,rhs.arg(i),cnsts); - res = (rhs_args[0] && (lhs == rhs_args[1])) || ((!rhs_args[0]) && (lhs == rhs_args[2])); - goto done; - } - } - if(t.decl().get_decl_kind() == Ite){ - func_decl sym = ctx.fresh_func_decl("@ite", t.get_sort()); - res = sym(); - cnsts.push_back(ElimIteRec(memo,ctx.make(Equal,res,t),cnsts)); - } - else { - for(int i = 0; i < nargs; i++) - args.push_back(ElimIteRec(memo,t.arg(i),cnsts)); - res = t.decl()(args.size(),&args[0]); - } - } - else if(t.is_quantifier()) - res = clone_quantifier(t,ElimIteRec(memo,t.body(),cnsts)); - else - res = t; - } - done: - return res; - } - - RPFP::Term RPFP::ElimIte(const Term &t){ - hash_map memo; - std::vector cnsts; - expr res = ElimIteRec(memo,t,cnsts); - if(!cnsts.empty()){ - cnsts.push_back(res); - res = ctx.make(And,cnsts); - } - return res; - } - - void RPFP::Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares){ - hash_set done[2]; - ImplicantRed(memo,f,lits,done,true, dont_cares); - } - - - /** Underapproximate a formula using current counterexample. */ - - RPFP::Term RPFP::UnderapproxFormula(const Term &f, hash_set &dont_cares){ - /* first compute truth values of subterms */ - hash_map memo; - #ifdef Z3OPS - stt = Z3_mk_subterm_truth(ctx,dualModel); - #endif - // SubtermTruth(memo,f); - /* now compute an implicant */ - std::vector lits; - Implicant(memo,f,lits, dont_cares); -#ifdef Z3OPS - delete stt; stt = 0; -#endif - /* return conjunction of literals */ - return conjoin(lits); - } - - RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, bool extensional){ - hash_set dont_cares; - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - /* first compute truth values of subterms */ - hash_map memo; - hash_set done; - std::vector lits; - ImplicantFullRed(memo,f,lits,done,dont_cares, extensional); - timer_stop("UnderapproxFormula"); - /* return conjunction of literals */ - return conjoin(lits); - } - - struct VariableProjector : Z3User { - - struct elim_cand { - Term var; - int sup; - Term val; - }; - - typedef expr Term; - - hash_set keep; - hash_map var_ord; - int num_vars; - std::vector elim_cands; - hash_map > sup_map; - hash_map elim_map; - std::vector ready_cands; - hash_map cand_map; - params simp_params; - - VariableProjector(Z3User &_user, std::vector &keep_vec) : - Z3User(_user), simp_params() - { - num_vars = 0; - for(unsigned i = 0; i < keep_vec.size(); i++){ - keep.insert(keep_vec[i]); - var_ord[keep_vec[i]] = num_vars++; - } - } - - int VarNum(const Term &v){ - if(var_ord.find(v) == var_ord.end()) - var_ord[v] = num_vars++; - return var_ord[v]; - } - - bool IsVar(const Term &t){ - return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; - } - - bool IsPropLit(const Term &t, Term &a){ - if(IsVar(t)){ - a = t; - return true; - } - else if(t.is_app() && t.decl().get_decl_kind() == Not) - return IsPropLit(t.arg(0),a); - return false; - } - - void CountOtherVarsRec(hash_map &memo, - const Term &t, - int id, - int &count){ - std::pair foo(t,0); - std::pair::iterator, bool> bar = memo.insert(foo); - // int &res = bar.first->second; - if(!bar.second) return; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ - if(cand_map.find(t) != cand_map.end()){ - count++; - sup_map[t].push_back(id); - } - } - for(int i = 0; i < nargs; i++) - CountOtherVarsRec(memo, t.arg(i), id, count); - } - else if (t.is_quantifier()) - CountOtherVarsRec(memo, t.body(), id, count); - } - - void NewElimCand(const Term &lhs, const Term &rhs){ - if(debug_gauss){ - std::cout << "mapping " << lhs << " to " << rhs << std::endl; - } - elim_cand cand; - cand.var = lhs; - cand.sup = 0; - cand.val = rhs; - elim_cands.push_back(cand); - cand_map[lhs] = elim_cands.size()-1; - } - - void MakeElimCand(const Term &lhs, const Term &rhs){ - if(eq(lhs,rhs)) - return; - if(!IsVar(lhs)){ - if(IsVar(rhs)){ - MakeElimCand(rhs,lhs); - return; - } - else{ - std::cout << "would have mapped a non-var\n"; - return; - } - } - if(IsVar(rhs) && VarNum(rhs) > VarNum(lhs)){ - MakeElimCand(rhs,lhs); - return; - } - if(keep.find(lhs) != keep.end()) - return; - if(cand_map.find(lhs) == cand_map.end()) - NewElimCand(lhs,rhs); - else { - int cand_idx = cand_map[lhs]; - if(IsVar(rhs) && cand_map.find(rhs) == cand_map.end() - && keep.find(rhs) == keep.end()) - NewElimCand(rhs,elim_cands[cand_idx].val); - elim_cands[cand_idx].val = rhs; - } - } - - Term FindRep(const Term &t){ - if(cand_map.find(t) == cand_map.end()) - return t; - Term &res = elim_cands[cand_map[t]].val; - if(IsVar(res)){ - assert(VarNum(res) < VarNum(t)); - res = FindRep(res); - return res; - } - return t; - } - - void GaussElimCheap(const std::vector &lits_in, - std::vector &lits_out){ - for(unsigned i = 0; i < lits_in.size(); i++){ - Term lit = lits_in[i]; - if(lit.is_app()){ - decl_kind k = lit.decl().get_decl_kind(); - if(k == Equal || k == Iff) - MakeElimCand(FindRep(lit.arg(0)),FindRep(lit.arg(1))); - } - } - - for(unsigned i = 0; i < elim_cands.size(); i++){ - elim_cand &cand = elim_cands[i]; - hash_map memo; - CountOtherVarsRec(memo,cand.val,i,cand.sup); - if(cand.sup == 0) - ready_cands.push_back(i); - } - - while(!ready_cands.empty()){ - elim_cand &cand = elim_cands[ready_cands.back()]; - ready_cands.pop_back(); - Term rep = FindRep(cand.var); - if(!eq(rep,cand.var)) - if(cand_map.find(rep) != cand_map.end()){ - int rep_pos = cand_map[rep]; - cand.val = elim_cands[rep_pos].val; - } - Term val = SubstRec(elim_map,cand.val); - if(debug_gauss){ - std::cout << "subbing " << cand.var << " --> " << val << std::endl; - } - elim_map[cand.var] = val; - std::vector &sup = sup_map[cand.var]; - for(unsigned i = 0; i < sup.size(); i++){ - int c = sup[i]; - if((--elim_cands[c].sup) == 0) - ready_cands.push_back(c); - } - } - - for(unsigned i = 0; i < lits_in.size(); i++){ - Term lit = lits_in[i]; - lit = SubstRec(elim_map,lit); - lit = lit.simplify(); - if(eq(lit,ctx.bool_val(true))) - continue; - Term a; - if(IsPropLit(lit,a)) - if(keep.find(lit) == keep.end()) - continue; - lits_out.push_back(lit); - } - } - - // maps variables to constrains in which the occur pos, neg - hash_map la_index[2]; - hash_map la_coeffs[2]; - std::vector la_pos_vars; - bool fixing; - - void IndexLAcoeff(const Term &coeff1, const Term &coeff2, Term t, int id){ - Term coeff = coeff1 * coeff2; - coeff = coeff.simplify(); - Term is_pos = (coeff >= ctx.int_val(0)); - is_pos = is_pos.simplify(); - if(eq(is_pos,ctx.bool_val(true))) - IndexLA(true,coeff,t, id); - else - IndexLA(false,coeff,t, id); - } - - void IndexLAremove(const Term &t){ - if(IsVar(t)){ - la_index[0][t] = -1; // means ineligible to be eliminated - la_index[1][t] = -1; // (more that one occurrence, or occurs not in linear comb) - } - else if(t.is_app()){ - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - IndexLAremove(t.arg(i)); - } - // TODO: quantifiers? - } - - - void IndexLA(bool pos, const Term &coeff, const Term &t, int id){ - if(t.is_numeral()) - return; - if(t.is_app()){ - int nargs = t.num_args(); - switch(t.decl().get_decl_kind()){ - case Plus: - for(int i = 0; i < nargs; i++) - IndexLA(pos,coeff,t.arg(i), id); - break; - case Sub: - IndexLA(pos,coeff,t.arg(0), id); - IndexLA(!pos,coeff,t.arg(1), id); - break; - case Times: - if(t.arg(0).is_numeral()) - IndexLAcoeff(coeff,t.arg(0),t.arg(1), id); - else if(t.arg(1).is_numeral()) - IndexLAcoeff(coeff,t.arg(1),t.arg(0), id); - break; - default: - if(IsVar(t) && (fixing || la_index[pos].find(t) == la_index[pos].end())){ - la_index[pos][t] = id; - la_coeffs[pos][t] = coeff; - if(pos && !fixing) - la_pos_vars.push_back(t); // this means we only add a var once - } - else - IndexLAremove(t); - } - } - } - - void IndexLAstart(bool pos, const Term &t, int id){ - IndexLA(pos,(pos ? ctx.int_val(1) : ctx.int_val(-1)), t, id); - } - - void IndexLApred(bool pos, const Term &p, int id){ - if(p.is_app()){ - switch(p.decl().get_decl_kind()){ - case Not: - IndexLApred(!pos, p.arg(0),id); - break; - case Leq: - case Lt: - IndexLAstart(!pos, p.arg(0), id); - IndexLAstart(pos, p.arg(1), id); - break; - case Geq: - case Gt: - IndexLAstart(pos,p.arg(0), id); - IndexLAstart(!pos,p.arg(1), id); - break; - default: - IndexLAremove(p); - } - } - } - - void IndexLAfix(const Term &p, int id){ - fixing = true; - IndexLApred(true,p,id); - fixing = false; - } - - bool IsCanonIneq(const Term &lit, Term &term, Term &bound){ - // std::cout << Z3_simplify_get_help(ctx) << std::endl; - bool pos = lit.decl().get_decl_kind() != Not; - Term atom = pos ? lit : lit.arg(0); - if(atom.decl().get_decl_kind() == Leq){ - if(pos){ - bound = atom.arg(0); - term = atom.arg(1).simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - else { - bound = (atom.arg(1) + ctx.int_val(1)); - term = atom.arg(0); - // std::cout << "simplifying bound: " << bound << std::endl; - bound = bound.simplify(); - term = term.simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - return true; - } - else if(atom.decl().get_decl_kind() == Geq){ - if(pos){ - bound = atom.arg(1); // integer axiom - term = atom.arg(0).simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - return true; - } - else{ - bound = -(atom.arg(1) - ctx.int_val(1)); // integer axiom - term = -atom.arg(0); - bound = bound.simplify(); - term = term.simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - return true; - } - return false; - } - - Term CanonIneqTerm(const Term &p){ - Term term,bound; - Term ps = p.simplify(); - bool ok = IsCanonIneq(ps,term,bound); - assert(ok); - return term - bound; - } - - void ElimRedundantBounds(std::vector &lits){ - hash_map best_bound; - for(unsigned i = 0; i < lits.size(); i++){ - lits[i] = lits[i].simplify(simp_params); - Term term,bound; - if(IsCanonIneq(lits[i],term,bound)){ - if(best_bound.find(term) == best_bound.end()) - best_bound[term] = i; - else { - int best = best_bound[term]; - Term bterm,bbound; - IsCanonIneq(lits[best],bterm,bbound); - Term comp = bound > bbound; - comp = comp.simplify(); - if(eq(comp,ctx.bool_val(true))){ - lits[best] = ctx.bool_val(true); - best_bound[term] = i; - } - else { - lits[i] = ctx.bool_val(true); - } - } - } - } - } - - void FourierMotzkinCheap(const std::vector &lits_in, - std::vector &lits_out){ - simp_params.set(":som",true); - simp_params.set(":sort-sums",true); - fixing = false; lits_out = lits_in; - ElimRedundantBounds(lits_out); - for(unsigned i = 0; i < lits_out.size(); i++) - IndexLApred(true,lits_out[i],i); - - for(unsigned i = 0; i < la_pos_vars.size(); i++){ - Term var = la_pos_vars[i]; - if(la_index[false].find(var) != la_index[false].end()){ - int pos_idx = la_index[true][var]; - int neg_idx = la_index[false][var]; - if(pos_idx >= 0 && neg_idx >= 0){ - if(keep.find(var) != keep.end()){ - std::cout << "would have eliminated keep var\n"; - continue; - } - Term tpos = CanonIneqTerm(lits_out[pos_idx]); - Term tneg = CanonIneqTerm(lits_out[neg_idx]); - Term pos_coeff = la_coeffs[true][var]; - Term neg_coeff = -la_coeffs[false][var]; - Term comb = neg_coeff * tpos + pos_coeff * tneg; - Term ineq = ctx.int_val(0) <= comb; - ineq = ineq.simplify(); - lits_out[pos_idx] = ineq; - lits_out[neg_idx] = ctx.bool_val(true); - IndexLAfix(ineq,pos_idx); - } - } - } - } - - Term ProjectFormula(const Term &f){ - std::vector lits, new_lits1, new_lits2; - CollectConjuncts(f,lits); - timer_start("GaussElimCheap"); - GaussElimCheap(lits,new_lits1); - timer_stop("GaussElimCheap"); - timer_start("FourierMotzkinCheap"); - FourierMotzkinCheap(new_lits1,new_lits2); - timer_stop("FourierMotzkinCheap"); - return conjoin(new_lits2); - } - }; - - void Z3User::CollectConjuncts(const Term &f, std::vector &lits, bool pos){ - if(f.is_app() && f.decl().get_decl_kind() == Not) - CollectConjuncts(f.arg(0), lits, !pos); - else if(pos && f.is_app() && f.decl().get_decl_kind() == And){ - int num_args = f.num_args(); - for(int i = 0; i < num_args; i++) - CollectConjuncts(f.arg(i),lits,true); - } - else if(!pos && f.is_app() && f.decl().get_decl_kind() == Or){ - int num_args = f.num_args(); - for(int i = 0; i < num_args; i++) - CollectConjuncts(f.arg(i),lits,false); - } - else if(pos){ - if(!eq(f,ctx.bool_val(true))) - lits.push_back(f); - } - else { - if(!eq(f,ctx.bool_val(false))) - lits.push_back(!f); - } - } - - struct TermLt { - bool operator()(const expr &x, const expr &y){ - unsigned xid = x.get_id(); - unsigned yid = y.get_id(); - return xid < yid; - } - }; - - void Z3User::SortTerms(std::vector &terms){ - TermLt foo; - std::sort(terms.begin(),terms.end(),foo); - } - - Z3User::Term Z3User::SortSum(const Term &t){ - if(!(t.is_app() && t.decl().get_decl_kind() == Plus)) - return t; - int nargs = t.num_args(); - if(nargs < 2) return t; - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = t.arg(i); - SortTerms(args); - if(nargs == 2) - return args[0] + args[1]; - return sum(args); - } - - - RPFP::Term RPFP::ProjectFormula(std::vector &keep_vec, const Term &f){ - VariableProjector vp(*this,keep_vec); - return vp.ProjectFormula(f); - } - - /** Compute an underapproximation of every node in a tree rooted at "root", - based on a previously computed counterexample. The underapproximation - may contain free variables that are implicitly existentially quantified. - */ - - RPFP::Term RPFP::ComputeUnderapprox(Node *root, int persist){ - /* if terminated underapprox is empty set (false) */ - bool show_model = false; - if(show_model) - std::cout << dualModel << std::endl; - if(!root->Outgoing){ - root->Underapprox.SetEmpty(); - return ctx.bool_val(true); - } - /* if not used in cex, underapprox is empty set (false) */ - if(Empty(root)){ - root->Underapprox.SetEmpty(); - return ctx.bool_val(true); - } - /* compute underapprox of children first */ - std::vector &chs = root->Outgoing->Children; - std::vector chu(chs.size()); - for(unsigned i = 0; i < chs.size(); i++) - chu[i] = ComputeUnderapprox(chs[i],persist); - - Term b; std::vector v; - RedVars(root, b, v); - /* underapproximate the edge formula */ - hash_set dont_cares; - dont_cares.insert(b); - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - Term dual = root->Outgoing->dual.null() ? ctx.bool_val(true) : root->Outgoing->dual; - Term eu = UnderapproxFormula(dual,dont_cares); - timer_stop("UnderapproxFormula"); - /* combine with children */ - chu.push_back(eu); - eu = conjoin(chu); - /* project onto appropriate variables */ - eu = ProjectFormula(v,eu); - eu = eu.simplify(); - -#if 0 - /* check the result is consistent */ - { - hash_map memo; - int res = SubtermTruth(memo, eu); - if(res != 1) - throw "inconsistent projection"; - } -#endif - - /* rename to appropriate variable names */ - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = root->Annotation.IndParams[i]; /* copy names from annotation */ - Term funder = SubstRec(memo, eu); - root->Underapprox = CreateRelation(root->Annotation.IndParams,funder); -#if 0 - if(persist) - Z3_persist_ast(ctx,root->Underapprox.Formula,persist); -#endif - return eu; - } - - void RPFP::FixCurrentState(Edge *edge){ - hash_set dont_cares; - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; - Term eu = UnderapproxFormula(dual,dont_cares); - timer_stop("UnderapproxFormula"); - ConstrainEdgeLocalized(edge,eu); - } - - void RPFP::GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under){ - if(memo[under].find(f) != memo[under].end()) - return; - memo[under].insert(f); - if(f.is_app()){ - if(!under && !f.has_quantifiers()) - return; - decl_kind k = f.decl().get_decl_kind(); - if(k == And || k == Or || k == Implies || k == Iff){ - int num_args = f.num_args(); - for(int i = 0; i < num_args; i++) - GetGroundLitsUnderQuants(memo,f.arg(i),res,under); - return; - } - } - else if (f.is_quantifier()){ -#if 0 - // treat closed quantified formula as a literal 'cause we hate nested quantifiers - if(under && IsClosedFormula(f)) - res.push_back(f); - else -#endif - GetGroundLitsUnderQuants(memo,f.body(),res,1); - return; - } - if(under && f.is_ground()) - res.push_back(f); - } - - RPFP::Term RPFP::StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits){ - hash_set memo[2]; - std::vector lits; - GetGroundLitsUnderQuants(memo, f, lits, 0); - hash_set lits_hash; - for(unsigned i = 0; i < lits.size(); i++) - lits_hash.insert(lits[i]); - hash_map subst; - hash_map stt_memo; - std::vector conjuncts; - for(unsigned i = 0; i < lits.size(); i++){ - const expr &lit = lits[i]; - if(lits_hash.find(NegateLit(lit)) == lits_hash.end()){ - case_lits.push_back(lit); - bool tval = false; - expr atom = lit; - if(lit.is_app() && lit.decl().get_decl_kind() == Not){ - tval = true; - atom = lit.arg(0); - } - expr etval = ctx.bool_val(tval); - if(atom.is_quantifier()) - subst[atom] = etval; // this is a bit desperate, since we can't eval quants - else { - int b = SubtermTruth(stt_memo,atom); - if(b == (tval ? 1 : 0)) - subst[atom] = etval; - else { - if(b == 0 || b == 1){ - etval = ctx.bool_val(b ? true : false); - subst[atom] = etval; - conjuncts.push_back(b ? atom : !atom); - } - } - } - } - } - expr g = f; - if(!subst.empty()){ - g = SubstRec(subst,f); - if(conjuncts.size()) - g = g && ctx.make(And,conjuncts); - g = g.simplify(); - } -#if 1 - expr g_old = g; - g = RemoveRedundancy(g); - bool changed = !eq(g,g_old); - g = g.simplify(); - if(changed) { // a second pass can get some more simplification - g = RemoveRedundancy(g); - g = g.simplify(); - } -#else - g = RemoveRedundancy(g); - g = g.simplify(); -#endif - g = AdjustQuantifiers(g); - return g; - } - - RPFP::Term RPFP::ModelValueAsConstraint(const Term &t){ - if(t.is_array()){ - ArrayValue arr; - Term e = dualModel.eval(t); - EvalArrayTerm(e, arr); - if(arr.defined){ - std::vector cs; - for(std::map::iterator it = arr.entries.begin(), en = arr.entries.end(); it != en; ++it){ - expr foo = select(t,expr(ctx,it->first)) == expr(ctx,it->second); - cs.push_back(foo); - } - return conjoin(cs); - } - } - else { - expr r = dualModel.get_const_interp(t.decl()); - if(!r.null()){ - expr res = t == expr(ctx,r); - return res; - } - } - return ctx.bool_val(true); - } - - void RPFP::EvalNodeAsConstraint(Node *p, Transformer &res) - { - Term b; std::vector v; - RedVars(p, b, v); - std::vector args; - for(unsigned i = 0; i < v.size(); i++){ - expr val = ModelValueAsConstraint(v[i]); - if(!eq(val,ctx.bool_val(true))) - args.push_back(val); - } - expr cnst = conjoin(args); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = p->Annotation.IndParams[i]; /* copy names from annotation */ - Term funder = SubstRec(memo, cnst); - res = CreateRelation(p->Annotation.IndParams,funder); - } - -#if 0 - void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ - // verify - s.push(); - expr conj = ctx.make(And,conjuncts); - s.add(conj); - check_result res = s.check(); - if(res != unsat) - throw "should be unsat"; - s.pop(1); - - for(unsigned i = 0; i < conjuncts.size(); ){ - std::swap(conjuncts[i],conjuncts.back()); - expr save = conjuncts.back(); - conjuncts.pop_back(); - s.push(); - expr conj = ctx.make(And,conjuncts); - s.add(conj); - check_result res = s.check(); - s.pop(1); - if(res != unsat){ - conjuncts.push_back(save); - std::swap(conjuncts[i],conjuncts.back()); - i++; - } - } - } -#endif - - void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ - std::vector lits(conjuncts.size()); - for(unsigned i = 0; i < lits.size(); i++){ - func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); - lits[i] = pred(); - s.add(ctx.make(Implies,lits[i],conjuncts[i])); - } - - // verify - check_result res = s.check(lits.size(),&lits[0]); - if(res != unsat){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - s.add(theory[i]); - for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! - if(s.check(lits.size(),&lits[0]) == unsat) - goto is_unsat; - throw "should be unsat"; - } - is_unsat: - for(unsigned i = 0; i < conjuncts.size(); ){ - std::swap(conjuncts[i],conjuncts.back()); - std::swap(lits[i],lits.back()); - check_result res = s.check(lits.size()-1,&lits[0]); - if(res != unsat){ - std::swap(conjuncts[i],conjuncts.back()); - std::swap(lits[i],lits.back()); - i++; - } - else { - conjuncts.pop_back(); - lits.pop_back(); - } - } - } - - void RPFP_caching::FilterCore(std::vector &core, std::vector &full_core){ - hash_set core_set; - std::copy(full_core.begin(),full_core.end(),std::inserter(core_set,core_set.begin())); - std::vector new_core; - for(unsigned i = 0; i < core.size(); i++) - if(core_set.find(core[i]) != core_set.end()) - new_core.push_back(core[i]); - core.swap(new_core); - } - - void RPFP_caching::GreedyReduceCache(std::vector &assumps, std::vector &core){ - std::vector lits = assumps, full_core; - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - - // verify - check_result res = CheckCore(lits,full_core); - if(res != unsat){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - GetAssumptionLits(theory[i],assumps); - lits = assumps; - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - - for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! - if((res = CheckCore(lits,full_core)) == unsat) - goto is_unsat; - throw "should be unsat"; - } - is_unsat: - FilterCore(core,full_core); - - std::vector dummy; - if(CheckCore(full_core,dummy) != unsat) - throw "should be unsat"; - - for(unsigned i = 0; i < core.size(); ){ - expr temp = core[i]; - std::swap(core[i],core.back()); - core.pop_back(); - lits.resize(assumps.size()); - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - res = CheckCore(lits,full_core); - if(res != unsat){ - core.push_back(temp); - std::swap(core[i],core.back()); - i++; - } - } - } - - expr RPFP::NegateLit(const expr &f){ - if(f.is_app() && f.decl().get_decl_kind() == Not) - return f.arg(0); - else - return !f; - } - - void RPFP::NegateLits(std::vector &lits){ - for(unsigned i = 0; i < lits.size(); i++){ - expr &f = lits[i]; - if(f.is_app() && f.decl().get_decl_kind() == Not) - f = f.arg(0); - else - f = !f; - } - } - - expr RPFP::SimplifyOr(std::vector &lits){ - if(lits.size() == 0) - return ctx.bool_val(false); - if(lits.size() == 1) - return lits[0]; - return ctx.make(Or,lits); - } - - expr RPFP::SimplifyAnd(std::vector &lits){ - if(lits.size() == 0) - return ctx.bool_val(true); - if(lits.size() == 1) - return lits[0]; - return ctx.make(And,lits); - } - - - /* This is a wrapper for a solver that is intended to compute - implicants from models. It works around a problem in Z3 with - models in the non-extensional array theory. It does this by - naming all of the store terms in a formula. That is, (store ...) - is replaced by "name" with an added constraint name = (store - ...). This allows us to determine from the model whether an array - equality is true or false (it is false if the two sides are - mapped to different function symbols, even if they have the same - contents). - */ - - struct implicant_solver { - RPFP *owner; - solver &aux_solver; - std::vector assumps, namings; - std::vector assump_stack, naming_stack; - hash_map renaming, renaming_memo; - - void add(const expr &e){ - expr t = e; - if(!aux_solver.extensional_array_theory()){ - unsigned i = namings.size(); - t = owner->ExtractStores(renaming_memo,t,namings,renaming); - for(; i < namings.size(); i++) - aux_solver.add(namings[i]); - } - assumps.push_back(t); - aux_solver.add(t); - } - - void push() { - assump_stack.push_back(assumps.size()); - naming_stack.push_back(namings.size()); - aux_solver.push(); - } - - // When we pop the solver, we have to re-add any namings that were lost - - void pop(int n) { - aux_solver.pop(n); - int new_assumps = assump_stack[assump_stack.size()-n]; - int new_namings = naming_stack[naming_stack.size()-n]; - for(unsigned i = new_namings; i < namings.size(); i++) - aux_solver.add(namings[i]); - assumps.resize(new_assumps); - namings.resize(new_namings); - assump_stack.resize(assump_stack.size()-1); - naming_stack.resize(naming_stack.size()-1); - } - - check_result check() { - return aux_solver.check(); - } - - model get_model() { - return aux_solver.get_model(); - } - - expr get_implicant() { - owner->dualModel = aux_solver.get_model(); - expr dual = owner->ctx.make(And,assumps); - bool ext = aux_solver.extensional_array_theory(); - expr eu = owner->UnderapproxFullFormula(dual,ext); - // if we renamed store terms, we have to undo - if(!ext) - eu = owner->SubstRec(renaming,eu); - return eu; - } - - implicant_solver(RPFP *_owner, solver &_aux_solver) - : owner(_owner), aux_solver(_aux_solver) - {} - }; - - // set up edge constraint in aux solver - void RPFP::AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge){ - if(!edge->dual.null()) - aux_solver.add(edge->dual); - for(unsigned i = 0; i < edge->constraints.size(); i++){ - expr tl = edge->constraints[i]; - aux_solver.add(tl); - } - } - - void RPFP::AddEdgeToSolver(Edge *edge){ - if(!edge->dual.null()) - aux_solver.add(edge->dual); - for(unsigned i = 0; i < edge->constraints.size(); i++){ - expr tl = edge->constraints[i]; - aux_solver.add(tl); - } - } - - static int by_case_counter = 0; - - void RPFP::InterpolateByCases(Node *root, Node *node){ - timer_start("InterpolateByCases"); - bool axioms_added = false; - hash_set axioms_needed; - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - axioms_needed.insert(theory[i]); - implicant_solver is(this,aux_solver); - is.push(); - AddEdgeToSolver(is,node->Outgoing); - node->Annotation.SetEmpty(); - hash_set *core = new hash_set; - core->insert(node->Outgoing->dual); - while(1){ - by_case_counter++; - is.push(); - expr annot = !GetAnnotation(node); - is.add(annot); - if(is.check() == unsat){ - is.pop(1); - break; - } - is.pop(1); - Push(); - ConstrainEdgeLocalized(node->Outgoing,is.get_implicant()); - ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this? - check_result foo = Check(root); - if(foo != unsat){ - slvr().print("should_be_unsat.smt2"); - throw "should be unsat"; - } - std::vector assumps, axioms_to_add; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++){ - (*core).insert(assumps[i]); - if(axioms_needed.find(assumps[i]) != axioms_needed.end()){ - axioms_to_add.push_back(assumps[i]); - axioms_needed.erase(assumps[i]); - } - } - // AddToProofCore(*core); - Transformer old_annot = node->Annotation; - SolveSingleNode(root,node); - - { - expr itp = GetAnnotation(node); - dualModel = is.get_model(); // TODO: what does this mean? - std::vector case_lits; - itp = StrengthenFormulaByCaseSplitting(itp, case_lits); - SetAnnotation(node,itp); - node->Annotation.Formula = node->Annotation.Formula.simplify(); - } - - for(unsigned i = 0; i < axioms_to_add.size(); i++) - is.add(axioms_to_add[i]); - -#define TEST_BAD -#ifdef TEST_BAD - { - static int bad_count = 0, num_bads = 1; - if(bad_count >= num_bads){ - bad_count = 0; - num_bads = num_bads * 2; - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw Bad(); - } - bad_count++; - } -#endif - - if(node->Annotation.IsEmpty()){ - if(!axioms_added){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - is.add(theory[i]); - axioms_added = true; - } - else { -#ifdef KILL_ON_BAD_INTERPOLANT - std::cout << "bad in InterpolateByCase -- core:\n"; -#if 0 - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++) - assumps[i].show(); -#endif - std::cout << "checking for inconsistency\n"; - std::cout << "model:\n"; - is.get_model().show(); - expr impl = is.get_implicant(); - std::vector conjuncts; - CollectConjuncts(impl,conjuncts,true); - std::cout << "impl:\n"; - for(unsigned i = 0; i < conjuncts.size(); i++) - conjuncts[i].show(); - std::cout << "annot:\n"; - annot.show(); - is.add(annot); - for(unsigned i = 0; i < conjuncts.size(); i++) - is.add(conjuncts[i]); - if(is.check() == unsat){ - std::cout << "inconsistent!\n"; - std::vector is_assumps; - is.aux_solver.get_proof().get_assumptions(is_assumps); - std::cout << "core:\n"; - for(unsigned i = 0; i < is_assumps.size(); i++) - is_assumps[i].show(); - } - else { - std::cout << "consistent!\n"; - is.aux_solver.print("should_be_inconsistent.smt2"); - } - std::cout << "by_case_counter = " << by_case_counter << "\n"; - throw "ack!"; -#endif - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw Bad(); - } - } - Pop(1); - node->Annotation.UnionWith(old_annot); - } - if(proof_core) - delete proof_core; // shouldn't happen - proof_core = core; - is.pop(1); - timer_stop("InterpolateByCases"); - } - - void RPFP::Generalize(Node *root, Node *node){ - timer_start("Generalize"); - aux_solver.push(); - AddEdgeToSolver(node->Outgoing); - expr fmla = GetAnnotation(node); - std::vector conjuncts; - CollectConjuncts(fmla,conjuncts,false); - GreedyReduce(aux_solver,conjuncts); // try to remove conjuncts one at a tme - aux_solver.pop(1); - NegateLits(conjuncts); - SetAnnotation(node,SimplifyOr(conjuncts)); - timer_stop("Generalize"); - } - - RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models, bool axioms){ - edge_solver &es = edge_solvers[edge]; - uptr &p = es.slvr; - if(!p.get()){ - scoped_no_proof no_proofs_please(ctx.m()); // no proofs - p.set(new solver(ctx,true,models)); // no models - if(axioms){ - RPFP::LogicSolver *ls = edge->owner->ls; - const std::vector &axs = ls->get_axioms(); - for(unsigned i = 0; i < axs.size(); i++) - p.get()->add(axs[i]); - } - } - return es; - } - - - // caching version of above - void RPFP_caching::GeneralizeCache(Edge *edge){ - timer_start("Generalize"); - scoped_solver_for_edge ssfe(this,edge); - Node *node = edge->Parent; - std::vector assumps, core, conjuncts; - AssertEdgeCache(edge,assumps); - for(unsigned i = 0; i < edge->Children.size(); i++){ - expr ass = GetAnnotation(edge->Children[i]); - std::vector clauses; - if(!ass.is_true()){ - CollectConjuncts(ass.arg(1),clauses); - for(unsigned j = 0; j < clauses.size(); j++) - GetAssumptionLits(ass.arg(0) || clauses[j],assumps); - } - } - expr fmla = GetAnnotation(node); - std::vector lits; - if(fmla.is_true()){ - timer_stop("Generalize"); - return; - } - assumps.push_back(fmla.arg(0).arg(0)); - CollectConjuncts(!fmla.arg(1),lits); -#if 0 - for(unsigned i = 0; i < lits.size(); i++){ - const expr &lit = lits[i]; - if(lit.is_app() && lit.decl().get_decl_kind() == Equal){ - lits[i] = ctx.make(Leq,lit.arg(0),lit.arg(1)); - lits.push_back(ctx.make(Leq,lit.arg(1),lit.arg(0))); - } - } -#endif - hash_map lit_map; - for(unsigned i = 0; i < lits.size(); i++) - GetAssumptionLits(lits[i],core,&lit_map); - GreedyReduceCache(assumps,core); - for(unsigned i = 0; i < core.size(); i++) - conjuncts.push_back(lit_map[core[i]]); - NegateLits(conjuncts); - SetAnnotation(node,SimplifyOr(conjuncts)); - timer_stop("Generalize"); - } - - // caching version of above - bool RPFP_caching::PropagateCache(Edge *edge){ - timer_start("PropagateCache"); - scoped_solver_for_edge ssfe(this,edge); - bool some = false; - { - std::vector candidates, skip; - Node *node = edge->Parent; - CollectConjuncts(node->Annotation.Formula,skip); - for(unsigned i = 0; i < edge->Children.size(); i++){ - Node *child = edge->Children[i]; - if(child->map == node->map){ - CollectConjuncts(child->Annotation.Formula,candidates); - break; - } - } - if(candidates.empty()) goto done; - hash_set skip_set; - std::copy(skip.begin(),skip.end(),std::inserter(skip_set,skip_set.begin())); - std::vector new_candidates; - for(unsigned i = 0; i < candidates.size(); i++) - if(skip_set.find(candidates[i]) == skip_set.end()) - new_candidates.push_back(candidates[i]); - candidates.swap(new_candidates); - if(candidates.empty()) goto done; - std::vector assumps, core, conjuncts; - AssertEdgeCache(edge,assumps); - for(unsigned i = 0; i < edge->Children.size(); i++){ - expr ass = GetAnnotation(edge->Children[i]); - if(eq(ass,ctx.bool_val(true))) - continue; - std::vector clauses; - CollectConjuncts(ass.arg(1),clauses); - for(unsigned j = 0; j < clauses.size(); j++) - GetAssumptionLits(ass.arg(0) || clauses[j],assumps); - } - for(unsigned i = 0; i < candidates.size(); i++){ - unsigned old_size = assumps.size(); - node->Annotation.Formula = candidates[i]; - expr fmla = GetAnnotation(node); - assumps.push_back(fmla.arg(0).arg(0)); - GetAssumptionLits(!fmla.arg(1),assumps); - std::vector full_core; - check_result res = CheckCore(assumps,full_core); - if(res == unsat) - conjuncts.push_back(candidates[i]); - assumps.resize(old_size); - } - if(conjuncts.empty()) - goto done; - SetAnnotation(node,SimplifyAnd(conjuncts)); - some = true; - } - done: - timer_stop("PropagateCache"); - return some; - } - - - /** Push a scope. Assertions made after Push can be undone by Pop. */ - - void RPFP::Push() - { - stack.push_back(stack_entry()); - slvr_push(); - } - - /** Pop a scope (see Push). Note, you cannot pop axioms. */ - - void RPFP::Pop(int num_scopes) - { - slvr_pop(num_scopes); - for (int i = 0; i < num_scopes; i++) - { - stack_entry &back = stack.back(); - for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) - (*it)->dual = expr(ctx,NULL); - for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) - (*it)->dual = expr(ctx,NULL); - for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) - (*it).first->constraints.pop_back(); - stack.pop_back(); - } - } - - /** Erase the proof by performing a Pop, Push and re-assertion of - all the popped constraints */ - - void RPFP::PopPush(){ - slvr_pop(1); - slvr_push(); - stack_entry &back = stack.back(); - for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) - slvr_add((*it)->dual); - for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) - slvr_add((*it)->dual); - for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) - slvr_add((*it).second); - } - - - - // This returns a new FuncDel with same sort as top-level function - // of term t, but with numeric suffix appended to name. - - Z3User::FuncDecl Z3User::SuffixFuncDecl(Term t, int n) - { - std::string name = t.decl().name().str() + "_" + string_of_int(n); - std::vector sorts; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - sorts.push_back(t.arg(i).get_sort()); - return ctx.function(name.c_str(), nargs, &sorts[0], t.get_sort()); - } - - Z3User::FuncDecl Z3User::RenumberPred(const FuncDecl &f, int n) - { - std::string name = f.name().str(); - name = name.substr(0,name.rfind('_')) + "_" + string_of_int(n); - int arity = f.arity(); - std::vector domain; - for(int i = 0; i < arity; i++) - domain.push_back(f.domain(i)); - return ctx.function(name.c_str(), arity, &domain[0], f.range()); - } - - // Scan the clause body for occurrences of the predicate unknowns - - RPFP::Term RPFP::ScanBody(hash_map &memo, - const Term &t, - hash_map &pmap, - std::vector &parms, - std::vector &nodes) - { - if(memo.find(t) != memo.end()) - return memo[t]; - Term res(ctx); - if (t.is_app()) { - func_decl f = t.decl(); - if(pmap.find(f) != pmap.end()){ - nodes.push_back(pmap[f]); - f = SuffixFuncDecl(t,parms.size()); - parms.push_back(f); - } - int nargs = t.num_args(); - std::vector args; - for(int i = 0; i < nargs; i++) - args.push_back(ScanBody(memo,t.arg(i),pmap,parms,nodes)); - res = f(nargs,&args[0]); - } - else if (t.is_quantifier()) - res = CloneQuantifier(t,ScanBody(memo,t.body(),pmap,parms,nodes)); - else - res = t; - memo[t] = res; - return res; - } - - // return the func_del of an app if it is uninterpreted - - bool Z3User::get_relation(const Term &t, func_decl &R){ - if(!t.is_app()) - return false; - R = t.decl(); - return R.get_decl_kind() == Uninterpreted; - } - - // return true if term is an individual variable - // TODO: have to check that it is not a background symbol - - bool Z3User::is_variable(const Term &t){ - if(!t.is_app()) - return t.is_var(); - return t.decl().get_decl_kind() == Uninterpreted - && t.num_args() == 0; - } - - RPFP::Term RPFP::RemoveLabelsRec(hash_map &memo, const Term &t, - std::vector &lbls){ - if(memo.find(t) != memo.end()) - return memo[t]; - Term res(ctx); - if (t.is_app()){ - func_decl f = t.decl(); - std::vector names; - bool pos; - if(t.is_label(pos,names)){ - res = RemoveLabelsRec(memo,t.arg(0),lbls); - for(unsigned i = 0; i < names.size(); i++) - lbls.push_back(label_struct(names[i],res,pos)); - } - else { - int nargs = t.num_args(); - std::vector args; - for(int i = 0; i < nargs; i++) - args.push_back(RemoveLabelsRec(memo,t.arg(i),lbls)); - res = f(nargs,&args[0]); - } - } - else if (t.is_quantifier()) - res = CloneQuantifier(t,RemoveLabelsRec(memo,t.body(),lbls)); - else - res = t; - memo[t] = res; - return res; - } - - RPFP::Term RPFP::RemoveLabels(const Term &t, std::vector &lbls){ - hash_map memo ; - return RemoveLabelsRec(memo,t,lbls); - } - - RPFP::Term RPFP::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo[level].insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - 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))); - res = f(args.size(),&args[0]); - } - else if (t.is_quantifier()){ - int bound = t.get_quantifier_num_bound(); - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstBoundRec(memo, subst, level + bound, pats[i]); - res = clone_quantifier(t, SubstBoundRec(memo, subst, level + bound, t.body()), pats); - } - else if (t.is_var()) { - int idx = t.get_index_value(); - if(idx >= level && subst.find(idx-level) != subst.end()){ - res = subst[idx-level]; - } - else res = t; - } - else res = t; - return res; - } - - RPFP::Term RPFP::SubstBound(hash_map &subst, const Term &t){ - hash_map > memo; - return SubstBoundRec(memo, subst, 0, t); - } - - int Z3User::MaxIndex(hash_map &memo, const Term &t) - { - std::pair foo(t,-1); - std::pair::iterator, bool> bar = memo.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()){ - func_decl f = t.decl(); - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - int m = MaxIndex(memo, t.arg(i)); - if(m > res) - res = m; - } - } - else if (t.is_quantifier()){ - int bound = t.get_quantifier_num_bound(); - res = MaxIndex(memo,t.body()) - bound; - } - else if (t.is_var()) { - res = t.get_index_value(); - } - return res; - } - - bool Z3User::IsClosedFormula(const Term &t){ - hash_map memo; - return MaxIndex(memo,t) < 0; - } - - - /** Convert a collection of clauses to Nodes and Edges in the RPFP. - - Predicate unknowns are uninterpreted predicates not - occurring in the background theory. - - Clauses are of the form - - B => P(t_1,...,t_k) - - where P is a predicate unknown and predicate unknowns - occur only positivey in H and only under existential - quantifiers in prenex form. - - Each predicate unknown maps to a node. Each clause maps to - an edge. Let C be a clause B => P(t_1,...,t_k) where the - sequence of predicate unknowns occurring in B (in order - of occurrence) is P_1..P_n. The clause maps to a transformer - T where: - - T.Relparams = P_1..P_n - T.Indparams = x_1...x+k - T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k - - Throws exception bad_clause(msg,i) if a clause i is - in the wrong form. - - */ - - - static bool canonical_clause(const expr &clause){ - if(clause.decl().get_decl_kind() != Implies) - return false; - expr arg = clause.arg(1); - return arg.is_app() && (arg.decl().get_decl_kind() == False || - arg.decl().get_decl_kind() == Uninterpreted); - } - -#define USE_QE_LITE - - void RPFP::FromClauses(const std::vector &unskolemized_clauses){ - hash_map pmap; - func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); - - std::vector clauses(unskolemized_clauses); - // first, skolemize the clauses - -#ifndef USE_QE_LITE - for(unsigned i = 0; i < clauses.size(); i++){ - expr &t = clauses[i]; - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - hash_map subst; - for(int j = 0; j < bound; j++){ - 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)); - subst[bound-1-j] = skolem; - } - t = SubstBound(subst,t.body()); - } - } -#else - std::vector > substs(clauses.size()); -#endif - - // create the nodes from the heads of the clauses - - for(unsigned i = 0; i < clauses.size(); i++){ - Term &clause = clauses[i]; - -#ifdef USE_QE_LITE - Term &t = clause; - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - for(int j = 0; j < bound; j++){ - 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)); - substs[i][bound-1-j] = skolem; - } - clause = t.body(); - } - -#endif - - if(clause.is_app() && clause.decl().get_decl_kind() == Uninterpreted) - clause = implies(ctx.bool_val(true),clause); - if(!canonical_clause(clause)) - clause = implies((!clause).simplify(),ctx.bool_val(false)); - Term head = clause.arg(1); - func_decl R(ctx); - bool is_query = false; - if (eq(head,ctx.bool_val(false))){ - R = fail_pred; - // R = ctx.constant("@Fail", ctx.bool_sort()).decl(); - is_query = true; - } - else if(!get_relation(head,R)) - throw bad_clause("rhs must be a predicate application",i); - if(pmap.find(R) == pmap.end()){ - - // If the node doesn't exitst, create it. The Indparams - // are arbitrary, but we use the rhs arguments if they - // are variables for mnomonic value. - - hash_set seen; - std::vector Indparams; - for(unsigned j = 0; j < head.num_args(); j++){ - Term arg = head.arg(j); - if(!is_variable(arg) || seen.find(arg) != seen.end()){ - std::string name = std::string("@a_") + string_of_int(j); - arg = ctx.constant(name.c_str(),arg.get_sort()); - } - seen.insert(arg); - Indparams.push_back(arg); - } -#ifdef USE_QE_LITE - { - hash_map > sb_memo; - for(unsigned j = 0; j < Indparams.size(); j++) - Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); - } -#endif - Node *node = CreateNode(R(Indparams.size(),&Indparams[0])); - //nodes.push_back(node); - pmap[R] = node; - if (is_query) - node->Bound = CreateRelation(std::vector(), ctx.bool_val(false)); - } - } - - bool some_labels = false; - - // create the edges - - for(unsigned i = 0; i < clauses.size(); i++){ - Term clause = clauses[i]; - Term body = clause.arg(0); - Term head = clause.arg(1); - func_decl R(ctx); - if (eq(head,ctx.bool_val(false))) - R = fail_pred; - //R = ctx.constant("@Fail", ctx.bool_sort()).decl(); - else get_relation(head,R); - Node *Parent = pmap[R]; - std::vector Indparams; - hash_set seen; - for(unsigned j = 0; j < head.num_args(); j++){ - Term arg = head.arg(j); - if(!is_variable(arg) || seen.find(arg) != seen.end()){ - std::string name = std::string("@a_") + string_of_int(j); - Term var = ctx.constant(name.c_str(),arg.get_sort()); - body = body && (arg == var); - arg = var; - } - seen.insert(arg); - Indparams.push_back(arg); - } - - // We extract the relparams positionally - - std::vector Relparams; - hash_map scan_memo; - std::vector Children; - body = ScanBody(scan_memo,body,pmap,Relparams,Children); - Term labeled = body; - std::vector lbls; // TODO: throw this away for now - body = RemoveLabels(body,lbls); - if(!eq(labeled,body)) - some_labels = true; // remember if there are labels, as we then can't do qe_lite - // body = IneqToEq(body); // UFO converts x=y to (x<=y & x >= y). Undo this. - body = body.simplify(); - -#ifdef USE_QE_LITE - std::set idxs; - if(!some_labels){ // can't do qe_lite if we have to reconstruct labels - for(unsigned j = 0; j < Indparams.size(); j++) - if(Indparams[j].is_var()) - idxs.insert(Indparams[j].get_index_value()); - body = body.qe_lite(idxs,false); - } - hash_map > sb_memo; - body = SubstBoundRec(sb_memo,substs[i],0,body); - if(some_labels) - labeled = SubstBoundRec(sb_memo,substs[i],0,labeled); - for(unsigned j = 0; j < Indparams.size(); j++) - Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); -#endif - - // Create the edge - Transformer T = CreateTransformer(Relparams,Indparams,body); - Edge *edge = CreateEdge(Parent,T,Children); - edge->labeled = labeled;; // remember for label extraction - // edges.push_back(edge); - } - - // undo hoisting of expressions out of loops - RemoveDeadNodes(); - Unhoist(); - // FuseEdges(); - } - - - // The following mess is used to undo hoisting of expressions outside loops by compilers - - expr RPFP::UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params){ - if(memo.find(w) != memo.end()) - return memo[w]; - expr res; - if(init_defs.find(w) != init_defs.end()){ - expr d = init_defs[w]; - std::vector vars; - hash_set get_vars_memo; - GetVarsRec(get_vars_memo,d,vars); - hash_map map; - for(unsigned j = 0; j < vars.size(); j++){ - expr x = vars[j]; - map[x] = UnhoistPullRec(memo,x,init_defs,const_params,const_params_inv,new_params); - } - expr defn = SubstRec(map,d); - res = defn; - } - else if(const_params_inv.find(w) == const_params_inv.end()){ - std::string old_name = w.decl().name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); - expr y = fresh(); - const_params[y] = w; - const_params_inv[w] = y; - new_params.push_back(y); - res = y; - } - else - res = const_params_inv[w]; - memo[w] = res; - return res; - } - - void RPFP::AddParamsToTransformer(Transformer &trans, const std::vector ¶ms){ - std::copy(params.begin(),params.end(),std::inserter(trans.IndParams,trans.IndParams.end())); - } - - expr RPFP::AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms){ - int n = app.num_args(); - std::vector args(n); - for (int i = 0; i < n; i++) - args[i] = app.arg(i); - std::copy(params.begin(),params.end(),std::inserter(args,args.end())); - return new_decl(args); - } - - expr RPFP::GetRelRec(hash_set &memo, const expr &t, const func_decl &rel){ - if(memo.find(t) != memo.end()) - return expr(); - memo.insert(t); - if (t.is_app()) - { - func_decl f = t.decl(); - if(f == rel) - return t; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - expr res = GetRelRec(memo,t.arg(i),rel); - if(!res.null()) - return res; - } - } - else if (t.is_quantifier()) - return GetRelRec(memo,t.body(),rel); - return expr(); - } - - expr RPFP::GetRel(Edge *edge, int child_idx){ - func_decl &rel = edge->F.RelParams[child_idx]; - hash_set memo; - return GetRelRec(memo,edge->F.Formula,rel); - } - - void RPFP::GetDefsRec(const expr &cnst, hash_map &defs){ - if(cnst.is_app()){ - switch(cnst.decl().get_decl_kind()){ - case And: { - int n = cnst.num_args(); - for(int i = 0; i < n; i++) - GetDefsRec(cnst.arg(i),defs); - break; - } - case Equal: { - expr lhs = cnst.arg(0); - expr rhs = cnst.arg(1); - if(IsVar(lhs)) - defs[lhs] = rhs; - break; - } - default: - break; - } - } - } - - void RPFP::GetDefs(const expr &cnst, hash_map &defs){ - // GetDefsRec(IneqToEq(cnst),defs); - GetDefsRec(cnst,defs); - } - - bool RPFP::IsVar(const expr &t){ - return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; - } - - void RPFP::GetVarsRec(hash_set &memo, const expr &t, std::vector &vars){ - if(memo.find(t) != memo.end()) - return; - memo.insert(t); - if (t.is_app()) - { - if(IsVar(t)){ - vars.push_back(t); - return; - } - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - GetVarsRec(memo,t.arg(i),vars); - } - } - else if (t.is_quantifier()) - GetVarsRec(memo,t.body(),vars); - } - - void RPFP::AddParamsToNode(Node *node, const std::vector ¶ms){ - int arity = node->Annotation.IndParams.size(); - std::vector domain; - for(int i = 0; i < arity; i++) - domain.push_back(node->Annotation.IndParams[i].get_sort()); - for(unsigned i = 0; i < params.size(); i++) - domain.push_back(params[i].get_sort()); - std::string old_name = node->Name.name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), domain, ctx.bool_sort()); - node->Name = fresh; - AddParamsToTransformer(node->Annotation,params); - AddParamsToTransformer(node->Bound,params); - AddParamsToTransformer(node->Underapprox,params); - } - - void RPFP::UnhoistLoop(Edge *loop_edge, Edge *init_edge){ - loop_edge->F.Formula = IneqToEq(loop_edge->F.Formula); - init_edge->F.Formula = IneqToEq(init_edge->F.Formula); - expr pre = GetRel(loop_edge,0); - if(pre.null()) - return; // this means the loop got simplified away - int nparams = loop_edge->F.IndParams.size(); - hash_map const_params, const_params_inv; - std::vector work_list; - // find the parameters that are constant in the loop - for(int i = 0; i < nparams; i++){ - if(eq(pre.arg(i),loop_edge->F.IndParams[i])){ - const_params[pre.arg(i)] = init_edge->F.IndParams[i]; - const_params_inv[init_edge->F.IndParams[i]] = pre.arg(i); - work_list.push_back(pre.arg(i)); - } - } - // get the definitions in the initialization - hash_map defs,memo,subst; - GetDefs(init_edge->F.Formula,defs); - // try to pull them inside the loop - std::vector new_params; - for(unsigned i = 0; i < work_list.size(); i++){ - expr v = work_list[i]; - expr w = const_params[v]; - expr def = UnhoistPullRec(memo,w,defs,const_params,const_params_inv,new_params); - if(!eq(def,v)) - subst[v] = def; - } - // do the substitutions - if(subst.empty()) - return; - subst[pre] = pre; // don't substitute inside the precondition itself - loop_edge->F.Formula = SubstRec(subst,loop_edge->F.Formula); - loop_edge->F.Formula = ElimIte(loop_edge->F.Formula); - init_edge->F.Formula = ElimIte(init_edge->F.Formula); - // add the new parameters - if(new_params.empty()) - return; - Node *parent = loop_edge->Parent; - AddParamsToNode(parent,new_params); - AddParamsToTransformer(loop_edge->F,new_params); - AddParamsToTransformer(init_edge->F,new_params); - std::vector &incoming = parent->Incoming; - for(unsigned i = 0; i < incoming.size(); i++){ - Edge *in_edge = incoming[i]; - std::vector &chs = in_edge->Children; - for(unsigned j = 0; j < chs.size(); j++) - if(chs[j] == parent){ - expr lit = GetRel(in_edge,j); - expr new_lit = AddParamsToApp(lit,parent->Name,new_params); - func_decl fd = SuffixFuncDecl(new_lit,j); - int nargs = new_lit.num_args(); - std::vector args; - for(int k = 0; k < nargs; k++) - args.push_back(new_lit.arg(k)); - new_lit = fd(nargs,&args[0]); - in_edge->F.RelParams[j] = fd; - hash_map map; - map[lit] = new_lit; - in_edge->F.Formula = SubstRec(map,in_edge->F.Formula); - } - } - } - - void RPFP::Unhoist(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++) - outgoing[edges[i]->Parent].push_back(edges[i]); - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - std::vector &outs = outgoing[node]; - // if we're not a simple loop with one entry, give up - if(outs.size() == 2){ - for(int j = 0; j < 2; j++){ - Edge *loop_edge = outs[j]; - Edge *init_edge = outs[1-j]; - if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent){ - UnhoistLoop(loop_edge,init_edge); - break; - } - } - } - } - } - - void RPFP::FuseEdges(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++){ - outgoing[edges[i]->Parent].push_back(edges[i]); - } - hash_set edges_to_delete; - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - std::vector &outs = outgoing[node]; - if(outs.size() > 1 && outs.size() <= 16){ - std::vector trs(outs.size()); - for(unsigned j = 0; j < outs.size(); j++) - trs[j] = &outs[j]->F; - Transformer tr = Fuse(trs); - std::vector chs; - for(unsigned j = 0; j < outs.size(); j++) - for(unsigned k = 0; k < outs[j]->Children.size(); k++) - chs.push_back(outs[j]->Children[k]); - CreateEdge(node,tr,chs); - for(unsigned j = 0; j < outs.size(); j++) - edges_to_delete.insert(outs[j]); - } - } - std::vector new_edges; - hash_set all_nodes; - for(unsigned j = 0; j < edges.size(); j++){ - if(edges_to_delete.find(edges[j]) == edges_to_delete.end()){ -#if 0 - if(all_nodes.find(edges[j]->Parent) != all_nodes.end()) - throw "help!"; - all_nodes.insert(edges[j]->Parent); -#endif - new_edges.push_back(edges[j]); - } - else - delete edges[j]; - } - edges.swap(new_edges); - } - - void RPFP::MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node){ - if(live_nodes.find(node) != live_nodes.end()) - return; - live_nodes.insert(node); - std::vector &outs = outgoing[node]; - for(unsigned i = 0; i < outs.size(); i++) - for(unsigned j = 0; j < outs[i]->Children.size(); j++) - MarkLiveNodes(outgoing, live_nodes,outs[i]->Children[j]); - } - - void RPFP::RemoveDeadNodes(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++) - outgoing[edges[i]->Parent].push_back(edges[i]); - hash_set live_nodes; - for(unsigned i = 0; i < nodes.size(); i++) - if(!nodes[i]->Bound.IsFull()) - MarkLiveNodes(outgoing,live_nodes,nodes[i]); - std::vector new_edges; - for(unsigned j = 0; j < edges.size(); j++){ - if(live_nodes.find(edges[j]->Parent) != live_nodes.end()) - new_edges.push_back(edges[j]); - else { - Edge *edge = edges[j]; - for(unsigned int i = 0; i < edge->Children.size(); i++){ - std::vector &ic = edge->Children[i]->Incoming; - for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ - if(*it == edge){ - ic.erase(it); - break; - } - } - } - delete edge; - } - } - edges.swap(new_edges); - std::vector new_nodes; - for(unsigned j = 0; j < nodes.size(); j++){ - if(live_nodes.find(nodes[j]) != live_nodes.end()) - new_nodes.push_back(nodes[j]); - else - delete nodes[j]; - } - nodes.swap(new_nodes); - } - - void RPFP::WriteSolution(std::ostream &s){ - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - Term asgn = (node->Name)(node->Annotation.IndParams) == node->Annotation.Formula; - s << asgn << std::endl; - } - } - - void RPFP::WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s) - { - std::pair foo(t,0); - std::pair::iterator, bool> bar = memo.insert(foo); - // int &res = bar.first->second; - if(!bar.second) return; - hash_map::iterator it = e->varMap.find(t); - if (it != e->varMap.end()){ - return; - } - if (t.is_app()) - { - func_decl f = t.decl(); - // int idx; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - WriteEdgeVars(e, memo, t.arg(i),s); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)){ - Term rename = HideVariable(t,e->number); - Term value = dualModel.eval(rename); - s << " (= " << t << " " << value << ")\n"; - } - } - else if (t.is_quantifier()) - WriteEdgeVars(e,memo,t.body(),s); - return; - } - - void RPFP::WriteEdgeAssignment(std::ostream &s, Edge *e){ - s << "(\n"; - hash_map memo; - WriteEdgeVars(e, memo, e->F.Formula ,s); - s << ")\n"; - } - - void RPFP::WriteCounterexample(std::ostream &s, Node *node){ - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *child = node->Outgoing->Children[i]; - if(!Empty(child)) - WriteCounterexample(s,child); - } - s << "(" << node->number << " : " << EvalNode(node) << " <- "; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *child = node->Outgoing->Children[i]; - if(!Empty(child)) - s << " " << node->Outgoing->Children[i]->number; - } - s << ")" << std::endl; - WriteEdgeAssignment(s,node->Outgoing); - } - - RPFP::Term RPFP::ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - // int idx; - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(ToRuleRec(e, memo, t.arg(i),quants)); - hash_map::iterator rit = e->relMap.find(f); - if(rit != e->relMap.end()){ - Node* child = e->Children[rit->second]; - FuncDecl op = child->Name; - res = op(args.size(),&args[0]); - } - else { - res = f(args.size(),&args[0]); - if(nargs == 0 && t.decl().get_decl_kind() == Uninterpreted) - quants.push_back(t); - } - } - else if (t.is_quantifier()) - { - Term body = ToRuleRec(e,memo,t.body(),quants); - res = CloneQuantifier(t,body); - } - else res = t; - return res; - } - - - void RPFP::ToClauses(std::vector &cnsts, FileFormat format){ - cnsts.resize(edges.size()); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - SetEdgeMaps(edge); - std::vector quants; - hash_map memo; - Term lhs = ToRuleRec(edge, memo, edge->F.Formula,quants); - Term rhs = (edge->Parent->Name)(edge->F.IndParams.size(),&edge->F.IndParams[0]); - for(unsigned j = 0; j < edge->F.IndParams.size(); j++) - ToRuleRec(edge,memo,edge->F.IndParams[j],quants); // just to get quants - Term cnst = implies(lhs,rhs); -#if 0 - for(unsigned i = 0; i < quants.size(); i++){ - std::cout << expr(ctx,(Z3_ast)quants[i]) << " : " << sort(ctx,Z3_get_sort(ctx,(Z3_ast)quants[i])) << std::endl; - } -#endif - if(format != DualityFormat) - cnst= forall(quants,cnst); - cnsts[i] = cnst; - } - // int num_rules = cnsts.size(); - - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - if(!node->Bound.IsFull()){ - Term lhs = (node->Name)(node->Bound.IndParams) && !node->Bound.Formula; - Term cnst = implies(lhs,ctx.bool_val(false)); - if(format != DualityFormat){ - std::vector quants; - for(unsigned j = 0; j < node->Bound.IndParams.size(); j++) - quants.push_back(node->Bound.IndParams[j]); - if(format == HornFormat) - cnst= exists(quants,!cnst); - else - cnst= forall(quants, cnst); - } - cnsts.push_back(cnst); - } - } - - } - - - bool RPFP::proof_core_contains(const expr &e){ - return proof_core->find(e) != proof_core->end(); - } - - bool RPFP_caching::proof_core_contains(const expr &e){ - std::vector foo; - GetAssumptionLits(e,foo); - for(unsigned i = 0; i < foo.size(); i++) - if(proof_core->find(foo[i]) != proof_core->end()) - return true; - return false; - } - - bool RPFP::EdgeUsedInProof(Edge *edge){ - ComputeProofCore(); - if(!edge->dual.null() && proof_core_contains(edge->dual)) - return true; - for(unsigned i = 0; i < edge->constraints.size(); i++) - if(proof_core_contains(edge->constraints[i])) - return true; - return false; - } - -RPFP::~RPFP(){ - ClearProofCore(); - for(unsigned i = 0; i < nodes.size(); i++) - delete nodes[i]; - for(unsigned i = 0; i < edges.size(); i++) - delete edges[i]; - } -} - -#if 0 -void show_ast(expr *a){ - std::cout << *a; -} -#endif +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality_rpfp.h + +Abstract: + + implements relational post-fixedpoint problem + (RPFP) data structure. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + + + +#ifdef _WINDOWS +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#endif + +#include +#include +#include +#include + + +#include "duality.h" +#include "duality_profiling.h" + +// TODO: do we need these? +#ifdef Z3OPS + +class Z3_subterm_truth { + public: + virtual bool eval(Z3_ast f) = 0; + ~Z3_subterm_truth(){} +}; + +Z3_subterm_truth *Z3_mk_subterm_truth(Z3_context ctx, Z3_model model); + +#endif + +#include + +// TODO: use something official for this +int debug_gauss = 0; + +namespace Duality { + + static char string_of_int_buffer[20]; + + const char *Z3User::string_of_int(int n){ + sprintf(string_of_int_buffer,"%d",n); + return string_of_int_buffer; + } + + RPFP::Term RPFP::SuffixVariable(const Term &t, int n) + { + std::string name = t.decl().name().str() + "_" + string_of_int(n); + return ctx.constant(name.c_str(), t.get_sort()); + } + + RPFP::Term RPFP::HideVariable(const Term &t, int n) + { + std::string name = std::string("@p_") + t.decl().name().str() + "_" + string_of_int(n); + return ctx.constant(name.c_str(), t.get_sort()); + } + + void RPFP::RedVars(Node *node, Term &b, std::vector &v) + { + int idx = node->number; + if(HornClauses) + b = ctx.bool_val(true); + else { + std::string name = std::string("@b_") + string_of_int(idx); + symbol sym = ctx.str_symbol(name.c_str()); + b = ctx.constant(sym,ctx.bool_sort()); + } + // ctx.constant(name.c_str(), ctx.bool_sort()); + v = node->Annotation.IndParams; + for(unsigned i = 0; i < v.size(); i++) + v[i] = SuffixVariable(v[i],idx); + } + + void Z3User::SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t){ + if(memo.find(t) != memo.end()) + return; + memo.insert(t); + if(t.is_app()){ + decl_kind k = t.decl().get_decl_kind(); + if(k == And || k == Or || k == Not || k == Implies || k == Iff){ + ops++; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + SummarizeRec(memo,lits,ops,t.arg(i)); + return; + } + } + lits.push_back(t); + } + + int RPFP::CumulativeDecisions(){ +#if 0 + std::string stats = Z3_statistics_to_string(ctx); + int pos = stats.find("decisions:"); + pos += 10; + int end = stats.find('\n',pos); + std::string val = stats.substr(pos,end-pos); + return atoi(val.c_str()); +#endif + return slvr().get_num_decisions(); + } + + + void Z3User::Summarize(const Term &t){ + hash_set memo; std::vector lits; int ops = 0; + SummarizeRec(memo, lits, ops, t); + std::cout << ops << ": "; + for(unsigned i = 0; i < lits.size(); i++){ + if(i > 0) std::cout << ", "; + std::cout << lits[i]; + } + } + + int Z3User::CountOperatorsRec(hash_set &memo, const Term &t){ + if(memo.find(t) != memo.end()) + return 0; + memo.insert(t); + if(t.is_app()){ + decl_kind k = t.decl().get_decl_kind(); + if(k == And || k == Or){ + int count = 1; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + count += CountOperatorsRec(memo,t.arg(i)); + return count; + } + return 0; + } + if(t.is_quantifier()){ + int nbv = t.get_quantifier_num_bound(); + return CountOperatorsRec(memo,t.body()) + 2 * nbv; // count 2 for each quantifier + } + return 0; + } + + int Z3User::CountOperators(const Term &t){ + hash_set memo; + return CountOperatorsRec(memo,t); + } + + + Z3User::Term Z3User::conjoin(const std::vector &args){ + return ctx.make(And,args); + } + + Z3User::Term Z3User::sum(const std::vector &args){ + return ctx.make(Plus,args); + } + + RPFP::Term RPFP::RedDualRela(Edge *e, std::vector &args, int idx){ + Node *child = e->Children[idx]; + Term b(ctx); + std::vector v; + RedVars(child, b, v); + for (unsigned i = 0; i < args.size(); i++) + { + if (eq(args[i].get_sort(),ctx.bool_sort())) + args[i] = ctx.make(Iff,args[i], v[i]); + else + args[i] = args[i] == v[i]; + } + return args.size() > 0 ? (b && conjoin(args)) : b; + } + + Z3User::Term Z3User::CloneQuantifier(const Term &t, const Term &new_body){ +#if 0 + Z3_context c = ctx; + Z3_ast a = t; + std::vector pats; + int num_pats = Z3_get_quantifier_num_patterns(c,a); + for(int i = 0; i < num_pats; i++) + pats.push_back(Z3_get_quantifier_pattern_ast(c,a,i)); + std::vector no_pats; + int num_no_pats = Z3_get_quantifier_num_patterns(c,a); + for(int i = 0; i < num_no_pats; i++) + no_pats.push_back(Z3_get_quantifier_no_pattern_ast(c,a,i)); + int bound = Z3_get_quantifier_num_bound(c,a); + std::vector sorts; + std::vector names; + for(int i = 0; i < bound; i++){ + sorts.push_back(Z3_get_quantifier_bound_sort(c,a,i)); + names.push_back(Z3_get_quantifier_bound_name(c,a,i)); + } + Z3_ast foo = Z3_mk_quantifier_ex(c, + Z3_is_quantifier_forall(c,a), + Z3_get_quantifier_weight(c,a), + 0, + 0, + num_pats, + &pats[0], + num_no_pats, + &no_pats[0], + bound, + &sorts[0], + &names[0], + new_body); + return expr(ctx,foo); +#endif + return clone_quantifier(t,new_body); + } + + + RPFP::Term RPFP::LocalizeRec(Edge *e, hash_map &memo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + hash_map::iterator it = e->varMap.find(t); + if (it != e->varMap.end()){ + res = it->second; + return res; + } + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(LocalizeRec(e, memo, t.arg(i))); + hash_map::iterator rit = e->relMap.find(f); + if(rit != e->relMap.end()) + res = RedDualRela(e,args,(rit->second)); + else { + if (args.size() == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)) + { + res = HideVariable(t,e->number); + } + else + { + res = f(args.size(),&args[0]); + } + } + } + else if (t.is_quantifier()) + { + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = LocalizeRec(e,memo,pats[i]); + Term body = LocalizeRec(e,memo,t.body()); + res = clone_quantifier(t, body, pats); + } + else res = t; + return res; + } + + void RPFP::SetEdgeMaps(Edge *e){ + timer_start("SetEdgeMaps"); + e->relMap.clear(); + e->varMap.clear(); + for(unsigned i = 0; i < e->F.RelParams.size(); i++){ + e->relMap[e->F.RelParams[i]] = i; + } + Term b(ctx); + std::vector v; + RedVars(e->Parent, b, v); + for(unsigned i = 0; i < e->F.IndParams.size(); i++){ + // func_decl parentParam = e->Parent.Annotation.IndParams[i]; + expr oldname = e->F.IndParams[i]; + expr newname = v[i]; + e->varMap[oldname] = newname; + } + timer_stop("SetEdgeMaps"); + + } + + + RPFP::Term RPFP::Localize(Edge *e, const Term &t){ + timer_start("Localize"); + hash_map memo; + if(e->F.IndParams.size() > 0 && e->varMap.empty()) + SetEdgeMaps(e); // TODO: why is this needed? + Term res = LocalizeRec(e,memo,t); + timer_stop("Localize"); + return res; + } + + + RPFP::Term RPFP::ReducedDualEdge(Edge *e) + { + SetEdgeMaps(e); + timer_start("RedVars"); + Term b(ctx); + std::vector v; + RedVars(e->Parent, b, v); + timer_stop("RedVars"); + // ast_to_track = (ast)b; + return implies(b, Localize(e, e->F.Formula)); + } + + TermTree *RPFP::ToTermTree(Node *root, Node *skip_descendant) + { + if(skip_descendant && root == skip_descendant) + return new TermTree(ctx.bool_val(true)); + Edge *e = root->Outgoing; + if(!e) return new TermTree(ctx.bool_val(true), std::vector()); + std::vector children(e->Children.size()); + for(unsigned i = 0; i < children.size(); i++) + children[i] = ToTermTree(e->Children[i],skip_descendant); + // Term top = ReducedDualEdge(e); + Term top = e->dual.null() ? ctx.bool_val(true) : e->dual; + TermTree *res = new TermTree(top, children); + for(unsigned i = 0; i < e->constraints.size(); i++) + res->addTerm(e->constraints[i]); + return res; + } + + TermTree *RPFP::GetGoalTree(Node *root){ + std::vector children(1); + children[0] = ToGoalTree(root); + return new TermTree(ctx.bool_val(false),children); + } + + TermTree *RPFP::ToGoalTree(Node *root) + { + Term b(ctx); + std::vector v; + RedVars(root, b, v); + Term goal = root->Name(v); + Edge *e = root->Outgoing; + if(!e) return new TermTree(goal, std::vector()); + std::vector children(e->Children.size()); + for(unsigned i = 0; i < children.size(); i++) + children[i] = ToGoalTree(e->Children[i]); + // Term top = ReducedDualEdge(e); + return new TermTree(goal, children); + } + + Z3User::Term Z3User::SubstRec(hash_map &memo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(SubstRec(memo, t.arg(i))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + { + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = SubstRec(memo,pats[i]); + Term body = SubstRec(memo,t.body()); + res = clone_quantifier(t, body, pats); + } + // res = CloneQuantifier(t,SubstRec(memo, t.body())); + else res = t; + return res; + } + + Z3User::Term Z3User::SubstRec(hash_map &memo, hash_map &map, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(SubstRec(memo, map, t.arg(i))); + hash_map::iterator it = map.find(f); + if(it != map.end()) + f = it->second; + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + { + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = SubstRec(memo, map, pats[i]); + Term body = SubstRec(memo, map, t.body()); + res = clone_quantifier(t, body, pats); + } + // res = CloneQuantifier(t,SubstRec(memo, t.body())); + else res = t; + return res; + } + + Z3User::Term Z3User::ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(ExtractStores(memo, t.arg(i),cnstrs,renaming)); + res = f(args.size(),&args[0]); + if(f.get_decl_kind() == Store){ + func_decl fresh = ctx.fresh_func_decl("@arr", res.get_sort()); + expr y = fresh(); + expr equ = ctx.make(Equal,y,res); + cnstrs.push_back(equ); + renaming[y] = res; + res = y; + } + } + else res = t; + return res; + } + + + bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ + if(!(lit.is_quantifier() && IsClosedFormula(lit))){ + if(!lit.is_app()) + return false; + decl_kind k = lit.decl().get_decl_kind(); + if(k == Not){ + if(IsLiteral(lit.arg(0),atom,val)){ + val = eq(val,ctx.bool_val(true)) ? ctx.bool_val(false) : ctx.bool_val(true); + return true; + } + return false; + } + if(k == And || k == Or || k == Iff || k == Implies) + return false; + } + atom = lit; + val = ctx.bool_val(true); + return true; + } + + expr Z3User::Negate(const expr &f){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + return f.arg(0); + else if(eq(f,ctx.bool_val(true))) + return ctx.bool_val(false); + else if(eq(f,ctx.bool_val(false))) + return ctx.bool_val(true); + return !f; + } + + expr Z3User::ReduceAndOr(const std::vector &args, bool is_and, std::vector &res){ + for(unsigned i = 0; i < args.size(); i++) + if(!eq(args[i],ctx.bool_val(is_and))){ + if(eq(args[i],ctx.bool_val(!is_and))) + return ctx.bool_val(!is_and); + res.push_back(args[i]); + } + return expr(); + } + + expr Z3User::FinishAndOr(const std::vector &args, bool is_and){ + if(args.size() == 0) + return ctx.bool_val(is_and); + if(args.size() == 1) + return args[0]; + return ctx.make(is_and ? And : Or,args); + } + + expr Z3User::SimplifyAndOr(const std::vector &args, bool is_and){ + std::vector sargs; + expr res = ReduceAndOr(args,is_and,sargs); + if(!res.null()) return res; + return FinishAndOr(sargs,is_and); + } + + expr Z3User::PullCommonFactors(std::vector &args, bool is_and){ + + // first check if there's anything to do... + if(args.size() < 2) + return FinishAndOr(args,is_and); + for(unsigned i = 0; i < args.size(); i++){ + const expr &a = args[i]; + if(!(a.is_app() && a.decl().get_decl_kind() == (is_and ? Or : And))) + return FinishAndOr(args,is_and); + } + std::vector common; + for(unsigned i = 0; i < args.size(); i++){ + unsigned n = args[i].num_args(); + std::vector v(n),w; + for(unsigned j = 0; j < n; j++) + v[j] = args[i].arg(j); + std::less comp; + std::sort(v.begin(),v.end(),comp); + if(i == 0) + common.swap(v); + else { + std::set_intersection(common.begin(),common.end(),v.begin(),v.end(),std::inserter(w,w.begin()),comp); + common.swap(w); + } + } + if(common.empty()) + return FinishAndOr(args,is_and); + std::set common_set(common.begin(),common.end()); + for(unsigned i = 0; i < args.size(); i++){ + unsigned n = args[i].num_args(); + std::vector lits; + for(unsigned j = 0; j < n; j++){ + const expr b = args[i].arg(j); + if(common_set.find(b) == common_set.end()) + lits.push_back(b); + } + args[i] = SimplifyAndOr(lits,!is_and); + } + common.push_back(SimplifyAndOr(args,is_and)); + return SimplifyAndOr(common,!is_and); + } + + expr Z3User::ReallySimplifyAndOr(const std::vector &args, bool is_and){ + std::vector sargs; + expr res = ReduceAndOr(args,is_and,sargs); + if(!res.null()) return res; + return PullCommonFactors(sargs,is_and); + } + + Z3User::Term Z3User::SubstAtomTriv(const expr &foo, const expr &atom, const expr &val){ + if(eq(foo,atom)) + return val; + else if(foo.is_app() && foo.decl().get_decl_kind() == Not && eq(foo.arg(0),atom)) + return Negate(val); + else + return foo; + } + + Z3User::Term Z3User::PushQuantifier(const expr &t, const expr &body, bool is_forall){ + if(t.get_quantifier_num_bound() == 1){ + std::vector fmlas,free,not_free; + CollectConjuncts(body,fmlas, !is_forall); + for(unsigned i = 0; i < fmlas.size(); i++){ + const expr &fmla = fmlas[i]; + if(fmla.has_free(0)) + free.push_back(fmla); + else + not_free.push_back(fmla); + } + decl_kind op = is_forall ? Or : And; + if(free.empty()) + return SimplifyAndOr(not_free,op == And); + expr q = clone_quantifier(is_forall ? Forall : Exists,t, SimplifyAndOr(free, op == And)); + if(!not_free.empty()) + q = ctx.make(op,q,SimplifyAndOr(not_free, op == And)); + return q; + } + return clone_quantifier(is_forall ? Forall : Exists,t,body); + } + + Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall){ + if(body.is_app()){ + if(body.decl().get_decl_kind() == (is_forall ? And : Or)){ // quantifier distributes + int nargs = body.num_args(); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = CloneQuantAndSimp(t, body.arg(i), is_forall); + return SimplifyAndOr(args, body.decl().get_decl_kind() == And); + } + else if(body.decl().get_decl_kind() == (is_forall ? And : Or)){ // quantifier distributes + return PushQuantifier(t,body,is_forall); // may distribute partially + } + else if(body.decl().get_decl_kind() == Not){ + return CloneQuantAndSimp(t,body.arg(0),!is_forall); + } + } + return clone_quantifier(t,body); + } + + Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ + return CloneQuantAndSimp(t,body,t.is_quantifier_forall()); + } + + Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val){ + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()){ + func_decl f = t.decl(); + decl_kind k = f.get_decl_kind(); + + // TODO: recur here, but how much? We don't want to be quadractic in formula size + + if(k == And || k == Or){ + int nargs = t.num_args(); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = SubstAtom(memo,t.arg(i),atom,val); + res = ReallySimplifyAndOr(args, k==And); + return res; + } + } + else if(t.is_quantifier() && atom.is_quantifier()){ + if(eq(t,atom)) + res = val; + else + res = clone_quantifier(t,SubstAtom(memo,t.body(),atom,val)); + return res; + } + res = SubstAtomTriv(t,atom,val); + return res; + } + + void Z3User::RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo){ + for(unsigned i = 0; i < args.size(); i++){ + const expr &lit = args[i]; + expr atom, val; + if(IsLiteral(lit,atom,val)){ + if(atom.is_app() && atom.decl().get_decl_kind() == Equal) + if(pol ? eq(val,ctx.bool_val(true)) : eq(val,ctx.bool_val(false))){ + expr lhs = atom.arg(0), rhs = atom.arg(1); + if(lhs.is_numeral()) + std::swap(lhs,rhs); + if(rhs.is_numeral() && lhs.is_app()){ + for(unsigned j = 0; j < args.size(); j++) + if(j != i){ + smemo.clear(); + smemo[lhs] = rhs; + args[j] = SubstRec(smemo,args[j]); + } + } + } + for(unsigned j = 0; j < args.size(); j++) + if(j != i){ + smemo.clear(); + args[j] = SubstAtom(smemo,args[j],atom,pol ? val : !val); + } + } + } + } + + + Z3User::Term Z3User::RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(RemoveRedundancyRec(memo, smemo, t.arg(i))); + + decl_kind k = f.get_decl_kind(); + if(k == And){ + RemoveRedundancyOp(true,args,smemo); + res = ReallySimplifyAndOr(args, true); + } + else if(k == Or){ + RemoveRedundancyOp(false,args,smemo); + res = ReallySimplifyAndOr(args, false); + } + else { + if(k == Equal && args[0].get_id() > args[1].get_id()) + std::swap(args[0],args[1]); + res = f(args.size(),&args[0]); + } + } + else if (t.is_quantifier()) + { + Term body = RemoveRedundancyRec(memo,smemo,t.body()); + res = CloneQuantAndSimp(t, body); + } + else res = t; + return res; + } + + Z3User::Term Z3User::RemoveRedundancy(const Term &t){ + hash_map memo; + hash_map smemo; + return RemoveRedundancyRec(memo,smemo,t); + } + + Z3User::Term Z3User::AdjustQuantifiers(const Term &t) + { + if(t.is_quantifier() || (t.is_app() && t.has_quantifiers())) + return t.qe_lite(); + return t; + } + + Z3User::Term Z3User::IneqToEqRec(hash_map &memo, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(IneqToEqRec(memo, t.arg(i))); + + decl_kind k = f.get_decl_kind(); + if(k == And){ + for(int i = 0; i < nargs-1; i++){ + if((args[i].is_app() && args[i].decl().get_decl_kind() == Geq && + args[i+1].is_app() && args[i+1].decl().get_decl_kind() == Leq) + || + (args[i].is_app() && args[i].decl().get_decl_kind() == Leq && + args[i+1].is_app() && args[i+1].decl().get_decl_kind() == Geq)) + if(eq(args[i].arg(0),args[i+1].arg(0)) && eq(args[i].arg(1),args[i+1].arg(1))){ + args[i] = ctx.make(Equal,args[i].arg(0),args[i].arg(1)); + args[i+1] = ctx.bool_val(true); + } + } + } + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + { + Term body = IneqToEqRec(memo,t.body()); + res = clone_quantifier(t, body); + } + else res = t; + return res; + } + + Z3User::Term Z3User::IneqToEq(const Term &t){ + hash_map memo; + return IneqToEqRec(memo,t); + } + + Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ + std::string name = std::string("@q_") + t.decl().name().str() + "_" + string_of_int(number); + res = ctx.constant(name.c_str(), t.get_sort()); + return res; + } + for(int i = 0; i < nargs; i++) + args.push_back(SubstRec(memo, t.arg(i))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()) + res = CloneQuantifier(t,SubstRec(memo, t.body())); + else res = t; + return res; + } + + RPFP::Term RPFP::SubstParams(const std::vector &from, + const std::vector &to, const Term &t){ + hash_map memo; + bool some_diff = false; + for(unsigned i = 0; i < from.size(); i++) + if(i < to.size() && !eq(from[i],to[i])){ + memo[from[i]] = to[i]; + some_diff = true; + } + return some_diff ? SubstRec(memo,t) : t; + } + + RPFP::Term RPFP::SubstParamsNoCapture(const std::vector &from, + const std::vector &to, const Term &t){ + hash_map memo; + bool some_diff = false; + for(unsigned i = 0; i < from.size(); i++) + if(i < to.size() && !eq(from[i],to[i])){ + memo[from[i]] = to[i]; + // if the new param is not being mapped to anything else, we need to rename it to prevent capture + // note, if the new param *is* mapped later in the list, it will override this substitution + const expr &w = to[i]; + if(memo.find(w) == memo.end()){ + std::string old_name = w.decl().name().str(); + func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); + expr y = fresh(); + memo[w] = y; + } + some_diff = true; + } + return some_diff ? SubstRec(memo,t) : t; + } + + + + RPFP::Transformer RPFP::Fuse(const std::vector &trs){ + assert(!trs.empty()); + const std::vector ¶ms = trs[0]->IndParams; + std::vector fmlas(trs.size()); + fmlas[0] = trs[0]->Formula; + for(unsigned i = 1; i < trs.size(); i++) + fmlas[i] = SubstParamsNoCapture(trs[i]->IndParams,params,trs[i]->Formula); + std::vector rel_params = trs[0]->RelParams; + for(unsigned i = 1; i < trs.size(); i++){ + const std::vector ¶ms2 = trs[i]->RelParams; + hash_map map; + for(unsigned j = 0; j < params2.size(); j++){ + func_decl rel = RenumberPred(params2[j],rel_params.size()); + rel_params.push_back(rel); + map[params2[j]] = rel; + } + hash_map memo; + fmlas[i] = SubstRec(memo,map,fmlas[i]); + } + return Transformer(rel_params,params,ctx.make(Or,fmlas),trs[0]->owner); + } + + + void Z3User::Strengthen(Term &x, const Term &y) + { + if (eq(x,ctx.bool_val(true))) + x = y; + else + x = x && y; + } + + void RPFP::SetAnnotation(Node *root, const expr &t){ + hash_map memo; + Term b; + std::vector v; + RedVars(root, b, v); + memo[b] = ctx.bool_val(true); + for (unsigned i = 0; i < v.size(); i++) + memo[v[i]] = root->Annotation.IndParams[i]; + Term annot = SubstRec(memo, t); + // Strengthen(ref root.Annotation.Formula, annot); + root->Annotation.Formula = annot; + } + + void RPFP::DecodeTree(Node *root, TermTree *interp, int persist) + { + std::vector &ic = interp->getChildren(); + if (ic.size() > 0) + { + std::vector &nc = root->Outgoing->Children; + for (unsigned i = 0; i < nc.size(); i++) + DecodeTree(nc[i], ic[i], persist); + } + SetAnnotation(root,interp->getTerm()); +#if 0 + if(persist != 0) + Z3_persist_ast(ctx,root->Annotation.Formula,persist); +#endif + } + + RPFP::Term RPFP::GetUpperBound(Node *n) + { + // TODO: cache this + Term b(ctx); std::vector v; + RedVars(n, b, v); + hash_map memo; + for (unsigned int i = 0; i < v.size(); i++) + memo[n->Bound.IndParams[i]] = v[i]; + Term cnst = SubstRec(memo, n->Bound.Formula); + return b && !cnst; + } + + RPFP::Term RPFP::GetAnnotation(Node *n) + { + if(eq(n->Annotation.Formula,ctx.bool_val(true))) + return n->Annotation.Formula; + // TODO: cache this + Term b(ctx); std::vector v; + RedVars(n, b, v); + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[n->Annotation.IndParams[i]] = v[i]; + Term cnst = SubstRec(memo, n->Annotation.Formula); + return !b || cnst; + } + + RPFP::Term RPFP::GetUnderapprox(Node *n) + { + // TODO: cache this + Term b(ctx); std::vector v; + RedVars(n, b, v); + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[n->Underapprox.IndParams[i]] = v[i]; + Term cnst = SubstRecHide(memo, n->Underapprox.Formula, n->number); + return !b || cnst; + } + + TermTree *RPFP::AddUpperBound(Node *root, TermTree *t) + { + Term f = !((ast)(root->dual)) ? ctx.bool_val(true) : root->dual; + std::vector c(1); c[0] = t; + return new TermTree(f, c); + } + +#if 0 + void RPFP::WriteInterps(System.IO.StreamWriter f, TermTree t) + { + foreach (var c in t.getChildren()) + WriteInterps(f, c); + f.WriteLine(t.getTerm()); + } +#endif + + + expr RPFP::GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox) + { + if (e->dual.null()) { + timer_start("ReducedDualEdge"); + e->dual = ReducedDualEdge(e); + timer_stop("ReducedDualEdge"); + timer_start("getting children"); + if(underapprox){ + std::vector cus(e->Children.size()); + for(unsigned i = 0; i < e->Children.size(); i++) + cus[i] = !UnderapproxFlag(e->Children[i]) || GetUnderapprox(e->Children[i]); + expr cnst = conjoin(cus); + e->dual = e->dual && cnst; + } + timer_stop("getting children"); + timer_start("Persisting"); + std::list::reverse_iterator it = stack.rbegin(); + for(int i = 0; i < persist && it != stack.rend(); i++) + it++; + if(it != stack.rend()) + it->edges.push_back(e); + timer_stop("Persisting"); + //Console.WriteLine("{0}", cnst); + } + return e->dual; + } + + /** For incremental solving, asserts the constraint associated + * with this edge in the SMT context. If this edge is removed, + * you must pop the context accordingly. The second argument is + * the number of pushes we are inside. The constraint formula + * will survive "persist" pops of the context. If you plan + * to reassert the edge after popping the context once, + * you can save time re-constructing the formula by setting + * "persist" to one. If you set "persist" too high, however, + * you could have a memory leak. + * + * The flag "with children" causes the annotations of the children + * to be asserted. The flag underapprox causes the underapproximations + * of the children to be asserted *conditionally*. See Check() on + * how to actually enforce these constraints. + * + */ + + void RPFP::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) + { + if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) + return; + expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); + timer_start("solver add"); + slvr_add(e->dual); + timer_stop("solver add"); + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + ConstrainParent(e,e->Children[i]); + } + + +#ifdef LIMIT_STACK_WEIGHT + void RPFP_caching::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) + { + unsigned old_new_alits = new_alits.size(); + if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) + return; + expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); + timer_start("solver add"); + slvr_add(e->dual); + timer_stop("solver add"); + if(old_new_alits < new_alits.size()) + weight_added.val++; + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + ConstrainParent(e,e->Children[i]); + } +#endif + + // caching verion of above + void RPFP_caching::AssertEdgeCache(Edge *e, std::vector &lits, bool with_children){ + if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) + return; + expr fmla = GetEdgeFormula(e, 0, with_children, false); + GetAssumptionLits(fmla,lits); + if(with_children) + for(unsigned i = 0; i < e->Children.size(); i++) + ConstrainParentCache(e,e->Children[i],lits); + } + + void RPFP::slvr_add(const expr &e){ + slvr().add(e); + } + + void RPFP_caching::slvr_add(const expr &e){ + GetAssumptionLits(e,alit_stack); + } + + void RPFP::slvr_pop(int i){ + slvr().pop(i); + } + + void RPFP::slvr_push(){ + slvr().push(); + } + + void RPFP_caching::slvr_pop(int i){ + for(int j = 0; j < i; j++){ +#ifdef LIMIT_STACK_WEIGHT + if(alit_stack_sizes.empty()){ + if(big_stack.empty()) + throw "stack underflow"; + for(unsigned k = 0; k < new_alits.size(); k++){ + if(AssumptionLits.find(new_alits[k]) == AssumptionLits.end()) + throw "foo!"; + AssumptionLits.erase(new_alits[k]); + } + big_stack_entry &bsb = big_stack.back(); + bsb.alit_stack_sizes.swap(alit_stack_sizes); + bsb.alit_stack.swap(alit_stack); + bsb.new_alits.swap(new_alits); + bsb.weight_added.swap(weight_added); + big_stack.pop_back(); + slvr().pop(1); + continue; + } +#endif + alit_stack.resize(alit_stack_sizes.back()); + alit_stack_sizes.pop_back(); + } + } + + void RPFP_caching::slvr_push(){ +#ifdef LIMIT_STACK_WEIGHT + if(weight_added.val > LIMIT_STACK_WEIGHT){ + big_stack.resize(big_stack.size()+1); + big_stack_entry &bsb = big_stack.back(); + bsb.alit_stack_sizes.swap(alit_stack_sizes); + bsb.alit_stack.swap(alit_stack); + bsb.new_alits.swap(new_alits); + bsb.weight_added.swap(weight_added); + slvr().push(); + for(unsigned i = 0; i < bsb.alit_stack.size(); i++) + slvr().add(bsb.alit_stack[i]); + return; + } +#endif + alit_stack_sizes.push_back(alit_stack.size()); + } + + check_result RPFP::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ + return slvr().check(n, assumptions, core_size, core); + } + + check_result RPFP_caching::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ + unsigned oldsiz = alit_stack.size(); + if(n && assumptions) + std::copy(assumptions,assumptions+n,std::inserter(alit_stack,alit_stack.end())); + check_result res; + if(core_size && core){ + std::vector full_core(alit_stack.size()), core1(n); + std::copy(assumptions,assumptions+n,core1.begin()); + res = slvr().check(alit_stack.size(), &alit_stack[0], core_size, &full_core[0]); + full_core.resize(*core_size); + if(res == unsat){ + FilterCore(core1,full_core); + *core_size = core1.size(); + std::copy(core1.begin(),core1.end(),core); + } + } + else + res = slvr().check(alit_stack.size(), &alit_stack[0]); + alit_stack.resize(oldsiz); + return res; + } + + lbool RPFP::ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals, + bool weak){ + return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); + } + + lbool RPFP_caching::ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals, + bool weak){ + GetTermTreeAssertionLiterals(assumptions); + return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); + } + + void RPFP_caching::GetTermTreeAssertionLiteralsRec(TermTree *assumptions){ + std::vector alits; + hash_map map; + GetAssumptionLits(assumptions->getTerm(),alits,&map); + std::vector &ts = assumptions->getTerms(); + for(unsigned i = 0; i < ts.size(); i++) + GetAssumptionLits(ts[i],alits,&map); + assumptions->setTerm(ctx.bool_val(true)); + ts = alits; + for(unsigned i = 0; i < alits.size(); i++) + ts.push_back(ctx.make(Implies,alits[i],map[alits[i]])); + for(unsigned i = 0; i < assumptions->getChildren().size(); i++) + GetTermTreeAssertionLiterals(assumptions->getChildren()[i]); + return; + } + + void RPFP_caching::GetTermTreeAssertionLiterals(TermTree *assumptions){ + // optimize binary case + if(assumptions->getChildren().size() == 1 + && assumptions->getChildren()[0]->getChildren().size() == 0){ + hash_map map; + TermTree *child = assumptions->getChildren()[0]; + std::vector dummy; + GetAssumptionLits(child->getTerm(),dummy,&map); + std::vector &ts = child->getTerms(); + for(unsigned i = 0; i < ts.size(); i++) + GetAssumptionLits(ts[i],dummy,&map); + std::vector assumps; + slvr().get_proof().get_assumptions(assumps); + if(!proof_core){ // save the proof core for later use + proof_core = new hash_set; + for(unsigned i = 0; i < assumps.size(); i++) + proof_core->insert(assumps[i]); + } + std::vector *cnsts[2] = {&child->getTerms(),&assumptions->getTerms()}; + for(unsigned i = 0; i < assumps.size(); i++){ + expr &ass = assumps[i]; + expr alit = (ass.is_app() && ass.decl().get_decl_kind() == Implies) ? ass.arg(0) : ass; + bool isA = map.find(alit) != map.end(); + cnsts[isA ? 0 : 1]->push_back(ass); + } + } + else + GetTermTreeAssertionLiteralsRec(assumptions); + } + + void RPFP::AddToProofCore(hash_set &core){ + std::vector assumps; + slvr().get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++) + core.insert(assumps[i]); + } + + void RPFP::ComputeProofCore(){ + if(!proof_core){ + proof_core = new hash_set; + AddToProofCore(*proof_core); + } + } + + + void RPFP_caching::GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map){ + std::vector conjs; + CollectConjuncts(fmla,conjs); + for(unsigned i = 0; i < conjs.size(); i++){ + const expr &conj = conjs[i]; + std::pair foo(conj,expr(ctx)); + std::pair::iterator, bool> bar = AssumptionLits.insert(foo); + Term &res = bar.first->second; + if(bar.second){ + func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); + res = pred(); +#ifdef LIMIT_STACK_WEIGHT + new_alits.push_back(conj); +#endif + slvr().add(ctx.make(Implies,res,conj)); + // std::cout << res << ": " << conj << "\n"; + } + if(opt_map) + (*opt_map)[res] = conj; + lits.push_back(res); + } + } + + void RPFP::ConstrainParent(Edge *parent, Node *child){ + ConstrainEdgeLocalized(parent,GetAnnotation(child)); + } + + void RPFP_caching::ConstrainParentCache(Edge *parent, Node *child, std::vector &lits){ + ConstrainEdgeLocalizedCache(parent,GetAnnotation(child),lits); + } + + + /** For incremental solving, asserts the negation of the upper bound associated + * with a node. + * */ + + void RPFP::AssertNode(Node *n) + { + if (n->dual.null()) + { + n->dual = GetUpperBound(n); + stack.back().nodes.push_back(n); + slvr_add(n->dual); + } + } + + // caching version of above + void RPFP_caching::AssertNodeCache(Node *n, std::vector lits){ + if (n->dual.null()) + { + n->dual = GetUpperBound(n); + stack.back().nodes.push_back(n); + GetAssumptionLits(n->dual,lits); + } + } + + /** Clone another RPFP into this one, keeping a map */ + void RPFP_caching::Clone(RPFP *other){ +#if 0 + for(unsigned i = 0; i < other->nodes.size(); i++) + NodeCloneMap[other->nodes[i]] = CloneNode(other->nodes[i]); +#endif + for(unsigned i = 0; i < other->edges.size(); i++){ + Edge *edge = other->edges[i]; + Node *parent = CloneNode(edge->Parent); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++) + // cs.push_back(NodeCloneMap[edge->Children[j]]); + cs.push_back(CloneNode(edge->Children[j])); + EdgeCloneMap[edge] = CreateEdge(parent,edge->F,cs); + } + } + + /** Get the clone of a node */ + RPFP::Node *RPFP_caching::GetNodeClone(Node *other_node){ + return NodeCloneMap[other_node]; + } + + /** Get the clone of an edge */ + RPFP::Edge *RPFP_caching::GetEdgeClone(Edge *other_edge){ + return EdgeCloneMap[other_edge]; + } + + /** check assumption lits, and return core */ + check_result RPFP_caching::CheckCore(const std::vector &assumps, std::vector &core){ + core.resize(assumps.size()); + unsigned core_size; + check_result res = slvr().check(assumps.size(),(expr *)&assumps[0],&core_size,&core[0]); + if(res == unsat) + core.resize(core_size); + else + core.clear(); + return res; + } + + + /** Assert a constraint on an edge in the SMT context. + */ + + void RPFP::ConstrainEdge(Edge *e, const Term &t) + { + Term tl = Localize(e, t); + ConstrainEdgeLocalized(e,tl); + } + + void RPFP::ConstrainEdgeLocalized(Edge *e, const Term &tl) + { + e->constraints.push_back(tl); + stack.back().constraints.push_back(std::pair(e,tl)); + slvr_add(tl); + } + + void RPFP_caching::ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits) + { + e->constraints.push_back(tl); + stack.back().constraints.push_back(std::pair(e,tl)); + GetAssumptionLits(tl,lits); + } + + + /** Declare a constant in the background theory. */ + + void RPFP::DeclareConstant(const FuncDecl &f){ + ls->declare_constant(f); + } + + /** Assert a background axiom. Background axioms can be used to provide the + * theory of auxilliary functions or relations. All symbols appearing in + * background axioms are considered global, and may appear in both transformer + * and relational solutions. Semantically, a solution to the RPFP gives + * an interpretation of the unknown relations for each interpretation of the + * auxilliary symbols that is consistent with the axioms. Axioms should be + * asserted before any calls to Push. They cannot be de-asserted by Pop. */ + + void RPFP::AssertAxiom(const Term &t) + { + ls->assert_axiom(t); + axioms.push_back(t); // for printing only + } + +#if 0 + /** Do not call this. */ + + void RPFP::RemoveAxiom(const Term &t) + { + slvr().RemoveInterpolationAxiom(t); + } +#endif + + /** Solve an RPFP graph. This means either strengthen the annotation + * so that the bound at the given root node is satisfied, or + * show that this cannot be done by giving a dual solution + * (i.e., a counterexample). + * + * In the current implementation, this only works for graphs that + * are: + * - tree-like + * + * - closed. + * + * In a tree-like graph, every nod has out most one incoming and one out-going edge, + * and there are no cycles. In a closed graph, every node has exactly one out-going + * edge. This means that the leaves of the tree are all hyper-edges with no + * children. Such an edge represents a relation (nullary transformer) and thus + * a lower bound on its parent. The parameter root must be the root of this tree. + * + * If Solve returns LBool.False, this indicates success. The annotation of the tree + * has been updated to satisfy the upper bound at the root. + * + * If Solve returns LBool.True, this indicates a counterexample. For each edge, + * you can then call Eval to determine the values of symbols in the transformer formula. + * You can also call Empty on a node to determine if its value in the counterexample + * is the empty relation. + * + * \param root The root of the tree + * \param persist Number of context pops through which result should persist + * + * + */ + + lbool RPFP::Solve(Node *root, int persist) + { + timer_start("Solve"); + TermTree *tree = GetConstraintTree(root); + TermTree *interpolant = NULL; + TermTree *goals = NULL; + if(ls->need_goals) + goals = GetGoalTree(root); + ClearProofCore(); + + // if (dualModel != null) dualModel.Dispose(); + // if (dualLabels != null) dualLabels.Dispose(); + + timer_start("interpolate_tree"); + lbool res = ls_interpolate_tree(tree, interpolant, dualModel,goals,true); + timer_stop("interpolate_tree"); + if (res == l_false) + { + DecodeTree(root, interpolant->getChildren()[0], persist); + delete interpolant; + } + + delete tree; + if(goals) + delete goals; + + timer_stop("Solve"); + return res; + } + + void RPFP::CollapseTermTreeRec(TermTree *root, TermTree *node){ + root->addTerm(node->getTerm()); + std::vector &cnsts = node->getTerms(); + for(unsigned i = 0; i < cnsts.size(); i++) + root->addTerm(cnsts[i]); + std::vector &chs = node->getChildren(); + for(unsigned i = 0; i < chs.size(); i++){ + CollapseTermTreeRec(root,chs[i]); + } + } + + TermTree *RPFP::CollapseTermTree(TermTree *node){ + std::vector &chs = node->getChildren(); + for(unsigned i = 0; i < chs.size(); i++) + CollapseTermTreeRec(node,chs[i]); + for(unsigned i = 0; i < chs.size(); i++) + delete chs[i]; + chs.clear(); + return node; + } + + lbool RPFP::SolveSingleNode(Node *root, Node *node) + { + timer_start("Solve"); + TermTree *tree = CollapseTermTree(GetConstraintTree(root,node)); + tree->getChildren().push_back(CollapseTermTree(ToTermTree(node))); + TermTree *interpolant = NULL; + ClearProofCore(); + + timer_start("interpolate_tree"); + lbool res = ls_interpolate_tree(tree, interpolant, dualModel,0,true); + timer_stop("interpolate_tree"); + if (res == l_false) + { + DecodeTree(node, interpolant->getChildren()[0], 0); + delete interpolant; + } + + delete tree; + timer_stop("Solve"); + return res; + } + + /** Get the constraint tree (but don't solve it) */ + + TermTree *RPFP::GetConstraintTree(Node *root, Node *skip_descendant) + { + return AddUpperBound(root, ToTermTree(root,skip_descendant)); + } + + /** Dispose of the dual model (counterexample) if there is one. */ + + void RPFP::DisposeDualModel() + { + dualModel = model(ctx,NULL); + } + + RPFP::Term RPFP::UnderapproxFlag(Node *n){ + expr flag = ctx.constant((std::string("@under") + string_of_int(n->number)).c_str(), ctx.bool_sort()); + underapprox_flag_rev[flag] = n; + return flag; + } + + RPFP::Node *RPFP::UnderapproxFlagRev(const Term &flag){ + return underapprox_flag_rev[flag]; + } + + /** Check satisfiability of asserted edges and nodes. Same functionality as + * Solve, except no primal solution (interpolant) is generated in the unsat case. + * The vector underapproxes gives the set of node underapproximations to be enforced + * (assuming these were conditionally asserted by AssertEdge). + * + */ + + check_result RPFP::Check(Node *root, std::vector underapproxes, std::vector *underapprox_core ) + { + timer_start("Check"); + ClearProofCore(); + // if (dualModel != null) dualModel.Dispose(); + check_result res; + if(!underapproxes.size()) + res = slvr_check(); + else { + std::vector us(underapproxes.size()); + for(unsigned i = 0; i < underapproxes.size(); i++) + us[i] = UnderapproxFlag(underapproxes[i]); + slvr_check(); // TODO: no idea why I need to do this + if(underapprox_core){ + std::vector unsat_core(us.size()); + unsigned core_size = 0; + res = slvr_check(us.size(),&us[0],&core_size,&unsat_core[0]); + underapprox_core->resize(core_size); + for(unsigned i = 0; i < core_size; i++) + (*underapprox_core)[i] = UnderapproxFlagRev(unsat_core[i]); + } + else { + res = slvr_check(us.size(),&us[0]); + bool dump = false; + if(dump){ + std::vector cnsts; + // cnsts.push_back(axioms[0]); + cnsts.push_back(root->dual); + cnsts.push_back(root->Outgoing->dual); + ls->write_interpolation_problem("temp.smt",cnsts,std::vector()); + } + } + // check_result temp = slvr_check(); + } + dualModel = slvr().get_model(); + timer_stop("Check"); + return res; + } + + check_result RPFP::CheckUpdateModel(Node *root, std::vector assumps){ + // check_result temp1 = slvr_check(); // no idea why I need to do this + ClearProofCore(); + check_result res = slvr_check(assumps.size(),&assumps[0]); + model mod = slvr().get_model(); + if(!mod.null()) + dualModel = mod;; + return res; + } + + /** Determines the value in the counterexample of a symbol occuring in the transformer formula of + * a given edge. */ + + RPFP::Term RPFP::Eval(Edge *e, Term t) + { + Term tl = Localize(e, t); + return dualModel.eval(tl); + } + + /** Returns true if the given node is empty in the primal solution. For proecudure summaries, + this means that the procedure is not called in the current counter-model. */ + + bool RPFP::Empty(Node *p) + { + Term b; std::vector v; + RedVars(p, b, v); + // dualModel.show_internals(); + // std::cout << "b: " << b << std::endl; + expr bv = dualModel.eval(b); + // std::cout << "bv: " << bv << std::endl; + bool res = !eq(bv,ctx.bool_val(true)); + return res; + } + + RPFP::Term RPFP::EvalNode(Node *p) + { + Term b; std::vector v; + RedVars(p, b, v); + std::vector args; + for(unsigned i = 0; i < v.size(); i++) + args.push_back(dualModel.eval(v[i])); + return (p->Name)(args); + } + + void RPFP::EvalArrayTerm(const RPFP::Term &t, ArrayValue &res){ + if(t.is_app()){ + decl_kind k = t.decl().get_decl_kind(); + if(k == AsArray){ + func_decl fd = t.decl().get_func_decl_parameter(0); + func_interp r = dualModel.get_func_interp(fd); + int num = r.num_entries(); + res.defined = true; + for(int i = 0; i < num; i++){ + expr arg = r.get_arg(i,0); + expr value = r.get_value(i); + res.entries[arg] = value; + } + res.def_val = r.else_value(); + return; + } + else if(k == Store){ + EvalArrayTerm(t.arg(0),res); + if(!res.defined)return; + expr addr = t.arg(1); + expr val = t.arg(2); + if(addr.is_numeral() && val.is_numeral()){ + if(eq(val,res.def_val)) + res.entries.erase(addr); + else + res.entries[addr] = val; + } + else + res.defined = false; + return; + } + } + res.defined = false; + } + + int eae_count = 0; + + RPFP::Term RPFP::EvalArrayEquality(const RPFP::Term &f){ + ArrayValue lhs,rhs; + eae_count++; + EvalArrayTerm(f.arg(0),lhs); + EvalArrayTerm(f.arg(1),rhs); + if(lhs.defined && rhs.defined){ + if(eq(lhs.def_val,rhs.def_val)) + if(lhs.entries == rhs.entries) + return ctx.bool_val(true); + return ctx.bool_val(false); + } + return f; + } + + /** Compute truth values of all boolean subterms in current model. + Assumes formulas has been simplified by Z3, so only boolean ops + ands and, or, not. Returns result in memo. + */ + + int RPFP::SubtermTruth(hash_map &memo, const Term &f){ + if(memo.find(f) != memo.end()){ + return memo[f]; + } + int res; + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + res = SubtermTruth(memo,!f.arg(0) || f.arg(1)); + goto done; + } + if(k == And) { + res = 1; + for(int i = 0; i < nargs; i++){ + int ar = SubtermTruth(memo,f.arg(i)); + if(ar == 0){ + res = 0; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Or) { + res = 0; + for(int i = 0; i < nargs; i++){ + int ar = SubtermTruth(memo,f.arg(i)); + if(ar == 1){ + res = 1; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Not) { + int ar = SubtermTruth(memo,f.arg(0)); + res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); + goto done; + } + } + { + bool pos; std::vector names; + if(f.is_label(pos,names)){ + res = SubtermTruth(memo,f.arg(0)); + goto done; + } + } + { + expr bv = dualModel.eval(f); + if(bv.is_app() && bv.decl().get_decl_kind() == Equal && + bv.arg(0).is_array()){ + bv = EvalArrayEquality(bv); + } + // Hack!!!! array equalities can occur negatively! + if(bv.is_app() && bv.decl().get_decl_kind() == Not && + bv.arg(0).decl().get_decl_kind() == Equal && + bv.arg(0).arg(0).is_array()){ + bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); + } + if(eq(bv,ctx.bool_val(true))) + res = 1; + else if(eq(bv,ctx.bool_val(false))) + res = 0; + else + res = 2; + } + done: + memo[f] = res; + return res; + } + + int RPFP::EvalTruth(hash_map &memo, Edge *e, const Term &f){ + Term tl = Localize(e, f); + return SubtermTruth(memo,tl); + } + + /** Compute truth values of all boolean subterms in current model. + Assumes formulas has been simplified by Z3, so only boolean ops + ands and, or, not. Returns result in memo. + */ + +#if 0 + int RPFP::GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos){ + if(memo[labpos].find(f) != memo[labpos].end()){ + return memo[labpos][f]; + } + int res; + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + res = GetLabelsRec(memo,f.arg(1) || !f.arg(0), labels, labpos); + goto done; + } + if(k == And) { + res = 1; + for(int i = 0; i < nargs; i++){ + int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); + if(ar == 0){ + res = 0; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Or) { + res = 0; + for(int i = 0; i < nargs; i++){ + int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); + if(ar == 1){ + res = 1; + goto done; + } + if(ar == 2)res = 2; + } + goto done; + } + else if(k == Not) { + int ar = GetLabelsRec(memo,f.arg(0), labels, !labpos); + res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); + goto done; + } + } + { + bool pos; std::vector names; + if(f.is_label(pos,names)){ + res = GetLabelsRec(memo,f.arg(0), labels, labpos); + if(pos == labpos && res == (pos ? 1 : 0)) + for(unsigned i = 0; i < names.size(); i++) + labels.push_back(names[i]); + goto done; + } + } + { + expr bv = dualModel.eval(f); + if(bv.is_app() && bv.decl().get_decl_kind() == Equal && + bv.arg(0).is_array()){ + bv = EvalArrayEquality(bv); + } + // Hack!!!! array equalities can occur negatively! + if(bv.is_app() && bv.decl().get_decl_kind() == Not && + bv.arg(0).decl().get_decl_kind() == Equal && + bv.arg(0).arg(0).is_array()){ + bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); + } + if(eq(bv,ctx.bool_val(true))) + res = 1; + else if(eq(bv,ctx.bool_val(false))) + res = 0; + else + res = 2; + } + done: + memo[labpos][f] = res; + return res; + } +#endif + + void RPFP::GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, + hash_set *done, bool truth){ + if(done[truth].find(f) != done[truth].end()) + return; /* already processed */ + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + GetLabelsRec(memo,f.arg(1) || !f.arg(0) ,labels,done,truth); + goto done; + } + if(k == Iff){ + int b = SubtermTruth(memo,f.arg(0)); + if(b == 2) + throw "disaster in GetLabelsRec"; + GetLabelsRec(memo,f.arg(1),labels,done,truth ? b : !b); + goto done; + } + if(truth ? k == And : k == Or) { + for(int i = 0; i < nargs; i++) + GetLabelsRec(memo,f.arg(i),labels,done,truth); + goto done; + } + if(truth ? k == Or : k == And) { + for(int i = 0; i < nargs; i++){ + Term a = f.arg(i); + timer_start("SubtermTruth"); + int b = SubtermTruth(memo,a); + timer_stop("SubtermTruth"); + if(truth ? (b == 1) : (b == 0)){ + GetLabelsRec(memo,a,labels,done,truth); + goto done; + } + } + /* Unreachable! */ + // throw "error in RPFP::GetLabelsRec"; + goto done; + } + else if(k == Not) { + GetLabelsRec(memo,f.arg(0),labels,done,!truth); + goto done; + } + else { + bool pos; std::vector names; + if(f.is_label(pos,names)){ + GetLabelsRec(memo,f.arg(0), labels, done, truth); + if(pos == truth) + for(unsigned i = 0; i < names.size(); i++) + labels.push_back(names[i]); + goto done; + } + } + } + done: + done[truth].insert(f); + } + + void RPFP::GetLabels(Edge *e, std::vector &labels){ + if(!e->map || e->map->labeled.null()) + return; + Term tl = Localize(e, e->map->labeled); + hash_map memo; + hash_set done[2]; + GetLabelsRec(memo,tl,labels,done,true); + } + +#ifdef Z3OPS + static Z3_subterm_truth *stt; +#endif + + int ir_count = 0; + + void RPFP::ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set *done, bool truth, hash_set &dont_cares){ + if(done[truth].find(f) != done[truth].end()) + return; /* already processed */ +#if 0 + int this_count = ir_count++; + if(this_count == 50092) + std::cout << "foo!\n"; +#endif + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies){ + ImplicantRed(memo,f.arg(1) || !f.arg(0) ,lits,done,truth,dont_cares); + goto done; + } + if(k == Iff){ + int b = SubtermTruth(memo,f.arg(0)); + if(b == 2) + throw "disaster in ImplicantRed"; + ImplicantRed(memo,f.arg(1),lits,done,truth ? b : !b,dont_cares); + goto done; + } + if(truth ? k == And : k == Or) { + for(int i = 0; i < nargs; i++) + ImplicantRed(memo,f.arg(i),lits,done,truth,dont_cares); + goto done; + } + if(truth ? k == Or : k == And) { + for(int i = 0; i < nargs; i++){ + Term a = f.arg(i); +#if 0 + if(i == nargs - 1){ // last chance! + ImplicantRed(memo,a,lits,done,truth,dont_cares); + goto done; + } +#endif + timer_start("SubtermTruth"); +#ifdef Z3OPS + bool b = stt->eval(a); +#else + int b = SubtermTruth(memo,a); +#endif + timer_stop("SubtermTruth"); + if(truth ? (b == 1) : (b == 0)){ + ImplicantRed(memo,a,lits,done,truth,dont_cares); + goto done; + } + } + /* Unreachable! */ + // TODO: need to indicate this failure to caller + // std::cerr << "error in RPFP::ImplicantRed"; + goto done; + } + else if(k == Not) { + ImplicantRed(memo,f.arg(0),lits,done,!truth,dont_cares); + goto done; + } + } + { + if(dont_cares.find(f) == dont_cares.end()){ + expr rf = ResolveIte(memo,f,lits,done,dont_cares); + expr bv = truth ? rf : !rf; + lits.push_back(bv); + } + } + done: + done[truth].insert(f); + } + + void RPFP::ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set &done, hash_set &dont_cares, bool extensional){ + if(done.find(f) != done.end()) + return; /* already processed */ + if(f.is_app()){ + int nargs = f.num_args(); + decl_kind k = f.decl().get_decl_kind(); + if(k == Implies || k == Iff || k == And || k == Or || k == Not){ + for(int i = 0; i < nargs; i++) + ImplicantFullRed(memo,f.arg(i),lits,done,dont_cares, extensional); + goto done; + } + } + { + if(dont_cares.find(f) == dont_cares.end()){ + int b = SubtermTruth(memo,f); + if(b != 0 && b != 1) goto done; + if(f.is_app() && f.decl().get_decl_kind() == Equal && f.arg(0).is_array()){ + if(b == 1 && !extensional){ + expr x = dualModel.eval(f.arg(0)); expr y = dualModel.eval(f.arg(1)); + if(!eq(x,y)) + b = 0; + } + if(b == 0) + goto done; + } + expr bv = (b==1) ? f : !f; + lits.push_back(bv); + } + } + done: + done.insert(f); + } + + RPFP::Term RPFP::ResolveIte(hash_map &memo, const Term &t, std::vector &lits, + hash_set *done, hash_set &dont_cares){ + if(resolve_ite_memo.find(t) != resolve_ite_memo.end()) + return resolve_ite_memo[t]; + Term res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + if(f.get_decl_kind() == Ite){ + timer_start("SubtermTruth"); +#ifdef Z3OPS + bool sel = stt->eval(t.arg(0)); +#else + int xval = SubtermTruth(memo,t.arg(0)); + bool sel; + if(xval == 0)sel = false; + else if(xval == 1)sel = true; + else + throw "unresolved ite in model"; +#endif + timer_stop("SubtermTruth"); + ImplicantRed(memo,t.arg(0),lits,done,sel,dont_cares); + res = ResolveIte(memo,t.arg(sel?1:2),lits,done,dont_cares); + } + else { + for(int i = 0; i < nargs; i++) + args.push_back(ResolveIte(memo,t.arg(i),lits,done,dont_cares)); + res = f(args.size(),&args[0]); + } + } + else res = t; + resolve_ite_memo[t] = res; + return res; + } + + RPFP::Term RPFP::ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts){ + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(bar.second){ + if(t.is_app()){ + int nargs = t.num_args(); + std::vector args; + if(t.decl().get_decl_kind() == Equal){ + expr lhs = t.arg(0); + expr rhs = t.arg(1); + if(rhs.decl().get_decl_kind() == Ite){ + expr rhs_args[3]; + lhs = ElimIteRec(memo,lhs,cnsts); + for(int i = 0; i < 3; i++) + rhs_args[i] = ElimIteRec(memo,rhs.arg(i),cnsts); + res = (rhs_args[0] && (lhs == rhs_args[1])) || ((!rhs_args[0]) && (lhs == rhs_args[2])); + goto done; + } + } + if(t.decl().get_decl_kind() == Ite){ + func_decl sym = ctx.fresh_func_decl("@ite", t.get_sort()); + res = sym(); + cnsts.push_back(ElimIteRec(memo,ctx.make(Equal,res,t),cnsts)); + } + else { + for(int i = 0; i < nargs; i++) + args.push_back(ElimIteRec(memo,t.arg(i),cnsts)); + res = t.decl()(args.size(),&args[0]); + } + } + else if(t.is_quantifier()) + res = clone_quantifier(t,ElimIteRec(memo,t.body(),cnsts)); + else + res = t; + } + done: + return res; + } + + RPFP::Term RPFP::ElimIte(const Term &t){ + hash_map memo; + std::vector cnsts; + expr res = ElimIteRec(memo,t,cnsts); + if(!cnsts.empty()){ + cnsts.push_back(res); + res = ctx.make(And,cnsts); + } + return res; + } + + void RPFP::Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares){ + hash_set done[2]; + ImplicantRed(memo,f,lits,done,true, dont_cares); + } + + + /** Underapproximate a formula using current counterexample. */ + + RPFP::Term RPFP::UnderapproxFormula(const Term &f, hash_set &dont_cares){ + /* first compute truth values of subterms */ + hash_map memo; + #ifdef Z3OPS + stt = Z3_mk_subterm_truth(ctx,dualModel); + #endif + // SubtermTruth(memo,f); + /* now compute an implicant */ + std::vector lits; + Implicant(memo,f,lits, dont_cares); +#ifdef Z3OPS + delete stt; stt = 0; +#endif + /* return conjunction of literals */ + return conjoin(lits); + } + + RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, bool extensional){ + hash_set dont_cares; + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); + /* first compute truth values of subterms */ + hash_map memo; + hash_set done; + std::vector lits; + ImplicantFullRed(memo,f,lits,done,dont_cares, extensional); + timer_stop("UnderapproxFormula"); + /* return conjunction of literals */ + return conjoin(lits); + } + + struct VariableProjector : Z3User { + + struct elim_cand { + Term var; + int sup; + Term val; + }; + + typedef expr Term; + + hash_set keep; + hash_map var_ord; + int num_vars; + std::vector elim_cands; + hash_map > sup_map; + hash_map elim_map; + std::vector ready_cands; + hash_map cand_map; + params simp_params; + + VariableProjector(Z3User &_user, std::vector &keep_vec) : + Z3User(_user), simp_params() + { + num_vars = 0; + for(unsigned i = 0; i < keep_vec.size(); i++){ + keep.insert(keep_vec[i]); + var_ord[keep_vec[i]] = num_vars++; + } + } + + int VarNum(const Term &v){ + if(var_ord.find(v) == var_ord.end()) + var_ord[v] = num_vars++; + return var_ord[v]; + } + + bool IsVar(const Term &t){ + return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; + } + + bool IsPropLit(const Term &t, Term &a){ + if(IsVar(t)){ + a = t; + return true; + } + else if(t.is_app() && t.decl().get_decl_kind() == Not) + return IsPropLit(t.arg(0),a); + return false; + } + + void CountOtherVarsRec(hash_map &memo, + const Term &t, + int id, + int &count){ + std::pair foo(t,0); + std::pair::iterator, bool> bar = memo.insert(foo); + // int &res = bar.first->second; + if(!bar.second) return; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + if (nargs == 0 && f.get_decl_kind() == Uninterpreted){ + if(cand_map.find(t) != cand_map.end()){ + count++; + sup_map[t].push_back(id); + } + } + for(int i = 0; i < nargs; i++) + CountOtherVarsRec(memo, t.arg(i), id, count); + } + else if (t.is_quantifier()) + CountOtherVarsRec(memo, t.body(), id, count); + } + + void NewElimCand(const Term &lhs, const Term &rhs){ + if(debug_gauss){ + std::cout << "mapping " << lhs << " to " << rhs << std::endl; + } + elim_cand cand; + cand.var = lhs; + cand.sup = 0; + cand.val = rhs; + elim_cands.push_back(cand); + cand_map[lhs] = elim_cands.size()-1; + } + + void MakeElimCand(const Term &lhs, const Term &rhs){ + if(eq(lhs,rhs)) + return; + if(!IsVar(lhs)){ + if(IsVar(rhs)){ + MakeElimCand(rhs,lhs); + return; + } + else{ + std::cout << "would have mapped a non-var\n"; + return; + } + } + if(IsVar(rhs) && VarNum(rhs) > VarNum(lhs)){ + MakeElimCand(rhs,lhs); + return; + } + if(keep.find(lhs) != keep.end()) + return; + if(cand_map.find(lhs) == cand_map.end()) + NewElimCand(lhs,rhs); + else { + int cand_idx = cand_map[lhs]; + if(IsVar(rhs) && cand_map.find(rhs) == cand_map.end() + && keep.find(rhs) == keep.end()) + NewElimCand(rhs,elim_cands[cand_idx].val); + elim_cands[cand_idx].val = rhs; + } + } + + Term FindRep(const Term &t){ + if(cand_map.find(t) == cand_map.end()) + return t; + Term &res = elim_cands[cand_map[t]].val; + if(IsVar(res)){ + assert(VarNum(res) < VarNum(t)); + res = FindRep(res); + return res; + } + return t; + } + + void GaussElimCheap(const std::vector &lits_in, + std::vector &lits_out){ + for(unsigned i = 0; i < lits_in.size(); i++){ + Term lit = lits_in[i]; + if(lit.is_app()){ + decl_kind k = lit.decl().get_decl_kind(); + if(k == Equal || k == Iff) + MakeElimCand(FindRep(lit.arg(0)),FindRep(lit.arg(1))); + } + } + + for(unsigned i = 0; i < elim_cands.size(); i++){ + elim_cand &cand = elim_cands[i]; + hash_map memo; + CountOtherVarsRec(memo,cand.val,i,cand.sup); + if(cand.sup == 0) + ready_cands.push_back(i); + } + + while(!ready_cands.empty()){ + elim_cand &cand = elim_cands[ready_cands.back()]; + ready_cands.pop_back(); + Term rep = FindRep(cand.var); + if(!eq(rep,cand.var)) + if(cand_map.find(rep) != cand_map.end()){ + int rep_pos = cand_map[rep]; + cand.val = elim_cands[rep_pos].val; + } + Term val = SubstRec(elim_map,cand.val); + if(debug_gauss){ + std::cout << "subbing " << cand.var << " --> " << val << std::endl; + } + elim_map[cand.var] = val; + std::vector &sup = sup_map[cand.var]; + for(unsigned i = 0; i < sup.size(); i++){ + int c = sup[i]; + if((--elim_cands[c].sup) == 0) + ready_cands.push_back(c); + } + } + + for(unsigned i = 0; i < lits_in.size(); i++){ + Term lit = lits_in[i]; + lit = SubstRec(elim_map,lit); + lit = lit.simplify(); + if(eq(lit,ctx.bool_val(true))) + continue; + Term a; + if(IsPropLit(lit,a)) + if(keep.find(lit) == keep.end()) + continue; + lits_out.push_back(lit); + } + } + + // maps variables to constrains in which the occur pos, neg + hash_map la_index[2]; + hash_map la_coeffs[2]; + std::vector la_pos_vars; + bool fixing; + + void IndexLAcoeff(const Term &coeff1, const Term &coeff2, Term t, int id){ + Term coeff = coeff1 * coeff2; + coeff = coeff.simplify(); + Term is_pos = (coeff >= ctx.int_val(0)); + is_pos = is_pos.simplify(); + if(eq(is_pos,ctx.bool_val(true))) + IndexLA(true,coeff,t, id); + else + IndexLA(false,coeff,t, id); + } + + void IndexLAremove(const Term &t){ + if(IsVar(t)){ + la_index[0][t] = -1; // means ineligible to be eliminated + la_index[1][t] = -1; // (more that one occurrence, or occurs not in linear comb) + } + else if(t.is_app()){ + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + IndexLAremove(t.arg(i)); + } + // TODO: quantifiers? + } + + + void IndexLA(bool pos, const Term &coeff, const Term &t, int id){ + if(t.is_numeral()) + return; + if(t.is_app()){ + int nargs = t.num_args(); + switch(t.decl().get_decl_kind()){ + case Plus: + for(int i = 0; i < nargs; i++) + IndexLA(pos,coeff,t.arg(i), id); + break; + case Sub: + IndexLA(pos,coeff,t.arg(0), id); + IndexLA(!pos,coeff,t.arg(1), id); + break; + case Times: + if(t.arg(0).is_numeral()) + IndexLAcoeff(coeff,t.arg(0),t.arg(1), id); + else if(t.arg(1).is_numeral()) + IndexLAcoeff(coeff,t.arg(1),t.arg(0), id); + break; + default: + if(IsVar(t) && (fixing || la_index[pos].find(t) == la_index[pos].end())){ + la_index[pos][t] = id; + la_coeffs[pos][t] = coeff; + if(pos && !fixing) + la_pos_vars.push_back(t); // this means we only add a var once + } + else + IndexLAremove(t); + } + } + } + + void IndexLAstart(bool pos, const Term &t, int id){ + IndexLA(pos,(pos ? ctx.int_val(1) : ctx.int_val(-1)), t, id); + } + + void IndexLApred(bool pos, const Term &p, int id){ + if(p.is_app()){ + switch(p.decl().get_decl_kind()){ + case Not: + IndexLApred(!pos, p.arg(0),id); + break; + case Leq: + case Lt: + IndexLAstart(!pos, p.arg(0), id); + IndexLAstart(pos, p.arg(1), id); + break; + case Geq: + case Gt: + IndexLAstart(pos,p.arg(0), id); + IndexLAstart(!pos,p.arg(1), id); + break; + default: + IndexLAremove(p); + } + } + } + + void IndexLAfix(const Term &p, int id){ + fixing = true; + IndexLApred(true,p,id); + fixing = false; + } + + bool IsCanonIneq(const Term &lit, Term &term, Term &bound){ + // std::cout << Z3_simplify_get_help(ctx) << std::endl; + bool pos = lit.decl().get_decl_kind() != Not; + Term atom = pos ? lit : lit.arg(0); + if(atom.decl().get_decl_kind() == Leq){ + if(pos){ + bound = atom.arg(0); + term = atom.arg(1).simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + } + else { + bound = (atom.arg(1) + ctx.int_val(1)); + term = atom.arg(0); + // std::cout << "simplifying bound: " << bound << std::endl; + bound = bound.simplify(); + term = term.simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + } + return true; + } + else if(atom.decl().get_decl_kind() == Geq){ + if(pos){ + bound = atom.arg(1); // integer axiom + term = atom.arg(0).simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + return true; + } + else{ + bound = -(atom.arg(1) - ctx.int_val(1)); // integer axiom + term = -atom.arg(0); + bound = bound.simplify(); + term = term.simplify(simp_params); +#if Z3_MAJOR_VERSION < 4 + term = SortSum(term); +#endif + } + return true; + } + return false; + } + + Term CanonIneqTerm(const Term &p){ + Term term,bound; + Term ps = p.simplify(); + bool ok = IsCanonIneq(ps,term,bound); + assert(ok); + return term - bound; + } + + void ElimRedundantBounds(std::vector &lits){ + hash_map best_bound; + for(unsigned i = 0; i < lits.size(); i++){ + lits[i] = lits[i].simplify(simp_params); + Term term,bound; + if(IsCanonIneq(lits[i],term,bound)){ + if(best_bound.find(term) == best_bound.end()) + best_bound[term] = i; + else { + int best = best_bound[term]; + Term bterm,bbound; + IsCanonIneq(lits[best],bterm,bbound); + Term comp = bound > bbound; + comp = comp.simplify(); + if(eq(comp,ctx.bool_val(true))){ + lits[best] = ctx.bool_val(true); + best_bound[term] = i; + } + else { + lits[i] = ctx.bool_val(true); + } + } + } + } + } + + void FourierMotzkinCheap(const std::vector &lits_in, + std::vector &lits_out){ + simp_params.set(":som",true); + simp_params.set(":sort-sums",true); + fixing = false; lits_out = lits_in; + ElimRedundantBounds(lits_out); + for(unsigned i = 0; i < lits_out.size(); i++) + IndexLApred(true,lits_out[i],i); + + for(unsigned i = 0; i < la_pos_vars.size(); i++){ + Term var = la_pos_vars[i]; + if(la_index[false].find(var) != la_index[false].end()){ + int pos_idx = la_index[true][var]; + int neg_idx = la_index[false][var]; + if(pos_idx >= 0 && neg_idx >= 0){ + if(keep.find(var) != keep.end()){ + std::cout << "would have eliminated keep var\n"; + continue; + } + Term tpos = CanonIneqTerm(lits_out[pos_idx]); + Term tneg = CanonIneqTerm(lits_out[neg_idx]); + Term pos_coeff = la_coeffs[true][var]; + Term neg_coeff = -la_coeffs[false][var]; + Term comb = neg_coeff * tpos + pos_coeff * tneg; + Term ineq = ctx.int_val(0) <= comb; + ineq = ineq.simplify(); + lits_out[pos_idx] = ineq; + lits_out[neg_idx] = ctx.bool_val(true); + IndexLAfix(ineq,pos_idx); + } + } + } + } + + Term ProjectFormula(const Term &f){ + std::vector lits, new_lits1, new_lits2; + CollectConjuncts(f,lits); + timer_start("GaussElimCheap"); + GaussElimCheap(lits,new_lits1); + timer_stop("GaussElimCheap"); + timer_start("FourierMotzkinCheap"); + FourierMotzkinCheap(new_lits1,new_lits2); + timer_stop("FourierMotzkinCheap"); + return conjoin(new_lits2); + } + }; + + void Z3User::CollectConjuncts(const Term &f, std::vector &lits, bool pos){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + CollectConjuncts(f.arg(0), lits, !pos); + else if(pos && f.is_app() && f.decl().get_decl_kind() == And){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + CollectConjuncts(f.arg(i),lits,true); + } + else if(!pos && f.is_app() && f.decl().get_decl_kind() == Or){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + CollectConjuncts(f.arg(i),lits,false); + } + else if(pos){ + if(!eq(f,ctx.bool_val(true))) + lits.push_back(f); + } + else { + if(!eq(f,ctx.bool_val(false))) + lits.push_back(!f); + } + } + + struct TermLt { + bool operator()(const expr &x, const expr &y){ + unsigned xid = x.get_id(); + unsigned yid = y.get_id(); + return xid < yid; + } + }; + + void Z3User::SortTerms(std::vector &terms){ + TermLt foo; + std::sort(terms.begin(),terms.end(),foo); + } + + Z3User::Term Z3User::SortSum(const Term &t){ + if(!(t.is_app() && t.decl().get_decl_kind() == Plus)) + return t; + int nargs = t.num_args(); + if(nargs < 2) return t; + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = t.arg(i); + SortTerms(args); + if(nargs == 2) + return args[0] + args[1]; + return sum(args); + } + + + RPFP::Term RPFP::ProjectFormula(std::vector &keep_vec, const Term &f){ + VariableProjector vp(*this,keep_vec); + return vp.ProjectFormula(f); + } + + /** Compute an underapproximation of every node in a tree rooted at "root", + based on a previously computed counterexample. The underapproximation + may contain free variables that are implicitly existentially quantified. + */ + + RPFP::Term RPFP::ComputeUnderapprox(Node *root, int persist){ + /* if terminated underapprox is empty set (false) */ + bool show_model = false; + if(show_model) + std::cout << dualModel << std::endl; + if(!root->Outgoing){ + root->Underapprox.SetEmpty(); + return ctx.bool_val(true); + } + /* if not used in cex, underapprox is empty set (false) */ + if(Empty(root)){ + root->Underapprox.SetEmpty(); + return ctx.bool_val(true); + } + /* compute underapprox of children first */ + std::vector &chs = root->Outgoing->Children; + std::vector chu(chs.size()); + for(unsigned i = 0; i < chs.size(); i++) + chu[i] = ComputeUnderapprox(chs[i],persist); + + Term b; std::vector v; + RedVars(root, b, v); + /* underapproximate the edge formula */ + hash_set dont_cares; + dont_cares.insert(b); + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); + Term dual = root->Outgoing->dual.null() ? ctx.bool_val(true) : root->Outgoing->dual; + Term eu = UnderapproxFormula(dual,dont_cares); + timer_stop("UnderapproxFormula"); + /* combine with children */ + chu.push_back(eu); + eu = conjoin(chu); + /* project onto appropriate variables */ + eu = ProjectFormula(v,eu); + eu = eu.simplify(); + +#if 0 + /* check the result is consistent */ + { + hash_map memo; + int res = SubtermTruth(memo, eu); + if(res != 1) + throw "inconsistent projection"; + } +#endif + + /* rename to appropriate variable names */ + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[v[i]] = root->Annotation.IndParams[i]; /* copy names from annotation */ + Term funder = SubstRec(memo, eu); + root->Underapprox = CreateRelation(root->Annotation.IndParams,funder); +#if 0 + if(persist) + Z3_persist_ast(ctx,root->Underapprox.Formula,persist); +#endif + return eu; + } + + void RPFP::FixCurrentState(Edge *edge){ + hash_set dont_cares; + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); + Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; + Term eu = UnderapproxFormula(dual,dont_cares); + timer_stop("UnderapproxFormula"); + ConstrainEdgeLocalized(edge,eu); + } + + void RPFP::GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under){ + if(memo[under].find(f) != memo[under].end()) + return; + memo[under].insert(f); + if(f.is_app()){ + if(!under && !f.has_quantifiers()) + return; + decl_kind k = f.decl().get_decl_kind(); + if(k == And || k == Or || k == Implies || k == Iff){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + GetGroundLitsUnderQuants(memo,f.arg(i),res,under); + return; + } + } + else if (f.is_quantifier()){ +#if 0 + // treat closed quantified formula as a literal 'cause we hate nested quantifiers + if(under && IsClosedFormula(f)) + res.push_back(f); + else +#endif + GetGroundLitsUnderQuants(memo,f.body(),res,1); + return; + } + if(under && f.is_ground()) + res.push_back(f); + } + + RPFP::Term RPFP::StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits){ + hash_set memo[2]; + std::vector lits; + GetGroundLitsUnderQuants(memo, f, lits, 0); + hash_set lits_hash; + for(unsigned i = 0; i < lits.size(); i++) + lits_hash.insert(lits[i]); + hash_map subst; + hash_map stt_memo; + std::vector conjuncts; + for(unsigned i = 0; i < lits.size(); i++){ + const expr &lit = lits[i]; + if(lits_hash.find(NegateLit(lit)) == lits_hash.end()){ + case_lits.push_back(lit); + bool tval = false; + expr atom = lit; + if(lit.is_app() && lit.decl().get_decl_kind() == Not){ + tval = true; + atom = lit.arg(0); + } + expr etval = ctx.bool_val(tval); + if(atom.is_quantifier()) + subst[atom] = etval; // this is a bit desperate, since we can't eval quants + else { + int b = SubtermTruth(stt_memo,atom); + if(b == (tval ? 1 : 0)) + subst[atom] = etval; + else { + if(b == 0 || b == 1){ + etval = ctx.bool_val(b ? true : false); + subst[atom] = etval; + conjuncts.push_back(b ? atom : !atom); + } + } + } + } + } + expr g = f; + if(!subst.empty()){ + g = SubstRec(subst,f); + if(conjuncts.size()) + g = g && ctx.make(And,conjuncts); + g = g.simplify(); + } +#if 1 + expr g_old = g; + g = RemoveRedundancy(g); + bool changed = !eq(g,g_old); + g = g.simplify(); + if(changed) { // a second pass can get some more simplification + g = RemoveRedundancy(g); + g = g.simplify(); + } +#else + g = RemoveRedundancy(g); + g = g.simplify(); +#endif + g = AdjustQuantifiers(g); + return g; + } + + RPFP::Term RPFP::ModelValueAsConstraint(const Term &t){ + if(t.is_array()){ + ArrayValue arr; + Term e = dualModel.eval(t); + EvalArrayTerm(e, arr); + if(arr.defined){ + std::vector cs; + for(std::map::iterator it = arr.entries.begin(), en = arr.entries.end(); it != en; ++it){ + expr foo = select(t,expr(ctx,it->first)) == expr(ctx,it->second); + cs.push_back(foo); + } + return conjoin(cs); + } + } + else { + expr r = dualModel.get_const_interp(t.decl()); + if(!r.null()){ + expr res = t == expr(ctx,r); + return res; + } + } + return ctx.bool_val(true); + } + + void RPFP::EvalNodeAsConstraint(Node *p, Transformer &res) + { + Term b; std::vector v; + RedVars(p, b, v); + std::vector args; + for(unsigned i = 0; i < v.size(); i++){ + expr val = ModelValueAsConstraint(v[i]); + if(!eq(val,ctx.bool_val(true))) + args.push_back(val); + } + expr cnst = conjoin(args); + hash_map memo; + for (unsigned i = 0; i < v.size(); i++) + memo[v[i]] = p->Annotation.IndParams[i]; /* copy names from annotation */ + Term funder = SubstRec(memo, cnst); + res = CreateRelation(p->Annotation.IndParams,funder); + } + +#if 0 + void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ + // verify + s.push(); + expr conj = ctx.make(And,conjuncts); + s.add(conj); + check_result res = s.check(); + if(res != unsat) + throw "should be unsat"; + s.pop(1); + + for(unsigned i = 0; i < conjuncts.size(); ){ + std::swap(conjuncts[i],conjuncts.back()); + expr save = conjuncts.back(); + conjuncts.pop_back(); + s.push(); + expr conj = ctx.make(And,conjuncts); + s.add(conj); + check_result res = s.check(); + s.pop(1); + if(res != unsat){ + conjuncts.push_back(save); + std::swap(conjuncts[i],conjuncts.back()); + i++; + } + } + } +#endif + + void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ + std::vector lits(conjuncts.size()); + for(unsigned i = 0; i < lits.size(); i++){ + func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); + lits[i] = pred(); + s.add(ctx.make(Implies,lits[i],conjuncts[i])); + } + + // verify + check_result res = s.check(lits.size(),&lits[0]); + if(res != unsat){ + // add the axioms in the off chance they are useful + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + s.add(theory[i]); + for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! + if(s.check(lits.size(),&lits[0]) == unsat) + goto is_unsat; + throw "should be unsat"; + } + is_unsat: + for(unsigned i = 0; i < conjuncts.size(); ){ + std::swap(conjuncts[i],conjuncts.back()); + std::swap(lits[i],lits.back()); + check_result res = s.check(lits.size()-1,&lits[0]); + if(res != unsat){ + std::swap(conjuncts[i],conjuncts.back()); + std::swap(lits[i],lits.back()); + i++; + } + else { + conjuncts.pop_back(); + lits.pop_back(); + } + } + } + + void RPFP_caching::FilterCore(std::vector &core, std::vector &full_core){ + hash_set core_set; + std::copy(full_core.begin(),full_core.end(),std::inserter(core_set,core_set.begin())); + std::vector new_core; + for(unsigned i = 0; i < core.size(); i++) + if(core_set.find(core[i]) != core_set.end()) + new_core.push_back(core[i]); + core.swap(new_core); + } + + void RPFP_caching::GreedyReduceCache(std::vector &assumps, std::vector &core){ + std::vector lits = assumps, full_core; + std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); + + // verify + check_result res = CheckCore(lits,full_core); + if(res != unsat){ + // add the axioms in the off chance they are useful + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + GetAssumptionLits(theory[i],assumps); + lits = assumps; + std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); + + for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! + if((res = CheckCore(lits,full_core)) == unsat) + goto is_unsat; + throw "should be unsat"; + } + is_unsat: + FilterCore(core,full_core); + + std::vector dummy; + if(CheckCore(full_core,dummy) != unsat) + throw "should be unsat"; + + for(unsigned i = 0; i < core.size(); ){ + expr temp = core[i]; + std::swap(core[i],core.back()); + core.pop_back(); + lits.resize(assumps.size()); + std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); + res = CheckCore(lits,full_core); + if(res != unsat){ + core.push_back(temp); + std::swap(core[i],core.back()); + i++; + } + } + } + + expr RPFP::NegateLit(const expr &f){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + return f.arg(0); + else + return !f; + } + + void RPFP::NegateLits(std::vector &lits){ + for(unsigned i = 0; i < lits.size(); i++){ + expr &f = lits[i]; + if(f.is_app() && f.decl().get_decl_kind() == Not) + f = f.arg(0); + else + f = !f; + } + } + + expr RPFP::SimplifyOr(std::vector &lits){ + if(lits.size() == 0) + return ctx.bool_val(false); + if(lits.size() == 1) + return lits[0]; + return ctx.make(Or,lits); + } + + expr RPFP::SimplifyAnd(std::vector &lits){ + if(lits.size() == 0) + return ctx.bool_val(true); + if(lits.size() == 1) + return lits[0]; + return ctx.make(And,lits); + } + + + /* This is a wrapper for a solver that is intended to compute + implicants from models. It works around a problem in Z3 with + models in the non-extensional array theory. It does this by + naming all of the store terms in a formula. That is, (store ...) + is replaced by "name" with an added constraint name = (store + ...). This allows us to determine from the model whether an array + equality is true or false (it is false if the two sides are + mapped to different function symbols, even if they have the same + contents). + */ + + struct implicant_solver { + RPFP *owner; + solver &aux_solver; + std::vector assumps, namings; + std::vector assump_stack, naming_stack; + hash_map renaming, renaming_memo; + + void add(const expr &e){ + expr t = e; + if(!aux_solver.extensional_array_theory()){ + unsigned i = namings.size(); + t = owner->ExtractStores(renaming_memo,t,namings,renaming); + for(; i < namings.size(); i++) + aux_solver.add(namings[i]); + } + assumps.push_back(t); + aux_solver.add(t); + } + + void push() { + assump_stack.push_back(assumps.size()); + naming_stack.push_back(namings.size()); + aux_solver.push(); + } + + // When we pop the solver, we have to re-add any namings that were lost + + void pop(int n) { + aux_solver.pop(n); + int new_assumps = assump_stack[assump_stack.size()-n]; + int new_namings = naming_stack[naming_stack.size()-n]; + for(unsigned i = new_namings; i < namings.size(); i++) + aux_solver.add(namings[i]); + assumps.resize(new_assumps); + namings.resize(new_namings); + assump_stack.resize(assump_stack.size()-1); + naming_stack.resize(naming_stack.size()-1); + } + + check_result check() { + return aux_solver.check(); + } + + model get_model() { + return aux_solver.get_model(); + } + + expr get_implicant() { + owner->dualModel = aux_solver.get_model(); + expr dual = owner->ctx.make(And,assumps); + bool ext = aux_solver.extensional_array_theory(); + expr eu = owner->UnderapproxFullFormula(dual,ext); + // if we renamed store terms, we have to undo + if(!ext) + eu = owner->SubstRec(renaming,eu); + return eu; + } + + implicant_solver(RPFP *_owner, solver &_aux_solver) + : owner(_owner), aux_solver(_aux_solver) + {} + }; + + // set up edge constraint in aux solver + void RPFP::AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge){ + if(!edge->dual.null()) + aux_solver.add(edge->dual); + for(unsigned i = 0; i < edge->constraints.size(); i++){ + expr tl = edge->constraints[i]; + aux_solver.add(tl); + } + } + + void RPFP::AddEdgeToSolver(Edge *edge){ + if(!edge->dual.null()) + aux_solver.add(edge->dual); + for(unsigned i = 0; i < edge->constraints.size(); i++){ + expr tl = edge->constraints[i]; + aux_solver.add(tl); + } + } + + static int by_case_counter = 0; + + void RPFP::InterpolateByCases(Node *root, Node *node){ + timer_start("InterpolateByCases"); + bool axioms_added = false; + hash_set axioms_needed; + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + axioms_needed.insert(theory[i]); + implicant_solver is(this,aux_solver); + is.push(); + AddEdgeToSolver(is,node->Outgoing); + node->Annotation.SetEmpty(); + hash_set *core = new hash_set; + core->insert(node->Outgoing->dual); + while(1){ + by_case_counter++; + is.push(); + expr annot = !GetAnnotation(node); + is.add(annot); + if(is.check() == unsat){ + is.pop(1); + break; + } + is.pop(1); + Push(); + ConstrainEdgeLocalized(node->Outgoing,is.get_implicant()); + ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this? + check_result foo = Check(root); + if(foo != unsat){ + slvr().print("should_be_unsat.smt2"); + throw "should be unsat"; + } + std::vector assumps, axioms_to_add; + slvr().get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++){ + (*core).insert(assumps[i]); + if(axioms_needed.find(assumps[i]) != axioms_needed.end()){ + axioms_to_add.push_back(assumps[i]); + axioms_needed.erase(assumps[i]); + } + } + // AddToProofCore(*core); + Transformer old_annot = node->Annotation; + SolveSingleNode(root,node); + + { + expr itp = GetAnnotation(node); + dualModel = is.get_model(); // TODO: what does this mean? + std::vector case_lits; + itp = StrengthenFormulaByCaseSplitting(itp, case_lits); + SetAnnotation(node,itp); + node->Annotation.Formula = node->Annotation.Formula.simplify(); + } + + for(unsigned i = 0; i < axioms_to_add.size(); i++) + is.add(axioms_to_add[i]); + +#define TEST_BAD +#ifdef TEST_BAD + { + static int bad_count = 0, num_bads = 1; + if(bad_count >= num_bads){ + bad_count = 0; + num_bads = num_bads * 2; + Pop(1); + is.pop(1); + delete core; + timer_stop("InterpolateByCases"); + throw Bad(); + } + bad_count++; + } +#endif + + if(node->Annotation.IsEmpty()){ + if(!axioms_added){ + // add the axioms in the off chance they are useful + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + is.add(theory[i]); + axioms_added = true; + } + else { +#ifdef KILL_ON_BAD_INTERPOLANT + std::cout << "bad in InterpolateByCase -- core:\n"; +#if 0 + std::vector assumps; + slvr().get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++) + assumps[i].show(); +#endif + std::cout << "checking for inconsistency\n"; + std::cout << "model:\n"; + is.get_model().show(); + expr impl = is.get_implicant(); + std::vector conjuncts; + CollectConjuncts(impl,conjuncts,true); + std::cout << "impl:\n"; + for(unsigned i = 0; i < conjuncts.size(); i++) + conjuncts[i].show(); + std::cout << "annot:\n"; + annot.show(); + is.add(annot); + for(unsigned i = 0; i < conjuncts.size(); i++) + is.add(conjuncts[i]); + if(is.check() == unsat){ + std::cout << "inconsistent!\n"; + std::vector is_assumps; + is.aux_solver.get_proof().get_assumptions(is_assumps); + std::cout << "core:\n"; + for(unsigned i = 0; i < is_assumps.size(); i++) + is_assumps[i].show(); + } + else { + std::cout << "consistent!\n"; + is.aux_solver.print("should_be_inconsistent.smt2"); + } + std::cout << "by_case_counter = " << by_case_counter << "\n"; + throw "ack!"; +#endif + Pop(1); + is.pop(1); + delete core; + timer_stop("InterpolateByCases"); + throw Bad(); + } + } + Pop(1); + node->Annotation.UnionWith(old_annot); + } + if(proof_core) + delete proof_core; // shouldn't happen + proof_core = core; + is.pop(1); + timer_stop("InterpolateByCases"); + } + + void RPFP::Generalize(Node *root, Node *node){ + timer_start("Generalize"); + aux_solver.push(); + AddEdgeToSolver(node->Outgoing); + expr fmla = GetAnnotation(node); + std::vector conjuncts; + CollectConjuncts(fmla,conjuncts,false); + GreedyReduce(aux_solver,conjuncts); // try to remove conjuncts one at a tme + aux_solver.pop(1); + NegateLits(conjuncts); + SetAnnotation(node,SimplifyOr(conjuncts)); + timer_stop("Generalize"); + } + + RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models, bool axioms){ + edge_solver &es = edge_solvers[edge]; + uptr &p = es.slvr; + if(!p.get()){ + scoped_no_proof no_proofs_please(ctx.m()); // no proofs + p.set(new solver(ctx,true,models)); // no models + if(axioms){ + RPFP::LogicSolver *ls = edge->owner->ls; + const std::vector &axs = ls->get_axioms(); + for(unsigned i = 0; i < axs.size(); i++) + p.get()->add(axs[i]); + } + } + return es; + } + + + // caching version of above + void RPFP_caching::GeneralizeCache(Edge *edge){ + timer_start("Generalize"); + scoped_solver_for_edge ssfe(this,edge); + Node *node = edge->Parent; + std::vector assumps, core, conjuncts; + AssertEdgeCache(edge,assumps); + for(unsigned i = 0; i < edge->Children.size(); i++){ + expr ass = GetAnnotation(edge->Children[i]); + std::vector clauses; + if(!ass.is_true()){ + CollectConjuncts(ass.arg(1),clauses); + for(unsigned j = 0; j < clauses.size(); j++) + GetAssumptionLits(ass.arg(0) || clauses[j],assumps); + } + } + expr fmla = GetAnnotation(node); + std::vector lits; + if(fmla.is_true()){ + timer_stop("Generalize"); + return; + } + assumps.push_back(fmla.arg(0).arg(0)); + CollectConjuncts(!fmla.arg(1),lits); +#if 0 + for(unsigned i = 0; i < lits.size(); i++){ + const expr &lit = lits[i]; + if(lit.is_app() && lit.decl().get_decl_kind() == Equal){ + lits[i] = ctx.make(Leq,lit.arg(0),lit.arg(1)); + lits.push_back(ctx.make(Leq,lit.arg(1),lit.arg(0))); + } + } +#endif + hash_map lit_map; + for(unsigned i = 0; i < lits.size(); i++) + GetAssumptionLits(lits[i],core,&lit_map); + GreedyReduceCache(assumps,core); + for(unsigned i = 0; i < core.size(); i++) + conjuncts.push_back(lit_map[core[i]]); + NegateLits(conjuncts); + SetAnnotation(node,SimplifyOr(conjuncts)); + timer_stop("Generalize"); + } + + // caching version of above + bool RPFP_caching::PropagateCache(Edge *edge){ + timer_start("PropagateCache"); + scoped_solver_for_edge ssfe(this,edge); + bool some = false; + { + std::vector candidates, skip; + Node *node = edge->Parent; + CollectConjuncts(node->Annotation.Formula,skip); + for(unsigned i = 0; i < edge->Children.size(); i++){ + Node *child = edge->Children[i]; + if(child->map == node->map){ + CollectConjuncts(child->Annotation.Formula,candidates); + break; + } + } + if(candidates.empty()) goto done; + hash_set skip_set; + std::copy(skip.begin(),skip.end(),std::inserter(skip_set,skip_set.begin())); + std::vector new_candidates; + for(unsigned i = 0; i < candidates.size(); i++) + if(skip_set.find(candidates[i]) == skip_set.end()) + new_candidates.push_back(candidates[i]); + candidates.swap(new_candidates); + if(candidates.empty()) goto done; + std::vector assumps, core, conjuncts; + AssertEdgeCache(edge,assumps); + for(unsigned i = 0; i < edge->Children.size(); i++){ + expr ass = GetAnnotation(edge->Children[i]); + if(eq(ass,ctx.bool_val(true))) + continue; + std::vector clauses; + CollectConjuncts(ass.arg(1),clauses); + for(unsigned j = 0; j < clauses.size(); j++) + GetAssumptionLits(ass.arg(0) || clauses[j],assumps); + } + for(unsigned i = 0; i < candidates.size(); i++){ + unsigned old_size = assumps.size(); + node->Annotation.Formula = candidates[i]; + expr fmla = GetAnnotation(node); + assumps.push_back(fmla.arg(0).arg(0)); + GetAssumptionLits(!fmla.arg(1),assumps); + std::vector full_core; + check_result res = CheckCore(assumps,full_core); + if(res == unsat) + conjuncts.push_back(candidates[i]); + assumps.resize(old_size); + } + if(conjuncts.empty()) + goto done; + SetAnnotation(node,SimplifyAnd(conjuncts)); + some = true; + } + done: + timer_stop("PropagateCache"); + return some; + } + + + /** Push a scope. Assertions made after Push can be undone by Pop. */ + + void RPFP::Push() + { + stack.push_back(stack_entry()); + slvr_push(); + } + + /** Pop a scope (see Push). Note, you cannot pop axioms. */ + + void RPFP::Pop(int num_scopes) + { + slvr_pop(num_scopes); + for (int i = 0; i < num_scopes; i++) + { + stack_entry &back = stack.back(); + for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) + (*it)->dual = expr(ctx,NULL); + for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) + (*it)->dual = expr(ctx,NULL); + for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) + (*it).first->constraints.pop_back(); + stack.pop_back(); + } + } + + /** Erase the proof by performing a Pop, Push and re-assertion of + all the popped constraints */ + + void RPFP::PopPush(){ + slvr_pop(1); + slvr_push(); + stack_entry &back = stack.back(); + for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) + slvr_add((*it)->dual); + for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) + slvr_add((*it)->dual); + for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) + slvr_add((*it).second); + } + + + + // This returns a new FuncDel with same sort as top-level function + // of term t, but with numeric suffix appended to name. + + Z3User::FuncDecl Z3User::SuffixFuncDecl(Term t, int n) + { + std::string name = t.decl().name().str() + "_" + string_of_int(n); + std::vector sorts; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + sorts.push_back(t.arg(i).get_sort()); + return ctx.function(name.c_str(), nargs, &sorts[0], t.get_sort()); + } + + Z3User::FuncDecl Z3User::RenumberPred(const FuncDecl &f, int n) + { + std::string name = f.name().str(); + name = name.substr(0,name.rfind('_')) + "_" + string_of_int(n); + int arity = f.arity(); + std::vector domain; + for(int i = 0; i < arity; i++) + domain.push_back(f.domain(i)); + return ctx.function(name.c_str(), arity, &domain[0], f.range()); + } + + // Scan the clause body for occurrences of the predicate unknowns + + RPFP::Term RPFP::ScanBody(hash_map &memo, + const Term &t, + hash_map &pmap, + std::vector &parms, + std::vector &nodes) + { + if(memo.find(t) != memo.end()) + return memo[t]; + Term res(ctx); + if (t.is_app()) { + func_decl f = t.decl(); + if(pmap.find(f) != pmap.end()){ + nodes.push_back(pmap[f]); + f = SuffixFuncDecl(t,parms.size()); + parms.push_back(f); + } + int nargs = t.num_args(); + std::vector args; + for(int i = 0; i < nargs; i++) + args.push_back(ScanBody(memo,t.arg(i),pmap,parms,nodes)); + res = f(nargs,&args[0]); + } + else if (t.is_quantifier()) + res = CloneQuantifier(t,ScanBody(memo,t.body(),pmap,parms,nodes)); + else + res = t; + memo[t] = res; + return res; + } + + // return the func_del of an app if it is uninterpreted + + bool Z3User::get_relation(const Term &t, func_decl &R){ + if(!t.is_app()) + return false; + R = t.decl(); + return R.get_decl_kind() == Uninterpreted; + } + + // return true if term is an individual variable + // TODO: have to check that it is not a background symbol + + bool Z3User::is_variable(const Term &t){ + if(!t.is_app()) + return t.is_var(); + return t.decl().get_decl_kind() == Uninterpreted + && t.num_args() == 0; + } + + RPFP::Term RPFP::RemoveLabelsRec(hash_map &memo, const Term &t, + std::vector &lbls){ + if(memo.find(t) != memo.end()) + return memo[t]; + Term res(ctx); + if (t.is_app()){ + func_decl f = t.decl(); + std::vector names; + bool pos; + if(t.is_label(pos,names)){ + res = RemoveLabelsRec(memo,t.arg(0),lbls); + for(unsigned i = 0; i < names.size(); i++) + lbls.push_back(label_struct(names[i],res,pos)); + } + else { + int nargs = t.num_args(); + std::vector args; + for(int i = 0; i < nargs; i++) + args.push_back(RemoveLabelsRec(memo,t.arg(i),lbls)); + res = f(nargs,&args[0]); + } + } + else if (t.is_quantifier()) + res = CloneQuantifier(t,RemoveLabelsRec(memo,t.body(),lbls)); + else + res = t; + memo[t] = res; + return res; + } + + RPFP::Term RPFP::RemoveLabels(const Term &t, std::vector &lbls){ + hash_map memo ; + return RemoveLabelsRec(memo,t,lbls); + } + + RPFP::Term RPFP::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo[level].insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + 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))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()){ + int bound = t.get_quantifier_num_bound(); + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = SubstBoundRec(memo, subst, level + bound, pats[i]); + res = clone_quantifier(t, SubstBoundRec(memo, subst, level + bound, t.body()), pats); + } + else if (t.is_var()) { + int idx = t.get_index_value(); + if(idx >= level && subst.find(idx-level) != subst.end()){ + res = subst[idx-level]; + } + else res = t; + } + else res = t; + return res; + } + + RPFP::Term RPFP::SubstBound(hash_map &subst, const Term &t){ + hash_map > memo; + return SubstBoundRec(memo, subst, 0, t); + } + + int Z3User::MaxIndex(hash_map &memo, const Term &t) + { + std::pair foo(t,-1); + std::pair::iterator, bool> bar = memo.insert(foo); + int &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()){ + func_decl f = t.decl(); + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++){ + int m = MaxIndex(memo, t.arg(i)); + if(m > res) + res = m; + } + } + else if (t.is_quantifier()){ + int bound = t.get_quantifier_num_bound(); + res = MaxIndex(memo,t.body()) - bound; + } + else if (t.is_var()) { + res = t.get_index_value(); + } + return res; + } + + bool Z3User::IsClosedFormula(const Term &t){ + hash_map memo; + return MaxIndex(memo,t) < 0; + } + + + /** Convert a collection of clauses to Nodes and Edges in the RPFP. + + Predicate unknowns are uninterpreted predicates not + occurring in the background theory. + + Clauses are of the form + + B => P(t_1,...,t_k) + + where P is a predicate unknown and predicate unknowns + occur only positivey in H and only under existential + quantifiers in prenex form. + + Each predicate unknown maps to a node. Each clause maps to + an edge. Let C be a clause B => P(t_1,...,t_k) where the + sequence of predicate unknowns occurring in B (in order + of occurrence) is P_1..P_n. The clause maps to a transformer + T where: + + T.Relparams = P_1..P_n + T.Indparams = x_1...x+k + T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k + + Throws exception bad_clause(msg,i) if a clause i is + in the wrong form. + + */ + + + static bool canonical_clause(const expr &clause){ + if(clause.decl().get_decl_kind() != Implies) + return false; + expr arg = clause.arg(1); + return arg.is_app() && (arg.decl().get_decl_kind() == False || + arg.decl().get_decl_kind() == Uninterpreted); + } + +#define USE_QE_LITE + + void RPFP::FromClauses(const std::vector &unskolemized_clauses){ + hash_map pmap; + func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); + + std::vector clauses(unskolemized_clauses); + // first, skolemize the clauses + +#ifndef USE_QE_LITE + for(unsigned i = 0; i < clauses.size(); i++){ + expr &t = clauses[i]; + if (t.is_quantifier() && t.is_quantifier_forall()) { + int bound = t.get_quantifier_num_bound(); + std::vector sorts; + std::vector names; + hash_map subst; + for(int j = 0; j < bound; j++){ + 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)); + subst[bound-1-j] = skolem; + } + t = SubstBound(subst,t.body()); + } + } +#else + std::vector > substs(clauses.size()); +#endif + + // create the nodes from the heads of the clauses + + for(unsigned i = 0; i < clauses.size(); i++){ + Term &clause = clauses[i]; + +#ifdef USE_QE_LITE + Term &t = clause; + if (t.is_quantifier() && t.is_quantifier_forall()) { + int bound = t.get_quantifier_num_bound(); + std::vector sorts; + std::vector names; + for(int j = 0; j < bound; j++){ + 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)); + substs[i][bound-1-j] = skolem; + } + clause = t.body(); + } + +#endif + + if(clause.is_app() && clause.decl().get_decl_kind() == Uninterpreted) + clause = implies(ctx.bool_val(true),clause); + if(!canonical_clause(clause)) + clause = implies((!clause).simplify(),ctx.bool_val(false)); + Term head = clause.arg(1); + func_decl R(ctx); + bool is_query = false; + if (eq(head,ctx.bool_val(false))){ + R = fail_pred; + // R = ctx.constant("@Fail", ctx.bool_sort()).decl(); + is_query = true; + } + else if(!get_relation(head,R)) + throw bad_clause("rhs must be a predicate application",i); + if(pmap.find(R) == pmap.end()){ + + // If the node doesn't exitst, create it. The Indparams + // are arbitrary, but we use the rhs arguments if they + // are variables for mnomonic value. + + hash_set seen; + std::vector Indparams; + for(unsigned j = 0; j < head.num_args(); j++){ + Term arg = head.arg(j); + if(!is_variable(arg) || seen.find(arg) != seen.end()){ + std::string name = std::string("@a_") + string_of_int(j); + arg = ctx.constant(name.c_str(),arg.get_sort()); + } + seen.insert(arg); + Indparams.push_back(arg); + } +#ifdef USE_QE_LITE + { + hash_map > sb_memo; + for(unsigned j = 0; j < Indparams.size(); j++) + Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); + } +#endif + Node *node = CreateNode(R(Indparams.size(),&Indparams[0])); + //nodes.push_back(node); + pmap[R] = node; + if (is_query) + node->Bound = CreateRelation(std::vector(), ctx.bool_val(false)); + } + } + + bool some_labels = false; + + // create the edges + + for(unsigned i = 0; i < clauses.size(); i++){ + Term clause = clauses[i]; + Term body = clause.arg(0); + Term head = clause.arg(1); + func_decl R(ctx); + if (eq(head,ctx.bool_val(false))) + R = fail_pred; + //R = ctx.constant("@Fail", ctx.bool_sort()).decl(); + else get_relation(head,R); + Node *Parent = pmap[R]; + std::vector Indparams; + hash_set seen; + for(unsigned j = 0; j < head.num_args(); j++){ + Term arg = head.arg(j); + if(!is_variable(arg) || seen.find(arg) != seen.end()){ + std::string name = std::string("@a_") + string_of_int(j); + Term var = ctx.constant(name.c_str(),arg.get_sort()); + body = body && (arg == var); + arg = var; + } + seen.insert(arg); + Indparams.push_back(arg); + } + + // We extract the relparams positionally + + std::vector Relparams; + hash_map scan_memo; + std::vector Children; + body = ScanBody(scan_memo,body,pmap,Relparams,Children); + Term labeled = body; + std::vector lbls; // TODO: throw this away for now + body = RemoveLabels(body,lbls); + if(!eq(labeled,body)) + some_labels = true; // remember if there are labels, as we then can't do qe_lite + // body = IneqToEq(body); // UFO converts x=y to (x<=y & x >= y). Undo this. + body = body.simplify(); + +#ifdef USE_QE_LITE + std::set idxs; + if(!some_labels){ // can't do qe_lite if we have to reconstruct labels + for(unsigned j = 0; j < Indparams.size(); j++) + if(Indparams[j].is_var()) + idxs.insert(Indparams[j].get_index_value()); + body = body.qe_lite(idxs,false); + } + hash_map > sb_memo; + body = SubstBoundRec(sb_memo,substs[i],0,body); + if(some_labels) + labeled = SubstBoundRec(sb_memo,substs[i],0,labeled); + for(unsigned j = 0; j < Indparams.size(); j++) + Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); +#endif + + // Create the edge + Transformer T = CreateTransformer(Relparams,Indparams,body); + Edge *edge = CreateEdge(Parent,T,Children); + edge->labeled = labeled;; // remember for label extraction + // edges.push_back(edge); + } + + // undo hoisting of expressions out of loops + RemoveDeadNodes(); + Unhoist(); + // FuseEdges(); + } + + + // The following mess is used to undo hoisting of expressions outside loops by compilers + + expr RPFP::UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params){ + if(memo.find(w) != memo.end()) + return memo[w]; + expr res; + if(init_defs.find(w) != init_defs.end()){ + expr d = init_defs[w]; + std::vector vars; + hash_set get_vars_memo; + GetVarsRec(get_vars_memo,d,vars); + hash_map map; + for(unsigned j = 0; j < vars.size(); j++){ + expr x = vars[j]; + map[x] = UnhoistPullRec(memo,x,init_defs,const_params,const_params_inv,new_params); + } + expr defn = SubstRec(map,d); + res = defn; + } + else if(const_params_inv.find(w) == const_params_inv.end()){ + std::string old_name = w.decl().name().str(); + func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); + expr y = fresh(); + const_params[y] = w; + const_params_inv[w] = y; + new_params.push_back(y); + res = y; + } + else + res = const_params_inv[w]; + memo[w] = res; + return res; + } + + void RPFP::AddParamsToTransformer(Transformer &trans, const std::vector ¶ms){ + std::copy(params.begin(),params.end(),std::inserter(trans.IndParams,trans.IndParams.end())); + } + + expr RPFP::AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms){ + int n = app.num_args(); + std::vector args(n); + for (int i = 0; i < n; i++) + args[i] = app.arg(i); + std::copy(params.begin(),params.end(),std::inserter(args,args.end())); + return new_decl(args); + } + + expr RPFP::GetRelRec(hash_set &memo, const expr &t, const func_decl &rel){ + if(memo.find(t) != memo.end()) + return expr(); + memo.insert(t); + if (t.is_app()) + { + func_decl f = t.decl(); + if(f == rel) + return t; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++){ + expr res = GetRelRec(memo,t.arg(i),rel); + if(!res.null()) + return res; + } + } + else if (t.is_quantifier()) + return GetRelRec(memo,t.body(),rel); + return expr(); + } + + expr RPFP::GetRel(Edge *edge, int child_idx){ + func_decl &rel = edge->F.RelParams[child_idx]; + hash_set memo; + return GetRelRec(memo,edge->F.Formula,rel); + } + + void RPFP::GetDefsRec(const expr &cnst, hash_map &defs){ + if(cnst.is_app()){ + switch(cnst.decl().get_decl_kind()){ + case And: { + int n = cnst.num_args(); + for(int i = 0; i < n; i++) + GetDefsRec(cnst.arg(i),defs); + break; + } + case Equal: { + expr lhs = cnst.arg(0); + expr rhs = cnst.arg(1); + if(IsVar(lhs)) + defs[lhs] = rhs; + break; + } + default: + break; + } + } + } + + void RPFP::GetDefs(const expr &cnst, hash_map &defs){ + // GetDefsRec(IneqToEq(cnst),defs); + GetDefsRec(cnst,defs); + } + + bool RPFP::IsVar(const expr &t){ + return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; + } + + void RPFP::GetVarsRec(hash_set &memo, const expr &t, std::vector &vars){ + if(memo.find(t) != memo.end()) + return; + memo.insert(t); + if (t.is_app()) + { + if(IsVar(t)){ + vars.push_back(t); + return; + } + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++){ + GetVarsRec(memo,t.arg(i),vars); + } + } + else if (t.is_quantifier()) + GetVarsRec(memo,t.body(),vars); + } + + void RPFP::AddParamsToNode(Node *node, const std::vector ¶ms){ + int arity = node->Annotation.IndParams.size(); + std::vector domain; + for(int i = 0; i < arity; i++) + domain.push_back(node->Annotation.IndParams[i].get_sort()); + for(unsigned i = 0; i < params.size(); i++) + domain.push_back(params[i].get_sort()); + std::string old_name = node->Name.name().str(); + func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), domain, ctx.bool_sort()); + node->Name = fresh; + AddParamsToTransformer(node->Annotation,params); + AddParamsToTransformer(node->Bound,params); + AddParamsToTransformer(node->Underapprox,params); + } + + void RPFP::UnhoistLoop(Edge *loop_edge, Edge *init_edge){ + loop_edge->F.Formula = IneqToEq(loop_edge->F.Formula); + init_edge->F.Formula = IneqToEq(init_edge->F.Formula); + expr pre = GetRel(loop_edge,0); + if(pre.null()) + return; // this means the loop got simplified away + int nparams = loop_edge->F.IndParams.size(); + hash_map const_params, const_params_inv; + std::vector work_list; + // find the parameters that are constant in the loop + for(int i = 0; i < nparams; i++){ + if(eq(pre.arg(i),loop_edge->F.IndParams[i])){ + const_params[pre.arg(i)] = init_edge->F.IndParams[i]; + const_params_inv[init_edge->F.IndParams[i]] = pre.arg(i); + work_list.push_back(pre.arg(i)); + } + } + // get the definitions in the initialization + hash_map defs,memo,subst; + GetDefs(init_edge->F.Formula,defs); + // try to pull them inside the loop + std::vector new_params; + for(unsigned i = 0; i < work_list.size(); i++){ + expr v = work_list[i]; + expr w = const_params[v]; + expr def = UnhoistPullRec(memo,w,defs,const_params,const_params_inv,new_params); + if(!eq(def,v)) + subst[v] = def; + } + // do the substitutions + if(subst.empty()) + return; + subst[pre] = pre; // don't substitute inside the precondition itself + loop_edge->F.Formula = SubstRec(subst,loop_edge->F.Formula); + loop_edge->F.Formula = ElimIte(loop_edge->F.Formula); + init_edge->F.Formula = ElimIte(init_edge->F.Formula); + // add the new parameters + if(new_params.empty()) + return; + Node *parent = loop_edge->Parent; + AddParamsToNode(parent,new_params); + AddParamsToTransformer(loop_edge->F,new_params); + AddParamsToTransformer(init_edge->F,new_params); + std::vector &incoming = parent->Incoming; + for(unsigned i = 0; i < incoming.size(); i++){ + Edge *in_edge = incoming[i]; + std::vector &chs = in_edge->Children; + for(unsigned j = 0; j < chs.size(); j++) + if(chs[j] == parent){ + expr lit = GetRel(in_edge,j); + expr new_lit = AddParamsToApp(lit,parent->Name,new_params); + func_decl fd = SuffixFuncDecl(new_lit,j); + int nargs = new_lit.num_args(); + std::vector args; + for(int k = 0; k < nargs; k++) + args.push_back(new_lit.arg(k)); + new_lit = fd(nargs,&args[0]); + in_edge->F.RelParams[j] = fd; + hash_map map; + map[lit] = new_lit; + in_edge->F.Formula = SubstRec(map,in_edge->F.Formula); + } + } + } + + void RPFP::Unhoist(){ + hash_map > outgoing; + for(unsigned i = 0; i < edges.size(); i++) + outgoing[edges[i]->Parent].push_back(edges[i]); + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + std::vector &outs = outgoing[node]; + // if we're not a simple loop with one entry, give up + if(outs.size() == 2){ + for(int j = 0; j < 2; j++){ + Edge *loop_edge = outs[j]; + Edge *init_edge = outs[1-j]; + if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent){ + UnhoistLoop(loop_edge,init_edge); + break; + } + } + } + } + } + + void RPFP::FuseEdges(){ + hash_map > outgoing; + for(unsigned i = 0; i < edges.size(); i++){ + outgoing[edges[i]->Parent].push_back(edges[i]); + } + hash_set edges_to_delete; + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + std::vector &outs = outgoing[node]; + if(outs.size() > 1 && outs.size() <= 16){ + std::vector trs(outs.size()); + for(unsigned j = 0; j < outs.size(); j++) + trs[j] = &outs[j]->F; + Transformer tr = Fuse(trs); + std::vector chs; + for(unsigned j = 0; j < outs.size(); j++) + for(unsigned k = 0; k < outs[j]->Children.size(); k++) + chs.push_back(outs[j]->Children[k]); + CreateEdge(node,tr,chs); + for(unsigned j = 0; j < outs.size(); j++) + edges_to_delete.insert(outs[j]); + } + } + std::vector new_edges; + hash_set all_nodes; + for(unsigned j = 0; j < edges.size(); j++){ + if(edges_to_delete.find(edges[j]) == edges_to_delete.end()){ +#if 0 + if(all_nodes.find(edges[j]->Parent) != all_nodes.end()) + throw "help!"; + all_nodes.insert(edges[j]->Parent); +#endif + new_edges.push_back(edges[j]); + } + else + delete edges[j]; + } + edges.swap(new_edges); + } + + void RPFP::MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node){ + if(live_nodes.find(node) != live_nodes.end()) + return; + live_nodes.insert(node); + std::vector &outs = outgoing[node]; + for(unsigned i = 0; i < outs.size(); i++) + for(unsigned j = 0; j < outs[i]->Children.size(); j++) + MarkLiveNodes(outgoing, live_nodes,outs[i]->Children[j]); + } + + void RPFP::RemoveDeadNodes(){ + hash_map > outgoing; + for(unsigned i = 0; i < edges.size(); i++) + outgoing[edges[i]->Parent].push_back(edges[i]); + hash_set live_nodes; + for(unsigned i = 0; i < nodes.size(); i++) + if(!nodes[i]->Bound.IsFull()) + MarkLiveNodes(outgoing,live_nodes,nodes[i]); + std::vector new_edges; + for(unsigned j = 0; j < edges.size(); j++){ + if(live_nodes.find(edges[j]->Parent) != live_nodes.end()) + new_edges.push_back(edges[j]); + else { + Edge *edge = edges[j]; + for(unsigned int i = 0; i < edge->Children.size(); i++){ + std::vector &ic = edge->Children[i]->Incoming; + for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ + if(*it == edge){ + ic.erase(it); + break; + } + } + } + delete edge; + } + } + edges.swap(new_edges); + std::vector new_nodes; + for(unsigned j = 0; j < nodes.size(); j++){ + if(live_nodes.find(nodes[j]) != live_nodes.end()) + new_nodes.push_back(nodes[j]); + else + delete nodes[j]; + } + nodes.swap(new_nodes); + } + + void RPFP::WriteSolution(std::ostream &s){ + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + Term asgn = (node->Name)(node->Annotation.IndParams) == node->Annotation.Formula; + s << asgn << std::endl; + } + } + + void RPFP::WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s) + { + std::pair foo(t,0); + std::pair::iterator, bool> bar = memo.insert(foo); + // int &res = bar.first->second; + if(!bar.second) return; + hash_map::iterator it = e->varMap.find(t); + if (it != e->varMap.end()){ + return; + } + if (t.is_app()) + { + func_decl f = t.decl(); + // int idx; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + WriteEdgeVars(e, memo, t.arg(i),s); + if (nargs == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)){ + Term rename = HideVariable(t,e->number); + Term value = dualModel.eval(rename); + s << " (= " << t << " " << value << ")\n"; + } + } + else if (t.is_quantifier()) + WriteEdgeVars(e,memo,t.body(),s); + return; + } + + void RPFP::WriteEdgeAssignment(std::ostream &s, Edge *e){ + s << "(\n"; + hash_map memo; + WriteEdgeVars(e, memo, e->F.Formula ,s); + s << ")\n"; + } + + void RPFP::WriteCounterexample(std::ostream &s, Node *node){ + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ + Node *child = node->Outgoing->Children[i]; + if(!Empty(child)) + WriteCounterexample(s,child); + } + s << "(" << node->number << " : " << EvalNode(node) << " <- "; + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ + Node *child = node->Outgoing->Children[i]; + if(!Empty(child)) + s << " " << node->Outgoing->Children[i]->number; + } + s << ")" << std::endl; + WriteEdgeAssignment(s,node->Outgoing); + } + + RPFP::Term RPFP::ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + // int idx; + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(ToRuleRec(e, memo, t.arg(i),quants)); + hash_map::iterator rit = e->relMap.find(f); + if(rit != e->relMap.end()){ + Node* child = e->Children[rit->second]; + FuncDecl op = child->Name; + res = op(args.size(),&args[0]); + } + else { + res = f(args.size(),&args[0]); + if(nargs == 0 && t.decl().get_decl_kind() == Uninterpreted) + quants.push_back(t); + } + } + else if (t.is_quantifier()) + { + Term body = ToRuleRec(e,memo,t.body(),quants); + res = CloneQuantifier(t,body); + } + else res = t; + return res; + } + + + void RPFP::ToClauses(std::vector &cnsts, FileFormat format){ + cnsts.resize(edges.size()); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *edge = edges[i]; + SetEdgeMaps(edge); + std::vector quants; + hash_map memo; + Term lhs = ToRuleRec(edge, memo, edge->F.Formula,quants); + Term rhs = (edge->Parent->Name)(edge->F.IndParams.size(),&edge->F.IndParams[0]); + for(unsigned j = 0; j < edge->F.IndParams.size(); j++) + ToRuleRec(edge,memo,edge->F.IndParams[j],quants); // just to get quants + Term cnst = implies(lhs,rhs); +#if 0 + for(unsigned i = 0; i < quants.size(); i++){ + std::cout << expr(ctx,(Z3_ast)quants[i]) << " : " << sort(ctx,Z3_get_sort(ctx,(Z3_ast)quants[i])) << std::endl; + } +#endif + if(format != DualityFormat) + cnst= forall(quants,cnst); + cnsts[i] = cnst; + } + // int num_rules = cnsts.size(); + + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + if(!node->Bound.IsFull()){ + Term lhs = (node->Name)(node->Bound.IndParams) && !node->Bound.Formula; + Term cnst = implies(lhs,ctx.bool_val(false)); + if(format != DualityFormat){ + std::vector quants; + for(unsigned j = 0; j < node->Bound.IndParams.size(); j++) + quants.push_back(node->Bound.IndParams[j]); + if(format == HornFormat) + cnst= exists(quants,!cnst); + else + cnst= forall(quants, cnst); + } + cnsts.push_back(cnst); + } + } + + } + + + bool RPFP::proof_core_contains(const expr &e){ + return proof_core->find(e) != proof_core->end(); + } + + bool RPFP_caching::proof_core_contains(const expr &e){ + std::vector foo; + GetAssumptionLits(e,foo); + for(unsigned i = 0; i < foo.size(); i++) + if(proof_core->find(foo[i]) != proof_core->end()) + return true; + return false; + } + + bool RPFP::EdgeUsedInProof(Edge *edge){ + ComputeProofCore(); + if(!edge->dual.null() && proof_core_contains(edge->dual)) + return true; + for(unsigned i = 0; i < edge->constraints.size(); i++) + if(proof_core_contains(edge->constraints[i])) + return true; + return false; + } + +RPFP::~RPFP(){ + ClearProofCore(); + for(unsigned i = 0; i < nodes.size(); i++) + delete nodes[i]; + for(unsigned i = 0; i < edges.size(); i++) + delete edges[i]; + } +} + +#if 0 +void show_ast(expr *a){ + std::cout << *a; +} +#endif From a833c9ac4124d90c8188dd34a18a4faffbdc294d Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 2 Apr 2014 17:56:55 +0100 Subject: [PATCH 314/509] Fixed bug (codeplex issue 102) Signed-off-by: Christoph M. Wintersteiger --- examples/dotnet/Program.cs | 3 + examples/java/JavaExample.java | 3 + src/api/dotnet/Expr.cs | 288 ++++++++++++++++----------------- src/api/java/Expr.java | 288 ++++++++++++++++----------------- 4 files changed, 294 insertions(+), 288 deletions(-) diff --git a/examples/dotnet/Program.cs b/examples/dotnet/Program.cs index 4361cab96..6f12d81a4 100644 --- a/examples/dotnet/Program.cs +++ b/examples/dotnet/Program.cs @@ -93,6 +93,9 @@ namespace test_mapi 1, new Pattern[] { p } /* patterns */); + if (q.IsTrue) + Console.WriteLine("is true."); + return q; } diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 48395d8c2..992b0c477 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -89,6 +89,9 @@ class JavaExample names, /* names of quantified variables */ eq, 1, new Pattern[] { p } /* patterns */, null, null, null); + if (q.isTrue()) + System.out.println("is true"); + return q; } diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index 49b46edeb..dae4df445 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -269,57 +269,57 @@ namespace Microsoft.Z3 /// /// Indicates whether the term is the constant true. /// - public bool IsTrue { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TRUE; } } + public bool IsTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TRUE; } } /// /// Indicates whether the term is the constant false. /// - public bool IsFalse { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FALSE; } } + public bool IsFalse { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FALSE; } } /// /// Indicates whether the term is an equality predicate. /// - public bool IsEq { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EQ; } } + public bool IsEq { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EQ; } } /// /// Indicates whether the term is an n-ary distinct predicate (every argument is mutually distinct). /// - public bool IsDistinct { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DISTINCT; } } + public bool IsDistinct { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DISTINCT; } } /// /// Indicates whether the term is a ternary if-then-else term /// - public bool IsITE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ITE; } } + public bool IsITE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ITE; } } /// /// Indicates whether the term is an n-ary conjunction /// - public bool IsAnd { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AND; } } + public bool IsAnd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AND; } } /// /// Indicates whether the term is an n-ary disjunction /// - public bool IsOr { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OR; } } + public bool IsOr { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OR; } } /// /// Indicates whether the term is an if-and-only-if (Boolean equivalence, binary) /// - public bool IsIff { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IFF; } } + public bool IsIff { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IFF; } } /// /// Indicates whether the term is an exclusive or /// - public bool IsXor { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR; } } + public bool IsXor { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR; } } /// /// Indicates whether the term is a negation /// - public bool IsNot { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_NOT; } } + public bool IsNot { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_NOT; } } /// /// Indicates whether the term is an implication /// - public bool IsImplies { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IMPLIES; } } + public bool IsImplies { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IMPLIES; } } #endregion @@ -347,82 +347,82 @@ namespace Microsoft.Z3 /// /// Indicates whether the term is an arithmetic numeral. /// - public bool IsArithmeticNumeral { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ANUM; } } + public bool IsArithmeticNumeral { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ANUM; } } /// /// Indicates whether the term is a less-than-or-equal /// - public bool IsLE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LE; } } + public bool IsLE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LE; } } /// /// Indicates whether the term is a greater-than-or-equal /// - public bool IsGE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GE; } } + public bool IsGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GE; } } /// /// Indicates whether the term is a less-than /// - public bool IsLT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LT; } } + public bool IsLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LT; } } /// /// Indicates whether the term is a greater-than /// - public bool IsGT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GT; } } + public bool IsGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GT; } } /// /// Indicates whether the term is addition (binary) /// - public bool IsAdd { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ADD; } } + public bool IsAdd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ADD; } } /// /// Indicates whether the term is subtraction (binary) /// - public bool IsSub { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SUB; } } + public bool IsSub { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SUB; } } /// /// Indicates whether the term is a unary minus /// - public bool IsUMinus { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UMINUS; } } + public bool IsUMinus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UMINUS; } } /// /// Indicates whether the term is multiplication (binary) /// - public bool IsMul { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MUL; } } + public bool IsMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MUL; } } /// /// Indicates whether the term is division (binary) /// - public bool IsDiv { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DIV; } } + public bool IsDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DIV; } } /// /// Indicates whether the term is integer division (binary) /// - public bool IsIDiv { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IDIV; } } + public bool IsIDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IDIV; } } /// /// Indicates whether the term is remainder (binary) /// - public bool IsRemainder { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REM; } } + public bool IsRemainder { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REM; } } /// /// Indicates whether the term is modulus (binary) /// - public bool IsModulus { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MOD; } } + public bool IsModulus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MOD; } } /// /// Indicates whether the term is a coercion of integer to real (unary) /// - public bool IsIntToReal { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_REAL; } } + public bool IsIntToReal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_REAL; } } /// /// Indicates whether the term is a coercion of real to integer (unary) /// - public bool IsRealToInt { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_INT; } } + public bool IsRealToInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_INT; } } /// /// Indicates whether the term is a check that tests whether a real is integral (unary) /// - public bool IsRealIsInt { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IS_INT; } } + public bool IsRealIsInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IS_INT; } } #endregion #region Array Terms @@ -444,64 +444,64 @@ namespace Microsoft.Z3 /// /// It satisfies select(store(a,i,v),j) = if i = j then v else select(a,j). /// Array store takes at least 3 arguments. - public bool IsStore { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_STORE; } } + public bool IsStore { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_STORE; } } /// /// Indicates whether the term is an array select. /// - public bool IsSelect { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SELECT; } } + public bool IsSelect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SELECT; } } /// /// Indicates whether the term is a constant array. /// /// For example, select(const(v),i) = v holds for every v and i. The function is unary. - public bool IsConstantArray { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONST_ARRAY; } } + public bool IsConstantArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONST_ARRAY; } } /// /// Indicates whether the term is a default array. /// /// For example default(const(v)) = v. The function is unary. - public bool IsDefaultArray { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; } } + public bool IsDefaultArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; } } /// /// Indicates whether the term is an array map. /// /// It satisfies map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. - public bool IsArrayMap { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_MAP; } } + public bool IsArrayMap { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_MAP; } } /// /// Indicates whether the term is an as-array term. /// /// An as-array term is n array value that behaves as the function graph of the /// function passed as parameter. - public bool IsAsArray { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AS_ARRAY; } } + public bool IsAsArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AS_ARRAY; } } #endregion #region Set Terms /// /// Indicates whether the term is set union /// - public bool IsSetUnion { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_UNION; } } + public bool IsSetUnion { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_UNION; } } /// /// Indicates whether the term is set intersection /// - public bool IsSetIntersect { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_INTERSECT; } } + public bool IsSetIntersect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_INTERSECT; } } /// /// Indicates whether the term is set difference /// - public bool IsSetDifference { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; } } + public bool IsSetDifference { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; } } /// /// Indicates whether the term is set complement /// - public bool IsSetComplement { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; } } + public bool IsSetComplement { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; } } /// /// Indicates whether the term is set subset /// - public bool IsSetSubset { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_SUBSET; } } + public bool IsSetSubset { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_SUBSET; } } #endregion #region Bit-vector terms @@ -516,266 +516,266 @@ namespace Microsoft.Z3 /// /// Indicates whether the term is a bit-vector numeral /// - public bool IsBVNumeral { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNUM; } } + public bool IsBVNumeral { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNUM; } } /// /// Indicates whether the term is a one-bit bit-vector with value one /// - public bool IsBVBitOne { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT1; } } + public bool IsBVBitOne { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT1; } } /// /// Indicates whether the term is a one-bit bit-vector with value zero /// - public bool IsBVBitZero { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT0; } } + public bool IsBVBitZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT0; } } /// /// Indicates whether the term is a bit-vector unary minus /// - public bool IsBVUMinus { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNEG; } } + public bool IsBVUMinus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNEG; } } /// /// Indicates whether the term is a bit-vector addition (binary) /// - public bool IsBVAdd { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BADD; } } + public bool IsBVAdd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BADD; } } /// /// Indicates whether the term is a bit-vector subtraction (binary) /// - public bool IsBVSub { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSUB; } } + public bool IsBVSub { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSUB; } } /// /// Indicates whether the term is a bit-vector multiplication (binary) /// - public bool IsBVMul { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BMUL; } } + public bool IsBVMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BMUL; } } /// /// Indicates whether the term is a bit-vector signed division (binary) /// - public bool IsBVSDiv { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV; } } + public bool IsBVSDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV; } } /// /// Indicates whether the term is a bit-vector unsigned division (binary) /// - public bool IsBVUDiv { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV; } } + public bool IsBVUDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV; } } /// /// Indicates whether the term is a bit-vector signed remainder (binary) /// - public bool IsBVSRem { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM; } } + public bool IsBVSRem { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM; } } /// /// Indicates whether the term is a bit-vector unsigned remainder (binary) /// - public bool IsBVURem { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM; } } + public bool IsBVURem { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM; } } /// /// Indicates whether the term is a bit-vector signed modulus /// - public bool IsBVSMod { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD; } } + public bool IsBVSMod { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD; } } /// /// Indicates whether the term is a bit-vector signed division by zero /// - internal bool IsBVSDiv0 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV0; } } + internal bool IsBVSDiv0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV0; } } /// /// Indicates whether the term is a bit-vector unsigned division by zero /// - internal bool IsBVUDiv0 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV0; } } + internal bool IsBVUDiv0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV0; } } /// /// Indicates whether the term is a bit-vector signed remainder by zero /// - internal bool IsBVSRem0 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM0; } } + internal bool IsBVSRem0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM0; } } /// /// Indicates whether the term is a bit-vector unsigned remainder by zero /// - internal bool IsBVURem0 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM0; } } + internal bool IsBVURem0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM0; } } /// /// Indicates whether the term is a bit-vector signed modulus by zero /// - internal bool IsBVSMod0 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD0; } } + internal bool IsBVSMod0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD0; } } /// /// Indicates whether the term is an unsigned bit-vector less-than-or-equal /// - public bool IsBVULE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULEQ; } } + public bool IsBVULE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULEQ; } } /// /// Indicates whether the term is a signed bit-vector less-than-or-equal /// - public bool IsBVSLE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLEQ; } } + public bool IsBVSLE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLEQ; } } /// /// Indicates whether the term is an unsigned bit-vector greater-than-or-equal /// - public bool IsBVUGE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGEQ; } } + public bool IsBVUGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGEQ; } } /// /// Indicates whether the term is a signed bit-vector greater-than-or-equal /// - public bool IsBVSGE { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGEQ; } } + public bool IsBVSGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGEQ; } } /// /// Indicates whether the term is an unsigned bit-vector less-than /// - public bool IsBVULT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULT; } } + public bool IsBVULT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULT; } } /// /// Indicates whether the term is a signed bit-vector less-than /// - public bool IsBVSLT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLT; } } + public bool IsBVSLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLT; } } /// /// Indicates whether the term is an unsigned bit-vector greater-than /// - public bool IsBVUGT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGT; } } + public bool IsBVUGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGT; } } /// /// Indicates whether the term is a signed bit-vector greater-than /// - public bool IsBVSGT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGT; } } + public bool IsBVSGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGT; } } /// /// Indicates whether the term is a bit-wise AND /// - public bool IsBVAND { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BAND; } } + public bool IsBVAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BAND; } } /// /// Indicates whether the term is a bit-wise OR /// - public bool IsBVOR { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BOR; } } + public bool IsBVOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BOR; } } /// /// Indicates whether the term is a bit-wise NOT /// - public bool IsBVNOT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOT; } } + public bool IsBVNOT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOT; } } /// /// Indicates whether the term is a bit-wise XOR /// - public bool IsBVXOR { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXOR; } } + public bool IsBVXOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXOR; } } /// /// Indicates whether the term is a bit-wise NAND /// - public bool IsBVNAND { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNAND; } } + public bool IsBVNAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNAND; } } /// /// Indicates whether the term is a bit-wise NOR /// - public bool IsBVNOR { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOR; } } + public bool IsBVNOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOR; } } /// /// Indicates whether the term is a bit-wise XNOR /// - public bool IsBVXNOR { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXNOR; } } + public bool IsBVXNOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXNOR; } } /// /// Indicates whether the term is a bit-vector concatenation (binary) /// - public bool IsBVConcat { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONCAT; } } + public bool IsBVConcat { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONCAT; } } /// /// Indicates whether the term is a bit-vector sign extension /// - public bool IsBVSignExtension { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SIGN_EXT; } } + public bool IsBVSignExtension { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SIGN_EXT; } } /// /// Indicates whether the term is a bit-vector zero extension /// - public bool IsBVZeroExtension { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ZERO_EXT; } } + public bool IsBVZeroExtension { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ZERO_EXT; } } /// /// Indicates whether the term is a bit-vector extraction /// - public bool IsBVExtract { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXTRACT; } } + public bool IsBVExtract { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXTRACT; } } /// /// Indicates whether the term is a bit-vector repetition /// - public bool IsBVRepeat { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REPEAT; } } + public bool IsBVRepeat { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REPEAT; } } /// /// Indicates whether the term is a bit-vector reduce OR /// - public bool IsBVReduceOR { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDOR; } } + public bool IsBVReduceOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDOR; } } /// /// Indicates whether the term is a bit-vector reduce AND /// - public bool IsBVReduceAND { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDAND; } } + public bool IsBVReduceAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDAND; } } /// /// Indicates whether the term is a bit-vector comparison /// - public bool IsBVComp { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BCOMP; } } + public bool IsBVComp { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BCOMP; } } /// /// Indicates whether the term is a bit-vector shift left /// - public bool IsBVShiftLeft { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSHL; } } + public bool IsBVShiftLeft { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSHL; } } /// /// Indicates whether the term is a bit-vector logical shift right /// - public bool IsBVShiftRightLogical { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BLSHR; } } + public bool IsBVShiftRightLogical { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BLSHR; } } /// /// Indicates whether the term is a bit-vector arithmetic shift left /// - public bool IsBVShiftRightArithmetic { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BASHR; } } + public bool IsBVShiftRightArithmetic { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BASHR; } } /// /// Indicates whether the term is a bit-vector rotate left /// - public bool IsBVRotateLeft { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_LEFT; } } + public bool IsBVRotateLeft { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_LEFT; } } /// /// Indicates whether the term is a bit-vector rotate right /// - public bool IsBVRotateRight { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; } } + public bool IsBVRotateRight { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; } } /// /// Indicates whether the term is a bit-vector rotate left (extended) /// /// Similar to Z3_OP_ROTATE_LEFT, but it is a binary operator instead of a parametric one. - public bool IsBVRotateLeftExtended { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; } } + public bool IsBVRotateLeftExtended { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; } } /// /// Indicates whether the term is a bit-vector rotate right (extended) /// /// Similar to Z3_OP_ROTATE_RIGHT, but it is a binary operator instead of a parametric one. - public bool IsBVRotateRightExtended { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; } } + public bool IsBVRotateRightExtended { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; } } /// /// Indicates whether the term is a coercion from integer to bit-vector /// /// This function is not supported by the decision procedures. Only the most /// rudimentary simplification rules are applied to this function. - public bool IsIntToBV { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_INT2BV; } } + public bool IsIntToBV { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_INT2BV; } } /// /// Indicates whether the term is a coercion from bit-vector to integer /// /// This function is not supported by the decision procedures. Only the most /// rudimentary simplification rules are applied to this function. - public bool IsBVToInt { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BV2INT; } } + public bool IsBVToInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BV2INT; } } /// /// Indicates whether the term is a bit-vector carry /// /// Compute the carry bit in a full-adder. The meaning is given by the /// equivalence (carry l1 l2 l3) <=> (or (and l1 l2) (and l1 l3) (and l2 l3))) - public bool IsBVCarry { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CARRY; } } + public bool IsBVCarry { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CARRY; } } /// /// Indicates whether the term is a bit-vector ternary XOR /// /// The meaning is given by the equivalence (xor3 l1 l2 l3) <=> (xor (xor l1 l2) l3) - public bool IsBVXOR3 { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR3; } } + public bool IsBVXOR3 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR3; } } #endregion @@ -784,13 +784,13 @@ namespace Microsoft.Z3 /// Indicates whether the term is a label (used by the Boogie Verification condition generator). /// /// The label has two parameters, a string and a Boolean polarity. It takes one argument, a formula. - public bool IsLabel { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL; } } + public bool IsLabel { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL; } } /// /// Indicates whether the term is a label literal (used by the Boogie Verification condition generator). /// /// A label literal has a set of string parameters. It takes no arguments. - public bool IsLabelLit { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL_LIT; } } + public bool IsLabelLit { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL_LIT; } } #endregion #region Proof Terms @@ -799,22 +799,22 @@ namespace Microsoft.Z3 /// /// This binary predicate is used in proof terms. /// It captures equisatisfiability and equivalence modulo renamings. - public bool IsOEQ { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OEQ; } } + public bool IsOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OEQ; } } /// /// Indicates whether the term is a Proof for the expression 'true'. /// - public bool IsProofTrue { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRUE; } } + public bool IsProofTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRUE; } } /// /// Indicates whether the term is a proof for a fact asserted by the user. /// - public bool IsProofAsserted { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ASSERTED; } } + public bool IsProofAsserted { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ASSERTED; } } /// /// Indicates whether the term is a proof for a fact (tagged as goal) asserted by the user. /// - public bool IsProofGoal { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_GOAL; } } + public bool IsProofGoal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_GOAL; } } /// /// Indicates whether the term is proof via modus ponens @@ -825,7 +825,7 @@ namespace Microsoft.Z3 /// T2: (implies p q) /// [mp T1 T2]: q /// The second antecedents may also be a proof for (iff p q). - public bool IsProofModusPonens { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; } } + public bool IsProofModusPonens { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; } } /// /// Indicates whether the term is a proof for (R t t), where R is a reflexive relation. @@ -834,7 +834,7 @@ namespace Microsoft.Z3 /// The only reflexive relations that are used are /// equivalence modulo namings, equality and equivalence. /// That is, R is either '~', '=' or 'iff'. - public bool IsProofReflexivity { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; } } + public bool IsProofReflexivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; } } /// /// Indicates whether the term is proof by symmetricity of a relation @@ -845,7 +845,7 @@ namespace Microsoft.Z3 /// [symmetry T1]: (R s t) /// T1 is the antecedent of this proof object. /// - public bool IsProofSymmetry { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SYMMETRY; } } + public bool IsProofSymmetry { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SYMMETRY; } } /// /// Indicates whether the term is a proof by transitivity of a relation @@ -857,7 +857,7 @@ namespace Microsoft.Z3 /// T2: (R s u) /// [trans T1 T2]: (R t u) /// - public bool IsProofTransitivity { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; } } + public bool IsProofTransitivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; } } /// /// Indicates whether the term is a proof by condensed transitivity of a relation @@ -878,7 +878,7 @@ namespace Microsoft.Z3 /// if there is a path from s to t, if we view every /// antecedent (R a b) as an edge between a and b. /// - public bool IsProofTransitivityStar { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; } } + public bool IsProofTransitivityStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; } } /// @@ -892,7 +892,7 @@ namespace Microsoft.Z3 /// Remark: if t_i == s_i, then the antecedent Ti is suppressed. /// That is, reflexivity proofs are supressed to save space. /// - public bool IsProofMonotonicity { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } } + public bool IsProofMonotonicity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } } /// /// Indicates whether the term is a quant-intro proof @@ -902,7 +902,7 @@ namespace Microsoft.Z3 /// T1: (~ p q) /// [quant-intro T1]: (~ (forall (x) p) (forall (x) q)) /// - public bool IsProofQuantIntro { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; } } + public bool IsProofQuantIntro { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; } } /// /// Indicates whether the term is a distributivity proof object. @@ -920,7 +920,7 @@ namespace Microsoft.Z3 /// Remark. This rule is used by the CNF conversion pass and /// instantiated by f = or, and g = and. /// - public bool IsProofDistributivity { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; } } + public bool IsProofDistributivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; } } /// /// Indicates whether the term is a proof by elimination of AND @@ -930,7 +930,7 @@ namespace Microsoft.Z3 /// T1: (and l_1 ... l_n) /// [and-elim T1]: l_i /// - public bool IsProofAndElimination { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } } + public bool IsProofAndElimination { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } } /// /// Indicates whether the term is a proof by eliminiation of not-or @@ -940,7 +940,7 @@ namespace Microsoft.Z3 /// T1: (not (or l_1 ... l_n)) /// [not-or-elim T1]: (not l_i) /// - public bool IsProofOrElimination { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; } } + public bool IsProofOrElimination { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; } } /// /// Indicates whether the term is a proof by rewriting @@ -959,7 +959,7 @@ namespace Microsoft.Z3 /// (= (+ x 1 2) (+ 3 x)) /// (iff (or x false) x) /// - public bool IsProofRewrite { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE; } } + public bool IsProofRewrite { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE; } } /// /// Indicates whether the term is a proof by rewriting @@ -975,7 +975,7 @@ namespace Microsoft.Z3 /// - When converting bit-vectors to Booleans (BIT2BOOL=true) /// - When pulling ite expression up (PULL_CHEAP_ITE_TREES=true) /// - public bool IsProofRewriteStar { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } } + public bool IsProofRewriteStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } } /// /// Indicates whether the term is a proof for pulling quantifiers out. @@ -983,7 +983,7 @@ namespace Microsoft.Z3 /// /// A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) r))). This proof object has no antecedents. /// - public bool IsProofPullQuant { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } } + public bool IsProofPullQuant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } } /// /// Indicates whether the term is a proof for pulling quantifiers out. @@ -993,7 +993,7 @@ namespace Microsoft.Z3 /// This proof object is only used if the parameter PROOF_MODE is 1. /// This proof object has no antecedents /// - public bool IsProofPullQuantStar { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; } } + public bool IsProofPullQuantStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; } } /// /// Indicates whether the term is a proof for pushing quantifiers in. @@ -1006,7 +1006,7 @@ namespace Microsoft.Z3 /// (forall (x_1 ... x_m) p_n[x_1 ... x_m]))) /// This proof object has no antecedents /// - public bool IsProofPushQuant { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; } } + public bool IsProofPushQuant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; } } /// /// Indicates whether the term is a proof for elimination of unused variables. @@ -1018,7 +1018,7 @@ namespace Microsoft.Z3 /// It is used to justify the elimination of unused variables. /// This proof object has no antecedents. /// - public bool IsProofElimUnusedVars { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; } } + public bool IsProofElimUnusedVars { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; } } /// /// Indicates whether the term is a proof for destructive equality resolution @@ -1032,7 +1032,7 @@ namespace Microsoft.Z3 /// /// Several variables can be eliminated simultaneously. /// - public bool IsProofDER { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DER; } } + public bool IsProofDER { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DER; } } /// /// Indicates whether the term is a proof for quantifier instantiation @@ -1040,13 +1040,13 @@ namespace Microsoft.Z3 /// /// A proof of (or (not (forall (x) (P x))) (P a)) /// - public bool IsProofQuantInst { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } } + public bool IsProofQuantInst { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } } /// /// Indicates whether the term is a hypthesis marker. /// /// Mark a hypothesis in a natural deduction style proof. - public bool IsProofHypothesis { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } } + public bool IsProofHypothesis { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } } /// /// Indicates whether the term is a proof by lemma @@ -1059,7 +1059,7 @@ namespace Microsoft.Z3 /// It converts the proof in a proof for (or (not l_1) ... (not l_n)), /// when T1 contains the hypotheses: l_1, ..., l_n. /// - public bool IsProofLemma { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_LEMMA; } } + public bool IsProofLemma { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_LEMMA; } } /// /// Indicates whether the term is a proof by unit resolution @@ -1071,7 +1071,7 @@ namespace Microsoft.Z3 /// T(n+1): (not l_n) /// [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') /// - public bool IsProofUnitResolution { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; } } + public bool IsProofUnitResolution { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; } } /// /// Indicates whether the term is a proof by iff-true @@ -1080,7 +1080,7 @@ namespace Microsoft.Z3 /// T1: p /// [iff-true T1]: (iff p true) /// - public bool IsProofIFFTrue { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; } } + public bool IsProofIFFTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; } } /// /// Indicates whether the term is a proof by iff-false @@ -1089,7 +1089,7 @@ namespace Microsoft.Z3 /// T1: (not p) /// [iff-false T1]: (iff p false) /// - public bool IsProofIFFFalse { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; } } + public bool IsProofIFFFalse { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; } } /// /// Indicates whether the term is a proof by commutativity @@ -1102,7 +1102,7 @@ namespace Microsoft.Z3 /// This proof object has no antecedents. /// Remark: if f is bool, then = is iff. /// - public bool IsProofCommutativity { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; } } + public bool IsProofCommutativity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; } } /// /// Indicates whether the term is a proof for Tseitin-like axioms @@ -1138,7 +1138,7 @@ namespace Microsoft.Z3 /// unfolding the Boolean connectives in the axioms a small /// bounded number of steps (=3). /// - public bool IsProofDefAxiom { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; } } + public bool IsProofDefAxiom { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; } } /// /// Indicates whether the term is a proof for introduction of a name @@ -1161,7 +1161,7 @@ namespace Microsoft.Z3 /// Otherwise: /// [def-intro]: (= n e) /// - public bool IsProofDefIntro { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; } } + public bool IsProofDefIntro { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; } } /// /// Indicates whether the term is a proof for application of a definition @@ -1171,7 +1171,7 @@ namespace Microsoft.Z3 /// F is 'equivalent' to n, given that T1 is a proof that /// n is a name for F. /// - public bool IsProofApplyDef { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; } } + public bool IsProofApplyDef { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; } } /// /// Indicates whether the term is a proof iff-oeq @@ -1180,7 +1180,7 @@ namespace Microsoft.Z3 /// T1: (iff p q) /// [iff~ T1]: (~ p q) /// - public bool IsProofIFFOEQ { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; } } + public bool IsProofIFFOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; } } /// /// Indicates whether the term is a proof for a positive NNF step @@ -1208,7 +1208,7 @@ namespace Microsoft.Z3 /// NNF_NEG furthermore handles the case where negation is pushed /// over Boolean connectives 'and' and 'or'. /// - public bool IsProofNNFPos { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_POS; } } + public bool IsProofNNFPos { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_POS; } } /// /// Indicates whether the term is a proof for a negative NNF step @@ -1233,7 +1233,7 @@ namespace Microsoft.Z3 /// [nnf-neg T1 T2 T3 T4]: (~ (not (iff s_1 s_2)) /// (and (or r_1 r_2) (or r_1' r_2'))) /// - public bool IsProofNNFNeg { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } } + public bool IsProofNNFNeg { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } } /// /// Indicates whether the term is a proof for (~ P Q) here Q is in negation normal form. @@ -1245,7 +1245,7 @@ namespace Microsoft.Z3 /// /// This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. /// - public bool IsProofNNFStar { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_STAR; } } + public bool IsProofNNFStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_STAR; } } /// /// Indicates whether the term is a proof for (~ P Q) where Q is in conjunctive normal form. @@ -1255,7 +1255,7 @@ namespace Microsoft.Z3 /// This proof object is only used if the parameter PROOF_MODE is 1. /// This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. /// - public bool IsProofCNFStar { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_CNF_STAR; } } + public bool IsProofCNFStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_CNF_STAR; } } /// /// Indicates whether the term is a proof for a Skolemization step @@ -1268,7 +1268,7 @@ namespace Microsoft.Z3 /// /// This proof object has no antecedents. /// - public bool IsProofSkolemize { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; } } + public bool IsProofSkolemize { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; } } /// /// Indicates whether the term is a proof by modus ponens for equi-satisfiability. @@ -1279,7 +1279,7 @@ namespace Microsoft.Z3 /// T2: (~ p q) /// [mp~ T1 T2]: q /// - public bool IsProofModusPonensOEQ { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; } } + public bool IsProofModusPonensOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; } } /// /// Indicates whether the term is a proof for theory lemma @@ -1298,7 +1298,7 @@ namespace Microsoft.Z3 /// (iff (= t1 t2) (and (<= t1 t2) (<= t2 t1))) /// - gcd-test - Indicates an integer linear arithmetic lemma that uses a gcd test. /// - public bool IsProofTheoryLemma { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; } } + public bool IsProofTheoryLemma { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; } } #endregion #region Relational Terms @@ -1323,40 +1323,40 @@ namespace Microsoft.Z3 /// The function takes n+1 arguments, where the first argument is the relation and the remaining n elements /// correspond to the n columns of the relation. /// - public bool IsRelationStore { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_STORE; } } + public bool IsRelationStore { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_STORE; } } /// /// Indicates whether the term is an empty relation /// - public bool IsEmptyRelation { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_EMPTY; } } + public bool IsEmptyRelation { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_EMPTY; } } /// /// Indicates whether the term is a test for the emptiness of a relation /// - public bool IsIsEmptyRelation { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; } } + public bool IsIsEmptyRelation { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; } } /// /// Indicates whether the term is a relational join /// - public bool IsRelationalJoin { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_JOIN; } } + public bool IsRelationalJoin { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_JOIN; } } /// /// Indicates whether the term is the union or convex hull of two relations. /// /// The function takes two arguments. - public bool IsRelationUnion { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_UNION; } } + public bool IsRelationUnion { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_UNION; } } /// /// Indicates whether the term is the widening of two relations /// /// The function takes two arguments. - public bool IsRelationWiden { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_WIDEN; } } + public bool IsRelationWiden { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_WIDEN; } } /// /// Indicates whether the term is a projection of columns (provided as numbers in the parameters). /// /// The function takes one argument. - public bool IsRelationProject { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_PROJECT; } } + public bool IsRelationProject { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_PROJECT; } } /// /// Indicates whether the term is a relation filter @@ -1368,7 +1368,7 @@ namespace Microsoft.Z3 /// corresponding to the columns of the relation. /// So the first column in the relation has index 0. /// - public bool IsRelationFilter { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_FILTER; } } + public bool IsRelationFilter { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_FILTER; } } /// /// Indicates whether the term is an intersection of a relation with the negation of another. @@ -1384,7 +1384,7 @@ namespace Microsoft.Z3 /// target are elements in x in pos, such that there is no y in neg that agrees with /// x on the columns c1, d1, .., cN, dN. /// - public bool IsRelationNegationFilter { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; } } + public bool IsRelationNegationFilter { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; } } /// /// Indicates whether the term is the renaming of a column in a relation @@ -1393,12 +1393,12 @@ namespace Microsoft.Z3 /// The function takes one argument. /// The parameters contain the renaming as a cycle. /// - public bool IsRelationRename { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_RENAME; } } + public bool IsRelationRename { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_RENAME; } } /// /// Indicates whether the term is the complement of a relation /// - public bool IsRelationComplement { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; } } + public bool IsRelationComplement { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; } } /// /// Indicates whether the term is a relational select @@ -1408,7 +1408,7 @@ namespace Microsoft.Z3 /// The function takes n+1 arguments, where the first argument is a relation, /// and the remaining n arguments correspond to a record. /// - public bool IsRelationSelect { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_SELECT; } } + public bool IsRelationSelect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_SELECT; } } /// /// Indicates whether the term is a relational clone (copy) @@ -1420,7 +1420,7 @@ namespace Microsoft.Z3 /// for terms of kind /// to perform destructive updates to the first argument. /// - public bool IsRelationClone { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_CLONE; } } + public bool IsRelationClone { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_CLONE; } } #endregion #region Finite domain terms @@ -1439,7 +1439,7 @@ namespace Microsoft.Z3 /// /// Indicates whether the term is a less than predicate over a finite domain. /// - public bool IsFiniteDomainLT { get { return FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FD_LT; } } + public bool IsFiniteDomainLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FD_LT; } } #endregion #endregion diff --git a/src/api/java/Expr.java b/src/api/java/Expr.java index 7793a16e5..5c4f90920 100644 --- a/src/api/java/Expr.java +++ b/src/api/java/Expr.java @@ -245,7 +245,7 @@ public class Expr extends AST **/ public boolean isTrue() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TRUE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TRUE; } /** @@ -253,7 +253,7 @@ public class Expr extends AST **/ public boolean isFalse() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FALSE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FALSE; } /** @@ -261,7 +261,7 @@ public class Expr extends AST **/ public boolean isEq() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EQ; } /** @@ -270,7 +270,7 @@ public class Expr extends AST **/ public boolean isDistinct() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DISTINCT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DISTINCT; } /** @@ -278,7 +278,7 @@ public class Expr extends AST **/ public boolean isITE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ITE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ITE; } /** @@ -286,7 +286,7 @@ public class Expr extends AST **/ public boolean isAnd() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AND; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AND; } /** @@ -294,7 +294,7 @@ public class Expr extends AST **/ public boolean isOr() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OR; } /** @@ -303,7 +303,7 @@ public class Expr extends AST **/ public boolean isIff() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IFF; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IFF; } /** @@ -311,7 +311,7 @@ public class Expr extends AST **/ public boolean isXor() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR; } /** @@ -319,7 +319,7 @@ public class Expr extends AST **/ public boolean isNot() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_NOT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_NOT; } /** @@ -327,7 +327,7 @@ public class Expr extends AST **/ public boolean isImplies() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IMPLIES; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IMPLIES; } /** @@ -356,7 +356,7 @@ public class Expr extends AST **/ public boolean isArithmeticNumeral() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ANUM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ANUM; } /** @@ -364,7 +364,7 @@ public class Expr extends AST **/ public boolean isLE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LE; } /** @@ -372,7 +372,7 @@ public class Expr extends AST **/ public boolean isGE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GE; } /** @@ -380,7 +380,7 @@ public class Expr extends AST **/ public boolean isLT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LT; } /** @@ -388,7 +388,7 @@ public class Expr extends AST **/ public boolean isGT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GT; } /** @@ -396,7 +396,7 @@ public class Expr extends AST **/ public boolean isAdd() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ADD; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ADD; } /** @@ -404,7 +404,7 @@ public class Expr extends AST **/ public boolean isSub() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SUB; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SUB; } /** @@ -412,7 +412,7 @@ public class Expr extends AST **/ public boolean isUMinus() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UMINUS; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UMINUS; } /** @@ -420,7 +420,7 @@ public class Expr extends AST **/ public boolean isMul() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MUL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MUL; } /** @@ -428,7 +428,7 @@ public class Expr extends AST **/ public boolean isDiv() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DIV; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DIV; } /** @@ -436,7 +436,7 @@ public class Expr extends AST **/ public boolean isIDiv() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IDIV; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IDIV; } /** @@ -444,7 +444,7 @@ public class Expr extends AST **/ public boolean isRemainder() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REM; } /** @@ -452,7 +452,7 @@ public class Expr extends AST **/ public boolean isModulus() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MOD; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MOD; } /** @@ -460,7 +460,7 @@ public class Expr extends AST **/ public boolean isIntToReal() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_REAL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_REAL; } /** @@ -468,7 +468,7 @@ public class Expr extends AST **/ public boolean isRealToInt() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_INT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_INT; } /** @@ -477,7 +477,7 @@ public class Expr extends AST **/ public boolean isRealIsInt() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IS_INT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IS_INT; } /** @@ -497,7 +497,7 @@ public class Expr extends AST **/ public boolean isStore() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_STORE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_STORE; } /** @@ -505,7 +505,7 @@ public class Expr extends AST **/ public boolean isSelect() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SELECT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SELECT; } /** @@ -515,7 +515,7 @@ public class Expr extends AST **/ public boolean isConstantArray() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONST_ARRAY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONST_ARRAY; } /** @@ -524,7 +524,7 @@ public class Expr extends AST **/ public boolean isDefaultArray() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; } /** @@ -533,7 +533,7 @@ public class Expr extends AST **/ public boolean isArrayMap() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_MAP; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_MAP; } /** @@ -543,7 +543,7 @@ public class Expr extends AST **/ public boolean isAsArray() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AS_ARRAY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AS_ARRAY; } /** @@ -551,7 +551,7 @@ public class Expr extends AST **/ public boolean isSetUnion() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_UNION; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_UNION; } /** @@ -559,7 +559,7 @@ public class Expr extends AST **/ public boolean isSetIntersect() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_INTERSECT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_INTERSECT; } /** @@ -567,7 +567,7 @@ public class Expr extends AST **/ public boolean isSetDifference() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; } /** @@ -575,7 +575,7 @@ public class Expr extends AST **/ public boolean isSetComplement() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; } /** @@ -583,7 +583,7 @@ public class Expr extends AST **/ public boolean isSetSubset() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_SUBSET; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_SUBSET; } /** @@ -601,7 +601,7 @@ public class Expr extends AST **/ public boolean isBVNumeral() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNUM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNUM; } /** @@ -609,7 +609,7 @@ public class Expr extends AST **/ public boolean isBVBitOne() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT1; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT1; } /** @@ -617,7 +617,7 @@ public class Expr extends AST **/ public boolean isBVBitZero() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT0; } /** @@ -625,7 +625,7 @@ public class Expr extends AST **/ public boolean isBVUMinus() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNEG; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNEG; } /** @@ -633,7 +633,7 @@ public class Expr extends AST **/ public boolean isBVAdd() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BADD; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BADD; } /** @@ -641,7 +641,7 @@ public class Expr extends AST **/ public boolean isBVSub() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSUB; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSUB; } /** @@ -649,7 +649,7 @@ public class Expr extends AST **/ public boolean isBVMul() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BMUL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BMUL; } /** @@ -657,7 +657,7 @@ public class Expr extends AST **/ public boolean isBVSDiv() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV; } /** @@ -665,7 +665,7 @@ public class Expr extends AST **/ public boolean isBVUDiv() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV; } /** @@ -673,7 +673,7 @@ public class Expr extends AST **/ public boolean isBVSRem() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM; } /** @@ -681,7 +681,7 @@ public class Expr extends AST **/ public boolean isBVURem() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM; } /** @@ -689,7 +689,7 @@ public class Expr extends AST **/ public boolean isBVSMod() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD; } /** @@ -697,7 +697,7 @@ public class Expr extends AST **/ boolean IsBVSDiv0() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV0; } /** @@ -705,7 +705,7 @@ public class Expr extends AST **/ boolean IsBVUDiv0() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV0; } /** @@ -713,7 +713,7 @@ public class Expr extends AST **/ boolean IsBVSRem0() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM0; } /** @@ -721,7 +721,7 @@ public class Expr extends AST **/ boolean IsBVURem0() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM0; } /** @@ -729,7 +729,7 @@ public class Expr extends AST **/ boolean IsBVSMod0() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD0; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD0; } /** @@ -737,7 +737,7 @@ public class Expr extends AST **/ public boolean isBVULE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULEQ; } /** @@ -745,7 +745,7 @@ public class Expr extends AST **/ public boolean isBVSLE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLEQ; } /** @@ -754,7 +754,7 @@ public class Expr extends AST **/ public boolean isBVUGE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGEQ; } /** @@ -762,7 +762,7 @@ public class Expr extends AST **/ public boolean isBVSGE() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGEQ; } /** @@ -770,7 +770,7 @@ public class Expr extends AST **/ public boolean isBVULT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULT; } /** @@ -778,7 +778,7 @@ public class Expr extends AST **/ public boolean isBVSLT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLT; } /** @@ -786,7 +786,7 @@ public class Expr extends AST **/ public boolean isBVUGT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGT; } /** @@ -794,7 +794,7 @@ public class Expr extends AST **/ public boolean isBVSGT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGT; } /** @@ -802,7 +802,7 @@ public class Expr extends AST **/ public boolean isBVAND() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BAND; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BAND; } /** @@ -810,7 +810,7 @@ public class Expr extends AST **/ public boolean isBVOR() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BOR; } /** @@ -818,7 +818,7 @@ public class Expr extends AST **/ public boolean isBVNOT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOT; } /** @@ -826,7 +826,7 @@ public class Expr extends AST **/ public boolean isBVXOR() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXOR; } /** @@ -834,7 +834,7 @@ public class Expr extends AST **/ public boolean isBVNAND() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNAND; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNAND; } /** @@ -842,7 +842,7 @@ public class Expr extends AST **/ public boolean isBVNOR() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOR; } /** @@ -850,7 +850,7 @@ public class Expr extends AST **/ public boolean isBVXNOR() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXNOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXNOR; } /** @@ -858,7 +858,7 @@ public class Expr extends AST **/ public boolean isBVConcat() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONCAT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONCAT; } /** @@ -866,7 +866,7 @@ public class Expr extends AST **/ public boolean isBVSignExtension() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SIGN_EXT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SIGN_EXT; } /** @@ -874,7 +874,7 @@ public class Expr extends AST **/ public boolean isBVZeroExtension() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ZERO_EXT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ZERO_EXT; } /** @@ -882,7 +882,7 @@ public class Expr extends AST **/ public boolean isBVExtract() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXTRACT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXTRACT; } /** @@ -890,7 +890,7 @@ public class Expr extends AST **/ public boolean isBVRepeat() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REPEAT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REPEAT; } /** @@ -898,7 +898,7 @@ public class Expr extends AST **/ public boolean isBVReduceOR() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDOR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDOR; } /** @@ -906,7 +906,7 @@ public class Expr extends AST **/ public boolean isBVReduceAND() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDAND; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDAND; } /** @@ -914,7 +914,7 @@ public class Expr extends AST **/ public boolean isBVComp() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BCOMP; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BCOMP; } /** @@ -922,7 +922,7 @@ public class Expr extends AST **/ public boolean isBVShiftLeft() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSHL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSHL; } /** @@ -930,7 +930,7 @@ public class Expr extends AST **/ public boolean isBVShiftRightLogical() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BLSHR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BLSHR; } /** @@ -938,7 +938,7 @@ public class Expr extends AST **/ public boolean isBVShiftRightArithmetic() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BASHR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BASHR; } /** @@ -946,7 +946,7 @@ public class Expr extends AST **/ public boolean isBVRotateLeft() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_LEFT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_LEFT; } /** @@ -954,7 +954,7 @@ public class Expr extends AST **/ public boolean isBVRotateRight() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; } /** @@ -964,7 +964,7 @@ public class Expr extends AST **/ public boolean isBVRotateLeftExtended() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; } /** @@ -974,7 +974,7 @@ public class Expr extends AST **/ public boolean isBVRotateRightExtended() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; } /** @@ -985,7 +985,7 @@ public class Expr extends AST **/ public boolean isIntToBV() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_INT2BV; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_INT2BV; } /** @@ -996,7 +996,7 @@ public class Expr extends AST **/ public boolean isBVToInt() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BV2INT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BV2INT; } /** @@ -1006,7 +1006,7 @@ public class Expr extends AST **/ public boolean isBVCarry() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CARRY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CARRY; } /** @@ -1016,7 +1016,7 @@ public class Expr extends AST **/ public boolean isBVXOR3() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR3; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR3; } /** @@ -1026,7 +1026,7 @@ public class Expr extends AST **/ public boolean isLabel() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL; } /** @@ -1036,7 +1036,7 @@ public class Expr extends AST **/ public boolean isLabelLit() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL_LIT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL_LIT; } /** @@ -1046,7 +1046,7 @@ public class Expr extends AST **/ public boolean isOEQ() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OEQ; } /** @@ -1054,7 +1054,7 @@ public class Expr extends AST **/ public boolean isProofTrue() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRUE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRUE; } /** @@ -1062,7 +1062,7 @@ public class Expr extends AST **/ public boolean isProofAsserted() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ASSERTED; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ASSERTED; } /** @@ -1071,7 +1071,7 @@ public class Expr extends AST **/ public boolean isProofGoal() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_GOAL; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_GOAL; } /** @@ -1082,7 +1082,7 @@ public class Expr extends AST **/ public boolean isProofModusPonens() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; } /** @@ -1094,7 +1094,7 @@ public class Expr extends AST **/ public boolean isProofReflexivity() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; } /** @@ -1105,7 +1105,7 @@ public class Expr extends AST **/ public boolean isProofSymmetry() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SYMMETRY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SYMMETRY; } /** @@ -1116,7 +1116,7 @@ public class Expr extends AST **/ public boolean isProofTransitivity() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; } /** @@ -1134,7 +1134,7 @@ public class Expr extends AST **/ public boolean isProofTransitivityStar() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; } /** @@ -1146,7 +1146,7 @@ public class Expr extends AST **/ public boolean isProofMonotonicity() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } /** @@ -1156,7 +1156,7 @@ public class Expr extends AST **/ public boolean isProofQuantIntro() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; } /** @@ -1172,7 +1172,7 @@ public class Expr extends AST **/ public boolean isProofDistributivity() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; } /** @@ -1182,7 +1182,7 @@ public class Expr extends AST **/ public boolean isProofAndElimination() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_AND_ELIM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } /** @@ -1192,7 +1192,7 @@ public class Expr extends AST **/ public boolean isProofOrElimination() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; } /** @@ -1209,7 +1209,7 @@ public class Expr extends AST **/ public boolean isProofRewrite() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE; } /** @@ -1225,7 +1225,7 @@ public class Expr extends AST **/ public boolean isProofRewriteStar() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } /** @@ -1235,7 +1235,7 @@ public class Expr extends AST **/ public boolean isProofPullQuant() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } /** @@ -1246,7 +1246,7 @@ public class Expr extends AST **/ public boolean isProofPullQuantStar() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; } /** @@ -1258,7 +1258,7 @@ public class Expr extends AST **/ public boolean isProofPushQuant() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; } /** @@ -1271,7 +1271,7 @@ public class Expr extends AST **/ public boolean isProofElimUnusedVars() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; } /** @@ -1285,7 +1285,7 @@ public class Expr extends AST **/ public boolean isProofDER() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DER; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DER; } /** @@ -1294,7 +1294,7 @@ public class Expr extends AST **/ public boolean isProofQuantInst() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INST; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } /** @@ -1303,7 +1303,7 @@ public class Expr extends AST **/ public boolean isProofHypothesis() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } /** @@ -1316,7 +1316,7 @@ public class Expr extends AST **/ public boolean isProofLemma() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_LEMMA; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_LEMMA; } /** @@ -1326,7 +1326,7 @@ public class Expr extends AST **/ public boolean isProofUnitResolution() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; } /** @@ -1335,7 +1335,7 @@ public class Expr extends AST **/ public boolean isProofIFFTrue() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; } /** @@ -1344,7 +1344,7 @@ public class Expr extends AST **/ public boolean isProofIFFFalse() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; } /** @@ -1358,7 +1358,7 @@ public class Expr extends AST **/ public boolean isProofCommutativity() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; } /** @@ -1381,7 +1381,7 @@ public class Expr extends AST **/ public boolean isProofDefAxiom() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; } /** @@ -1402,7 +1402,7 @@ public class Expr extends AST **/ public boolean isProofDefIntro() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; } /** @@ -1412,7 +1412,7 @@ public class Expr extends AST **/ public boolean isProofApplyDef() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; } /** @@ -1421,7 +1421,7 @@ public class Expr extends AST **/ public boolean isProofIFFOEQ() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; } /** @@ -1446,7 +1446,7 @@ public class Expr extends AST **/ public boolean isProofNNFPos() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_POS; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_POS; } /** @@ -1462,7 +1462,7 @@ public class Expr extends AST **/ public boolean isProofNNFNeg() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_NEG; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } /** @@ -1477,7 +1477,7 @@ public class Expr extends AST **/ public boolean isProofNNFStar() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_STAR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_STAR; } /** @@ -1489,7 +1489,7 @@ public class Expr extends AST **/ public boolean isProofCNFStar() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_CNF_STAR; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_CNF_STAR; } /** @@ -1503,7 +1503,7 @@ public class Expr extends AST **/ public boolean isProofSkolemize() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; } /** @@ -1513,7 +1513,7 @@ public class Expr extends AST **/ public boolean isProofModusPonensOEQ() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; } /** @@ -1532,7 +1532,7 @@ public class Expr extends AST **/ public boolean isProofTheoryLemma() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; } /** @@ -1554,7 +1554,7 @@ public class Expr extends AST **/ public boolean isRelationStore() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_STORE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_STORE; } /** @@ -1562,7 +1562,7 @@ public class Expr extends AST **/ public boolean isEmptyRelation() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_EMPTY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_EMPTY; } /** @@ -1570,7 +1570,7 @@ public class Expr extends AST **/ public boolean isIsEmptyRelation() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; } /** @@ -1578,7 +1578,7 @@ public class Expr extends AST **/ public boolean isRelationalJoin() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_JOIN; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_JOIN; } /** @@ -1587,7 +1587,7 @@ public class Expr extends AST **/ public boolean isRelationUnion() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_UNION; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_UNION; } /** @@ -1596,7 +1596,7 @@ public class Expr extends AST **/ public boolean isRelationWiden() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_WIDEN; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_WIDEN; } /** @@ -1606,7 +1606,7 @@ public class Expr extends AST **/ public boolean isRelationProject() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_PROJECT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_PROJECT; } /** @@ -1618,7 +1618,7 @@ public class Expr extends AST **/ public boolean isRelationFilter() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_FILTER; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_FILTER; } /** @@ -1635,7 +1635,7 @@ public class Expr extends AST **/ public boolean isRelationNegationFilter() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; } /** @@ -1645,7 +1645,7 @@ public class Expr extends AST **/ public boolean isRelationRename() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_RENAME; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_RENAME; } /** @@ -1653,7 +1653,7 @@ public class Expr extends AST **/ public boolean isRelationComplement() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; } /** @@ -1664,7 +1664,7 @@ public class Expr extends AST **/ public boolean isRelationSelect() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_SELECT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_SELECT; } /** @@ -1676,7 +1676,7 @@ public class Expr extends AST **/ public boolean isRelationClone() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_CLONE; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_CLONE; } /** @@ -1695,7 +1695,7 @@ public class Expr extends AST **/ public boolean isFiniteDomainLT() throws Z3Exception { - return getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FD_LT; + return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FD_LT; } /** From 7bb1469d71810870824abf00ad2a108e266fd9c7 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 2 Apr 2014 19:10:30 +0100 Subject: [PATCH 315/509] removed debugging code. Signed-off-by: Christoph M. Wintersteiger --- examples/dotnet/Program.cs | 3 --- examples/java/JavaExample.java | 3 --- 2 files changed, 6 deletions(-) diff --git a/examples/dotnet/Program.cs b/examples/dotnet/Program.cs index 6f12d81a4..4361cab96 100644 --- a/examples/dotnet/Program.cs +++ b/examples/dotnet/Program.cs @@ -93,9 +93,6 @@ namespace test_mapi 1, new Pattern[] { p } /* patterns */); - if (q.IsTrue) - Console.WriteLine("is true."); - return q; } diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 992b0c477..48395d8c2 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -89,9 +89,6 @@ class JavaExample names, /* names of quantified variables */ eq, 1, new Pattern[] { p } /* patterns */, null, null, null); - if (q.isTrue()) - System.out.println("is true"); - return q; } From 944dfee008a63673184008b7bb334cf0b7c04331 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 2 Apr 2014 19:25:05 +0100 Subject: [PATCH 316/509] .NET and Java API Bugfix (Codeplex issue 101) Signed-off-by: Christoph M. Wintersteiger --- src/api/dotnet/Expr.cs | 2 +- src/api/java/Expr.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index dae4df445..c8fdfb51f 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -99,7 +99,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(args, a => a != null)); Context.CheckContextMatch(args); - if (args.Length != NumArgs) + if (IsApp && args.Length != NumArgs) throw new Z3Exception("Number of arguments does not match"); NativeObject = Native.Z3_update_term(Context.nCtx, NativeObject, (uint)args.Length, Expr.ArrayToNative(args)); } diff --git a/src/api/java/Expr.java b/src/api/java/Expr.java index 5c4f90920..f7edc6a2b 100644 --- a/src/api/java/Expr.java +++ b/src/api/java/Expr.java @@ -94,7 +94,7 @@ public class Expr extends AST public void update(Expr[] args) throws Z3Exception { getContext().checkContextMatch(args); - if (args.length != getNumArgs()) + if (isApp() && args.length != getNumArgs()) throw new Z3Exception("Number of arguments does not match"); setNativeObject(Native.updateTerm(getContext().nCtx(), getNativeObject(), (int) args.length, Expr.arrayToNative(args))); From 4444eb361c97d83b99ac2b02b2a31438f6184035 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 3 Apr 2014 13:11:39 +0100 Subject: [PATCH 317/509] bugfix Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_win_dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_win_dist.py b/scripts/mk_win_dist.py index a52c8af43..3a95fb730 100644 --- a/scripts/mk_win_dist.py +++ b/scripts/mk_win_dist.py @@ -144,7 +144,7 @@ def mk_z3_core(x64): cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" amd64') cmds.append('cd %s' % BUILD_X64_DIR) else: - cmds.append('"call %VCINSTALLDIR%vcvarsall.bat" x86') + cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" x86') cmds.append('cd %s' % BUILD_X86_DIR) cmds.append('nmake') if exec_cmds(cmds) != 0: From 9a2fe83697c1acbf60555fd79be9b93106c6f348 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 3 Apr 2014 13:20:08 -0700 Subject: [PATCH 318/509] interpolation fix --- src/duality/duality.h | 2640 +++++++++++++-------------- src/duality/duality_rpfp.cpp | 66 +- src/duality/duality_wrapper.cpp | 6 + src/duality/duality_wrapper.h | 2969 ++++++++++++++++--------------- src/interp/iz3proof_itp.cpp | 2 + 5 files changed, 2877 insertions(+), 2806 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index a37896af1..fc70ffa70 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1,1319 +1,1321 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - duality.h - -Abstract: - - main header for duality - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - - ---*/ - -#pragma once - -#include "duality_wrapper.h" -#include -#include - -// make hash_map and hash_set available -using namespace stl_ext; - -namespace Duality { - - class implicant_solver; - - /* Generic operations on Z3 formulas */ - - struct Z3User { - - context &ctx; - - typedef func_decl FuncDecl; - typedef expr Term; - - Z3User(context &_ctx) : ctx(_ctx){} - - const char *string_of_int(int n); - - Term conjoin(const std::vector &args); - - Term sum(const std::vector &args); - - Term CloneQuantifier(const Term &t, const Term &new_body); - - Term SubstRec(hash_map &memo, const Term &t); - - Term SubstRec(hash_map &memo, hash_map &map, const Term &t); - - void Strengthen(Term &x, const Term &y); - - // return the func_del of an app if it is uninterpreted - - bool get_relation(const Term &t, func_decl &R); - - // return true if term is an individual variable - // TODO: have to check that it is not a background symbol - - bool is_variable(const Term &t); - - FuncDecl SuffixFuncDecl(Term t, int n); - - - Term SubstRecHide(hash_map &memo, const Term &t, int number); - - void CollectConjuncts(const Term &f, std::vector &lits, bool pos = true); - - void SortTerms(std::vector &terms); - - Term SortSum(const Term &t); - - void Summarize(const Term &t); - - int CountOperators(const Term &t); - - Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); - - Term CloneQuantAndSimp(const expr &t, const expr &body); - - Term RemoveRedundancy(const Term &t); - - Term IneqToEq(const Term &t); - - bool IsLiteral(const expr &lit, expr &atom, expr &val); - - expr Negate(const expr &f); - - expr SimplifyAndOr(const std::vector &args, bool is_and); - - expr ReallySimplifyAndOr(const std::vector &args, bool is_and); - - int MaxIndex(hash_map &memo, const Term &t); - - bool IsClosedFormula(const Term &t); - - Term AdjustQuantifiers(const Term &t); - - FuncDecl RenumberPred(const FuncDecl &f, int n); - - Term ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming); - - -protected: - - void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); - int CountOperatorsRec(hash_set &memo, const Term &t); - void RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo); - Term RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t); - Term SubstAtomTriv(const expr &foo, const expr &atom, const expr &val); - expr ReduceAndOr(const std::vector &args, bool is_and, std::vector &res); - expr FinishAndOr(const std::vector &args, bool is_and); - expr PullCommonFactors(std::vector &args, bool is_and); - Term IneqToEqRec(hash_map &memo, const Term &t); - Term CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall); - Term PushQuantifier(const expr &t, const expr &body, bool is_forall); - - -}; - - /** This class represents a relation post-fixed point (RPFP) problem as - * a "problem graph". The graph consists of Nodes and hyper-edges. - * - * A node consists of - * - Annotation, a symbolic relation - * - Bound, a symbolic relation giving an upper bound on Annotation - * - * - * A hyper-edge consists of: - * - Children, a sequence of children Nodes, - * - F, a symbolic relational transformer, - * - Parent, a single parent Node. - * - * The graph is "solved" when: - * - For every Node n, n.Annotation subseteq n.Bound - * - For every hyperedge e, e.F(e.Children.Annotation) subseteq e.Parent.Annotation - * - * where, if x is a sequence of Nodes, x.Annotation is the sequences - * of Annotations of the nodes in the sequence. - * - * A symbolic Transformer consists of - * - RelParams, a sequence of relational symbols - * - IndParams, a sequence of individual symbols - * - Formula, a formula over RelParams and IndParams - * - * A Transformer t represents a function that takes sequence R of relations - * and yields the relation lambda (t.Indparams). Formula(R/RelParams). - * - * As a special case, a nullary Transformer (where RelParams is the empty sequence) - * represents a fixed relation. - * - * An RPFP consists of - * - Nodes, a set of Nodes - * - Edges, a set of hyper-edges - * - Context, a prover context that contains formula AST's - * - * Multiple RPFP's can use the same Context, but you should be careful - * that only one RPFP asserts constraints in the context at any time. - * - * */ - class RPFP : public Z3User - { - public: - - class Edge; - class Node; - bool HornClauses; - - - /** Interface class for interpolating solver. */ - - class LogicSolver { - public: - - context *ctx; /** Z3 context for formulas */ - solver *slvr; /** Z3 solver */ - bool need_goals; /** Can the solver use the goal tree to optimize interpolants? */ - solver aux_solver; /** For temporary use -- don't leave assertions here. */ - - /** Tree interpolation. This method assumes the formulas in TermTree - "assumptions" are currently asserted in the solver. The return - value indicates whether the assertions are satisfiable. In the - UNSAT case, a tree interpolant is returned in "interpolants". - In the SAT case, a model is returned. - */ - - virtual - lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0, - bool weak = false - ) = 0; - - /** Declare a constant in the background theory. */ - virtual void declare_constant(const func_decl &f) = 0; - - /** Is this a background constant? */ - virtual bool is_constant(const func_decl &f) = 0; - - /** Get the constants in the background vocabulary */ - virtual hash_set &get_constants() = 0; - - /** Assert a background axiom. */ - virtual void assert_axiom(const expr &axiom) = 0; - - /** Get the background axioms. */ - virtual const std::vector &get_axioms() = 0; - - /** Return a string describing performance. */ - virtual std::string profile() = 0; - - virtual void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ){} - - /** Cancel, throw Canceled object if possible. */ - virtual void cancel(){ } - - /* Note: aux solver uses extensional array theory, since it - needs to be able to produce counter-models for - interpolants the have array equalities in them. - */ - LogicSolver(context &c) : aux_solver(c,true){} - - virtual ~LogicSolver(){} - }; - - /** This solver uses iZ3. */ - class iZ3LogicSolver : public LogicSolver { - public: - interpolating_context *ictx; /** iZ3 context for formulas */ - interpolating_solver *islvr; /** iZ3 solver */ - - lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0, - bool weak = false) - { - literals _labels; - islvr->SetWeakInterpolants(weak); - return islvr->interpolate_tree(assumptions,interpolants,_model,_labels,true); - } - - void assert_axiom(const expr &axiom){ - islvr->AssertInterpolationAxiom(axiom); - } - - const std::vector &get_axioms() { - return islvr->GetInterpolationAxioms(); - } - - std::string profile(){ - return islvr->profile(); - } - -#if 0 - iZ3LogicSolver(config &_config){ - ctx = ictx = new interpolating_context(_config); - slvr = islvr = new interpolating_solver(*ictx); - need_goals = false; - islvr->SetWeakInterpolants(true); - } -#endif - - iZ3LogicSolver(context &c, bool models = true) : LogicSolver(c) { - ctx = ictx = &c; - slvr = islvr = new interpolating_solver(*ictx, models); - need_goals = false; - islvr->SetWeakInterpolants(true); - } - - void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ){ -#if 0 - islvr->write_interpolation_problem(file_name,assumptions,theory); -#endif - - } - - void cancel(){islvr->cancel();} - - /** Declare a constant in the background theory. */ - virtual void declare_constant(const func_decl &f){ - bckg.insert(f); - } - - /** Is this a background constant? */ - virtual bool is_constant(const func_decl &f){ - return bckg.find(f) != bckg.end(); - } - - /** Get the constants in the background vocabulary */ - virtual hash_set &get_constants(){ - return bckg; - } - - ~iZ3LogicSolver(){ - // delete ictx; - delete islvr; - } - private: - hash_set bckg; - - }; - -#if 0 - /** Create a logic solver from a Z3 configuration. */ - static iZ3LogicSolver *CreateLogicSolver(config &_config){ - return new iZ3LogicSolver(_config); - } -#endif - - /** Create a logic solver from a low-level Z3 context. - Only use this if you know what you're doing. */ - static iZ3LogicSolver *CreateLogicSolver(context c){ - return new iZ3LogicSolver(c); - } - - LogicSolver *ls; - - protected: - int nodeCount; - int edgeCount; - - class stack_entry - { - public: - std::list edges; - std::list nodes; - std::list > constraints; - }; - - - public: - model dualModel; - protected: - literals dualLabels; - std::list stack; - std::vector axioms; // only saved here for printing purposes - solver &aux_solver; - hash_set *proof_core; - - public: - - /** Construct an RPFP graph with a given interpolating prover context. It is allowed to - have multiple RPFP's use the same context, but you should never have teo RPFP's - with the same conext asserting nodes or edges at the same time. Note, if you create - axioms in one RPFP, them create a second RPFP with the same context, the second will - inherit the axioms. - */ - - RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx)), dualModel(*(_ls->ctx)), aux_solver(_ls->aux_solver) - { - ls = _ls; - nodeCount = 0; - edgeCount = 0; - stack.push_back(stack_entry()); - HornClauses = false; - proof_core = 0; - } - - virtual ~RPFP(); - - /** Symbolic representation of a relational transformer */ - class Transformer - { - public: - std::vector RelParams; - std::vector IndParams; - Term Formula; - RPFP *owner; - hash_map labels; - - Transformer *Clone() - { - return new Transformer(*this); - } - - void SetEmpty(){ - Formula = owner->ctx.bool_val(false); - } - - void SetFull(){ - Formula = owner->ctx.bool_val(true); - } - - bool IsEmpty(){ - return eq(Formula,owner->ctx.bool_val(false)); - } - - bool IsFull(){ - return eq(Formula,owner->ctx.bool_val(true)); - } - - void UnionWith(const Transformer &other){ - Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); - Formula = Formula || t; - } - - void IntersectWith(const Transformer &other){ - Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); - Formula = Formula && t; - } - - bool SubsetEq(const Transformer &other){ - Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); - expr test = Formula && !t; - owner->aux_solver.push(); - owner->aux_solver.add(test); - check_result res = owner->aux_solver.check(); - owner->aux_solver.pop(1); - return res == unsat; - } - - void Complement(){ - Formula = !Formula; - } - - void Simplify(){ - Formula = Formula.simplify(); - } - - Transformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula, RPFP *_owner) - : RelParams(_RelParams), IndParams(_IndParams), Formula(_Formula) {owner = _owner;} - }; - - /** Create a symbolic transformer. */ - Transformer CreateTransformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula) - { - // var ops = new Util.ContextOps(ctx); - // var foo = ops.simplify_lhs(_Formula); - // t.Formula = foo.Item1; - // t.labels = foo.Item2; - return Transformer(_RelParams,_IndParams,_Formula,this); - } - - /** Create a relation (nullary relational transformer) */ - Transformer CreateRelation(const std::vector &_IndParams, const Term &_Formula) - { - return CreateTransformer(std::vector(), _IndParams, _Formula); - } - - /** A node in the RPFP graph */ - class Node - { - public: - FuncDecl Name; - Transformer Annotation; - Transformer Bound; - Transformer Underapprox; - RPFP *owner; - int number; - Edge *Outgoing; - std::vector Incoming; - Term dual; - Node *map; - - Node(const FuncDecl &_Name, const Transformer &_Annotation, const Transformer &_Bound, const Transformer &_Underapprox, const Term &_dual, RPFP *_owner, int _number) - : Name(_Name), Annotation(_Annotation), Bound(_Bound), Underapprox(_Underapprox), dual(_dual) {owner = _owner; number = _number; Outgoing = 0;} - }; - - /** Create a node in the graph. The input is a term R(v_1...v_n) - * where R is an arbitrary relational symbol and v_1...v_n are - * arbitary distinct variables. The names are only of mnemonic value, - * however, the number and type of arguments determine the type - * of the relation at this node. */ - - Node *CreateNode(const Term &t) - { - std::vector _IndParams; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - _IndParams.push_back(t.arg(i)); - Node *n = new Node(t.decl(), - CreateRelation(_IndParams,ctx.bool_val(true)), - CreateRelation(_IndParams,ctx.bool_val(true)), - CreateRelation(_IndParams,ctx.bool_val(false)), - expr(ctx), this, ++nodeCount - ); - nodes.push_back(n); - return n; - } - - /** Clone a node (can be from another graph). */ - - Node *CloneNode(Node *old) - { - Node *n = new Node(old->Name, - old->Annotation, - old->Bound, - old->Underapprox, - expr(ctx), - this, - ++nodeCount - ); - nodes.push_back(n); - n->map = old; - return n; - } - - /** Delete a node. You can only do this if not connected to any edges.*/ - void DeleteNode(Node *node){ - if(node->Outgoing || !node->Incoming.empty()) - throw "cannot delete RPFP node"; - for(std::vector::iterator it = nodes.end(), en = nodes.begin(); it != en;){ - if(*(--it) == node){ - nodes.erase(it); - break; - } - } - delete node; - } - - /** This class represents a hyper-edge in the RPFP graph */ - - class Edge - { - public: - Transformer F; - Node *Parent; - std::vector Children; - RPFP *owner; - int number; - // these should be internal... - Term dual; - hash_map relMap; - hash_map varMap; - Edge *map; - Term labeled; - std::vector constraints; - - Edge(Node *_Parent, const Transformer &_F, const std::vector &_Children, RPFP *_owner, int _number) - : F(_F), Parent(_Parent), Children(_Children), dual(expr(_owner->ctx)) { - owner = _owner; - number = _number; - } - }; - - - /** Create a hyper-edge. */ - Edge *CreateEdge(Node *_Parent, const Transformer &_F, const std::vector &_Children) - { - Edge *e = new Edge(_Parent,_F,_Children,this,++edgeCount); - _Parent->Outgoing = e; - for(unsigned i = 0; i < _Children.size(); i++) - _Children[i]->Incoming.push_back(e); - edges.push_back(e); - return e; - } - - - /** Delete a hyper-edge and unlink it from any nodes. */ - void DeleteEdge(Edge *edge){ - if(edge->Parent) - edge->Parent->Outgoing = 0; - for(unsigned int i = 0; i < edge->Children.size(); i++){ - std::vector &ic = edge->Children[i]->Incoming; - for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ - if(*it == edge){ - ic.erase(it); - break; - } - } - } - for(std::vector::iterator it = edges.end(), en = edges.begin(); it != en;){ - if(*(--it) == edge){ - edges.erase(it); - break; - } - } - delete edge; - } - - /** Create an edge that lower-bounds its parent. */ - Edge *CreateLowerBoundEdge(Node *_Parent) - { - return CreateEdge(_Parent, _Parent->Annotation, std::vector()); - } - - - /** For incremental solving, asserts the constraint associated - * with this edge in the SMT context. If this edge is removed, - * you must pop the context accordingly. The second argument is - * the number of pushes we are inside. */ - - virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); - - /* Constrain an edge by the annotation of one of its children. */ - - void ConstrainParent(Edge *parent, Node *child); - - /** For incremental solving, asserts the negation of the upper bound associated - * with a node. - * */ - - void AssertNode(Node *n); - - /** Assert a constraint on an edge in the SMT context. - */ - void ConstrainEdge(Edge *e, const Term &t); - - /** Fix the truth values of atomic propositions in the given - edge to their values in the current assignment. */ - void FixCurrentState(Edge *root); - - void FixCurrentStateFull(Edge *edge, const expr &extra); - - void FixCurrentStateFull(Edge *edge, const std::vector &assumps, const hash_map &renaming); - - /** Declare a constant in the background theory. */ - - void DeclareConstant(const FuncDecl &f); - - /** Assert a background axiom. Background axioms can be used to provide the - * theory of auxilliary functions or relations. All symbols appearing in - * background axioms are considered global, and may appear in both transformer - * and relational solutions. Semantically, a solution to the RPFP gives - * an interpretation of the unknown relations for each interpretation of the - * auxilliary symbols that is consistent with the axioms. Axioms should be - * asserted before any calls to Push. They cannot be de-asserted by Pop. */ - - void AssertAxiom(const Term &t); - -#if 0 - /** Do not call this. */ - - void RemoveAxiom(const Term &t); -#endif - - /** Solve an RPFP graph. This means either strengthen the annotation - * so that the bound at the given root node is satisfied, or - * show that this cannot be done by giving a dual solution - * (i.e., a counterexample). - * - * In the current implementation, this only works for graphs that - * are: - * - tree-like - * - * - closed. - * - * In a tree-like graph, every nod has out most one incoming and one out-going edge, - * and there are no cycles. In a closed graph, every node has exactly one out-going - * edge. This means that the leaves of the tree are all hyper-edges with no - * children. Such an edge represents a relation (nullary transformer) and thus - * a lower bound on its parent. The parameter root must be the root of this tree. - * - * If Solve returns LBool.False, this indicates success. The annotation of the tree - * has been updated to satisfy the upper bound at the root. - * - * If Solve returns LBool.True, this indicates a counterexample. For each edge, - * you can then call Eval to determine the values of symbols in the transformer formula. - * You can also call Empty on a node to determine if its value in the counterexample - * is the empty relation. - * - * \param root The root of the tree - * \param persist Number of context pops through which result should persist - * - * - */ - - lbool Solve(Node *root, int persist); - - /** Same as Solve, but annotates only a single node. */ - - lbool SolveSingleNode(Node *root, Node *node); - - /** Get the constraint tree (but don't solve it) */ - - TermTree *GetConstraintTree(Node *root, Node *skip_descendant = 0); - - /** Dispose of the dual model (counterexample) if there is one. */ - - void DisposeDualModel(); - - /** Check satisfiability of asserted edges and nodes. Same functionality as - * Solve, except no primal solution (interpolant) is generated in the unsat case. */ - - check_result Check(Node *root, std::vector underapproxes = std::vector(), - std::vector *underapprox_core = 0); - - /** Update the model, attempting to make the propositional literals in assumps true. If possible, - return sat, else return unsat and keep the old model. */ - - check_result CheckUpdateModel(Node *root, std::vector assumps); - - /** Determines the value in the counterexample of a symbol occuring in the transformer formula of - * a given edge. */ - - Term Eval(Edge *e, Term t); - - /** Return the fact derived at node p in a counterexample. */ - - Term EvalNode(Node *p); - - /** Returns true if the given node is empty in the primal solution. For proecudure summaries, - this means that the procedure is not called in the current counter-model. */ - - bool Empty(Node *p); - - /** Compute an underapproximation of every node in a tree rooted at "root", - based on a previously computed counterexample. */ - - Term ComputeUnderapprox(Node *root, int persist); - - /** Try to strengthen the annotation of a node by removing disjuncts. */ - void Generalize(Node *root, Node *node); - - - /** Compute disjunctive interpolant for node by case splitting */ - void InterpolateByCases(Node *root, Node *node); - - /** Push a scope. Assertions made after Push can be undone by Pop. */ - - void Push(); - - /** Exception thrown when bad clause is encountered */ - - struct bad_clause { - std::string msg; - int i; - bad_clause(const std::string &_msg, int _i){ - msg = _msg; - i = _i; - } - }; - - struct parse_error { - std::string msg; - parse_error(const std::string &_msg){ - msg = _msg; - } - }; - - struct file_not_found { - }; - - struct bad_format { - }; - - // thrown on internal error - struct Bad { - }; - - /** Pop a scope (see Push). Note, you cannot pop axioms. */ - - void Pop(int num_scopes); - - /** Erase the proof by performing a Pop, Push and re-assertion of - all the popped constraints */ - void PopPush(); - - /** Return true if the given edge is used in the proof of unsat. - Can be called only after Solve or Check returns an unsat result. */ - - bool EdgeUsedInProof(Edge *edge); - - - /** Convert a collection of clauses to Nodes and Edges in the RPFP. - - Predicate unknowns are uninterpreted predicates not - occurring in the background theory. - - Clauses are of the form - - B => P(t_1,...,t_k) - - where P is a predicate unknown and predicate unknowns - occur only positivey in H and only under existential - quantifiers in prenex form. - - Each predicate unknown maps to a node. Each clause maps to - an edge. Let C be a clause B => P(t_1,...,t_k) where the - sequence of predicate unknowns occurring in B (in order - of occurrence) is P_1..P_n. The clause maps to a transformer - T where: - - T.Relparams = P_1..P_n - T.Indparams = x_1...x+k - T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k - - Throws exception bad_clause(msg,i) if a clause i is - in the wrong form. - - */ - - struct label_struct { - symbol name; - expr value; - bool pos; - label_struct(const symbol &s, const expr &e, bool b) - : name(s), value(e), pos(b) {} - }; - - -#ifdef _WINDOWS - __declspec(dllexport) -#endif - void FromClauses(const std::vector &clauses); - - void FromFixpointContext(fixedpoint fp, std::vector &queries); - - void WriteSolution(std::ostream &s); - - void WriteCounterexample(std::ostream &s, Node *node); - - enum FileFormat {DualityFormat, SMT2Format, HornFormat}; - - /** Write the RPFP to a file (currently in SMTLIB 1.2 format) */ - void WriteProblemToFile(std::string filename, FileFormat format = DualityFormat); - - /** Read the RPFP from a file (in specificed format) */ - void ReadProblemFromFile(std::string filename, FileFormat format = DualityFormat); - - /** Translate problem to Horn clause form */ - void ToClauses(std::vector &cnsts, FileFormat format = DualityFormat); - - /** Translate the RPFP to a fixed point context, with queries */ - fixedpoint ToFixedPointProblem(std::vector &queries); - - /** Nodes of the graph. */ - std::vector nodes; - - /** Edges of the graph. */ - std::vector edges; - - /** Fuse a vector of transformers. If the total number of inputs of the transformers - is N, then the result is an N-ary transfomer whose output is the union of - the outputs of the given transformers. The is, suppose we have a vetor of transfoermers - {T_i(r_i1,...,r_iN(i) : i=1..M}. The the result is a transformer - - F(r_11,...,r_iN(1),...,r_M1,...,r_MN(M)) = - T_1(r_11,...,r_iN(1)) U ... U T_M(r_M1,...,r_MN(M)) - */ - - Transformer Fuse(const std::vector &trs); - - /** Fuse edges so that each node is the output of at most one edge. This - transformation is solution-preserving, but changes the numbering of edges in - counterexamples. - */ - void FuseEdges(); - - void RemoveDeadNodes(); - - Term SubstParams(const std::vector &from, - const std::vector &to, const Term &t); - - Term SubstParamsNoCapture(const std::vector &from, - const std::vector &to, const Term &t); - - Term Localize(Edge *e, const Term &t); - - void EvalNodeAsConstraint(Node *p, Transformer &res); - - TermTree *GetGoalTree(Node *root); - - int EvalTruth(hash_map &memo, Edge *e, const Term &f); - - void GetLabels(Edge *e, std::vector &labels); - - // int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); - - /** Compute and save the proof core for future calls to - EdgeUsedInProof. You only need to call this if you will pop - the solver before calling EdgeUsedInProof. - */ - void ComputeProofCore(); - - int CumulativeDecisions(); - - solver &slvr(){ - return *ls->slvr; - } - - protected: - - void ClearProofCore(){ - if(proof_core) - delete proof_core; - proof_core = 0; - } - - Term SuffixVariable(const Term &t, int n); - - Term HideVariable(const Term &t, int n); - - void RedVars(Node *node, Term &b, std::vector &v); - - Term RedDualRela(Edge *e, std::vector &args, int idx); - - Term LocalizeRec(Edge *e, hash_map &memo, const Term &t); - - void SetEdgeMaps(Edge *e); - - Term ReducedDualEdge(Edge *e); - - TermTree *ToTermTree(Node *root, Node *skip_descendant = 0); - - TermTree *ToGoalTree(Node *root); - - void CollapseTermTreeRec(TermTree *root, TermTree *node); - - TermTree *CollapseTermTree(TermTree *node); - - void DecodeTree(Node *root, TermTree *interp, int persist); - - Term GetUpperBound(Node *n); - - TermTree *AddUpperBound(Node *root, TermTree *t); - -#if 0 - void WriteInterps(System.IO.StreamWriter f, TermTree t); -#endif - - void WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s); - - void WriteEdgeAssignment(std::ostream &s, Edge *e); - - - // Scan the clause body for occurrences of the predicate unknowns - - Term ScanBody(hash_map &memo, - const Term &t, - hash_map &pmap, - std::vector &res, - std::vector &nodes); - - Term RemoveLabelsRec(hash_map &memo, const Term &t, std::vector &lbls); - - Term RemoveLabels(const Term &t, std::vector &lbls); - - Term GetAnnotation(Node *n); - - - Term GetUnderapprox(Node *n); - - Term UnderapproxFlag(Node *n); - - hash_map underapprox_flag_rev; - - Node *UnderapproxFlagRev(const Term &flag); - - Term ProjectFormula(std::vector &keep_vec, const Term &f); - - int SubtermTruth(hash_map &memo, const Term &); - - void ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set *done, bool truth, hash_set &dont_cares); - - void Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares); - - Term UnderapproxFormula(const Term &f, hash_set &dont_cares); - - void ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares, bool extensional = true); - - public: - Term UnderapproxFullFormula(const Term &f, bool extensional = true); - - protected: - Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); - - hash_map resolve_ite_memo; - - Term ResolveIte(hash_map &memo, const Term &t, std::vector &lits, - hash_set *done, hash_set &dont_cares); - - struct ArrayValue { - bool defined; - std::map entries; - expr def_val; - }; - - void EvalArrayTerm(const Term &t, ArrayValue &res); - - Term EvalArrayEquality(const Term &f); - - Term ModelValueAsConstraint(const Term &t); - - void GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, - hash_set *done, bool truth); - - Term SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t); - - Term SubstBound(hash_map &subst, const Term &t); - - void ConstrainEdgeLocalized(Edge *e, const Term &t); - - void GreedyReduce(solver &s, std::vector &conjuncts); - - void NegateLits(std::vector &lits); - - expr SimplifyOr(std::vector &lits); - - expr SimplifyAnd(std::vector &lits); - - void SetAnnotation(Node *root, const expr &t); - - void AddEdgeToSolver(Edge *edge); - - void AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge); - - void AddToProofCore(hash_set &core); - - void GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under); - - Term StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits); - - expr NegateLit(const expr &f); - - expr GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox); - - bool IsVar(const expr &t); - - void GetVarsRec(hash_set &memo, const expr &cnst, std::vector &vars); - - expr UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params); - - void AddParamsToTransformer(Transformer &trans, const std::vector ¶ms); - - expr AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms); - - expr GetRelRec(hash_set &memo, const expr &t, const func_decl &rel); - - expr GetRel(Edge *edge, int child_idx); - - void GetDefs(const expr &cnst, hash_map &defs); - - void GetDefsRec(const expr &cnst, hash_map &defs); - - void AddParamsToNode(Node *node, const std::vector ¶ms); - - void UnhoistLoop(Edge *loop_edge, Edge *init_edge); - - void Unhoist(); - - Term ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts); - - Term ElimIte(const Term &t); - - void MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node); - - virtual void slvr_add(const expr &e); - - virtual void slvr_pop(int i); - - virtual void slvr_push(); - - virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); - - virtual lbool ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0, - bool weak = false); - - virtual bool proof_core_contains(const expr &e); - - }; - - - /** RPFP solver base class. */ - - class Solver { - - public: - - 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 - counterexample to use as a guide. This is chiefly useful for - abstraction refinement metholdologies, and is only used as a - heuristic. */ - - virtual bool Solve() = 0; - - virtual Counterexample &GetCounterexample() = 0; - - virtual bool SetOption(const std::string &option, const std::string &value) = 0; - - /** Learn heuristic information from another solver. This - is chiefly useful for abstraction refinement, when we want to - solve a series of similar problems. */ - - virtual void LearnFrom(Solver *old_solver) = 0; - - virtual ~Solver(){} - - static Solver *Create(const std::string &solver_class, RPFP *rpfp); - - /** This can be called asynchrnously to cause Solve to throw a - Canceled exception at some time in the future. - */ - virtual void Cancel() = 0; - - /** Object thrown on cancellation */ - struct Canceled {}; - - /** Object thrown on incompleteness */ - struct Incompleteness {}; - }; -} - - -// Allow to hash on nodes and edges in deterministic way - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::RPFP::Node *p) const { - return p->number; - } - }; -} - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::RPFP::Edge *p) const { - return p->number; - } - }; -} - -// allow to walk sets of nodes without address dependency - -namespace std { - template <> - class less { - public: - bool operator()(Duality::RPFP::Node * const &s, Duality::RPFP::Node * const &t) const { - return s->number < t->number; // s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// #define LIMIT_STACK_WEIGHT 5 - - -namespace Duality { - /** Caching version of RPFP. Instead of asserting constraints, returns assumption literals */ - - class RPFP_caching : public RPFP { - public: - - /** appends assumption literals for edge to lits. if with_children is true, - includes that annotation of the edge's children. - */ - void AssertEdgeCache(Edge *e, std::vector &lits, bool with_children = false); - - /** appends assumption literals for node to lits */ - void AssertNodeCache(Node *, std::vector lits); - - /** check assumption lits, and return core */ - check_result CheckCore(const std::vector &assumps, std::vector &core); - - /** Clone another RPFP into this one, keeping a map */ - void Clone(RPFP *other); - - /** Get the clone of a node */ - Node *GetNodeClone(Node *other_node); - - /** Get the clone of an edge */ - Edge *GetEdgeClone(Edge *other_edge); - - /** Try to strengthen the parent of an edge */ - void GeneralizeCache(Edge *edge); - - /** Try to propagate some facts from children to parents of edge. - Return true if success. */ - bool PropagateCache(Edge *edge); - - /** Construct a caching RPFP using a LogicSolver */ - RPFP_caching(LogicSolver *_ls) : RPFP(_ls) {} - - /** Constraint an edge by its child's annotation. Return - assumption lits. */ - void ConstrainParentCache(Edge *parent, Node *child, std::vector &lits); - -#ifdef LIMIT_STACK_WEIGHT - virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); -#endif - - virtual ~RPFP_caching(){} - - protected: - hash_map AssumptionLits; - hash_map NodeCloneMap; - hash_map EdgeCloneMap; - std::vector alit_stack; - std::vector alit_stack_sizes; - - // to let us use one solver per edge - struct edge_solver { - hash_map AssumptionLits; - uptr slvr; - }; - hash_map edge_solvers; - -#ifdef LIMIT_STACK_WEIGHT - struct weight_counter { - int val; - weight_counter(){val = 0;} - void swap(weight_counter &other){ - std::swap(val,other.val); - } - }; - - struct big_stack_entry { - weight_counter weight_added; - std::vector new_alits; - std::vector alit_stack; - std::vector alit_stack_sizes; - }; - - std::vector new_alits; - weight_counter weight_added; - std::vector big_stack; -#endif - - - - void GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map = 0); - - void GreedyReduceCache(std::vector &assumps, std::vector &core); - - void FilterCore(std::vector &core, std::vector &full_core); - void ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits); - - virtual void slvr_add(const expr &e); - - virtual void slvr_pop(int i); - - virtual void slvr_push(); - - virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); - - virtual lbool ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0, - bool weak = false); - - virtual bool proof_core_contains(const expr &e); - - void GetTermTreeAssertionLiterals(TermTree *assumptions); - - void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); - - edge_solver &SolverForEdge(Edge *edge, bool models, bool axioms); - - public: - struct scoped_solver_for_edge { - solver *orig_slvr; - RPFP_caching *rpfp; - edge_solver *es; - scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false, bool axioms = false){ - rpfp = _rpfp; - orig_slvr = rpfp->ls->slvr; - es = &(rpfp->SolverForEdge(edge,models,axioms)); - rpfp->ls->slvr = es->slvr.get(); - rpfp->AssumptionLits.swap(es->AssumptionLits); - } - ~scoped_solver_for_edge(){ - rpfp->ls->slvr = orig_slvr; - rpfp->AssumptionLits.swap(es->AssumptionLits); - } - }; - - }; - -} +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality.h + +Abstract: + + main header for duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + +#pragma once + +#include "duality_wrapper.h" +#include +#include + +// make hash_map and hash_set available +using namespace stl_ext; + +namespace Duality { + + class implicant_solver; + + /* Generic operations on Z3 formulas */ + + struct Z3User { + + context &ctx; + + typedef func_decl FuncDecl; + typedef expr Term; + + Z3User(context &_ctx) : ctx(_ctx){} + + const char *string_of_int(int n); + + Term conjoin(const std::vector &args); + + Term sum(const std::vector &args); + + Term CloneQuantifier(const Term &t, const Term &new_body); + + Term SubstRec(hash_map &memo, const Term &t); + + Term SubstRec(hash_map &memo, hash_map &map, const Term &t); + + void Strengthen(Term &x, const Term &y); + + // return the func_del of an app if it is uninterpreted + + bool get_relation(const Term &t, func_decl &R); + + // return true if term is an individual variable + // TODO: have to check that it is not a background symbol + + bool is_variable(const Term &t); + + FuncDecl SuffixFuncDecl(Term t, int n); + + + Term SubstRecHide(hash_map &memo, const Term &t, int number); + + void CollectConjuncts(const Term &f, std::vector &lits, bool pos = true); + + void SortTerms(std::vector &terms); + + Term SortSum(const Term &t); + + void Summarize(const Term &t); + + int CountOperators(const Term &t); + + Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); + + Term CloneQuantAndSimp(const expr &t, const expr &body); + + Term RemoveRedundancy(const Term &t); + + Term IneqToEq(const Term &t); + + bool IsLiteral(const expr &lit, expr &atom, expr &val); + + expr Negate(const expr &f); + + expr SimplifyAndOr(const std::vector &args, bool is_and); + + expr ReallySimplifyAndOr(const std::vector &args, bool is_and); + + int MaxIndex(hash_map &memo, const Term &t); + + bool IsClosedFormula(const Term &t); + + Term AdjustQuantifiers(const Term &t); + + FuncDecl RenumberPred(const FuncDecl &f, int n); + + Term ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming); + + +protected: + + void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); + int CountOperatorsRec(hash_set &memo, const Term &t); + void RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo); + Term RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t); + Term SubstAtomTriv(const expr &foo, const expr &atom, const expr &val); + expr ReduceAndOr(const std::vector &args, bool is_and, std::vector &res); + expr FinishAndOr(const std::vector &args, bool is_and); + expr PullCommonFactors(std::vector &args, bool is_and); + Term IneqToEqRec(hash_map &memo, const Term &t); + Term CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall); + Term PushQuantifier(const expr &t, const expr &body, bool is_forall); + void CollectJuncts(const Term &f, std::vector &lits, decl_kind op, bool negate); + Term DeleteBoundRec(hash_map > &memo, int level, int num, const Term &t); + Term DeleteBound(int level, int num, const Term &t); + +}; + + /** This class represents a relation post-fixed point (RPFP) problem as + * a "problem graph". The graph consists of Nodes and hyper-edges. + * + * A node consists of + * - Annotation, a symbolic relation + * - Bound, a symbolic relation giving an upper bound on Annotation + * + * + * A hyper-edge consists of: + * - Children, a sequence of children Nodes, + * - F, a symbolic relational transformer, + * - Parent, a single parent Node. + * + * The graph is "solved" when: + * - For every Node n, n.Annotation subseteq n.Bound + * - For every hyperedge e, e.F(e.Children.Annotation) subseteq e.Parent.Annotation + * + * where, if x is a sequence of Nodes, x.Annotation is the sequences + * of Annotations of the nodes in the sequence. + * + * A symbolic Transformer consists of + * - RelParams, a sequence of relational symbols + * - IndParams, a sequence of individual symbols + * - Formula, a formula over RelParams and IndParams + * + * A Transformer t represents a function that takes sequence R of relations + * and yields the relation lambda (t.Indparams). Formula(R/RelParams). + * + * As a special case, a nullary Transformer (where RelParams is the empty sequence) + * represents a fixed relation. + * + * An RPFP consists of + * - Nodes, a set of Nodes + * - Edges, a set of hyper-edges + * - Context, a prover context that contains formula AST's + * + * Multiple RPFP's can use the same Context, but you should be careful + * that only one RPFP asserts constraints in the context at any time. + * + * */ + class RPFP : public Z3User + { + public: + + class Edge; + class Node; + bool HornClauses; + + + /** Interface class for interpolating solver. */ + + class LogicSolver { + public: + + context *ctx; /** Z3 context for formulas */ + solver *slvr; /** Z3 solver */ + bool need_goals; /** Can the solver use the goal tree to optimize interpolants? */ + solver aux_solver; /** For temporary use -- don't leave assertions here. */ + + /** Tree interpolation. This method assumes the formulas in TermTree + "assumptions" are currently asserted in the solver. The return + value indicates whether the assertions are satisfiable. In the + UNSAT case, a tree interpolant is returned in "interpolants". + In the SAT case, a model is returned. + */ + + virtual + lbool interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals = 0, + bool weak = false + ) = 0; + + /** Declare a constant in the background theory. */ + virtual void declare_constant(const func_decl &f) = 0; + + /** Is this a background constant? */ + virtual bool is_constant(const func_decl &f) = 0; + + /** Get the constants in the background vocabulary */ + virtual hash_set &get_constants() = 0; + + /** Assert a background axiom. */ + virtual void assert_axiom(const expr &axiom) = 0; + + /** Get the background axioms. */ + virtual const std::vector &get_axioms() = 0; + + /** Return a string describing performance. */ + virtual std::string profile() = 0; + + virtual void write_interpolation_problem(const std::string &file_name, + const std::vector &assumptions, + const std::vector &theory + ){} + + /** Cancel, throw Canceled object if possible. */ + virtual void cancel(){ } + + /* Note: aux solver uses extensional array theory, since it + needs to be able to produce counter-models for + interpolants the have array equalities in them. + */ + LogicSolver(context &c) : aux_solver(c,true){} + + virtual ~LogicSolver(){} + }; + + /** This solver uses iZ3. */ + class iZ3LogicSolver : public LogicSolver { + public: + interpolating_context *ictx; /** iZ3 context for formulas */ + interpolating_solver *islvr; /** iZ3 solver */ + + lbool interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals = 0, + bool weak = false) + { + literals _labels; + islvr->SetWeakInterpolants(weak); + return islvr->interpolate_tree(assumptions,interpolants,_model,_labels,true); + } + + void assert_axiom(const expr &axiom){ + islvr->AssertInterpolationAxiom(axiom); + } + + const std::vector &get_axioms() { + return islvr->GetInterpolationAxioms(); + } + + std::string profile(){ + return islvr->profile(); + } + +#if 0 + iZ3LogicSolver(config &_config){ + ctx = ictx = new interpolating_context(_config); + slvr = islvr = new interpolating_solver(*ictx); + need_goals = false; + islvr->SetWeakInterpolants(true); + } +#endif + + iZ3LogicSolver(context &c, bool models = true) : LogicSolver(c) { + ctx = ictx = &c; + slvr = islvr = new interpolating_solver(*ictx, models); + need_goals = false; + islvr->SetWeakInterpolants(true); + } + + void write_interpolation_problem(const std::string &file_name, + const std::vector &assumptions, + const std::vector &theory + ){ +#if 0 + islvr->write_interpolation_problem(file_name,assumptions,theory); +#endif + + } + + void cancel(){islvr->cancel();} + + /** Declare a constant in the background theory. */ + virtual void declare_constant(const func_decl &f){ + bckg.insert(f); + } + + /** Is this a background constant? */ + virtual bool is_constant(const func_decl &f){ + return bckg.find(f) != bckg.end(); + } + + /** Get the constants in the background vocabulary */ + virtual hash_set &get_constants(){ + return bckg; + } + + ~iZ3LogicSolver(){ + // delete ictx; + delete islvr; + } + private: + hash_set bckg; + + }; + +#if 0 + /** Create a logic solver from a Z3 configuration. */ + static iZ3LogicSolver *CreateLogicSolver(config &_config){ + return new iZ3LogicSolver(_config); + } +#endif + + /** Create a logic solver from a low-level Z3 context. + Only use this if you know what you're doing. */ + static iZ3LogicSolver *CreateLogicSolver(context c){ + return new iZ3LogicSolver(c); + } + + LogicSolver *ls; + + protected: + int nodeCount; + int edgeCount; + + class stack_entry + { + public: + std::list edges; + std::list nodes; + std::list > constraints; + }; + + + public: + model dualModel; + protected: + literals dualLabels; + std::list stack; + std::vector axioms; // only saved here for printing purposes + solver &aux_solver; + hash_set *proof_core; + + public: + + /** Construct an RPFP graph with a given interpolating prover context. It is allowed to + have multiple RPFP's use the same context, but you should never have teo RPFP's + with the same conext asserting nodes or edges at the same time. Note, if you create + axioms in one RPFP, them create a second RPFP with the same context, the second will + inherit the axioms. + */ + + RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx)), dualModel(*(_ls->ctx)), aux_solver(_ls->aux_solver) + { + ls = _ls; + nodeCount = 0; + edgeCount = 0; + stack.push_back(stack_entry()); + HornClauses = false; + proof_core = 0; + } + + virtual ~RPFP(); + + /** Symbolic representation of a relational transformer */ + class Transformer + { + public: + std::vector RelParams; + std::vector IndParams; + Term Formula; + RPFP *owner; + hash_map labels; + + Transformer *Clone() + { + return new Transformer(*this); + } + + void SetEmpty(){ + Formula = owner->ctx.bool_val(false); + } + + void SetFull(){ + Formula = owner->ctx.bool_val(true); + } + + bool IsEmpty(){ + return eq(Formula,owner->ctx.bool_val(false)); + } + + bool IsFull(){ + return eq(Formula,owner->ctx.bool_val(true)); + } + + void UnionWith(const Transformer &other){ + Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); + Formula = Formula || t; + } + + void IntersectWith(const Transformer &other){ + Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); + Formula = Formula && t; + } + + bool SubsetEq(const Transformer &other){ + Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); + expr test = Formula && !t; + owner->aux_solver.push(); + owner->aux_solver.add(test); + check_result res = owner->aux_solver.check(); + owner->aux_solver.pop(1); + return res == unsat; + } + + void Complement(){ + Formula = !Formula; + } + + void Simplify(){ + Formula = Formula.simplify(); + } + + Transformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula, RPFP *_owner) + : RelParams(_RelParams), IndParams(_IndParams), Formula(_Formula) {owner = _owner;} + }; + + /** Create a symbolic transformer. */ + Transformer CreateTransformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula) + { + // var ops = new Util.ContextOps(ctx); + // var foo = ops.simplify_lhs(_Formula); + // t.Formula = foo.Item1; + // t.labels = foo.Item2; + return Transformer(_RelParams,_IndParams,_Formula,this); + } + + /** Create a relation (nullary relational transformer) */ + Transformer CreateRelation(const std::vector &_IndParams, const Term &_Formula) + { + return CreateTransformer(std::vector(), _IndParams, _Formula); + } + + /** A node in the RPFP graph */ + class Node + { + public: + FuncDecl Name; + Transformer Annotation; + Transformer Bound; + Transformer Underapprox; + RPFP *owner; + int number; + Edge *Outgoing; + std::vector Incoming; + Term dual; + Node *map; + + Node(const FuncDecl &_Name, const Transformer &_Annotation, const Transformer &_Bound, const Transformer &_Underapprox, const Term &_dual, RPFP *_owner, int _number) + : Name(_Name), Annotation(_Annotation), Bound(_Bound), Underapprox(_Underapprox), dual(_dual) {owner = _owner; number = _number; Outgoing = 0;} + }; + + /** Create a node in the graph. The input is a term R(v_1...v_n) + * where R is an arbitrary relational symbol and v_1...v_n are + * arbitary distinct variables. The names are only of mnemonic value, + * however, the number and type of arguments determine the type + * of the relation at this node. */ + + Node *CreateNode(const Term &t) + { + std::vector _IndParams; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + _IndParams.push_back(t.arg(i)); + Node *n = new Node(t.decl(), + CreateRelation(_IndParams,ctx.bool_val(true)), + CreateRelation(_IndParams,ctx.bool_val(true)), + CreateRelation(_IndParams,ctx.bool_val(false)), + expr(ctx), this, ++nodeCount + ); + nodes.push_back(n); + return n; + } + + /** Clone a node (can be from another graph). */ + + Node *CloneNode(Node *old) + { + Node *n = new Node(old->Name, + old->Annotation, + old->Bound, + old->Underapprox, + expr(ctx), + this, + ++nodeCount + ); + nodes.push_back(n); + n->map = old; + return n; + } + + /** Delete a node. You can only do this if not connected to any edges.*/ + void DeleteNode(Node *node){ + if(node->Outgoing || !node->Incoming.empty()) + throw "cannot delete RPFP node"; + for(std::vector::iterator it = nodes.end(), en = nodes.begin(); it != en;){ + if(*(--it) == node){ + nodes.erase(it); + break; + } + } + delete node; + } + + /** This class represents a hyper-edge in the RPFP graph */ + + class Edge + { + public: + Transformer F; + Node *Parent; + std::vector Children; + RPFP *owner; + int number; + // these should be internal... + Term dual; + hash_map relMap; + hash_map varMap; + Edge *map; + Term labeled; + std::vector constraints; + + Edge(Node *_Parent, const Transformer &_F, const std::vector &_Children, RPFP *_owner, int _number) + : F(_F), Parent(_Parent), Children(_Children), dual(expr(_owner->ctx)) { + owner = _owner; + number = _number; + } + }; + + + /** Create a hyper-edge. */ + Edge *CreateEdge(Node *_Parent, const Transformer &_F, const std::vector &_Children) + { + Edge *e = new Edge(_Parent,_F,_Children,this,++edgeCount); + _Parent->Outgoing = e; + for(unsigned i = 0; i < _Children.size(); i++) + _Children[i]->Incoming.push_back(e); + edges.push_back(e); + return e; + } + + + /** Delete a hyper-edge and unlink it from any nodes. */ + void DeleteEdge(Edge *edge){ + if(edge->Parent) + edge->Parent->Outgoing = 0; + for(unsigned int i = 0; i < edge->Children.size(); i++){ + std::vector &ic = edge->Children[i]->Incoming; + for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ + if(*it == edge){ + ic.erase(it); + break; + } + } + } + for(std::vector::iterator it = edges.end(), en = edges.begin(); it != en;){ + if(*(--it) == edge){ + edges.erase(it); + break; + } + } + delete edge; + } + + /** Create an edge that lower-bounds its parent. */ + Edge *CreateLowerBoundEdge(Node *_Parent) + { + return CreateEdge(_Parent, _Parent->Annotation, std::vector()); + } + + + /** For incremental solving, asserts the constraint associated + * with this edge in the SMT context. If this edge is removed, + * you must pop the context accordingly. The second argument is + * the number of pushes we are inside. */ + + virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); + + /* Constrain an edge by the annotation of one of its children. */ + + void ConstrainParent(Edge *parent, Node *child); + + /** For incremental solving, asserts the negation of the upper bound associated + * with a node. + * */ + + void AssertNode(Node *n); + + /** Assert a constraint on an edge in the SMT context. + */ + void ConstrainEdge(Edge *e, const Term &t); + + /** Fix the truth values of atomic propositions in the given + edge to their values in the current assignment. */ + void FixCurrentState(Edge *root); + + void FixCurrentStateFull(Edge *edge, const expr &extra); + + void FixCurrentStateFull(Edge *edge, const std::vector &assumps, const hash_map &renaming); + + /** Declare a constant in the background theory. */ + + void DeclareConstant(const FuncDecl &f); + + /** Assert a background axiom. Background axioms can be used to provide the + * theory of auxilliary functions or relations. All symbols appearing in + * background axioms are considered global, and may appear in both transformer + * and relational solutions. Semantically, a solution to the RPFP gives + * an interpretation of the unknown relations for each interpretation of the + * auxilliary symbols that is consistent with the axioms. Axioms should be + * asserted before any calls to Push. They cannot be de-asserted by Pop. */ + + void AssertAxiom(const Term &t); + +#if 0 + /** Do not call this. */ + + void RemoveAxiom(const Term &t); +#endif + + /** Solve an RPFP graph. This means either strengthen the annotation + * so that the bound at the given root node is satisfied, or + * show that this cannot be done by giving a dual solution + * (i.e., a counterexample). + * + * In the current implementation, this only works for graphs that + * are: + * - tree-like + * + * - closed. + * + * In a tree-like graph, every nod has out most one incoming and one out-going edge, + * and there are no cycles. In a closed graph, every node has exactly one out-going + * edge. This means that the leaves of the tree are all hyper-edges with no + * children. Such an edge represents a relation (nullary transformer) and thus + * a lower bound on its parent. The parameter root must be the root of this tree. + * + * If Solve returns LBool.False, this indicates success. The annotation of the tree + * has been updated to satisfy the upper bound at the root. + * + * If Solve returns LBool.True, this indicates a counterexample. For each edge, + * you can then call Eval to determine the values of symbols in the transformer formula. + * You can also call Empty on a node to determine if its value in the counterexample + * is the empty relation. + * + * \param root The root of the tree + * \param persist Number of context pops through which result should persist + * + * + */ + + lbool Solve(Node *root, int persist); + + /** Same as Solve, but annotates only a single node. */ + + lbool SolveSingleNode(Node *root, Node *node); + + /** Get the constraint tree (but don't solve it) */ + + TermTree *GetConstraintTree(Node *root, Node *skip_descendant = 0); + + /** Dispose of the dual model (counterexample) if there is one. */ + + void DisposeDualModel(); + + /** Check satisfiability of asserted edges and nodes. Same functionality as + * Solve, except no primal solution (interpolant) is generated in the unsat case. */ + + check_result Check(Node *root, std::vector underapproxes = std::vector(), + std::vector *underapprox_core = 0); + + /** Update the model, attempting to make the propositional literals in assumps true. If possible, + return sat, else return unsat and keep the old model. */ + + check_result CheckUpdateModel(Node *root, std::vector assumps); + + /** Determines the value in the counterexample of a symbol occuring in the transformer formula of + * a given edge. */ + + Term Eval(Edge *e, Term t); + + /** Return the fact derived at node p in a counterexample. */ + + Term EvalNode(Node *p); + + /** Returns true if the given node is empty in the primal solution. For proecudure summaries, + this means that the procedure is not called in the current counter-model. */ + + bool Empty(Node *p); + + /** Compute an underapproximation of every node in a tree rooted at "root", + based on a previously computed counterexample. */ + + Term ComputeUnderapprox(Node *root, int persist); + + /** Try to strengthen the annotation of a node by removing disjuncts. */ + void Generalize(Node *root, Node *node); + + + /** Compute disjunctive interpolant for node by case splitting */ + void InterpolateByCases(Node *root, Node *node); + + /** Push a scope. Assertions made after Push can be undone by Pop. */ + + void Push(); + + /** Exception thrown when bad clause is encountered */ + + struct bad_clause { + std::string msg; + int i; + bad_clause(const std::string &_msg, int _i){ + msg = _msg; + i = _i; + } + }; + + struct parse_error { + std::string msg; + parse_error(const std::string &_msg){ + msg = _msg; + } + }; + + struct file_not_found { + }; + + struct bad_format { + }; + + // thrown on internal error + struct Bad { + }; + + /** Pop a scope (see Push). Note, you cannot pop axioms. */ + + void Pop(int num_scopes); + + /** Erase the proof by performing a Pop, Push and re-assertion of + all the popped constraints */ + void PopPush(); + + /** Return true if the given edge is used in the proof of unsat. + Can be called only after Solve or Check returns an unsat result. */ + + bool EdgeUsedInProof(Edge *edge); + + + /** Convert a collection of clauses to Nodes and Edges in the RPFP. + + Predicate unknowns are uninterpreted predicates not + occurring in the background theory. + + Clauses are of the form + + B => P(t_1,...,t_k) + + where P is a predicate unknown and predicate unknowns + occur only positivey in H and only under existential + quantifiers in prenex form. + + Each predicate unknown maps to a node. Each clause maps to + an edge. Let C be a clause B => P(t_1,...,t_k) where the + sequence of predicate unknowns occurring in B (in order + of occurrence) is P_1..P_n. The clause maps to a transformer + T where: + + T.Relparams = P_1..P_n + T.Indparams = x_1...x+k + T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k + + Throws exception bad_clause(msg,i) if a clause i is + in the wrong form. + + */ + + struct label_struct { + symbol name; + expr value; + bool pos; + label_struct(const symbol &s, const expr &e, bool b) + : name(s), value(e), pos(b) {} + }; + + +#ifdef _WINDOWS + __declspec(dllexport) +#endif + void FromClauses(const std::vector &clauses); + + void FromFixpointContext(fixedpoint fp, std::vector &queries); + + void WriteSolution(std::ostream &s); + + void WriteCounterexample(std::ostream &s, Node *node); + + enum FileFormat {DualityFormat, SMT2Format, HornFormat}; + + /** Write the RPFP to a file (currently in SMTLIB 1.2 format) */ + void WriteProblemToFile(std::string filename, FileFormat format = DualityFormat); + + /** Read the RPFP from a file (in specificed format) */ + void ReadProblemFromFile(std::string filename, FileFormat format = DualityFormat); + + /** Translate problem to Horn clause form */ + void ToClauses(std::vector &cnsts, FileFormat format = DualityFormat); + + /** Translate the RPFP to a fixed point context, with queries */ + fixedpoint ToFixedPointProblem(std::vector &queries); + + /** Nodes of the graph. */ + std::vector nodes; + + /** Edges of the graph. */ + std::vector edges; + + /** Fuse a vector of transformers. If the total number of inputs of the transformers + is N, then the result is an N-ary transfomer whose output is the union of + the outputs of the given transformers. The is, suppose we have a vetor of transfoermers + {T_i(r_i1,...,r_iN(i) : i=1..M}. The the result is a transformer + + F(r_11,...,r_iN(1),...,r_M1,...,r_MN(M)) = + T_1(r_11,...,r_iN(1)) U ... U T_M(r_M1,...,r_MN(M)) + */ + + Transformer Fuse(const std::vector &trs); + + /** Fuse edges so that each node is the output of at most one edge. This + transformation is solution-preserving, but changes the numbering of edges in + counterexamples. + */ + void FuseEdges(); + + void RemoveDeadNodes(); + + Term SubstParams(const std::vector &from, + const std::vector &to, const Term &t); + + Term SubstParamsNoCapture(const std::vector &from, + const std::vector &to, const Term &t); + + Term Localize(Edge *e, const Term &t); + + void EvalNodeAsConstraint(Node *p, Transformer &res); + + TermTree *GetGoalTree(Node *root); + + int EvalTruth(hash_map &memo, Edge *e, const Term &f); + + void GetLabels(Edge *e, std::vector &labels); + + // int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); + + /** Compute and save the proof core for future calls to + EdgeUsedInProof. You only need to call this if you will pop + the solver before calling EdgeUsedInProof. + */ + void ComputeProofCore(); + + int CumulativeDecisions(); + + solver &slvr(){ + return *ls->slvr; + } + + protected: + + void ClearProofCore(){ + if(proof_core) + delete proof_core; + proof_core = 0; + } + + Term SuffixVariable(const Term &t, int n); + + Term HideVariable(const Term &t, int n); + + void RedVars(Node *node, Term &b, std::vector &v); + + Term RedDualRela(Edge *e, std::vector &args, int idx); + + Term LocalizeRec(Edge *e, hash_map &memo, const Term &t); + + void SetEdgeMaps(Edge *e); + + Term ReducedDualEdge(Edge *e); + + TermTree *ToTermTree(Node *root, Node *skip_descendant = 0); + + TermTree *ToGoalTree(Node *root); + + void CollapseTermTreeRec(TermTree *root, TermTree *node); + + TermTree *CollapseTermTree(TermTree *node); + + void DecodeTree(Node *root, TermTree *interp, int persist); + + Term GetUpperBound(Node *n); + + TermTree *AddUpperBound(Node *root, TermTree *t); + +#if 0 + void WriteInterps(System.IO.StreamWriter f, TermTree t); +#endif + + void WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s); + + void WriteEdgeAssignment(std::ostream &s, Edge *e); + + + // Scan the clause body for occurrences of the predicate unknowns + + Term ScanBody(hash_map &memo, + const Term &t, + hash_map &pmap, + std::vector &res, + std::vector &nodes); + + Term RemoveLabelsRec(hash_map &memo, const Term &t, std::vector &lbls); + + Term RemoveLabels(const Term &t, std::vector &lbls); + + Term GetAnnotation(Node *n); + + + Term GetUnderapprox(Node *n); + + Term UnderapproxFlag(Node *n); + + hash_map underapprox_flag_rev; + + Node *UnderapproxFlagRev(const Term &flag); + + Term ProjectFormula(std::vector &keep_vec, const Term &f); + + int SubtermTruth(hash_map &memo, const Term &); + + void ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set *done, bool truth, hash_set &dont_cares); + + void Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares); + + Term UnderapproxFormula(const Term &f, hash_set &dont_cares); + + void ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, + hash_set &done, hash_set &dont_cares, bool extensional = true); + + public: + Term UnderapproxFullFormula(const Term &f, bool extensional = true); + + protected: + Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); + + hash_map resolve_ite_memo; + + Term ResolveIte(hash_map &memo, const Term &t, std::vector &lits, + hash_set *done, hash_set &dont_cares); + + struct ArrayValue { + bool defined; + std::map entries; + expr def_val; + }; + + void EvalArrayTerm(const Term &t, ArrayValue &res); + + Term EvalArrayEquality(const Term &f); + + Term ModelValueAsConstraint(const Term &t); + + void GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, + hash_set *done, bool truth); + + Term SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t); + + Term SubstBound(hash_map &subst, const Term &t); + + void ConstrainEdgeLocalized(Edge *e, const Term &t); + + void GreedyReduce(solver &s, std::vector &conjuncts); + + void NegateLits(std::vector &lits); + + expr SimplifyOr(std::vector &lits); + + expr SimplifyAnd(std::vector &lits); + + void SetAnnotation(Node *root, const expr &t); + + void AddEdgeToSolver(Edge *edge); + + void AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge); + + void AddToProofCore(hash_set &core); + + void GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under); + + Term StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits); + + expr NegateLit(const expr &f); + + expr GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox); + + bool IsVar(const expr &t); + + void GetVarsRec(hash_set &memo, const expr &cnst, std::vector &vars); + + expr UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params); + + void AddParamsToTransformer(Transformer &trans, const std::vector ¶ms); + + expr AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms); + + expr GetRelRec(hash_set &memo, const expr &t, const func_decl &rel); + + expr GetRel(Edge *edge, int child_idx); + + void GetDefs(const expr &cnst, hash_map &defs); + + void GetDefsRec(const expr &cnst, hash_map &defs); + + void AddParamsToNode(Node *node, const std::vector ¶ms); + + void UnhoistLoop(Edge *loop_edge, Edge *init_edge); + + void Unhoist(); + + Term ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts); + + Term ElimIte(const Term &t); + + void MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node); + + virtual void slvr_add(const expr &e); + + virtual void slvr_pop(int i); + + virtual void slvr_push(); + + virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); + + virtual lbool ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals = 0, + bool weak = false); + + virtual bool proof_core_contains(const expr &e); + + }; + + + /** RPFP solver base class. */ + + class Solver { + + public: + + 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 + counterexample to use as a guide. This is chiefly useful for + abstraction refinement metholdologies, and is only used as a + heuristic. */ + + virtual bool Solve() = 0; + + virtual Counterexample &GetCounterexample() = 0; + + virtual bool SetOption(const std::string &option, const std::string &value) = 0; + + /** Learn heuristic information from another solver. This + is chiefly useful for abstraction refinement, when we want to + solve a series of similar problems. */ + + virtual void LearnFrom(Solver *old_solver) = 0; + + virtual ~Solver(){} + + static Solver *Create(const std::string &solver_class, RPFP *rpfp); + + /** This can be called asynchrnously to cause Solve to throw a + Canceled exception at some time in the future. + */ + virtual void Cancel() = 0; + + /** Object thrown on cancellation */ + struct Canceled {}; + + /** Object thrown on incompleteness */ + struct Incompleteness {}; + }; +} + + +// Allow to hash on nodes and edges in deterministic way + +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Duality::RPFP::Node *p) const { + return p->number; + } + }; +} + +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Duality::RPFP::Edge *p) const { + return p->number; + } + }; +} + +// allow to walk sets of nodes without address dependency + +namespace std { + template <> + class less { + public: + bool operator()(Duality::RPFP::Node * const &s, Duality::RPFP::Node * const &t) const { + return s->number < t->number; // s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + +// #define LIMIT_STACK_WEIGHT 5 + + +namespace Duality { + /** Caching version of RPFP. Instead of asserting constraints, returns assumption literals */ + + class RPFP_caching : public RPFP { + public: + + /** appends assumption literals for edge to lits. if with_children is true, + includes that annotation of the edge's children. + */ + void AssertEdgeCache(Edge *e, std::vector &lits, bool with_children = false); + + /** appends assumption literals for node to lits */ + void AssertNodeCache(Node *, std::vector lits); + + /** check assumption lits, and return core */ + check_result CheckCore(const std::vector &assumps, std::vector &core); + + /** Clone another RPFP into this one, keeping a map */ + void Clone(RPFP *other); + + /** Get the clone of a node */ + Node *GetNodeClone(Node *other_node); + + /** Get the clone of an edge */ + Edge *GetEdgeClone(Edge *other_edge); + + /** Try to strengthen the parent of an edge */ + void GeneralizeCache(Edge *edge); + + /** Try to propagate some facts from children to parents of edge. + Return true if success. */ + bool PropagateCache(Edge *edge); + + /** Construct a caching RPFP using a LogicSolver */ + RPFP_caching(LogicSolver *_ls) : RPFP(_ls) {} + + /** Constraint an edge by its child's annotation. Return + assumption lits. */ + void ConstrainParentCache(Edge *parent, Node *child, std::vector &lits); + +#ifdef LIMIT_STACK_WEIGHT + virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); +#endif + + virtual ~RPFP_caching(){} + + protected: + hash_map AssumptionLits; + hash_map NodeCloneMap; + hash_map EdgeCloneMap; + std::vector alit_stack; + std::vector alit_stack_sizes; + + // to let us use one solver per edge + struct edge_solver { + hash_map AssumptionLits; + uptr slvr; + }; + hash_map edge_solvers; + +#ifdef LIMIT_STACK_WEIGHT + struct weight_counter { + int val; + weight_counter(){val = 0;} + void swap(weight_counter &other){ + std::swap(val,other.val); + } + }; + + struct big_stack_entry { + weight_counter weight_added; + std::vector new_alits; + std::vector alit_stack; + std::vector alit_stack_sizes; + }; + + std::vector new_alits; + weight_counter weight_added; + std::vector big_stack; +#endif + + + + void GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map = 0); + + void GreedyReduceCache(std::vector &assumps, std::vector &core); + + void FilterCore(std::vector &core, std::vector &full_core); + void ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits); + + virtual void slvr_add(const expr &e); + + virtual void slvr_pop(int i); + + virtual void slvr_push(); + + virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); + + virtual lbool ls_interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + TermTree *goals = 0, + bool weak = false); + + virtual bool proof_core_contains(const expr &e); + + void GetTermTreeAssertionLiterals(TermTree *assumptions); + + void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); + + edge_solver &SolverForEdge(Edge *edge, bool models, bool axioms); + + public: + struct scoped_solver_for_edge { + solver *orig_slvr; + RPFP_caching *rpfp; + edge_solver *es; + scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false, bool axioms = false){ + rpfp = _rpfp; + orig_slvr = rpfp->ls->slvr; + es = &(rpfp->SolverForEdge(edge,models,axioms)); + rpfp->ls->slvr = es->slvr.get(); + rpfp->AssumptionLits.swap(es->AssumptionLits); + } + ~scoped_solver_for_edge(){ + rpfp->ls->slvr = orig_slvr; + rpfp->AssumptionLits.swap(es->AssumptionLits); + } + }; + + }; + +} diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index accbec81a..8040310ec 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -551,7 +551,7 @@ namespace Duality { Z3User::Term Z3User::PushQuantifier(const expr &t, const expr &body, bool is_forall){ if(t.get_quantifier_num_bound() == 1){ std::vector fmlas,free,not_free; - CollectConjuncts(body,fmlas, !is_forall); + CollectJuncts(body,fmlas, is_forall ? Or : And, false); for(unsigned i = 0; i < fmlas.size(); i++){ const expr &fmla = fmlas[i]; if(fmla.has_free(0)) @@ -564,7 +564,7 @@ namespace Duality { return SimplifyAndOr(not_free,op == And); expr q = clone_quantifier(is_forall ? Forall : Exists,t, SimplifyAndOr(free, op == And)); if(!not_free.empty()) - q = ctx.make(op,q,SimplifyAndOr(not_free, op == And)); + q = ctx.make(op,q,DeleteBound(0,1,SimplifyAndOr(not_free, op == And))); return q; } return clone_quantifier(is_forall ? Forall : Exists,t,body); @@ -579,14 +579,16 @@ namespace Duality { args[i] = CloneQuantAndSimp(t, body.arg(i), is_forall); return SimplifyAndOr(args, body.decl().get_decl_kind() == And); } - else if(body.decl().get_decl_kind() == (is_forall ? And : Or)){ // quantifier distributes + else if(body.decl().get_decl_kind() == (is_forall ? Or : And)){ // quantifier distributes return PushQuantifier(t,body,is_forall); // may distribute partially } else if(body.decl().get_decl_kind() == Not){ - return CloneQuantAndSimp(t,body.arg(0),!is_forall); + return ctx.make(Not,CloneQuantAndSimp(t,body.arg(0),!is_forall)); } } - return clone_quantifier(t,body); + if(t.get_quantifier_num_bound() == 1 && !body.has_free(0)) + return DeleteBound(0,1,body); // drop the quantifier + return clone_quantifier(is_forall ? Forall : Exists,t,body); } Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ @@ -2494,6 +2496,20 @@ namespace Duality { } } + void Z3User::CollectJuncts(const Term &f, std::vector &lits, decl_kind op, bool negate){ + if(f.is_app() && f.decl().get_decl_kind() == Not) + CollectJuncts(f.arg(0), lits, (op == And) ? Or : And, !negate); + else if(f.is_app() && f.decl().get_decl_kind() == op){ + int num_args = f.num_args(); + for(int i = 0; i < num_args; i++) + CollectJuncts(f.arg(i),lits,op,negate); + } + else { + expr junct = negate ? Negate(f) : f; + lits.push_back(junct); + } + } + struct TermLt { bool operator()(const expr &x, const expr &y){ unsigned xid = x.get_id(); @@ -3444,6 +3460,46 @@ namespace Duality { return SubstBoundRec(memo, subst, 0, t); } + // Eliminate the deBruijn indices from level to level+num-1 + Z3User::Term Z3User::DeleteBoundRec(hash_map > &memo, int level, int num, const Term &t) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo[level].insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(DeleteBoundRec(memo, level, num, t.arg(i))); + res = f(args.size(),&args[0]); + } + else if (t.is_quantifier()){ + int bound = t.get_quantifier_num_bound(); + std::vector pats; + t.get_patterns(pats); + for(unsigned i = 0; i < pats.size(); i++) + pats[i] = DeleteBoundRec(memo, level + bound, num, pats[i]); + res = clone_quantifier(t, DeleteBoundRec(memo, level + bound, num, t.body()), pats); + } + else if (t.is_var()) { + int idx = t.get_index_value(); + if(idx >= level){ + res = ctx.make_var(idx-num,t.get_sort()); + } + else res = t; + } + else res = t; + return res; + } + + Z3User::Term Z3User::DeleteBound(int level, int num, const Term &t){ + hash_map > memo; + return DeleteBoundRec(memo, level, num, t); + } + int Z3User::MaxIndex(hash_map &memo, const Term &t) { std::pair foo(t,-1); diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index d5c9a9575..604192ebc 100755 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -340,6 +340,12 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st params p; return simplify(p); } + + expr context::make_var(int idx, const sort &s){ + ::sort * a = to_sort(s.raw()); + return cook(m().mk_var(idx,a)); + } + expr expr::qe_lite() const { ::qe_lite qe(m()); diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index a85b3981d..3ee7c3882 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -1,1482 +1,1487 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - duality_wrapper.h - -Abstract: - - wrap various Z3 classes in the style expected by duality - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - - ---*/ -#ifndef __DUALITY_WRAPPER_H_ -#define __DUALITY_WRAPPER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include"version.h" -#include - -#include "iz3hash.h" -#include "model.h" -#include "solver.h" - -#include"well_sorted.h" -#include"arith_decl_plugin.h" -#include"bv_decl_plugin.h" -#include"datatype_decl_plugin.h" -#include"array_decl_plugin.h" -#include"ast_translation.h" -#include"ast_pp.h" -#include"ast_ll_pp.h" -#include"ast_smt_pp.h" -#include"ast_smt2_pp.h" -#include"th_rewriter.h" -#include"var_subst.h" -#include"expr_substitution.h" -#include"pp.h" -#include"scoped_ctrl_c.h" -#include"cancel_eh.h" -#include"scoped_timer.h" -#include"scoped_proof.h" - -namespace Duality { - - class exception; - class config; - class context; - class symbol; - class params; - class ast; - class sort; - class func_decl; - class expr; - class solver; - class goal; - class tactic; - class probe; - class model; - class func_interp; - class func_entry; - class statistics; - class apply_result; - class fixedpoint; - class literals; - - /** - Duality global configuration object. - */ - - class config { - params_ref m_params; - config & operator=(config const & s); - public: - config(config const & s) : m_params(s.m_params) {} - config(const params_ref &_params) : m_params(_params) {} - config() { } // TODO: create a new params object here - ~config() { } - void set(char const * param, char const * value) { m_params.set_str(param,value); } - void set(char const * param, bool value) { m_params.set_bool(param,value); } - void set(char const * param, int value) { m_params.set_uint(param,value); } - params_ref &get() {return m_params;} - const params_ref &get() const {return m_params;} - }; - - enum decl_kind { - True, - False, - And, - Or, - Not, - Iff, - Ite, - Equal, - Implies, - Distinct, - Xor, - Oeq, - Interp, - Leq, - Geq, - Lt, - Gt, - Plus, - Sub, - Uminus, - Times, - Div, - Idiv, - Rem, - Mod, - Power, - ToReal, - ToInt, - IsInt, - Select, - Store, - ConstArray, - ArrayDefault, - ArrayMap, - SetUnion, - SetIntersect, - SetDifference, - SetComplement, - SetSubSet, - AsArray, - Numeral, - Forall, - Exists, - Variable, - Uninterpreted, - OtherBasic, - OtherArith, - OtherArray, - Other - }; - - enum sort_kind {BoolSort,IntSort,RealSort,ArraySort,UninterpretedSort,UnknownSort}; - - /** - A context has an ast manager global configuration options, etc. - */ - - class context { - protected: - ast_manager &mgr; - config m_config; - - family_id m_basic_fid; - family_id m_array_fid; - family_id m_arith_fid; - family_id m_bv_fid; - family_id m_dt_fid; - family_id m_datalog_fid; - arith_util m_arith_util; - - public: - context(ast_manager &_manager, const config &_config) : mgr(_manager), m_config(_config), m_arith_util(_manager) { - m_basic_fid = m().get_basic_family_id(); - m_arith_fid = m().mk_family_id("arith"); - m_bv_fid = m().mk_family_id("bv"); - m_array_fid = m().mk_family_id("array"); - m_dt_fid = m().mk_family_id("datatype"); - m_datalog_fid = m().mk_family_id("datalog_relation"); - } - ~context() { } - - ast_manager &m() const {return *(ast_manager *)&mgr;} - - void set(char const * param, char const * value) { m_config.set(param,value); } - void set(char const * param, bool value) { m_config.set(param,value); } - void set(char const * param, int value) { m_config.set(param,value); } - config &get_config() {return m_config;} - - symbol str_symbol(char const * s); - symbol int_symbol(int n); - - sort bool_sort(); - sort int_sort(); - sort real_sort(); - sort bv_sort(unsigned sz); - sort array_sort(sort d, sort r); - - func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range); - func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range); - func_decl function(char const * name, sort const & domain, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); - func_decl fresh_func_decl(char const * name, const std::vector &domain, sort const & range); - func_decl fresh_func_decl(char const * name, sort const & range); - - expr constant(symbol const & name, sort const & s); - expr constant(char const * name, sort const & s); - expr constant(const std::string &name, sort const & s); - expr bool_const(char const * name); - expr int_const(char const * name); - expr real_const(char const * name); - expr bv_const(char const * name, unsigned sz); - - expr bool_val(bool b); - - expr int_val(int n); - expr int_val(unsigned n); - expr int_val(char const * n); - - expr real_val(int n, int d); - expr real_val(int n); - expr real_val(unsigned n); - expr real_val(char const * n); - - expr bv_val(int n, unsigned sz); - expr bv_val(unsigned n, unsigned sz); - expr bv_val(char const * n, unsigned sz); - - expr num_val(int n, sort const & s); - - expr mki(family_id fid, ::decl_kind dk, int n, ::expr **args); - expr make(decl_kind op, int n, ::expr **args); - expr make(decl_kind op, const std::vector &args); - expr make(decl_kind op); - expr make(decl_kind op, const expr &arg0); - expr make(decl_kind op, const expr &arg0, const expr &arg1); - expr make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2); - - expr make_quant(decl_kind op, const std::vector &bvs, const expr &body); - expr make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body); - - - decl_kind get_decl_kind(const func_decl &t); - - 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(); - - expr cook(::expr *a); - std::vector cook(ptr_vector< ::expr> v); - ::expr *uncook(const expr &a); - }; - - template - class z3array { - T * m_array; - unsigned m_size; - z3array(z3array const & s); - z3array & operator=(z3array const & s); - public: - z3array(unsigned sz):m_size(sz) { m_array = new T[sz]; } - ~z3array() { delete[] m_array; } - unsigned size() const { return m_size; } - T & operator[](unsigned i) { assert(i < m_size); return m_array[i]; } - T const & operator[](unsigned i) const { assert(i < m_size); return m_array[i]; } - T const * ptr() const { return m_array; } - T * ptr() { return m_array; } - }; - - class object { - protected: - context * m_ctx; - public: - object(): m_ctx((context *)0) {} - object(context & c):m_ctx(&c) {} - object(object const & s):m_ctx(s.m_ctx) {} - context & ctx() const { return *m_ctx; } - friend void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } - ast_manager &m() const {return m_ctx->m();} - }; - - class symbol : public object { - ::symbol m_sym; - public: - symbol(context & c, ::symbol s):object(c), m_sym(s) {} - symbol(symbol const & s):object(s), m_sym(s.m_sym) {} - symbol & operator=(symbol const & s) { m_ctx = s.m_ctx; m_sym = s.m_sym; return *this; } - operator ::symbol() const {return m_sym;} - std::string str() const { - if (m_sym.is_numerical()) { - std::ostringstream buffer; - buffer << m_sym.get_num(); - return buffer.str(); - } - else { - return m_sym.bare_str(); - } - } - friend std::ostream & operator<<(std::ostream & out, symbol const & s){ - return out << s.str(); - } - friend bool operator==(const symbol &x, const symbol &y){ - return x.m_sym == y.m_sym; - } - }; - - class params : public config {}; - - /** Wrapper around an ast pointer */ - class ast_i : public object { - protected: - ::ast *_ast; - public: - ::ast * const &raw() const {return _ast;} - ast_i(context & c, ::ast *a = 0) : object(c) {_ast = a;} - - ast_i(){_ast = 0;} - bool eq(const ast_i &other) const { - return _ast == other._ast; - } - bool lt(const ast_i &other) const { - return _ast < other._ast; - } - friend bool operator==(const ast_i &x, const ast_i&y){ - return x.eq(y); - } - friend bool operator!=(const ast_i &x, const ast_i&y){ - return !x.eq(y); - } - friend bool operator<(const ast_i &x, const ast_i&y){ - return x.lt(y); - } - size_t hash() const {return (size_t)_ast;} - bool null() const {return !_ast;} - }; - - /** Reference counting verison of above */ - class ast : public ast_i { - public: - operator ::ast*() const { return raw(); } - friend bool eq(ast const & a, ast const & b) { return a.raw() == b.raw(); } - - - ast(context &c, ::ast *a = 0) : ast_i(c,a) { - if(_ast) - m().inc_ref(a); - } - - ast() {} - - ast(const ast &other) : ast_i(other) { - if(_ast) - m().inc_ref(_ast); - } - - ast &operator=(const ast &other) { - if(_ast) - m().dec_ref(_ast); - _ast = other._ast; - m_ctx = other.m_ctx; - if(_ast) - m().inc_ref(_ast); - return *this; - } - - ~ast(){ - if(_ast) - m().dec_ref(_ast); - } - - void show() const; - - }; - - class sort : public ast { - public: - sort(context & c):ast(c) {} - sort(context & c, ::sort *s):ast(c, s) {} - sort(sort const & s):ast(s) {} - operator ::sort*() const { return to_sort(raw()); } - sort & operator=(sort const & s) { return static_cast(ast::operator=(s)); } - - bool is_bool() const { return m().is_bool(*this); } - bool is_int() const { return ctx().get_sort_kind(*this) == IntSort; } - bool is_real() const { return ctx().get_sort_kind(*this) == RealSort; } - bool is_arith() const; - bool is_array() const { return ctx().get_sort_kind(*this) == ArraySort; } - bool is_datatype() const; - bool is_relation() const; - bool is_finite_domain() const; - - - sort array_domain() const; - sort array_range() const; - }; - - - class func_decl : public ast { - public: - func_decl() : ast() {} - func_decl(context & c):ast(c) {} - func_decl(context & c, ::func_decl *n):ast(c, n) {} - func_decl(func_decl const & s):ast(s) {} - operator ::func_decl*() const { return to_func_decl(*this); } - func_decl & operator=(func_decl const & s) { return static_cast(ast::operator=(s)); } - - unsigned arity() const; - sort domain(unsigned i) const; - sort range() const; - symbol name() const {return symbol(ctx(),to_func_decl(raw())->get_name());} - decl_kind get_decl_kind() const; - - bool is_const() const { return arity() == 0; } - - expr operator()(unsigned n, expr const * args) const; - expr operator()(const std::vector &args) const; - expr operator()() const; - expr operator()(expr const & a) const; - expr operator()(int a) const; - expr operator()(expr const & a1, expr const & a2) const; - expr operator()(expr const & a1, int a2) const; - expr operator()(int a1, expr const & a2) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const; - - func_decl get_func_decl_parameter(unsigned idx){ - return func_decl(ctx(),to_func_decl(to_func_decl(raw())->get_parameters()[idx].get_ast())); - } - - }; - - class expr : public ast { - public: - expr() : ast() {} - expr(context & c):ast(c) {} - expr(context & c, ::ast *n):ast(c, n) {} - expr(expr const & n):ast(n) {} - expr & operator=(expr const & n) { return static_cast(ast::operator=(n)); } - operator ::expr*() const { return to_expr(raw()); } - unsigned get_id() const {return to_expr(raw())->get_id();} - - sort get_sort() const { return sort(ctx(),m().get_sort(to_expr(raw()))); } - - bool is_bool() const { return get_sort().is_bool(); } - bool is_int() const { return get_sort().is_int(); } - bool is_real() const { return get_sort().is_real(); } - bool is_arith() const { return get_sort().is_arith(); } - bool is_array() const { return get_sort().is_array(); } - bool is_datatype() const { return get_sort().is_datatype(); } - bool is_relation() const { return get_sort().is_relation(); } - bool is_finite_domain() const { return get_sort().is_finite_domain(); } - bool is_true() const {return is_app() && decl().get_decl_kind() == True; } - - bool is_numeral() const { - return is_app() && decl().get_decl_kind() == OtherArith && m().is_unique_value(to_expr(raw())); - } - bool is_app() const {return raw()->get_kind() == AST_APP;} - bool is_quantifier() const {return raw()->get_kind() == AST_QUANTIFIER;} - bool is_var() const {return raw()->get_kind() == AST_VAR;} - bool is_label (bool &pos,std::vector &names) const ; - bool is_ground() const {return to_app(raw())->is_ground();} - bool has_quantifiers() const {return to_app(raw())->has_quantifiers();} - bool has_free(int idx) const { - used_vars proc; - proc.process(to_expr(raw())); - return proc.contains(idx); - } - - // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } - func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} - unsigned num_args() const { - ast_kind dk = raw()->get_kind(); - switch(dk){ - case AST_APP: - return to_app(raw())->get_num_args(); - case AST_QUANTIFIER: - return 1; - case AST_VAR: - return 0; - default:; - } - SASSERT(0); - return 0; - } - expr arg(unsigned i) const { - ast_kind dk = raw()->get_kind(); - switch(dk){ - case AST_APP: - return ctx().cook(to_app(raw())->get_arg(i)); - case AST_QUANTIFIER: - return ctx().cook(to_quantifier(raw())->get_expr()); - default:; - } - assert(0); - return expr(); - } - - expr body() const { - return ctx().cook(to_quantifier(raw())->get_expr()); - } - - friend expr operator!(expr const & a) { - // ::expr *e = a; - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_NOT,a)); - } - - friend expr operator&&(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_AND,a,b)); - } - - friend expr operator||(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_OR,a,b)); - } - - friend expr implies(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_IMPLIES,a,b)); - } - - friend expr operator==(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_EQ,a,b)); - } - - friend expr operator!=(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DISTINCT,a,b)); - } - - friend expr operator+(expr const & a, expr const & b) { - return a.ctx().make(Plus,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_ADD,a,b)); - } - - friend expr operator*(expr const & a, expr const & b) { - return a.ctx().make(Times,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_MUL,a,b)); - } - - friend expr operator/(expr const & a, expr const & b) { - return a.ctx().make(Div,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DIV,a,b)); - } - - friend expr operator-(expr const & a) { - return a.ctx().make(Uminus,a); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_UMINUS,a)); - } - - friend expr operator-(expr const & a, expr const & b) { - return a.ctx().make(Sub,a,b); // expr(a.ctx(),a.m().mk_app(a.ctx().m_arith_fid,OP_SUB,a,b)); - } - - friend expr operator<=(expr const & a, expr const & b) { - return a.ctx().make(Leq,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LE,a,b)); - } - - friend expr operator>=(expr const & a, expr const & b) { - return a.ctx().make(Geq,a,b); //expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GE,a,b)); - } - - friend expr operator<(expr const & a, expr const & b) { - return a.ctx().make(Lt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LT,a,b)); - } - - friend expr operator>(expr const & a, expr const & b) { - return a.ctx().make(Gt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GT,a,b)); - } - - expr simplify() const; - - expr simplify(params const & p) const; - - expr qe_lite() const; - - expr qe_lite(const std::set &idxs, bool index_of_bound) const; - - friend expr clone_quantifier(const expr &, const expr &); - - friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); - - friend expr clone_quantifier(decl_kind, const expr &, const expr &); - - friend std::ostream & operator<<(std::ostream & out, expr const & m){ - m.ctx().print_expr(out,m); - return out; - } - - void get_patterns(std::vector &pats) const ; - - unsigned get_quantifier_num_bound() const { - return to_quantifier(raw())->get_num_decls(); - } - - unsigned get_index_value() const { - var* va = to_var(raw()); - return va->get_idx(); - } - - bool is_quantifier_forall() const { - return to_quantifier(raw())->is_forall(); - } - - sort get_quantifier_bound_sort(unsigned n) const { - return sort(ctx(),to_quantifier(raw())->get_decl_sort(n)); - } - - symbol get_quantifier_bound_name(unsigned n) const { - return symbol(ctx(),to_quantifier(raw())->get_decl_names()[n]); - } - - friend expr forall(const std::vector &quants, const expr &body); - - friend expr exists(const std::vector &quants, const expr &body); - - }; - - - typedef ::decl_kind pfrule; - - class proof : public ast { - public: - proof(context & c):ast(c) {} - proof(context & c, ::proof *s):ast(c, s) {} - proof(proof const & s):ast(s) {} - operator ::proof*() const { return to_app(raw()); } - proof & operator=(proof const & s) { return static_cast(ast::operator=(s)); } - - pfrule rule() const { - ::func_decl *d = to_app(raw())->get_decl(); - return d->get_decl_kind(); - } - - unsigned num_prems() const { - return to_app(raw())->get_num_args() - 1; - } - - expr conc() const { - return ctx().cook(to_app(raw())->get_arg(num_prems())); - } - - proof prem(unsigned i) const { - return proof(ctx(),to_app(to_app(raw())->get_arg(i))); - } - - void get_assumptions(std::vector &assumps); - }; - -#if 0 - -#if Z3_MAJOR_VERSION > 4 || Z3_MAJOR_VERSION == 4 && Z3_MINOR_VERSION >= 3 - template - class ast_vector_tpl : public object { - Z3_ast_vector m_vector; - void init(Z3_ast_vector v) { Z3_ast_vector_inc_ref(ctx(), v); m_vector = v; } - public: - ast_vector_tpl(context & c):object(c) { init(Z3_mk_ast_vector(c)); } - ast_vector_tpl(context & c, Z3_ast_vector v):object(c) { init(v); } - ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } - ~ast_vector_tpl() { /* Z3_ast_vector_dec_ref(ctx(), m_vector); */ } - operator Z3_ast_vector() const { return m_vector; } - unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } - T operator[](unsigned i) const { Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } - void push_back(T const & e) { Z3_ast_vector_push(ctx(), m_vector, e); check_error(); } - void resize(unsigned sz) { Z3_ast_vector_resize(ctx(), m_vector, sz); check_error(); } - T back() const { return operator[](size() - 1); } - void pop_back() { assert(size() > 0); resize(size() - 1); } - bool empty() const { return size() == 0; } - ast_vector_tpl & operator=(ast_vector_tpl const & s) { - Z3_ast_vector_inc_ref(s.ctx(), s.m_vector); - // Z3_ast_vector_dec_ref(ctx(), m_vector); - m_ctx = s.m_ctx; - m_vector = s.m_vector; - return *this; - } - friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } - }; - - typedef ast_vector_tpl ast_vector; - typedef ast_vector_tpl expr_vector; - typedef ast_vector_tpl sort_vector; - typedef ast_vector_tpl func_decl_vector; - -#endif - -#endif - - class func_interp : public object { - ::func_interp * m_interp; - void init(::func_interp * e) { - m_interp = e; - } - public: - func_interp(context & c, ::func_interp * e):object(c) { init(e); } - func_interp(func_interp const & s):object(s) { init(s.m_interp); } - ~func_interp() { } - operator ::func_interp *() const { return m_interp; } - func_interp & operator=(func_interp const & s) { - m_ctx = s.m_ctx; - m_interp = s.m_interp; - return *this; - } - unsigned num_entries() const { return m_interp->num_entries(); } - expr get_arg(unsigned ent, unsigned arg) const { - return expr(ctx(),m_interp->get_entry(ent)->get_arg(arg)); - } - expr get_value(unsigned ent) const { - return expr(ctx(),m_interp->get_entry(ent)->get_result()); - } - expr else_value() const { - return expr(ctx(),m_interp->get_else()); - } - }; - - - - class model : public object { - model_ref m_model; - void init(::model *m) { - m_model = m; - } - public: - model(context & c, ::model * m = 0):object(c), m_model(m) { } - model(model const & s):object(s), m_model(s.m_model) { } - ~model() { } - operator ::model *() const { return m_model.get(); } - model & operator=(model const & s) { - // ::model *_inc_ref(s.ctx(), s.m_model); - // ::model *_dec_ref(ctx(), m_model); - m_ctx = s.m_ctx; - m_model = s.m_model.get(); - return *this; - } - model & operator=(::model *s) { - m_model = s; - return *this; - } - bool null() const {return !m_model;} - - expr eval(expr const & n, bool model_completion=true) const { - ::model * _m = m_model.get(); - expr_ref result(ctx().m()); - _m->eval(n, result, model_completion); - return expr(ctx(), result); - } - - void show() const; - void show_hash() const; - - unsigned num_consts() const {return m_model.get()->get_num_constants();} - unsigned num_funcs() const {return m_model.get()->get_num_functions();} - func_decl get_const_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_constant(i));} - func_decl get_func_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_function(i));} - unsigned size() const; - func_decl operator[](unsigned i) const; - - expr get_const_interp(func_decl f) const { - return ctx().cook(m_model->get_const_interp(to_func_decl(f.raw()))); - } - - func_interp get_func_interp(func_decl f) const { - return func_interp(ctx(),m_model->get_func_interp(to_func_decl(f.raw()))); - } - -#if 0 - friend std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } -#endif - }; - -#if 0 - class stats : public object { - Z3_stats m_stats; - void init(Z3_stats e) { - m_stats = e; - Z3_stats_inc_ref(ctx(), m_stats); - } - public: - stats(context & c):object(c), m_stats(0) {} - stats(context & c, Z3_stats e):object(c) { init(e); } - stats(stats const & s):object(s) { init(s.m_stats); } - ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } - operator Z3_stats() const { return m_stats; } - stats & operator=(stats const & s) { - Z3_stats_inc_ref(s.ctx(), s.m_stats); - if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); - m_ctx = s.m_ctx; - m_stats = s.m_stats; - return *this; - } - unsigned size() const { return Z3_stats_size(ctx(), m_stats); } - std::string key(unsigned i) const { Z3_string s = Z3_stats_get_key(ctx(), m_stats, i); check_error(); return s; } - bool is_uint(unsigned i) const { Z3_bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } - bool is_double(unsigned i) const { Z3_bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } - unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } - double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } - friend std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } - }; -#endif - - enum check_result { - unsat, sat, unknown - }; - - class fixedpoint : public object { - - public: - void get_rules(std::vector &rules); - void get_assertions(std::vector &rules); - void register_relation(const func_decl &rela); - void add_rule(const expr &clause, const symbol &name); - void assert_cnst(const expr &cnst); - }; - - inline std::ostream & operator<<(std::ostream & out, check_result r) { - if (r == unsat) out << "unsat"; - else if (r == sat) out << "sat"; - else out << "unknown"; - return out; - } - - inline check_result to_check_result(lbool l) { - if (l == l_true) return sat; - else if (l == l_false) return unsat; - return unknown; - } - - class solver : public object { - protected: - ::solver *m_solver; - model the_model; - bool canceled; - proof_gen_mode m_mode; - bool extensional; - public: - solver(context & c, bool extensional = false, bool models = true); - solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} - solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver; canceled = false;} - ~solver() { - if(m_solver) - dealloc(m_solver); - } - operator ::solver*() const { return m_solver; } - solver & operator=(solver const & s) { - m_ctx = s.m_ctx; - m_solver = s.m_solver; - the_model = s.the_model; - m_mode = s.m_mode; - return *this; - } - struct cancel_exception {}; - void checkpoint(){ - if(canceled) - throw(cancel_exception()); - } - // void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } - void push() { scoped_proof_mode spm(m(),m_mode); m_solver->push(); } - void pop(unsigned n = 1) { scoped_proof_mode spm(m(),m_mode); m_solver->pop(n); } - // void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } - void add(expr const & e) { scoped_proof_mode spm(m(),m_mode); m_solver->assert_expr(e); } - check_result check() { - scoped_proof_mode spm(m(),m_mode); - checkpoint(); - lbool r = m_solver->check_sat(0,0); - model_ref m; - m_solver->get_model(m); - the_model = m.get(); - return to_check_result(r); - } - check_result check_keep_model(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { - scoped_proof_mode spm(m(),m_mode); - model old_model(the_model); - check_result res = check(n,assumptions,core_size,core); - if(the_model == 0) - the_model = old_model; - return res; - } - check_result check(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { - scoped_proof_mode spm(m(),m_mode); - checkpoint(); - std::vector< ::expr *> _assumptions(n); - for (unsigned i = 0; i < n; i++) { - _assumptions[i] = to_expr(assumptions[i]); - } - the_model = 0; - lbool r = m_solver->check_sat(n,&_assumptions[0]); - - if(core_size && core){ - ptr_vector< ::expr> _core; - m_solver->get_unsat_core(_core); - *core_size = _core.size(); - for(unsigned i = 0; i < *core_size; i++) - core[i] = expr(ctx(),_core[i]); - } - - model_ref m; - m_solver->get_model(m); - the_model = m.get(); - - return to_check_result(r); - } -#if 0 - check_result check(expr_vector assumptions) { - scoped_proof_mode spm(m(),m_mode); - unsigned n = assumptions.size(); - z3array _assumptions(n); - for (unsigned i = 0; i < n; i++) { - check_context(*this, assumptions[i]); - _assumptions[i] = assumptions[i]; - } - Z3_lbool r = Z3_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); - check_error(); - return to_check_result(r); - } -#endif - model get_model() const { return model(ctx(), the_model); } - // std::string reason_unknown() const { Z3_string r = Z3_solver_get_reason_unknown(ctx(), m_solver); check_error(); return r; } - // stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } -#if 0 - expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } - expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } -#endif - // expr proof() const { Z3_ast r = Z3_solver_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } - // friend std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } - - int get_num_decisions(); - - void cancel(){ - scoped_proof_mode spm(m(),m_mode); - canceled = true; - if(m_solver) - m_solver->cancel(); - } - - unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();} - - void show(); - void print(const char *filename); - void show_assertion_ids(); - - proof get_proof(){ - scoped_proof_mode spm(m(),m_mode); - return proof(ctx(),m_solver->get_proof()); - } - - bool extensional_array_theory() {return extensional;} - }; - -#if 0 - class goal : public object { - Z3_goal m_goal; - void init(Z3_goal s) { - m_goal = s; - Z3_goal_inc_ref(ctx(), s); - } - public: - goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } - goal(context & c, Z3_goal s):object(c) { init(s); } - goal(goal const & s):object(s) { init(s.m_goal); } - ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } - operator Z3_goal() const { return m_goal; } - goal & operator=(goal const & s) { - Z3_goal_inc_ref(s.ctx(), s.m_goal); - Z3_goal_dec_ref(ctx(), m_goal); - m_ctx = s.m_ctx; - m_goal = s.m_goal; - return *this; - } - void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } - unsigned size() const { return Z3_goal_size(ctx(), m_goal); } - expr operator[](unsigned i) const { Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } - Z3_goal_prec precision() const { return Z3_goal_precision(ctx(), m_goal); } - bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal) != 0; } - unsigned depth() const { return Z3_goal_depth(ctx(), m_goal); } - void reset() { Z3_goal_reset(ctx(), m_goal); } - unsigned num_exprs() const { Z3_goal_num_exprs(ctx(), m_goal); } - bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } - bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } - friend std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } - }; - - class apply_result : public object { - Z3_apply_result m_apply_result; - void init(Z3_apply_result s) { - m_apply_result = s; - Z3_apply_result_inc_ref(ctx(), s); - } - public: - apply_result(context & c, Z3_apply_result s):object(c) { init(s); } - apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } - ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } - operator Z3_apply_result() const { return m_apply_result; } - apply_result & operator=(apply_result const & s) { - Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); - Z3_apply_result_dec_ref(ctx(), m_apply_result); - m_ctx = s.m_ctx; - m_apply_result = s.m_apply_result; - return *this; - } - unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } - goal operator[](unsigned i) const { Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } - goal operator[](int i) const { assert(i >= 0); return this->operator[](static_cast(i)); } - model convert_model(model const & m, unsigned i = 0) const { - check_context(*this, m); - Z3_model new_m = Z3_apply_result_convert_model(ctx(), m_apply_result, i, m); - check_error(); - return model(ctx(), new_m); - } - friend std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } - }; - - class tactic : public object { - Z3_tactic m_tactic; - void init(Z3_tactic s) { - m_tactic = s; - Z3_tactic_inc_ref(ctx(), s); - } - public: - tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } - tactic(context & c, Z3_tactic s):object(c) { init(s); } - tactic(tactic const & s):object(s) { init(s.m_tactic); } - ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } - operator Z3_tactic() const { return m_tactic; } - tactic & operator=(tactic const & s) { - Z3_tactic_inc_ref(s.ctx(), s.m_tactic); - Z3_tactic_dec_ref(ctx(), m_tactic); - m_ctx = s.m_ctx; - m_tactic = s.m_tactic; - return *this; - } - solver mk_solver() const { Z3_solver r = Z3_mk_solver_from_tactic(ctx(), m_tactic); check_error(); return solver(ctx(), r); } - apply_result apply(goal const & g) const { - check_context(*this, g); - Z3_apply_result r = Z3_tactic_apply(ctx(), m_tactic, g); - check_error(); - return apply_result(ctx(), r); - } - apply_result operator()(goal const & g) const { - return apply(g); - } - std::string help() const { char const * r = Z3_tactic_get_help(ctx(), m_tactic); check_error(); return r; } - friend tactic operator&(tactic const & t1, tactic const & t2) { - check_context(t1, t2); - Z3_tactic r = Z3_tactic_and_then(t1.ctx(), t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - friend tactic operator|(tactic const & t1, tactic const & t2) { - check_context(t1, t2); - Z3_tactic r = Z3_tactic_or_else(t1.ctx(), t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - friend tactic repeat(tactic const & t, unsigned max=UINT_MAX) { - Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); - t.check_error(); - return tactic(t.ctx(), r); - } - friend tactic with(tactic const & t, params const & p) { - Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); - t.check_error(); - return tactic(t.ctx(), r); - } - friend tactic try_for(tactic const & t, unsigned ms) { - Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); - t.check_error(); - return tactic(t.ctx(), r); - } - }; - - class probe : public object { - Z3_probe m_probe; - void init(Z3_probe s) { - m_probe = s; - Z3_probe_inc_ref(ctx(), s); - } - public: - probe(context & c, char const * name):object(c) { Z3_probe r = Z3_mk_probe(c, name); check_error(); init(r); } - probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } - probe(context & c, Z3_probe s):object(c) { init(s); } - probe(probe const & s):object(s) { init(s.m_probe); } - ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } - operator Z3_probe() const { return m_probe; } - probe & operator=(probe const & s) { - Z3_probe_inc_ref(s.ctx(), s.m_probe); - Z3_probe_dec_ref(ctx(), m_probe); - m_ctx = s.m_ctx; - m_probe = s.m_probe; - return *this; - } - double apply(goal const & g) const { double r = Z3_probe_apply(ctx(), m_probe, g); check_error(); return r; } - double operator()(goal const & g) const { return apply(g); } - friend probe operator<=(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_le(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator<=(probe const & p1, double p2) { return p1 <= probe(p1.ctx(), p2); } - friend probe operator<=(double p1, probe const & p2) { return probe(p2.ctx(), p1) <= p2; } - friend probe operator>=(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_ge(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator>=(probe const & p1, double p2) { return p1 >= probe(p1.ctx(), p2); } - friend probe operator>=(double p1, probe const & p2) { return probe(p2.ctx(), p1) >= p2; } - friend probe operator<(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_lt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator<(probe const & p1, double p2) { return p1 < probe(p1.ctx(), p2); } - friend probe operator<(double p1, probe const & p2) { return probe(p2.ctx(), p1) < p2; } - friend probe operator>(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_gt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator>(probe const & p1, double p2) { return p1 > probe(p1.ctx(), p2); } - friend probe operator>(double p1, probe const & p2) { return probe(p2.ctx(), p1) > p2; } - friend probe operator==(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_eq(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator==(probe const & p1, double p2) { return p1 == probe(p1.ctx(), p2); } - friend probe operator==(double p1, probe const & p2) { return probe(p2.ctx(), p1) == p2; } - friend probe operator&&(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_and(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator||(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_or(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator!(probe const & p) { - Z3_probe r = Z3_probe_not(p.ctx(), p); p.check_error(); return probe(p.ctx(), r); - } - }; - - inline tactic fail_if(probe const & p) { - Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); - p.check_error(); - return tactic(p.ctx(), r); - } - inline tactic when(probe const & p, tactic const & t) { - check_context(p, t); - Z3_tactic r = Z3_tactic_when(t.ctx(), p, t); - t.check_error(); - return tactic(t.ctx(), r); - } - inline tactic cond(probe const & p, tactic const & t1, tactic const & t2) { - check_context(p, t1); check_context(p, t2); - Z3_tactic r = Z3_tactic_cond(t1.ctx(), p, t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - -#endif - - inline expr context::bool_val(bool b){return b ? make(True) : make(False);} - - inline symbol context::str_symbol(char const * s) { ::symbol r = ::symbol(s); return symbol(*this, r); } - inline symbol context::int_symbol(int n) { ::symbol r = ::symbol(n); return symbol(*this, r); } - - inline sort context::bool_sort() { - ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); - return sort(*this, s); - } - inline sort context::int_sort() { - ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); - return sort(*this, s); - } - inline sort context::real_sort() { - ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); - return sort(*this, s); - } - inline sort context::array_sort(sort d, sort r) { - parameter params[2] = { parameter(d), parameter(to_sort(r)) }; - ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); - return sort(*this, s); - } - - - inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { - std::vector< ::sort *> sv(arity); - for(unsigned i = 0; i < arity; i++) - sv[i] = domain[i]; - ::func_decl* d = m().mk_func_decl(name,arity,&sv[0],range); - return func_decl(*this,d); - } - - inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { - return function(str_symbol(name), arity, domain, range); - } - - inline func_decl context::function(char const * name, sort const & domain, sort const & range) { - sort args[1] = { domain }; - return function(name, 1, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & range) { - sort args[2] = { d1, d2 }; - return function(name, 2, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { - sort args[3] = { d1, d2, d3 }; - return function(name, 3, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { - sort args[4] = { d1, d2, d3, d4 }; - return function(name, 4, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { - sort args[5] = { d1, d2, d3, d4, d5 }; - return function(name, 5, args, range); - } - - - inline expr context::constant(symbol const & name, sort const & s) { - ::expr *r = m().mk_const(m().mk_const_decl(name, s)); - return expr(*this, r); - } - inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } - inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } - inline expr context::int_const(char const * name) { return constant(name, int_sort()); } - inline expr context::real_const(char const * name) { return constant(name, real_sort()); } - inline expr context::bv_const(char const * name, unsigned sz) { return constant(name, bv_sort(sz)); } - - inline expr func_decl::operator()(const std::vector &args) const { - return operator()(args.size(),&args[0]); - } - inline expr func_decl::operator()() const { - return operator()(0,0); - } - inline expr func_decl::operator()(expr const & a) const { - return operator()(1,&a); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2) const { - expr args[2] = {a1,a2}; - return operator()(2,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3) const { - expr args[3] = {a1,a2,a3}; - return operator()(3,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { - expr args[4] = {a1,a2,a3,a4}; - return operator()(4,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { - expr args[5] = {a1,a2,a3,a4,a5}; - return operator()(5,args); - } - - - inline expr select(expr const & a, expr const & i) { return a.ctx().make(Select,a,i); } - inline expr store(expr const & a, expr const & i, expr const & v) { return a.ctx().make(Store,a,i,v); } - - inline expr forall(const std::vector &quants, const expr &body){ - return body.ctx().make_quant(Forall,quants,body); - } - - inline expr exists(const std::vector &quants, const expr &body){ - return body.ctx().make_quant(Exists,quants,body); - } - - inline expr context::int_val(int n){ - :: sort *r = m().mk_sort(m_arith_fid, INT_SORT); - return cook(m_arith_util.mk_numeral(rational(n),r)); - } - - - class literals : public object { - }; - - class TermTree { - public: - - TermTree(expr _term){ - term = _term; - } - - TermTree(expr _term, const std::vector &_children){ - term = _term; - children = _children; - } - - inline expr getTerm(){return term;} - - inline std::vector &getTerms(){return terms;} - - inline std::vector &getChildren(){ - return children; - } - - inline int number(int from){ - for(unsigned i = 0; i < children.size(); i++) - from = children[i]->number(from); - num = from; - return from + 1; - } - - inline int getNumber(){ - return num; - } - - inline void setTerm(expr t){term = t;} - - inline void addTerm(expr t){terms.push_back(t);} - - inline void setChildren(const std::vector & _children){ - children = _children; - } - - inline void setNumber(int _num){ - num = _num; - } - - ~TermTree(){ - for(unsigned i = 0; i < children.size(); i++) - delete children[i]; - } - - private: - expr term; - std::vector terms; - std::vector children; - int num; - }; - - typedef context interpolating_context; - - class interpolating_solver : public solver { - public: - interpolating_solver(context &ctx, bool models = true) - : solver(ctx, true, models) - { - weak_mode = false; - } - - public: - lbool interpolate(const std::vector &assumptions, - std::vector &interpolants, - model &_model, - literals &lits, - bool incremental - ); - - lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - literals &lits, - bool incremental - ); - - bool read_interpolation_problem(const std::string &file_name, - std::vector &assumptions, - std::vector &theory, - std::string &error_message - ); - - void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ); - - void AssertInterpolationAxiom(const expr &expr); - void RemoveInterpolationAxiom(const expr &expr); - - void SetWeakInterpolants(bool weak); - void SetPrintToFile(const std::string &file_name); - - const std::vector &GetInterpolationAxioms() {return theory;} - const char *profile(); - - private: - bool weak_mode; - std::string print_filename; - std::vector theory; - }; - - - inline expr context::cook(::expr *a) {return expr(*this,a);} - - inline std::vector context::cook(ptr_vector< ::expr> v) { - std::vector _v(v.size()); - for(unsigned i = 0; i < v.size(); i++) - _v[i] = cook(v[i]); - return _v; - } - - inline ::expr *context::uncook(const expr &a) { - m().inc_ref(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; - clock_t current_time(); - inline void output_time(std::ostream &os, clock_t time){os << time;} - - template class uptr { - public: - X *ptr; - uptr(){ptr = 0;} - void set(X *_ptr){ - if(ptr) delete ptr; - ptr = _ptr; - } - X *get(){ return ptr;} - ~uptr(){ - if(ptr) delete ptr; - } - }; - -}; - -// to make Duality::ast hashable -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::ast &s) const { - return s.raw()->get_id(); - } - }; -} - - -// to make Duality::ast usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::ast &s, const Duality::ast &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// to make Duality::ast usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::expr &s, const Duality::expr &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// to make Duality::func_decl hashable -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::func_decl &s) const { - return s.raw()->get_id(); - } - }; -} - - -// to make Duality::func_decl usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::func_decl &s, const Duality::func_decl &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -#endif - +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality_wrapper.h + +Abstract: + + wrap various Z3 classes in the style expected by duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ +#ifndef __DUALITY_WRAPPER_H_ +#define __DUALITY_WRAPPER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include"version.h" +#include + +#include "iz3hash.h" +#include "model.h" +#include "solver.h" + +#include"well_sorted.h" +#include"arith_decl_plugin.h" +#include"bv_decl_plugin.h" +#include"datatype_decl_plugin.h" +#include"array_decl_plugin.h" +#include"ast_translation.h" +#include"ast_pp.h" +#include"ast_ll_pp.h" +#include"ast_smt_pp.h" +#include"ast_smt2_pp.h" +#include"th_rewriter.h" +#include"var_subst.h" +#include"expr_substitution.h" +#include"pp.h" +#include"scoped_ctrl_c.h" +#include"cancel_eh.h" +#include"scoped_timer.h" +#include"scoped_proof.h" + +namespace Duality { + + class exception; + class config; + class context; + class symbol; + class params; + class ast; + class sort; + class func_decl; + class expr; + class solver; + class goal; + class tactic; + class probe; + class model; + class func_interp; + class func_entry; + class statistics; + class apply_result; + class fixedpoint; + class literals; + + /** + Duality global configuration object. + */ + + class config { + params_ref m_params; + config & operator=(config const & s); + public: + config(config const & s) : m_params(s.m_params) {} + config(const params_ref &_params) : m_params(_params) {} + config() { } // TODO: create a new params object here + ~config() { } + void set(char const * param, char const * value) { m_params.set_str(param,value); } + void set(char const * param, bool value) { m_params.set_bool(param,value); } + void set(char const * param, int value) { m_params.set_uint(param,value); } + params_ref &get() {return m_params;} + const params_ref &get() const {return m_params;} + }; + + enum decl_kind { + True, + False, + And, + Or, + Not, + Iff, + Ite, + Equal, + Implies, + Distinct, + Xor, + Oeq, + Interp, + Leq, + Geq, + Lt, + Gt, + Plus, + Sub, + Uminus, + Times, + Div, + Idiv, + Rem, + Mod, + Power, + ToReal, + ToInt, + IsInt, + Select, + Store, + ConstArray, + ArrayDefault, + ArrayMap, + SetUnion, + SetIntersect, + SetDifference, + SetComplement, + SetSubSet, + AsArray, + Numeral, + Forall, + Exists, + Variable, + Uninterpreted, + OtherBasic, + OtherArith, + OtherArray, + Other + }; + + enum sort_kind {BoolSort,IntSort,RealSort,ArraySort,UninterpretedSort,UnknownSort}; + + /** + A context has an ast manager global configuration options, etc. + */ + + class context { + protected: + ast_manager &mgr; + config m_config; + + family_id m_basic_fid; + family_id m_array_fid; + family_id m_arith_fid; + family_id m_bv_fid; + family_id m_dt_fid; + family_id m_datalog_fid; + arith_util m_arith_util; + + public: + context(ast_manager &_manager, const config &_config) : mgr(_manager), m_config(_config), m_arith_util(_manager) { + m_basic_fid = m().get_basic_family_id(); + m_arith_fid = m().mk_family_id("arith"); + m_bv_fid = m().mk_family_id("bv"); + m_array_fid = m().mk_family_id("array"); + m_dt_fid = m().mk_family_id("datatype"); + m_datalog_fid = m().mk_family_id("datalog_relation"); + } + ~context() { } + + ast_manager &m() const {return *(ast_manager *)&mgr;} + + void set(char const * param, char const * value) { m_config.set(param,value); } + void set(char const * param, bool value) { m_config.set(param,value); } + void set(char const * param, int value) { m_config.set(param,value); } + config &get_config() {return m_config;} + + symbol str_symbol(char const * s); + symbol int_symbol(int n); + + sort bool_sort(); + sort int_sort(); + sort real_sort(); + sort bv_sort(unsigned sz); + sort array_sort(sort d, sort r); + + func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range); + func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range); + func_decl function(char const * name, sort const & domain, sort const & range); + func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range); + func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range); + func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); + func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); + func_decl fresh_func_decl(char const * name, const std::vector &domain, sort const & range); + func_decl fresh_func_decl(char const * name, sort const & range); + + expr constant(symbol const & name, sort const & s); + expr constant(char const * name, sort const & s); + expr constant(const std::string &name, sort const & s); + expr bool_const(char const * name); + expr int_const(char const * name); + expr real_const(char const * name); + expr bv_const(char const * name, unsigned sz); + + expr bool_val(bool b); + + expr int_val(int n); + expr int_val(unsigned n); + expr int_val(char const * n); + + expr real_val(int n, int d); + expr real_val(int n); + expr real_val(unsigned n); + expr real_val(char const * n); + + expr bv_val(int n, unsigned sz); + expr bv_val(unsigned n, unsigned sz); + expr bv_val(char const * n, unsigned sz); + + expr num_val(int n, sort const & s); + + expr mki(family_id fid, ::decl_kind dk, int n, ::expr **args); + expr make(decl_kind op, int n, ::expr **args); + expr make(decl_kind op, const std::vector &args); + expr make(decl_kind op); + expr make(decl_kind op, const expr &arg0); + expr make(decl_kind op, const expr &arg0, const expr &arg1); + expr make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2); + + expr make_quant(decl_kind op, const std::vector &bvs, const expr &body); + expr make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body); + expr make_var(int idx, const sort &s); + + decl_kind get_decl_kind(const func_decl &t); + + 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(); + + expr cook(::expr *a); + std::vector cook(ptr_vector< ::expr> v); + ::expr *uncook(const expr &a); + }; + + template + class z3array { + T * m_array; + unsigned m_size; + z3array(z3array const & s); + z3array & operator=(z3array const & s); + public: + z3array(unsigned sz):m_size(sz) { m_array = new T[sz]; } + ~z3array() { delete[] m_array; } + unsigned size() const { return m_size; } + T & operator[](unsigned i) { assert(i < m_size); return m_array[i]; } + T const & operator[](unsigned i) const { assert(i < m_size); return m_array[i]; } + T const * ptr() const { return m_array; } + T * ptr() { return m_array; } + }; + + class object { + protected: + context * m_ctx; + public: + object(): m_ctx((context *)0) {} + object(context & c):m_ctx(&c) {} + object(object const & s):m_ctx(s.m_ctx) {} + context & ctx() const { return *m_ctx; } + friend void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } + ast_manager &m() const {return m_ctx->m();} + }; + + class symbol : public object { + ::symbol m_sym; + public: + symbol(context & c, ::symbol s):object(c), m_sym(s) {} + symbol(symbol const & s):object(s), m_sym(s.m_sym) {} + symbol & operator=(symbol const & s) { m_ctx = s.m_ctx; m_sym = s.m_sym; return *this; } + operator ::symbol() const {return m_sym;} + std::string str() const { + if (m_sym.is_numerical()) { + std::ostringstream buffer; + buffer << m_sym.get_num(); + return buffer.str(); + } + else { + return m_sym.bare_str(); + } + } + friend std::ostream & operator<<(std::ostream & out, symbol const & s){ + return out << s.str(); + } + friend bool operator==(const symbol &x, const symbol &y){ + return x.m_sym == y.m_sym; + } + }; + + class params : public config {}; + + /** Wrapper around an ast pointer */ + class ast_i : public object { + protected: + ::ast *_ast; + public: + ::ast * const &raw() const {return _ast;} + ast_i(context & c, ::ast *a = 0) : object(c) {_ast = a;} + + ast_i(){_ast = 0;} + bool eq(const ast_i &other) const { + return _ast == other._ast; + } + bool lt(const ast_i &other) const { + return _ast < other._ast; + } + friend bool operator==(const ast_i &x, const ast_i&y){ + return x.eq(y); + } + friend bool operator!=(const ast_i &x, const ast_i&y){ + return !x.eq(y); + } + friend bool operator<(const ast_i &x, const ast_i&y){ + return x.lt(y); + } + size_t hash() const {return (size_t)_ast;} + bool null() const {return !_ast;} + }; + + /** Reference counting verison of above */ + class ast : public ast_i { + public: + operator ::ast*() const { return raw(); } + friend bool eq(ast const & a, ast const & b) { return a.raw() == b.raw(); } + + + ast(context &c, ::ast *a = 0) : ast_i(c,a) { + if(_ast) + m().inc_ref(a); + } + + ast() {} + + ast(const ast &other) : ast_i(other) { + if(_ast) + m().inc_ref(_ast); + } + + ast &operator=(const ast &other) { + if(_ast) + m().dec_ref(_ast); + _ast = other._ast; + m_ctx = other.m_ctx; + if(_ast) + m().inc_ref(_ast); + return *this; + } + + ~ast(){ + if(_ast) + m().dec_ref(_ast); + } + + void show() const; + + }; + + class sort : public ast { + public: + sort(context & c):ast(c) {} + sort(context & c, ::sort *s):ast(c, s) {} + sort(sort const & s):ast(s) {} + operator ::sort*() const { return to_sort(raw()); } + sort & operator=(sort const & s) { return static_cast(ast::operator=(s)); } + + bool is_bool() const { return m().is_bool(*this); } + bool is_int() const { return ctx().get_sort_kind(*this) == IntSort; } + bool is_real() const { return ctx().get_sort_kind(*this) == RealSort; } + bool is_arith() const; + bool is_array() const { return ctx().get_sort_kind(*this) == ArraySort; } + bool is_datatype() const; + bool is_relation() const; + bool is_finite_domain() const; + + + sort array_domain() const; + sort array_range() const; + }; + + + class func_decl : public ast { + public: + func_decl() : ast() {} + func_decl(context & c):ast(c) {} + func_decl(context & c, ::func_decl *n):ast(c, n) {} + func_decl(func_decl const & s):ast(s) {} + operator ::func_decl*() const { return to_func_decl(*this); } + func_decl & operator=(func_decl const & s) { return static_cast(ast::operator=(s)); } + + unsigned arity() const; + sort domain(unsigned i) const; + sort range() const; + symbol name() const {return symbol(ctx(),to_func_decl(raw())->get_name());} + decl_kind get_decl_kind() const; + + bool is_const() const { return arity() == 0; } + + expr operator()(unsigned n, expr const * args) const; + expr operator()(const std::vector &args) const; + expr operator()() const; + expr operator()(expr const & a) const; + expr operator()(int a) const; + expr operator()(expr const & a1, expr const & a2) const; + expr operator()(expr const & a1, int a2) const; + expr operator()(int a1, expr const & a2) const; + expr operator()(expr const & a1, expr const & a2, expr const & a3) const; + expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const; + expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const; + + func_decl get_func_decl_parameter(unsigned idx){ + return func_decl(ctx(),to_func_decl(to_func_decl(raw())->get_parameters()[idx].get_ast())); + } + + }; + + class expr : public ast { + public: + expr() : ast() {} + expr(context & c):ast(c) {} + expr(context & c, ::ast *n):ast(c, n) {} + expr(expr const & n):ast(n) {} + expr & operator=(expr const & n) { return static_cast(ast::operator=(n)); } + operator ::expr*() const { return to_expr(raw()); } + unsigned get_id() const {return to_expr(raw())->get_id();} + + sort get_sort() const { return sort(ctx(),m().get_sort(to_expr(raw()))); } + + bool is_bool() const { return get_sort().is_bool(); } + bool is_int() const { return get_sort().is_int(); } + bool is_real() const { return get_sort().is_real(); } + bool is_arith() const { return get_sort().is_arith(); } + bool is_array() const { return get_sort().is_array(); } + bool is_datatype() const { return get_sort().is_datatype(); } + bool is_relation() const { return get_sort().is_relation(); } + bool is_finite_domain() const { return get_sort().is_finite_domain(); } + bool is_true() const {return is_app() && decl().get_decl_kind() == True; } + + bool is_numeral() const { + return is_app() && decl().get_decl_kind() == OtherArith && m().is_unique_value(to_expr(raw())); + } + bool is_app() const {return raw()->get_kind() == AST_APP;} + bool is_quantifier() const {return raw()->get_kind() == AST_QUANTIFIER;} + bool is_var() const {return raw()->get_kind() == AST_VAR;} + bool is_label (bool &pos,std::vector &names) const ; + bool is_ground() const {return to_app(raw())->is_ground();} + bool has_quantifiers() const {return to_app(raw())->has_quantifiers();} + bool has_free(int idx) const { + used_vars proc; + proc.process(to_expr(raw())); + return proc.contains(idx); + } + unsigned get_max_var_idx_plus_1() const { + used_vars proc; + proc.process(to_expr(raw())); + return proc.get_max_found_var_idx_plus_1(); + } + + // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } + func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} + unsigned num_args() const { + ast_kind dk = raw()->get_kind(); + switch(dk){ + case AST_APP: + return to_app(raw())->get_num_args(); + case AST_QUANTIFIER: + return 1; + case AST_VAR: + return 0; + default:; + } + SASSERT(0); + return 0; + } + expr arg(unsigned i) const { + ast_kind dk = raw()->get_kind(); + switch(dk){ + case AST_APP: + return ctx().cook(to_app(raw())->get_arg(i)); + case AST_QUANTIFIER: + return ctx().cook(to_quantifier(raw())->get_expr()); + default:; + } + assert(0); + return expr(); + } + + expr body() const { + return ctx().cook(to_quantifier(raw())->get_expr()); + } + + friend expr operator!(expr const & a) { + // ::expr *e = a; + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_NOT,a)); + } + + friend expr operator&&(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_AND,a,b)); + } + + friend expr operator||(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_OR,a,b)); + } + + friend expr implies(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_IMPLIES,a,b)); + } + + friend expr operator==(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_EQ,a,b)); + } + + friend expr operator!=(expr const & a, expr const & b) { + return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DISTINCT,a,b)); + } + + friend expr operator+(expr const & a, expr const & b) { + return a.ctx().make(Plus,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_ADD,a,b)); + } + + friend expr operator*(expr const & a, expr const & b) { + return a.ctx().make(Times,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_MUL,a,b)); + } + + friend expr operator/(expr const & a, expr const & b) { + return a.ctx().make(Div,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DIV,a,b)); + } + + friend expr operator-(expr const & a) { + return a.ctx().make(Uminus,a); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_UMINUS,a)); + } + + friend expr operator-(expr const & a, expr const & b) { + return a.ctx().make(Sub,a,b); // expr(a.ctx(),a.m().mk_app(a.ctx().m_arith_fid,OP_SUB,a,b)); + } + + friend expr operator<=(expr const & a, expr const & b) { + return a.ctx().make(Leq,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LE,a,b)); + } + + friend expr operator>=(expr const & a, expr const & b) { + return a.ctx().make(Geq,a,b); //expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GE,a,b)); + } + + friend expr operator<(expr const & a, expr const & b) { + return a.ctx().make(Lt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LT,a,b)); + } + + friend expr operator>(expr const & a, expr const & b) { + return a.ctx().make(Gt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GT,a,b)); + } + + expr simplify() const; + + expr simplify(params const & p) const; + + expr qe_lite() const; + + expr qe_lite(const std::set &idxs, bool index_of_bound) const; + + friend expr clone_quantifier(const expr &, const expr &); + + friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); + + friend expr clone_quantifier(decl_kind, const expr &, const expr &); + + friend std::ostream & operator<<(std::ostream & out, expr const & m){ + m.ctx().print_expr(out,m); + return out; + } + + void get_patterns(std::vector &pats) const ; + + unsigned get_quantifier_num_bound() const { + return to_quantifier(raw())->get_num_decls(); + } + + unsigned get_index_value() const { + var* va = to_var(raw()); + return va->get_idx(); + } + + bool is_quantifier_forall() const { + return to_quantifier(raw())->is_forall(); + } + + sort get_quantifier_bound_sort(unsigned n) const { + return sort(ctx(),to_quantifier(raw())->get_decl_sort(n)); + } + + symbol get_quantifier_bound_name(unsigned n) const { + return symbol(ctx(),to_quantifier(raw())->get_decl_names()[n]); + } + + friend expr forall(const std::vector &quants, const expr &body); + + friend expr exists(const std::vector &quants, const expr &body); + + }; + + + typedef ::decl_kind pfrule; + + class proof : public ast { + public: + proof(context & c):ast(c) {} + proof(context & c, ::proof *s):ast(c, s) {} + proof(proof const & s):ast(s) {} + operator ::proof*() const { return to_app(raw()); } + proof & operator=(proof const & s) { return static_cast(ast::operator=(s)); } + + pfrule rule() const { + ::func_decl *d = to_app(raw())->get_decl(); + return d->get_decl_kind(); + } + + unsigned num_prems() const { + return to_app(raw())->get_num_args() - 1; + } + + expr conc() const { + return ctx().cook(to_app(raw())->get_arg(num_prems())); + } + + proof prem(unsigned i) const { + return proof(ctx(),to_app(to_app(raw())->get_arg(i))); + } + + void get_assumptions(std::vector &assumps); + }; + +#if 0 + +#if Z3_MAJOR_VERSION > 4 || Z3_MAJOR_VERSION == 4 && Z3_MINOR_VERSION >= 3 + template + class ast_vector_tpl : public object { + Z3_ast_vector m_vector; + void init(Z3_ast_vector v) { Z3_ast_vector_inc_ref(ctx(), v); m_vector = v; } + public: + ast_vector_tpl(context & c):object(c) { init(Z3_mk_ast_vector(c)); } + ast_vector_tpl(context & c, Z3_ast_vector v):object(c) { init(v); } + ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } + ~ast_vector_tpl() { /* Z3_ast_vector_dec_ref(ctx(), m_vector); */ } + operator Z3_ast_vector() const { return m_vector; } + unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } + T operator[](unsigned i) const { Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } + void push_back(T const & e) { Z3_ast_vector_push(ctx(), m_vector, e); check_error(); } + void resize(unsigned sz) { Z3_ast_vector_resize(ctx(), m_vector, sz); check_error(); } + T back() const { return operator[](size() - 1); } + void pop_back() { assert(size() > 0); resize(size() - 1); } + bool empty() const { return size() == 0; } + ast_vector_tpl & operator=(ast_vector_tpl const & s) { + Z3_ast_vector_inc_ref(s.ctx(), s.m_vector); + // Z3_ast_vector_dec_ref(ctx(), m_vector); + m_ctx = s.m_ctx; + m_vector = s.m_vector; + return *this; + } + friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } + }; + + typedef ast_vector_tpl ast_vector; + typedef ast_vector_tpl expr_vector; + typedef ast_vector_tpl sort_vector; + typedef ast_vector_tpl func_decl_vector; + +#endif + +#endif + + class func_interp : public object { + ::func_interp * m_interp; + void init(::func_interp * e) { + m_interp = e; + } + public: + func_interp(context & c, ::func_interp * e):object(c) { init(e); } + func_interp(func_interp const & s):object(s) { init(s.m_interp); } + ~func_interp() { } + operator ::func_interp *() const { return m_interp; } + func_interp & operator=(func_interp const & s) { + m_ctx = s.m_ctx; + m_interp = s.m_interp; + return *this; + } + unsigned num_entries() const { return m_interp->num_entries(); } + expr get_arg(unsigned ent, unsigned arg) const { + return expr(ctx(),m_interp->get_entry(ent)->get_arg(arg)); + } + expr get_value(unsigned ent) const { + return expr(ctx(),m_interp->get_entry(ent)->get_result()); + } + expr else_value() const { + return expr(ctx(),m_interp->get_else()); + } + }; + + + + class model : public object { + model_ref m_model; + void init(::model *m) { + m_model = m; + } + public: + model(context & c, ::model * m = 0):object(c), m_model(m) { } + model(model const & s):object(s), m_model(s.m_model) { } + ~model() { } + operator ::model *() const { return m_model.get(); } + model & operator=(model const & s) { + // ::model *_inc_ref(s.ctx(), s.m_model); + // ::model *_dec_ref(ctx(), m_model); + m_ctx = s.m_ctx; + m_model = s.m_model.get(); + return *this; + } + model & operator=(::model *s) { + m_model = s; + return *this; + } + bool null() const {return !m_model;} + + expr eval(expr const & n, bool model_completion=true) const { + ::model * _m = m_model.get(); + expr_ref result(ctx().m()); + _m->eval(n, result, model_completion); + return expr(ctx(), result); + } + + void show() const; + void show_hash() const; + + unsigned num_consts() const {return m_model.get()->get_num_constants();} + unsigned num_funcs() const {return m_model.get()->get_num_functions();} + func_decl get_const_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_constant(i));} + func_decl get_func_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_function(i));} + unsigned size() const; + func_decl operator[](unsigned i) const; + + expr get_const_interp(func_decl f) const { + return ctx().cook(m_model->get_const_interp(to_func_decl(f.raw()))); + } + + func_interp get_func_interp(func_decl f) const { + return func_interp(ctx(),m_model->get_func_interp(to_func_decl(f.raw()))); + } + +#if 0 + friend std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } +#endif + }; + +#if 0 + class stats : public object { + Z3_stats m_stats; + void init(Z3_stats e) { + m_stats = e; + Z3_stats_inc_ref(ctx(), m_stats); + } + public: + stats(context & c):object(c), m_stats(0) {} + stats(context & c, Z3_stats e):object(c) { init(e); } + stats(stats const & s):object(s) { init(s.m_stats); } + ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } + operator Z3_stats() const { return m_stats; } + stats & operator=(stats const & s) { + Z3_stats_inc_ref(s.ctx(), s.m_stats); + if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); + m_ctx = s.m_ctx; + m_stats = s.m_stats; + return *this; + } + unsigned size() const { return Z3_stats_size(ctx(), m_stats); } + std::string key(unsigned i) const { Z3_string s = Z3_stats_get_key(ctx(), m_stats, i); check_error(); return s; } + bool is_uint(unsigned i) const { Z3_bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } + bool is_double(unsigned i) const { Z3_bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } + unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } + double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } + friend std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } + }; +#endif + + enum check_result { + unsat, sat, unknown + }; + + class fixedpoint : public object { + + public: + void get_rules(std::vector &rules); + void get_assertions(std::vector &rules); + void register_relation(const func_decl &rela); + void add_rule(const expr &clause, const symbol &name); + void assert_cnst(const expr &cnst); + }; + + inline std::ostream & operator<<(std::ostream & out, check_result r) { + if (r == unsat) out << "unsat"; + else if (r == sat) out << "sat"; + else out << "unknown"; + return out; + } + + inline check_result to_check_result(lbool l) { + if (l == l_true) return sat; + else if (l == l_false) return unsat; + return unknown; + } + + class solver : public object { + protected: + ::solver *m_solver; + model the_model; + bool canceled; + proof_gen_mode m_mode; + bool extensional; + public: + solver(context & c, bool extensional = false, bool models = true); + solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} + solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver; canceled = false;} + ~solver() { + if(m_solver) + dealloc(m_solver); + } + operator ::solver*() const { return m_solver; } + solver & operator=(solver const & s) { + m_ctx = s.m_ctx; + m_solver = s.m_solver; + the_model = s.the_model; + m_mode = s.m_mode; + return *this; + } + struct cancel_exception {}; + void checkpoint(){ + if(canceled) + throw(cancel_exception()); + } + // void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } + void push() { scoped_proof_mode spm(m(),m_mode); m_solver->push(); } + void pop(unsigned n = 1) { scoped_proof_mode spm(m(),m_mode); m_solver->pop(n); } + // void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } + void add(expr const & e) { scoped_proof_mode spm(m(),m_mode); m_solver->assert_expr(e); } + check_result check() { + scoped_proof_mode spm(m(),m_mode); + checkpoint(); + lbool r = m_solver->check_sat(0,0); + model_ref m; + m_solver->get_model(m); + the_model = m.get(); + return to_check_result(r); + } + check_result check_keep_model(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { + scoped_proof_mode spm(m(),m_mode); + model old_model(the_model); + check_result res = check(n,assumptions,core_size,core); + if(the_model == 0) + the_model = old_model; + return res; + } + check_result check(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { + scoped_proof_mode spm(m(),m_mode); + checkpoint(); + std::vector< ::expr *> _assumptions(n); + for (unsigned i = 0; i < n; i++) { + _assumptions[i] = to_expr(assumptions[i]); + } + the_model = 0; + lbool r = m_solver->check_sat(n,&_assumptions[0]); + + if(core_size && core){ + ptr_vector< ::expr> _core; + m_solver->get_unsat_core(_core); + *core_size = _core.size(); + for(unsigned i = 0; i < *core_size; i++) + core[i] = expr(ctx(),_core[i]); + } + + model_ref m; + m_solver->get_model(m); + the_model = m.get(); + + return to_check_result(r); + } +#if 0 + check_result check(expr_vector assumptions) { + scoped_proof_mode spm(m(),m_mode); + unsigned n = assumptions.size(); + z3array _assumptions(n); + for (unsigned i = 0; i < n; i++) { + check_context(*this, assumptions[i]); + _assumptions[i] = assumptions[i]; + } + Z3_lbool r = Z3_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); + check_error(); + return to_check_result(r); + } +#endif + model get_model() const { return model(ctx(), the_model); } + // std::string reason_unknown() const { Z3_string r = Z3_solver_get_reason_unknown(ctx(), m_solver); check_error(); return r; } + // stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } +#if 0 + expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } + expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } +#endif + // expr proof() const { Z3_ast r = Z3_solver_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } + // friend std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } + + int get_num_decisions(); + + void cancel(){ + scoped_proof_mode spm(m(),m_mode); + canceled = true; + if(m_solver) + m_solver->cancel(); + } + + unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();} + + void show(); + void print(const char *filename); + void show_assertion_ids(); + + proof get_proof(){ + scoped_proof_mode spm(m(),m_mode); + return proof(ctx(),m_solver->get_proof()); + } + + bool extensional_array_theory() {return extensional;} + }; + +#if 0 + class goal : public object { + Z3_goal m_goal; + void init(Z3_goal s) { + m_goal = s; + Z3_goal_inc_ref(ctx(), s); + } + public: + goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } + goal(context & c, Z3_goal s):object(c) { init(s); } + goal(goal const & s):object(s) { init(s.m_goal); } + ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } + operator Z3_goal() const { return m_goal; } + goal & operator=(goal const & s) { + Z3_goal_inc_ref(s.ctx(), s.m_goal); + Z3_goal_dec_ref(ctx(), m_goal); + m_ctx = s.m_ctx; + m_goal = s.m_goal; + return *this; + } + void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } + unsigned size() const { return Z3_goal_size(ctx(), m_goal); } + expr operator[](unsigned i) const { Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } + Z3_goal_prec precision() const { return Z3_goal_precision(ctx(), m_goal); } + bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal) != 0; } + unsigned depth() const { return Z3_goal_depth(ctx(), m_goal); } + void reset() { Z3_goal_reset(ctx(), m_goal); } + unsigned num_exprs() const { Z3_goal_num_exprs(ctx(), m_goal); } + bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } + bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } + friend std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } + }; + + class apply_result : public object { + Z3_apply_result m_apply_result; + void init(Z3_apply_result s) { + m_apply_result = s; + Z3_apply_result_inc_ref(ctx(), s); + } + public: + apply_result(context & c, Z3_apply_result s):object(c) { init(s); } + apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } + ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } + operator Z3_apply_result() const { return m_apply_result; } + apply_result & operator=(apply_result const & s) { + Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); + Z3_apply_result_dec_ref(ctx(), m_apply_result); + m_ctx = s.m_ctx; + m_apply_result = s.m_apply_result; + return *this; + } + unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } + goal operator[](unsigned i) const { Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } + goal operator[](int i) const { assert(i >= 0); return this->operator[](static_cast(i)); } + model convert_model(model const & m, unsigned i = 0) const { + check_context(*this, m); + Z3_model new_m = Z3_apply_result_convert_model(ctx(), m_apply_result, i, m); + check_error(); + return model(ctx(), new_m); + } + friend std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } + }; + + class tactic : public object { + Z3_tactic m_tactic; + void init(Z3_tactic s) { + m_tactic = s; + Z3_tactic_inc_ref(ctx(), s); + } + public: + tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } + tactic(context & c, Z3_tactic s):object(c) { init(s); } + tactic(tactic const & s):object(s) { init(s.m_tactic); } + ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } + operator Z3_tactic() const { return m_tactic; } + tactic & operator=(tactic const & s) { + Z3_tactic_inc_ref(s.ctx(), s.m_tactic); + Z3_tactic_dec_ref(ctx(), m_tactic); + m_ctx = s.m_ctx; + m_tactic = s.m_tactic; + return *this; + } + solver mk_solver() const { Z3_solver r = Z3_mk_solver_from_tactic(ctx(), m_tactic); check_error(); return solver(ctx(), r); } + apply_result apply(goal const & g) const { + check_context(*this, g); + Z3_apply_result r = Z3_tactic_apply(ctx(), m_tactic, g); + check_error(); + return apply_result(ctx(), r); + } + apply_result operator()(goal const & g) const { + return apply(g); + } + std::string help() const { char const * r = Z3_tactic_get_help(ctx(), m_tactic); check_error(); return r; } + friend tactic operator&(tactic const & t1, tactic const & t2) { + check_context(t1, t2); + Z3_tactic r = Z3_tactic_and_then(t1.ctx(), t1, t2); + t1.check_error(); + return tactic(t1.ctx(), r); + } + friend tactic operator|(tactic const & t1, tactic const & t2) { + check_context(t1, t2); + Z3_tactic r = Z3_tactic_or_else(t1.ctx(), t1, t2); + t1.check_error(); + return tactic(t1.ctx(), r); + } + friend tactic repeat(tactic const & t, unsigned max=UINT_MAX) { + Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); + t.check_error(); + return tactic(t.ctx(), r); + } + friend tactic with(tactic const & t, params const & p) { + Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); + t.check_error(); + return tactic(t.ctx(), r); + } + friend tactic try_for(tactic const & t, unsigned ms) { + Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); + t.check_error(); + return tactic(t.ctx(), r); + } + }; + + class probe : public object { + Z3_probe m_probe; + void init(Z3_probe s) { + m_probe = s; + Z3_probe_inc_ref(ctx(), s); + } + public: + probe(context & c, char const * name):object(c) { Z3_probe r = Z3_mk_probe(c, name); check_error(); init(r); } + probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } + probe(context & c, Z3_probe s):object(c) { init(s); } + probe(probe const & s):object(s) { init(s.m_probe); } + ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } + operator Z3_probe() const { return m_probe; } + probe & operator=(probe const & s) { + Z3_probe_inc_ref(s.ctx(), s.m_probe); + Z3_probe_dec_ref(ctx(), m_probe); + m_ctx = s.m_ctx; + m_probe = s.m_probe; + return *this; + } + double apply(goal const & g) const { double r = Z3_probe_apply(ctx(), m_probe, g); check_error(); return r; } + double operator()(goal const & g) const { return apply(g); } + friend probe operator<=(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_le(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator<=(probe const & p1, double p2) { return p1 <= probe(p1.ctx(), p2); } + friend probe operator<=(double p1, probe const & p2) { return probe(p2.ctx(), p1) <= p2; } + friend probe operator>=(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_ge(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator>=(probe const & p1, double p2) { return p1 >= probe(p1.ctx(), p2); } + friend probe operator>=(double p1, probe const & p2) { return probe(p2.ctx(), p1) >= p2; } + friend probe operator<(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_lt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator<(probe const & p1, double p2) { return p1 < probe(p1.ctx(), p2); } + friend probe operator<(double p1, probe const & p2) { return probe(p2.ctx(), p1) < p2; } + friend probe operator>(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_gt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator>(probe const & p1, double p2) { return p1 > probe(p1.ctx(), p2); } + friend probe operator>(double p1, probe const & p2) { return probe(p2.ctx(), p1) > p2; } + friend probe operator==(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_eq(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator==(probe const & p1, double p2) { return p1 == probe(p1.ctx(), p2); } + friend probe operator==(double p1, probe const & p2) { return probe(p2.ctx(), p1) == p2; } + friend probe operator&&(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_and(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator||(probe const & p1, probe const & p2) { + check_context(p1, p2); Z3_probe r = Z3_probe_or(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); + } + friend probe operator!(probe const & p) { + Z3_probe r = Z3_probe_not(p.ctx(), p); p.check_error(); return probe(p.ctx(), r); + } + }; + + inline tactic fail_if(probe const & p) { + Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); + p.check_error(); + return tactic(p.ctx(), r); + } + inline tactic when(probe const & p, tactic const & t) { + check_context(p, t); + Z3_tactic r = Z3_tactic_when(t.ctx(), p, t); + t.check_error(); + return tactic(t.ctx(), r); + } + inline tactic cond(probe const & p, tactic const & t1, tactic const & t2) { + check_context(p, t1); check_context(p, t2); + Z3_tactic r = Z3_tactic_cond(t1.ctx(), p, t1, t2); + t1.check_error(); + return tactic(t1.ctx(), r); + } + +#endif + + inline expr context::bool_val(bool b){return b ? make(True) : make(False);} + + inline symbol context::str_symbol(char const * s) { ::symbol r = ::symbol(s); return symbol(*this, r); } + inline symbol context::int_symbol(int n) { ::symbol r = ::symbol(n); return symbol(*this, r); } + + inline sort context::bool_sort() { + ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); + return sort(*this, s); + } + inline sort context::int_sort() { + ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); + return sort(*this, s); + } + inline sort context::real_sort() { + ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); + return sort(*this, s); + } + inline sort context::array_sort(sort d, sort r) { + parameter params[2] = { parameter(d), parameter(to_sort(r)) }; + ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); + return sort(*this, s); + } + + + inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { + std::vector< ::sort *> sv(arity); + for(unsigned i = 0; i < arity; i++) + sv[i] = domain[i]; + ::func_decl* d = m().mk_func_decl(name,arity,&sv[0],range); + return func_decl(*this,d); + } + + inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { + return function(str_symbol(name), arity, domain, range); + } + + inline func_decl context::function(char const * name, sort const & domain, sort const & range) { + sort args[1] = { domain }; + return function(name, 1, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & range) { + sort args[2] = { d1, d2 }; + return function(name, 2, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { + sort args[3] = { d1, d2, d3 }; + return function(name, 3, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { + sort args[4] = { d1, d2, d3, d4 }; + return function(name, 4, args, range); + } + + inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { + sort args[5] = { d1, d2, d3, d4, d5 }; + return function(name, 5, args, range); + } + + + inline expr context::constant(symbol const & name, sort const & s) { + ::expr *r = m().mk_const(m().mk_const_decl(name, s)); + return expr(*this, r); + } + inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } + inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } + inline expr context::int_const(char const * name) { return constant(name, int_sort()); } + inline expr context::real_const(char const * name) { return constant(name, real_sort()); } + inline expr context::bv_const(char const * name, unsigned sz) { return constant(name, bv_sort(sz)); } + + inline expr func_decl::operator()(const std::vector &args) const { + return operator()(args.size(),&args[0]); + } + inline expr func_decl::operator()() const { + return operator()(0,0); + } + inline expr func_decl::operator()(expr const & a) const { + return operator()(1,&a); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2) const { + expr args[2] = {a1,a2}; + return operator()(2,args); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3) const { + expr args[3] = {a1,a2,a3}; + return operator()(3,args); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { + expr args[4] = {a1,a2,a3,a4}; + return operator()(4,args); + } + inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { + expr args[5] = {a1,a2,a3,a4,a5}; + return operator()(5,args); + } + + + inline expr select(expr const & a, expr const & i) { return a.ctx().make(Select,a,i); } + inline expr store(expr const & a, expr const & i, expr const & v) { return a.ctx().make(Store,a,i,v); } + + inline expr forall(const std::vector &quants, const expr &body){ + return body.ctx().make_quant(Forall,quants,body); + } + + inline expr exists(const std::vector &quants, const expr &body){ + return body.ctx().make_quant(Exists,quants,body); + } + + inline expr context::int_val(int n){ + :: sort *r = m().mk_sort(m_arith_fid, INT_SORT); + return cook(m_arith_util.mk_numeral(rational(n),r)); + } + + + class literals : public object { + }; + + class TermTree { + public: + + TermTree(expr _term){ + term = _term; + } + + TermTree(expr _term, const std::vector &_children){ + term = _term; + children = _children; + } + + inline expr getTerm(){return term;} + + inline std::vector &getTerms(){return terms;} + + inline std::vector &getChildren(){ + return children; + } + + inline int number(int from){ + for(unsigned i = 0; i < children.size(); i++) + from = children[i]->number(from); + num = from; + return from + 1; + } + + inline int getNumber(){ + return num; + } + + inline void setTerm(expr t){term = t;} + + inline void addTerm(expr t){terms.push_back(t);} + + inline void setChildren(const std::vector & _children){ + children = _children; + } + + inline void setNumber(int _num){ + num = _num; + } + + ~TermTree(){ + for(unsigned i = 0; i < children.size(); i++) + delete children[i]; + } + + private: + expr term; + std::vector terms; + std::vector children; + int num; + }; + + typedef context interpolating_context; + + class interpolating_solver : public solver { + public: + interpolating_solver(context &ctx, bool models = true) + : solver(ctx, true, models) + { + weak_mode = false; + } + + public: + lbool interpolate(const std::vector &assumptions, + std::vector &interpolants, + model &_model, + literals &lits, + bool incremental + ); + + lbool interpolate_tree(TermTree *assumptions, + TermTree *&interpolants, + model &_model, + literals &lits, + bool incremental + ); + + bool read_interpolation_problem(const std::string &file_name, + std::vector &assumptions, + std::vector &theory, + std::string &error_message + ); + + void write_interpolation_problem(const std::string &file_name, + const std::vector &assumptions, + const std::vector &theory + ); + + void AssertInterpolationAxiom(const expr &expr); + void RemoveInterpolationAxiom(const expr &expr); + + void SetWeakInterpolants(bool weak); + void SetPrintToFile(const std::string &file_name); + + const std::vector &GetInterpolationAxioms() {return theory;} + const char *profile(); + + private: + bool weak_mode; + std::string print_filename; + std::vector theory; + }; + + + inline expr context::cook(::expr *a) {return expr(*this,a);} + + inline std::vector context::cook(ptr_vector< ::expr> v) { + std::vector _v(v.size()); + for(unsigned i = 0; i < v.size(); i++) + _v[i] = cook(v[i]); + return _v; + } + + inline ::expr *context::uncook(const expr &a) { + m().inc_ref(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; + clock_t current_time(); + inline void output_time(std::ostream &os, clock_t time){os << time;} + + template class uptr { + public: + X *ptr; + uptr(){ptr = 0;} + void set(X *_ptr){ + if(ptr) delete ptr; + ptr = _ptr; + } + X *get(){ return ptr;} + ~uptr(){ + if(ptr) delete ptr; + } + }; + +}; + +// to make Duality::ast hashable +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Duality::ast &s) const { + return s.raw()->get_id(); + } + }; +} + + +// to make Duality::ast usable in ordered collections +namespace std { + template <> + class less { + public: + bool operator()(const Duality::ast &s, const Duality::ast &t) const { + // return s.raw() < t.raw(); + return s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + +// to make Duality::ast usable in ordered collections +namespace std { + template <> + class less { + public: + bool operator()(const Duality::expr &s, const Duality::expr &t) const { + // return s.raw() < t.raw(); + return s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + +// to make Duality::func_decl hashable +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const Duality::func_decl &s) const { + return s.raw()->get_id(); + } + }; +} + + +// to make Duality::func_decl usable in ordered collections +namespace std { + template <> + class less { + public: + bool operator()(const Duality::func_decl &s, const Duality::func_decl &t) const { + // return s.raw() < t.raw(); + return s.raw()->get_id() < t.raw()->get_id(); + } + }; +} + +#endif + diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index c880d60aa..5fe3338dc 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2682,9 +2682,11 @@ class iz3proof_itp_impl : public iz3proof_itp { pf = make_refl(e); // proof that e = e prover::range erng = pv->ast_scope(e); +#if 0 if(!(erng.lo > erng.hi) && pv->ranges_intersect(pv->ast_scope(e),rng)){ return e; // this term occurs in range, so it's O.K. } +#endif hash_map::iterator it = localization_map.find(e); From fc62be37b631bc984418f67d287c23df9f32f6cf Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 3 Apr 2014 17:09:11 -0700 Subject: [PATCH 319/509] getting rid of DOS line endings --- src/duality/duality_profiling.cpp | 268 +- src/duality/duality_profiling.h | 76 +- src/duality/duality_solver.cpp | 6264 ++++++++++++++-------------- src/interp/iz3base.h | 390 +- src/interp/iz3scopes.cpp | 642 +-- src/interp/iz3scopes.h | 394 +- src/interp/iz3translate.cpp | 6 +- src/interp/iz3translate_direct.cpp | 6 +- 8 files changed, 4023 insertions(+), 4023 deletions(-) diff --git a/src/duality/duality_profiling.cpp b/src/duality/duality_profiling.cpp index f409bfd53..d5dac0811 100755 --- a/src/duality/duality_profiling.cpp +++ b/src/duality/duality_profiling.cpp @@ -1,134 +1,134 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - duality_profiling.cpp - -Abstract: - - collection performance information for duality - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - - ---*/ - - -#include -#include -#include -#include -#include - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include "duality_wrapper.h" -#include "iz3profiling.h" - -namespace Duality { - - void show_time(){ - output_time(std::cout,current_time()); - std::cout << "\n"; - } - - typedef std::map nmap; - - struct node { - std::string name; - clock_t time; - clock_t start_time; - nmap sub; - struct node *parent; - - node(); - } top; - - node::node(){ - time = 0; - parent = 0; - } - - struct node *current; - - struct init { - init(){ - top.name = "TOTAL"; - current = ⊤ - } - } initializer; - - struct time_entry { - clock_t t; - time_entry(){t = 0;}; - void add(clock_t incr){t += incr;} - }; - - struct ltstr - { - bool operator()(const char* s1, const char* s2) const - { - return strcmp(s1, s2) < 0; - } - }; - - typedef std::map tmap; - - static std::ostream *pfs; - - void print_node(node &top, int indent, tmap &totals){ - for(int i = 0; i < indent; i++) (*pfs) << " "; - (*pfs) << top.name; - int dots = 70 - 2 * indent - top.name.size(); - for(int i = 0; i second,indent+1,totals); - } - - void print_profile(std::ostream &os) { - pfs = &os; - top.time = 0; - for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++) - top.time += it->second.time; - tmap totals; - print_node(top,0,totals); - (*pfs) << "TOTALS:" << std::endl; - for(tmap::iterator it = totals.begin(); it != totals.end(); it++){ - (*pfs) << (it->first) << " "; - output_time(*pfs, it->second.t); - (*pfs) << std::endl; - } - profiling::print(os); // print the interpolation stats - } - - void timer_start(const char *name){ - node &child = current->sub[name]; - if(child.name.empty()){ // a new node - child.parent = current; - child.name = name; - } - child.start_time = current_time(); - current = &child; - } - - void timer_stop(const char *name){ - if(current->name != name || !current->parent){ - std::cerr << "imbalanced timer_start and timer_stop"; - exit(1); - } - current->time += (current_time() - current->start_time); - current = current->parent; - } -} +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + duality_profiling.cpp + +Abstract: + + collection performance information for duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + + +#include +#include +#include +#include +#include + +#ifdef _WINDOWS +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#endif + +#include "duality_wrapper.h" +#include "iz3profiling.h" + +namespace Duality { + + void show_time(){ + output_time(std::cout,current_time()); + std::cout << "\n"; + } + + typedef std::map nmap; + + struct node { + std::string name; + clock_t time; + clock_t start_time; + nmap sub; + struct node *parent; + + node(); + } top; + + node::node(){ + time = 0; + parent = 0; + } + + struct node *current; + + struct init { + init(){ + top.name = "TOTAL"; + current = ⊤ + } + } initializer; + + struct time_entry { + clock_t t; + time_entry(){t = 0;}; + void add(clock_t incr){t += incr;} + }; + + struct ltstr + { + bool operator()(const char* s1, const char* s2) const + { + return strcmp(s1, s2) < 0; + } + }; + + typedef std::map tmap; + + static std::ostream *pfs; + + void print_node(node &top, int indent, tmap &totals){ + for(int i = 0; i < indent; i++) (*pfs) << " "; + (*pfs) << top.name; + int dots = 70 - 2 * indent - top.name.size(); + for(int i = 0; i second,indent+1,totals); + } + + void print_profile(std::ostream &os) { + pfs = &os; + top.time = 0; + for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++) + top.time += it->second.time; + tmap totals; + print_node(top,0,totals); + (*pfs) << "TOTALS:" << std::endl; + for(tmap::iterator it = totals.begin(); it != totals.end(); it++){ + (*pfs) << (it->first) << " "; + output_time(*pfs, it->second.t); + (*pfs) << std::endl; + } + profiling::print(os); // print the interpolation stats + } + + void timer_start(const char *name){ + node &child = current->sub[name]; + if(child.name.empty()){ // a new node + child.parent = current; + child.name = name; + } + child.start_time = current_time(); + current = &child; + } + + void timer_stop(const char *name){ + if(current->name != name || !current->parent){ + std::cerr << "imbalanced timer_start and timer_stop"; + exit(1); + } + current->time += (current_time() - current->start_time); + current = current->parent; + } +} diff --git a/src/duality/duality_profiling.h b/src/duality/duality_profiling.h index ff70fae23..2b2264405 100755 --- a/src/duality/duality_profiling.h +++ b/src/duality/duality_profiling.h @@ -1,38 +1,38 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - duality_profiling.h - -Abstract: - - collection performance information for duality - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - - ---*/ - -#ifndef DUALITYPROFILING_H -#define DUALITYPROFILING_H - -#include - -namespace Duality { - /** Start a timer with given name */ - void timer_start(const char *); - /** Stop a timer with given name */ - void timer_stop(const char *); - /** Print out timings */ - void print_profile(std::ostream &s); - /** Show the current time. */ - void show_time(); -} - -#endif - +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + duality_profiling.h + +Abstract: + + collection performance information for duality + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + +#ifndef DUALITYPROFILING_H +#define DUALITYPROFILING_H + +#include + +namespace Duality { + /** Start a timer with given name */ + void timer_start(const char *); + /** Stop a timer with given name */ + void timer_stop(const char *); + /** Print out timings */ + void print_profile(std::ostream &s); + /** Show the current time. */ + void show_time(); +} + +#endif + diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index a47e90f95..ff3bc190b 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1,3132 +1,3132 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - duality_solver.h - -Abstract: - - implements relational post-fixedpoint problem - (RPFP) solver - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - - ---*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include "duality.h" -#include "duality_profiling.h" - -#include -#include -#include -#include -#include - -// TODO: make these official options or get rid of them - -#define NEW_CAND_SEL -// #define LOCALIZE_CONJECTURES -// #define CANDS_FROM_UPDATES -#define CANDS_FROM_COVER_FAIL -#define DEPTH_FIRST_EXPAND -#define MINIMIZE_CANDIDATES -// #define MINIMIZE_CANDIDATES_HARDER -#define BOUNDED -// #define CHECK_CANDS_FROM_IND_SET -#define UNDERAPPROX_NODES -#define NEW_EXPAND -#define EARLY_EXPAND -// #define TOP_DOWN -// #define EFFORT_BOUNDED_STRAT -#define SKIP_UNDERAPPROX_NODES -// #define KEEP_EXPANSIONS -// #define USE_CACHING_RPFP -// #define PROPAGATE_BEFORE_CHECK -#define NEW_STRATIFIED_INLINING - -#define USE_RPFP_CLONE -#define USE_NEW_GEN_CANDS - -//#define NO_PROPAGATE -//#define NO_GENERALIZE -//#define NO_DECISIONS - -namespace Duality { - - // TODO: must be a better place for this... - static char string_of_int_buffer[20]; - - static const char *string_of_int(int n){ - sprintf(string_of_int_buffer,"%d",n); - return string_of_int_buffer; - } - - /** Generic object for producing diagnostic output. */ - - class Reporter { - protected: - RPFP *rpfp; - public: - Reporter(RPFP *_rpfp){ - rpfp = _rpfp; - } - virtual void Extend(RPFP::Node *node){} - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){} - virtual void Bound(RPFP::Node *node){} - virtual void Expand(RPFP::Edge *edge){} - virtual void AddCover(RPFP::Node *covered, std::vector &covering){} - virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){} - virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){} - virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){} - virtual void Dominates(RPFP::Node *node, RPFP::Node *other){} - virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){} - virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} - virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} - virtual void Message(const std::string &msg){} - virtual void Depth(int){} - virtual ~Reporter(){} - }; - - Reporter *CreateStdoutReporter(RPFP *rpfp); - - /** Object we throw in case of catastrophe. */ - - struct InternalError { - std::string msg; - InternalError(const std::string _msg) - : msg(_msg) {} - }; - - - /** This is the main solver. It takes anarbitrary (possibly cyclic) - RPFP and either annotates it with a solution, or returns a - counterexample derivation in the form of an embedd RPFP tree. */ - - class Duality : public Solver { - - public: - Duality(RPFP *_rpfp) - : ctx(_rpfp->ctx), - slvr(_rpfp->slvr()), - nodes(_rpfp->nodes), - edges(_rpfp->edges) - { - rpfp = _rpfp; - reporter = 0; - heuristic = 0; - unwinding = 0; - FullExpand = false; - NoConj = false; - FeasibleEdges = true; - UseUnderapprox = true; - Report = false; - StratifiedInlining = false; - RecursionBound = -1; - BatchExpand = false; - { - scoped_no_proof no_proofs_please(ctx.m()); -#ifdef USE_RPFP_CLONE - clone_rpfp = new RPFP_caching(rpfp->ls); - clone_rpfp->Clone(rpfp); -#endif -#ifdef USE_NEW_GEN_CANDS - gen_cands_rpfp = new RPFP_caching(rpfp->ls); - gen_cands_rpfp->Clone(rpfp); -#endif - } - } - - ~Duality(){ -#ifdef USE_RPFP_CLONE - delete clone_rpfp; -#endif -#ifdef USE_NEW_GEN_CANDS - delete gen_cands_rpfp; -#endif - if(unwinding) delete unwinding; - } - -#ifdef USE_RPFP_CLONE - RPFP_caching *clone_rpfp; -#endif -#ifdef USE_NEW_GEN_CANDS - RPFP_caching *gen_cands_rpfp; -#endif - - - typedef RPFP::Node Node; - typedef RPFP::Edge Edge; - - /** This struct represents a candidate for extending the - unwinding. It consists of an edge to instantiate - and a vector of children for the new instance. */ - - struct Candidate { - Edge *edge; std::vector - Children; - }; - - /** Comparison operator, allowing us to sort Nodes - by their number field. */ - - struct lnode - { - bool operator()(const Node* s1, const Node* s2) const - { - return s1->number < s2->number; - } - }; - - typedef std::set Unexpanded; // sorted set of Nodes - - /** This class provides a heuristic for expanding a derivation - tree. */ - - class Heuristic { - RPFP *rpfp; - - /** Heuristic score for unwinding nodes. Currently this - counts the number of updates. */ - struct score { - int updates; - score() : updates(0) {} - }; - hash_map scores; - - public: - Heuristic(RPFP *_rpfp){ - rpfp = _rpfp; - } - - virtual ~Heuristic(){} - - virtual void Update(RPFP::Node *node){ - scores[node].updates++; - } - - /** Heuristic choice of nodes to expand. Takes a set "choices" - and returns a subset "best". We currently choose the - nodes with the fewest updates. - */ -#if 0 - virtual void ChooseExpand(const std::set &choices, std::set &best){ - int best_score = INT_MAX; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it)->map; - int score = scores[node].updates; - best_score = std::min(best_score,score); - } - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) - if(scores[(*it)->map].updates == best_score) - best.insert(*it); - } -#else - virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority=false, bool best_only=false){ - if(high_priority) return; - int best_score = INT_MAX; - int worst_score = 0; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it)->map; - int score = scores[node].updates; - best_score = std::min(best_score,score); - worst_score = std::max(worst_score,score); - } - int cutoff = best_only ? best_score : (best_score + (worst_score-best_score)/2); - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) - if(scores[(*it)->map].updates <= cutoff) - best.insert(*it); - } -#endif - - /** Called when done expanding a tree */ - virtual void Done() {} - }; - - /** The Proposer class proposes conjectures eagerly. These can come - from any source, including predicate abstraction, templates, or - previous solver runs. The proposed conjectures are checked - with low effort when the unwinding is expanded. - */ - - class Proposer { - public: - /** Given a node in the unwinding, propose some conjectures */ - virtual std::vector &Propose(Node *node) = 0; - virtual ~Proposer(){}; - }; - - - class Covering; // see below - - // These members represent the state of the algorithm. - - RPFP *rpfp; // the input RPFP - Reporter *reporter; // object for logging - Heuristic *heuristic; // expansion heuristic - context &ctx; // Z3 context - solver &slvr; // Z3 solver - std::vector &nodes; // Nodes of input RPFP - std::vector &edges; // Edges of input RPFP - std::vector leaves; // leaf nodes of unwinding (unused) - Unexpanded unexpanded; // unexpanded nodes - std::list candidates; // candidates for expansion - // maps children to edges in input RPFP - hash_map > edges_by_child; - // maps each node in input RPFP to its expanded instances - hash_map > insts_of_node; - // maps each node in input RPFP to all its instances - hash_map > all_of_node; - RPFP *unwinding; // the unwinding - Covering *indset; // proposed inductive subset - Counterexample cex; // counterexample - std::list to_expand; - hash_set updated_nodes; - hash_map underapprox_map; // maps underapprox nodes to the nodes they approximate - int last_decisions; - hash_set overapproxes; - std::vector proposers; - -#ifdef BOUNDED - struct Counter { - int val; - Counter(){val = 0;} - }; - typedef std::map NodeToCounter; - hash_map back_edges; // counts of back edges -#endif - - /** Solve the problem. */ - virtual bool Solve(){ - reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); -#ifndef LOCALIZE_CONJECTURES - heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); -#else - heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp)) - : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); -#endif - 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(); -#ifndef TOP_DOWN - CreateInitialUnwinding(); -#else - CreateLeaves(); - for(unsigned i = 0; i < leaves.size(); i++) - if(!SatisfyUpperBound(leaves[i])) - return false; -#endif - StratifiedLeafCount = -1; - timer_start("SolveMain"); - bool res = SolveMain(); // does the actual work - timer_stop("SolveMain"); - // print_profile(std::cout); - delete indset; - delete heuristic; - // 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 - } - -#if 0 - virtual void Restart(RPFP *_rpfp){ - rpfp = _rpfp; - delete unwinding; - nodes = _rpfp->nodes; - edges = _rpfp->edges; - leaves.clear(); - unexpanded.clear(); // unexpanded nodes - candidates.clear(); // candidates for expansion - edges_by_child.clear(); - insts_of_node.clear(); - all_of_node.clear(); - to_expand.clear(); - } -#endif - - virtual void LearnFrom(Solver *other_solver){ - // get the counterexample as a guide - cex.swap(other_solver->GetCounterexample()); - - // propose conjectures based on old unwinding - Duality *old_duality = dynamic_cast(other_solver); - if(old_duality) - proposers.push_back(new HistoryProposer(old_duality,this)); - } - - /** Return a reference to the counterexample */ - virtual Counterexample &GetCounterexample(){ - return cex; - } - - // options - bool FullExpand; // do not use partial expansion of derivation tree - bool NoConj; // do not use conjectures (no forced covering) - bool FeasibleEdges; // use only feasible edges in unwinding - bool UseUnderapprox; // use underapproximations - bool Report; // spew on stdout - bool StratifiedInlining; // Do stratified inlining as preprocessing step - int RecursionBound; // Recursion bound for bounded verification - bool BatchExpand; - - bool SetBoolOption(bool &opt, const std::string &value){ - if(value == "0") { - opt = false; - return true; - } - if(value == "1") { - opt = true; - return true; - } - return false; - } - - bool SetIntOption(int &opt, const std::string &value){ - opt = atoi(value.c_str()); - return true; - } - - /** Set options (not currently used) */ - virtual bool SetOption(const std::string &option, const std::string &value){ - if(option == "full_expand"){ - return SetBoolOption(FullExpand,value); - } - if(option == "no_conj"){ - return SetBoolOption(NoConj,value); - } - if(option == "feasible_edges"){ - return SetBoolOption(FeasibleEdges,value); - } - if(option == "use_underapprox"){ - return SetBoolOption(UseUnderapprox,value); - } - if(option == "report"){ - return SetBoolOption(Report,value); - } - if(option == "stratified_inlining"){ - return SetBoolOption(StratifiedInlining,value); - } - if(option == "batch_expand"){ - return SetBoolOption(BatchExpand,value); - } - if(option == "recursion_bound"){ - return SetIntOption(RecursionBound,value); - } - return false; - } - - /** Create an instance of a node in the unwinding. Set its - annotation to true, and mark it unexpanded. */ - Node* CreateNodeInstance(Node *node, int number = 0){ - RPFP::Node *inst = unwinding->CloneNode(node); - inst->Annotation.SetFull(); - if(number < 0) inst->number = number; - unexpanded.insert(inst); - all_of_node[node].push_back(inst); - return inst; - } - - /** Create an instance of an edge in the unwinding, with given - parent and children. */ - void CreateEdgeInstance(Edge *edge, Node *parent, const std::vector &children){ - RPFP::Edge *inst = unwinding->CreateEdge(parent,edge->F,children); - inst->map = edge; - } - - void MakeLeaf(Node *node, bool do_not_expand = false){ - node->Annotation.SetEmpty(); - Edge *e = unwinding->CreateLowerBoundEdge(node); -#ifdef TOP_DOWN - node->Annotation.SetFull(); // allow this node to cover others -#endif - if(StratifiedInlining) - node->Annotation.SetFull(); // allow this node to cover others - else - updated_nodes.insert(node); - e->map = 0; - reporter->Extend(node); -#ifdef EARLY_EXPAND - if(!do_not_expand) - TryExpandNode(node); -#endif - // e->F.SetEmpty(); - } - - void MakeOverapprox(Node *node){ - node->Annotation.SetFull(); - Edge *e = unwinding->CreateLowerBoundEdge(node); - overapproxes.insert(node); - e->map = 0; - } - - /** We start the unwinding with leaves that under-approximate - each relation with false. */ - void CreateLeaves(){ - unexpanded.clear(); - leaves.clear(); - for(unsigned i = 0; i < nodes.size(); i++){ - RPFP::Node *node = CreateNodeInstance(nodes[i]); - if(0 && nodes[i]->Outgoing->Children.size() == 0) - CreateEdgeInstance(nodes[i]->Outgoing,node,std::vector()); - else { - if(!StratifiedInlining) - MakeLeaf(node); - else { - MakeOverapprox(node); - LeafMap[nodes[i]] = node; - } - } - leaves.push_back(node); - } - } - - /** Create the map from children to edges in the input RPFP. This - is used to generate candidates for expansion. */ - void CreateEdgesByChildMap(){ - edges_by_child.clear(); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *e = edges[i]; - std::set done; - for(unsigned j = 0; j < e->Children.size(); j++){ - Node *c = e->Children[j]; - if(done.find(c) == done.end()) // avoid duplicates - edges_by_child[c].push_back(e); - done.insert(c); - } - } - } - - void NullaryCandidates(){ - for(unsigned i = 0; i < edges.size(); i++){ - RPFP::Edge *edge = edges[i]; - if(edge->Children.size() == 0){ - Candidate cand; - cand.edge = edge; - candidates.push_back(cand); - } - } - } - - void InstantiateAllEdges(){ - hash_map leaf_map; - for(unsigned i = 0; i < leaves.size(); i++){ - leaf_map[leaves[i]->map] = leaves[i]; - insts_of_node[leaves[i]->map].push_back(leaves[i]); - } - unexpanded.clear(); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - Candidate c; c.edge = edge; - c.Children.resize(edge->Children.size()); - for(unsigned j = 0; j < c.Children.size(); j++) - c.Children[j] = leaf_map[edge->Children[j]]; - 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); - for(unsigned i = 0; i < leaves.size(); i++){ - std::vector &foo = insts_of_node[leaves[i]->map]; - foo.erase(foo.begin()); - } - } - - bool ProducedBySI(Edge *edge, std::vector &children){ - if(LeafMap.find(edge->Parent) == LeafMap.end()) return false; - Node *other = LeafMap[edge->Parent]; - if(other->Outgoing->map != edge) return false; - std::vector &ochs = other->Outgoing->Children; - for(unsigned i = 0; i < children.size(); i++) - if(ochs[i] != children[i]) return false; - return true; - } - - /** Add a candidate for expansion, but not if Stratified inlining has already - produced it */ - - void AddCandidate(Edge *edge, std::vector &children){ - if(StratifiedInlining && ProducedBySI(edge,children)) - return; - candidates.push_back(Candidate()); - candidates.back().edge = edge; - candidates.back().Children = children; - } - - /** Generate candidates for expansion, given a vector of candidate - sets for each argument position. This recursively produces - the cross product. - */ - void GenCandidatesRec(int pos, Edge *edge, - const std::vector > &vec, - std::vector &children){ - if(pos == (int)vec.size()){ - AddCandidate(edge,children); - } - else { - for(unsigned i = 0; i < vec[pos].size(); i++){ - children[pos] = vec[pos][i]; - GenCandidatesRec(pos+1,edge,vec,children); - } - } - } - - /** Setup for above recursion. */ - void GenCandidates(int pos, Edge *edge, - const std::vector > &vec){ - std::vector children(vec.size()); - GenCandidatesRec(0,edge,vec,children); - } - - /** Expand a node. We find all the candidates for expansion using - this node and other already expanded nodes. This is a little - tricky, since a node may be used for multiple argument - positions of an edge, and we don't want to produce duplicates. - */ - -#ifndef NEW_EXPAND - void ExpandNode(Node *node){ - std::vector &nedges = edges_by_child[node->map]; - for(unsigned i = 0; i < nedges.size(); i++){ - Edge *edge = nedges[i]; - for(unsigned npos = 0; npos < edge->Children.size(); ++npos){ - if(edge->Children[npos] == node->map){ - std::vector > vec(edge->Children.size()); - vec[npos].push_back(node); - for(unsigned j = 0; j < edge->Children.size(); j++){ - if(j != npos){ - std::vector &insts = insts_of_node[edge->Children[j]]; - for(unsigned k = 0; k < insts.size(); k++) - if(indset->Candidate(insts[k])) - vec[j].push_back(insts[k]); - } - if(j < npos && edge->Children[j] == node->map) - vec[j].push_back(node); - } - GenCandidates(0,edge,vec); - } - } - } - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } -#else - /** If the current proposed solution is not inductive, - use the induction failure to generate candidates for extension. */ - void ExpandNode(Node *node){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - timer_start("GenCandIndFailUsing"); - std::vector &nedges = edges_by_child[node->map]; - for(unsigned i = 0; i < nedges.size(); i++){ - Edge *edge = nedges[i]; - slvr.push(); - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerJustForEdge(edge,checker,true); - if(root){ - expr using_cond = ctx.bool_val(false); - for(unsigned npos = 0; npos < edge->Children.size(); ++npos) - if(edge->Children[npos] == node->map) - using_cond = using_cond || checker->Localize(root->Outgoing->Children[npos]->Outgoing,NodeMarker(node)); - slvr.add(using_cond); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - } - slvr.pop(1); - delete checker; - } - timer_stop("GenCandIndFailUsing"); - } -#endif - - void ExpandNodeFromOther(Node *node, Node *other){ - std::vector &in = other->Incoming; - for(unsigned i = 0; i < in.size(); i++){ - Edge *edge = in[i]; - Candidate cand; - cand.edge = edge->map; - cand.Children = edge->Children; - for(unsigned j = 0; j < cand.Children.size(); j++) - if(cand.Children[j] == other) - cand.Children[j] = node; - candidates.push_front(cand); - } - // unexpanded.erase(node); - // insts_of_node[node->map].push_back(node); - } - - /** Expand a node based on some uncovered node it dominates. - This pushes cahdidates onto the *front* of the candidate - queue, so these expansions are done depth-first. */ - bool ExpandNodeFromCoverFail(Node *node){ - if(!node->Outgoing || node->Outgoing->Children.size() == 0) - return false; - Node *other = indset->GetSimilarNode(node); - if(!other) - return false; -#ifdef UNDERAPPROX_NODES - Node *under_node = CreateUnderapproxNode(node); - underapprox_map[under_node] = node; - indset->CoverByNode(node,under_node); - ExpandNodeFromOther(under_node,other); - ExpandNode(under_node); -#else - ExpandNodeFromOther(node,other); - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); -#endif - return true; - } - - - /** Make a boolean variable to act as a "marker" for a node. */ - expr NodeMarker(Node *node){ - std::string name = std::string("@m_") + string_of_int(node->number); - return ctx.constant(name.c_str(),ctx.bool_sort()); - } - - /** Union the annotation of dst into src. If with_markers is - true, we conjoin the annotation formula of dst with its - marker. This allows us to discover which disjunct is - true in a satisfying assignment. */ - void UnionAnnotations(RPFP::Transformer &dst, Node *src, bool with_markers = false){ - if(!with_markers) - dst.UnionWith(src->Annotation); - else { - RPFP::Transformer t = src->Annotation; - t.Formula = t.Formula && NodeMarker(src); - dst.UnionWith(t); - } - } - - void GenNodeSolutionFromIndSet(Node *node, RPFP::Transformer &annot, bool with_markers = false){ - annot.SetEmpty(); - std::vector &insts = insts_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++) - if(indset->Contains(insts[j])) - UnionAnnotations(annot,insts[j],with_markers); - annot.Simplify(); - } - - /** Generate a proposed solution of the input RPFP from - the unwinding, by unioning the instances of each node. */ - void GenSolutionFromIndSet(bool with_markers = false){ - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - GenNodeSolutionFromIndSet(node,node->Annotation,with_markers); - } - } - -#ifdef BOUNDED - bool NodePastRecursionBound(Node *node){ - if(RecursionBound < 0) return false; - NodeToCounter &backs = back_edges[node]; - for(NodeToCounter::iterator it = backs.begin(), en = backs.end(); it != en; ++it){ - if(it->second.val > RecursionBound) - return true; - } - return false; - } -#endif - - /** Test whether a given extension candidate actually represents - an induction failure. Right now we approximate this: if - the resulting node in the unwinding could be labeled false, - it clearly is not an induction failure. */ - - bool CandidateFeasible(const Candidate &cand){ - if(!FeasibleEdges) return true; - timer_start("CandidateFeasible"); - RPFP *checker = new RPFP(rpfp->ls); - // std::cout << "Checking feasibility of extension " << cand.edge->Parent->number << std::endl; - checker->Push(); - std::vector chs(cand.Children.size()); - Node *root = checker->CloneNode(cand.edge->Parent); -#ifdef BOUNDED - for(unsigned i = 0; i < cand.Children.size(); i++) - if(NodePastRecursionBound(cand.Children[i])){ - timer_stop("CandidateFeasible"); - return false; - } -#endif -#ifdef NEW_CAND_SEL - GenNodeSolutionFromIndSet(cand.edge->Parent,root->Bound); -#else - root->Bound.SetEmpty(); -#endif - checker->AssertNode(root); - for(unsigned i = 0; i < cand.Children.size(); i++) - chs[i] = checker->CloneNode(cand.Children[i]); - Edge *e = checker->CreateEdge(root,cand.edge->F,chs); - checker->AssertEdge(e,0,true); - // std::cout << "Checking SAT: " << e->dual << std::endl; - bool res = checker->Check(root) != unsat; - // std::cout << "Result: " << res << std::endl; - if(!res)reporter->Reject(cand.edge,cand.Children); - checker->Pop(1); - delete checker; - timer_stop("CandidateFeasible"); - return res; - } - - - hash_map TopoSort; - int TopoSortCounter; - std::vector SortedEdges; - - void DoTopoSortRec(Node *node){ - if(TopoSort.find(node) != TopoSort.end()) - return; - TopoSort[node] = INT_MAX; // just to break cycles - Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge - if(edge){ - std::vector &chs = edge->Children; - for(unsigned i = 0; i < chs.size(); i++) - DoTopoSortRec(chs[i]); - } - TopoSort[node] = TopoSortCounter++; - SortedEdges.push_back(edge); - } - - void DoTopoSort(){ - TopoSort.clear(); - SortedEdges.clear(); - TopoSortCounter = 0; - for(unsigned i = 0; i < nodes.size(); i++) - DoTopoSortRec(nodes[i]); - } - - - int StratifiedLeafCount; - -#ifdef NEW_STRATIFIED_INLINING - - /** Stratified inlining builds an initial layered unwinding before - switching to the LAWI strategy. Currently the number of layers - is one. Only nodes that are the targets of back edges are - consider to be leaves. This assumes we have already computed a - topological sort. - */ - - bool DoStratifiedInlining(){ - DoTopoSort(); - int depth = 1; // TODO: make this an option - std::vector > unfolding_levels(depth+1); - for(int level = 1; level <= depth; level++) - for(unsigned i = 0; i < SortedEdges.size(); i++){ - Edge *edge = SortedEdges[i]; - Node *parent = edge->Parent; - std::vector &chs = edge->Children; - std::vector my_chs(chs.size()); - for(unsigned j = 0; j < chs.size(); j++){ - Node *child = chs[j]; - int ch_level = TopoSort[child] >= TopoSort[parent] ? level-1 : level; - if(unfolding_levels[ch_level].find(child) == unfolding_levels[ch_level].end()){ - if(ch_level == 0) - unfolding_levels[0][child] = CreateLeaf(child); - else - throw InternalError("in levelized unwinding"); - } - my_chs[j] = unfolding_levels[ch_level][child]; - } - Candidate cand; cand.edge = edge; cand.Children = my_chs; - Node *new_node; - bool ok = Extend(cand,new_node); - MarkExpanded(new_node); // we don't expand here -- just mark it done - if(!ok) return false; // got a counterexample - unfolding_levels[level][parent] = new_node; - } - return true; - } - - Node *CreateLeaf(Node *node){ - RPFP::Node *nchild = CreateNodeInstance(node); - MakeLeaf(nchild, /* do_not_expand = */ true); - nchild->Annotation.SetEmpty(); - return nchild; - } - - void MarkExpanded(Node *node){ - if(unexpanded.find(node) != unexpanded.end()){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } - } - -#else - - /** In stratified inlining, we build the unwinding from the bottom - down, trying to satisfy the node bounds. We do this as a pre-pass, - limiting the expansion. If we get a counterexample, we are done, - else we continue as usual expanding the unwinding upward. - */ - - bool DoStratifiedInlining(){ - timer_start("StratifiedInlining"); - DoTopoSort(); - for(unsigned i = 0; i < leaves.size(); i++){ - Node *node = leaves[i]; - bool res = SatisfyUpperBound(node); - if(!res){ - timer_stop("StratifiedInlining"); - return false; - } - } - // don't leave any dangling nodes! -#ifndef EFFORT_BOUNDED_STRAT - for(unsigned i = 0; i < leaves.size(); i++) - if(!leaves[i]->Outgoing) - MakeLeaf(leaves[i],true); -#endif - timer_stop("StratifiedInlining"); - return true; - } - -#endif - - /** Here, we do the downward expansion for stratified inlining */ - - hash_map LeafMap, StratifiedLeafMap; - - Edge *GetNodeOutgoing(Node *node, int last_decs = 0){ - if(overapproxes.find(node) == overapproxes.end()) return node->Outgoing; /* already expanded */ - overapproxes.erase(node); -#ifdef EFFORT_BOUNDED_STRAT - if(last_decs > 5000){ - // RPFP::Transformer save = node->Annotation; - node->Annotation.SetEmpty(); - Edge *e = unwinding->CreateLowerBoundEdge(node); - // node->Annotation = save; - insts_of_node[node->map].push_back(node); - // std::cout << "made leaf: " << node->number << std::endl; - return e; - } -#endif - Edge *edge = node->map->Outgoing; - std::vector &chs = edge->Children; - - // make sure we don't create a covered node in this process! - - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - if(TopoSort[child] < TopoSort[node->map]){ - Node *leaf = LeafMap[child]; - if(!indset->Contains(leaf)){ - node->Outgoing->F.Formula = ctx.bool_val(false); // make this a proper leaf, else bogus cex - return node->Outgoing; - } - } - } - - std::vector nchs(chs.size()); - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - if(TopoSort[child] < TopoSort[node->map]){ - Node *leaf = LeafMap[child]; - nchs[i] = leaf; - if(unexpanded.find(leaf) != unexpanded.end()){ - unexpanded.erase(leaf); - insts_of_node[child].push_back(leaf); - } - } - else { - if(StratifiedLeafMap.find(child) == StratifiedLeafMap.end()){ - RPFP::Node *nchild = CreateNodeInstance(child,StratifiedLeafCount--); - MakeLeaf(nchild); - nchild->Annotation.SetEmpty(); - StratifiedLeafMap[child] = nchild; - indset->SetDominated(nchild); - } - nchs[i] = StratifiedLeafMap[child]; - } - } - CreateEdgeInstance(edge,node,nchs); - reporter->Extend(node); - return node->Outgoing; - } - - void SetHeuristicOldNode(Node *node){ - LocalHeuristic *h = dynamic_cast(heuristic); - if(h) - h->SetOldNode(node); - } - - /** This does the actual solving work. We try to generate - candidates for extension. If we succed, we extend the - unwinding. If we fail, we have a solution. */ - bool SolveMain(){ - if(StratifiedInlining && !DoStratifiedInlining()) - return false; -#ifdef BOUNDED - DoTopoSort(); -#endif - while(true){ - timer_start("ProduceCandidatesForExtension"); - ProduceCandidatesForExtension(); - timer_stop("ProduceCandidatesForExtension"); - if(candidates.empty()){ - GenSolutionFromIndSet(); - return true; - } - Candidate cand = candidates.front(); - candidates.pop_front(); - if(CandidateFeasible(cand)){ - Node *new_node; - if(!Extend(cand,new_node)) - return false; -#ifdef EARLY_EXPAND - TryExpandNode(new_node); -#endif - } - } - } - - // hack: put something local into the underapproximation formula - // without this, interpolants can be pretty bad - void AddThing(expr &conj){ - std::string name = "@thing"; - expr thing = ctx.constant(name.c_str(),ctx.bool_sort()); - if(conj.is_app() && conj.decl().get_decl_kind() == And){ - std::vector conjs(conj.num_args()+1); - for(unsigned i = 0; i+1 < conjs.size(); i++) - conjs[i] = conj.arg(i); - conjs[conjs.size()-1] = thing; - conj = rpfp->conjoin(conjs); - } - } - - - Node *CreateUnderapproxNode(Node *node){ - // cex.get_tree()->ComputeUnderapprox(cex.get_root(),0); - RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */); - 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 - back_edges[under_node] = back_edges[node]; - e->map = 0; - reporter->Extend(under_node); - return under_node; - } - - /** Try to prove a conjecture about a node. If successful - update the unwinding annotation appropriately. */ - bool ProveConjecture(Node *node, const RPFP::Transformer &t,Node *other = 0, Counterexample *_cex = 0){ - reporter->Conjecture(node,t); - timer_start("ProveConjecture"); - RPFP::Transformer save = node->Bound; - node->Bound.IntersectWith(t); - -#ifndef LOCALIZE_CONJECTURES - bool ok = SatisfyUpperBound(node); -#else - SetHeuristicOldNode(other); - bool ok = SatisfyUpperBound(node); - SetHeuristicOldNode(0); -#endif - - if(ok){ - timer_stop("ProveConjecture"); - return true; - } -#ifdef UNDERAPPROX_NODES - if(UseUnderapprox && last_decisions > 500){ - std::cout << "making an underapprox\n"; - ExpandNodeFromCoverFail(node); - } -#endif - 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; - } - - /** If a node is part of the inductive subset, expand it. - We ask the inductive subset to exclude the node if possible. - */ - void TryExpandNode(RPFP::Node *node){ - if(indset->Close(node)) return; - if(!NoConj && indset->Conjecture(node)){ -#ifdef UNDERAPPROX_NODES - /* TODO: temporary fix. this prevents an infinite loop in case - the node is covered by multiple others. This should be - removed when covering by a set is implemented. - */ - if(indset->Contains(node)){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } -#endif - return; - } -#ifdef UNDERAPPROX_NODES - if(!indset->Contains(node)) - return; // could be covered by an underapprox node -#endif - indset->Add(node); -#if defined(CANDS_FROM_COVER_FAIL) && !defined(UNDERAPPROX_NODES) - if(ExpandNodeFromCoverFail(node)) - return; -#endif - ExpandNode(node); - } - - /** Make the conjunction of markers for all (expanded) instances of - a node in the input RPFP. */ - expr AllNodeMarkers(Node *node){ - expr res = ctx.bool_val(true); - std::vector &insts = insts_of_node[node]; - for(int k = insts.size()-1; k >= 0; k--) - res = res && NodeMarker(insts[k]); - return res; - } - - void RuleOutNodesPastBound(Node *node, RPFP::Transformer &t){ -#ifdef BOUNDED - if(RecursionBound < 0)return; - std::vector &insts = insts_of_node[node]; - for(unsigned i = 0; i < insts.size(); i++) - if(NodePastRecursionBound(insts[i])) - t.Formula = t.Formula && !NodeMarker(insts[i]); -#endif - } - - - void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction){ -#ifdef BOUNDED - if(RecursionBound >= 0 && NodePastRecursionBound(node)) - return; -#endif - RPFP::Transformer temp = node->Annotation; - expr marker = NodeMarker(node); - temp.Formula = (!marker || temp.Formula); - annot.IntersectWith(temp); - marker_disjunction = marker_disjunction || marker; - } - - bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false){ - bool res = false; - annot.SetFull(); - expr marker_disjunction = ctx.bool_val(false); - std::vector &insts = expanded_only ? insts_of_node[node] : all_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++){ - Node *node = insts[j]; - if(indset->Contains(insts[j])){ - GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction); res = true; - } - } - annot.Formula = annot.Formula && marker_disjunction; - annot.Simplify(); - return res; - } - - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerJustForEdge(Edge *edge, RPFP *checker, bool expanded_only = false){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); - if(root->Bound.IsFull()) - return 0; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - if(!GenNodeSolutionWithMarkers(oc,nc->Annotation,expanded_only)) - return 0; - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - -#ifndef MINIMIZE_CANDIDATES_HARDER - -#if 0 - /** Make a checker to detheermine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - root->Bound = edge->Parent->Annotation; - root->Bound.Formula = (!AllNodeMarkers(edge->Parent)) || root->Bound.Formula; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - nc->Annotation = oc->Annotation; - RuleOutNodesPastBound(oc,nc->Annotation); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - -#else - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); -#if 0 - if(root->Bound.IsFull()) - return = 0; -#endif - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } -#endif - - /** If an edge is not satisfied, produce an extension candidate - using instances of its children that violate the parent annotation. - We find these using the marker predicates. */ - void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ - candidate.edge = edge; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *node = root->Outgoing->Children[j]; - Edge *lb = node->Outgoing; - std::vector &insts = insts_of_node[edge->Children[j]]; -#ifndef MINIMIZE_CANDIDATES - for(int k = insts.size()-1; k >= 0; k--) -#else - for(unsigned k = 0; k < insts.size(); k++) -#endif - { - Node *inst = insts[k]; - if(indset->Contains(inst)){ - if(checker->Empty(node) || - eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst)),ctx.bool_val(true))){ - candidate.Children.push_back(inst); - goto next_child; - } - } - } - throw InternalError("No candidate from induction failure"); - next_child:; - } - } -#else - - - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); - if(root->Bound.IsFull()) - return = 0; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - - /** If an edge is not satisfied, produce an extension candidate - using instances of its children that violate the parent annotation. - We find these using the marker predicates. */ - void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ - candidate.edge = edge; - std::vector assumps; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Edge *lb = root->Outgoing->Children[j]->Outgoing; - std::vector &insts = insts_of_node[edge->Children[j]]; - for(unsigned k = 0; k < insts.size(); k++) - { - Node *inst = insts[k]; - expr marker = NodeMarker(inst); - if(indset->Contains(inst)){ - if(checker->Empty(lb->Parent) || - eq(checker->Eval(lb,marker),ctx.bool_val(true))){ - candidate.Children.push_back(inst); - assumps.push_back(checker->Localize(lb,marker)); - goto next_child; - } - assumps.push_back(checker->Localize(lb,marker)); - if(checker->CheckUpdateModel(root,assumps) != unsat){ - candidate.Children.push_back(inst); - goto next_child; - } - assumps.pop_back(); - } - } - throw InternalError("No candidate from induction failure"); - next_child:; - } - } - -#endif - - - Node *CheckerForEdgeClone(Edge *edge, RPFP_caching *checker){ - Edge *gen_cands_edge = checker->GetEdgeClone(edge); - Node *root = gen_cands_edge->Parent; - root->Outgoing = gen_cands_edge; - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); -#if 0 - if(root->Bound.IsFull()) - return = 0; -#endif - checker->AssertNode(root); - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = gen_cands_edge->Children[j]; - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); - } - checker->AssertEdge(gen_cands_edge,1,true); - return root; - } - - /** If the current proposed solution is not inductive, - use the induction failure to generate candidates for extension. */ - void GenCandidatesFromInductionFailure(bool full_scan = false){ - timer_start("GenCandIndFail"); - GenSolutionFromIndSet(true /* add markers */); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - if(!full_scan && updated_nodes.find(edge->Parent) == updated_nodes.end()) - continue; -#ifndef USE_NEW_GEN_CANDS - slvr.push(); - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerForEdge(edge,checker); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - slvr.pop(1); - delete checker; -#else - RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); - gen_cands_rpfp->Push(); - Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); - if(gen_cands_rpfp->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,gen_cands_rpfp,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - gen_cands_rpfp->Pop(1); -#endif - } - updated_nodes.clear(); - timer_stop("GenCandIndFail"); -#ifdef CHECK_CANDS_FROM_IND_SET - for(std::list::iterator it = candidates.begin(), en = candidates.end(); it != en; ++it){ - if(!CandidateFeasible(*it)) - throw "produced infeasible candidate"; - } -#endif - if(!full_scan && candidates.empty()){ - reporter->Message("No candidates from updates. Trying full scan."); - GenCandidatesFromInductionFailure(true); - } - } - -#ifdef CANDS_FROM_UPDATES - /** If the given edge is not inductive in the current proposed solution, - use the induction failure to generate candidates for extension. */ - void GenCandidatesFromEdgeInductionFailure(RPFP::Edge *edge){ - GenSolutionFromIndSet(true /* add markers */); - for(unsigned i = 0; i < edges.size(); i++){ - slvr.push(); - Edge *edge = edges[i]; - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerForEdge(edge,checker); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - slvr.pop(1); - delete checker; - } - } -#endif - - /** Find the unexpanded nodes in the inductive subset. */ - void FindNodesToExpand(){ - for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it){ - Node *node = *it; - if(indset->Candidate(node)) - to_expand.push_back(node); - } - } - - /** Try to create some extension candidates from the unexpanded - nodes. */ - void ProduceSomeCandidates(){ - while(candidates.empty() && !to_expand.empty()){ - Node *node = to_expand.front(); - to_expand.pop_front(); - TryExpandNode(node); - } - } - - std::list postponed_candidates; - - /** Try to produce some extension candidates, first from unexpanded - nides, and if this fails, from induction failure. */ - void ProduceCandidatesForExtension(){ - if(candidates.empty()) - ProduceSomeCandidates(); - while(candidates.empty()){ - FindNodesToExpand(); - if(to_expand.empty()) break; - ProduceSomeCandidates(); - } - if(candidates.empty()){ -#ifdef DEPTH_FIRST_EXPAND - if(postponed_candidates.empty()){ - GenCandidatesFromInductionFailure(); - postponed_candidates.swap(candidates); - } - if(!postponed_candidates.empty()){ - candidates.push_back(postponed_candidates.front()); - postponed_candidates.pop_front(); - } -#else - GenCandidatesFromInductionFailure(); -#endif - } - } - - 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(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. */ - void UpdateWithInterpolant(Node *node, RPFP *tree, Node *top){ - if(top->Outgoing) - for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) - UpdateWithInterpolant(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); - UpdateNodeToNode(node, top); - heuristic->Update(node); - } - - /** Update unwinding lower bounds, using a counterexample. */ - - void UpdateWithCounterexample(Node *node, RPFP *tree, Node *top){ - if(top->Outgoing) - for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) - UpdateWithCounterexample(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); - if(!top->Underapprox.SubsetEq(node->Underapprox)){ - reporter->UpdateUnderapprox(node,top->Underapprox); - // indset->Update(node,top->Annotation); - node->Underapprox.UnionWith(top->Underapprox); - heuristic->Update(node); - } - } - - /** Try to update the unwinding to satisfy the upper bound of a - node. */ - bool SatisfyUpperBound(Node *node){ - if(node->Bound.IsFull()) return true; -#ifdef PROPAGATE_BEFORE_CHECK - Propagate(); -#endif - reporter->Bound(node); - int start_decs = rpfp->CumulativeDecisions(); - DerivationTree *dtp = new DerivationTreeSlow(this,unwinding,reporter,heuristic,FullExpand); - DerivationTree &dt = *dtp; - bool res = dt.Derive(unwinding,node,UseUnderapprox); - int end_decs = rpfp->CumulativeDecisions(); - // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; - last_decisions = end_decs - start_decs; - if(res){ - cex.set(dt.tree,dt.top); // note tree is now owned by cex - if(UseUnderapprox){ - UpdateWithCounterexample(node,dt.tree,dt.top); - } - } - else { - UpdateWithInterpolant(node,dt.tree,dt.top); - delete dt.tree; - } - delete dtp; - return !res; - } - - /* For a given nod in the unwinding, get conjectures from the - proposers and check them locally. Update the node with any true - conjectures. - */ - - void DoEagerDeduction(Node *node){ - for(unsigned i = 0; i < proposers.size(); i++){ - const std::vector &conjectures = proposers[i]->Propose(node); - for(unsigned j = 0; j < conjectures.size(); j++){ - const RPFP::Transformer &conjecture = conjectures[j]; - RPFP::Transformer bound(conjecture); - std::vector conj_vec; - unwinding->CollectConjuncts(bound.Formula,conj_vec); - for(unsigned k = 0; k < conj_vec.size(); k++){ - bound.Formula = conj_vec[k]; - if(CheckEdgeCaching(node->Outgoing,bound) == unsat) - Update(node,bound, /* eager = */ true); - //else - //std::cout << "conjecture failed\n"; - } - } - } - } - - - check_result CheckEdge(RPFP *checker, Edge *edge){ - Node *root = edge->Parent; - checker->Push(); - checker->AssertNode(root); - checker->AssertEdge(edge,1,true); - check_result res = checker->Check(root); - checker->Pop(1); - return res; - } - - check_result CheckEdgeCaching(Edge *unwinding_edge, const RPFP::Transformer &bound){ - - // use a dedicated solver for this edge - // TODO: can this mess be hidden somehow? - - RPFP_caching *checker = gen_cands_rpfp; // TODO: a good choice? - Edge *edge = unwinding_edge->map; // get the edge in the original RPFP - RPFP_caching::scoped_solver_for_edge ssfe(checker,edge,true /* models */, true /*axioms*/); - Edge *checker_edge = checker->GetEdgeClone(edge); - - // copy the annotations and bound to the clone - Node *root = checker_edge->Parent; - root->Bound = bound; - for(unsigned j = 0; j < checker_edge->Children.size(); j++){ - Node *oc = unwinding_edge->Children[j]; - Node *nc = checker_edge->Children[j]; - nc->Annotation = oc->Annotation; - } - - return CheckEdge(checker,checker_edge); - } - - - /* If the counterexample derivation is partial due to - use of underapproximations, complete it. */ - - void BuildFullCex(Node *node){ - 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"; - cex.set(dt.tree,dt.top); - } - - void UpdateBackEdges(Node *node){ -#ifdef BOUNDED - std::vector &chs = node->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - bool is_back = TopoSort[child->map] >= TopoSort[node->map]; - NodeToCounter &nov = back_edges[node]; - NodeToCounter chv = back_edges[child]; - if(is_back) - chv[child->map].val++; - for(NodeToCounter::iterator it = chv.begin(), en = chv.end(); it != en; ++it){ - Node *back = it->first; - Counter &c = nov[back]; - c.val = std::max(c.val,it->second.val); - } - } -#endif - } - - /** Extend the unwinding, keeping it solved. */ - bool Extend(Candidate &cand, Node *&node){ - timer_start("Extend"); - node = CreateNodeInstance(cand.edge->Parent); - CreateEdgeInstance(cand.edge,node,cand.Children); - UpdateBackEdges(node); - reporter->Extend(node); - DoEagerDeduction(node); // first be eager... - bool res = SatisfyUpperBound(node); // then be lazy - if(res) indset->CloseDescendants(node); - else { -#ifdef UNDERAPPROX_NODES - ExpandUnderapproxNodes(cex.get_tree(), cex.get_root()); -#endif - if(UseUnderapprox) BuildFullCex(node); - timer_stop("Extend"); - return res; - } - timer_stop("Extend"); - return res; - } - - void ExpandUnderapproxNodes(RPFP *tree, Node *root){ - Node *node = root->map; - if(underapprox_map.find(node) != underapprox_map.end()){ - RPFP::Transformer cnst = root->Annotation; - tree->EvalNodeAsConstraint(root, cnst); - cnst.Complement(); - Node *orig = underapprox_map[node]; - RPFP::Transformer save = orig->Bound; - orig->Bound = cnst; - DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); - bool res = dt.Derive(unwinding,orig,UseUnderapprox,true,tree); - if(!res){ - UpdateWithInterpolant(orig,dt.tree,dt.top); - throw "bogus underapprox!"; - } - ExpandUnderapproxNodes(tree,dt.top); - } - else if(root->Outgoing){ - std::vector &chs = root->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++) - ExpandUnderapproxNodes(tree,chs[i]); - } - } - - // Propagate conjuncts up the unwinding - void Propagate(){ - reporter->Message("beginning propagation"); - timer_start("Propagate"); - std::vector sorted_nodes = unwinding->nodes; - std::sort(sorted_nodes.begin(),sorted_nodes.end(),std::less()); // sorts by sequence number - hash_map > facts; - for(unsigned i = 0; i < sorted_nodes.size(); i++){ - Node *node = sorted_nodes[i]; - std::set &node_facts = facts[node->map]; - if(!(node->Outgoing && indset->Contains(node))) - continue; - std::vector conj_vec; - unwinding->CollectConjuncts(node->Annotation.Formula,conj_vec); - std::set conjs; - std::copy(conj_vec.begin(),conj_vec.end(),std::inserter(conjs,conjs.begin())); - if(!node_facts.empty()){ - RPFP *checker = new RPFP(rpfp->ls); - slvr.push(); - Node *root = checker->CloneNode(node); - Edge *edge = node->Outgoing; - // checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - nc->Annotation = oc->Annotation; // is this needed? - cs.push_back(nc); - } - Edge *checker_edge = checker->CreateEdge(root,edge->F,cs); - checker->AssertEdge(checker_edge, 0, true, false); - std::vector propagated; - for(std::set ::iterator it = node_facts.begin(), en = node_facts.end(); it != en;){ - const expr &fact = *it; - if(conjs.find(fact) == conjs.end()){ - root->Bound.Formula = fact; - slvr.push(); - checker->AssertNode(root); - check_result res = checker->Check(root); - slvr.pop(); - if(res != unsat){ - std::set ::iterator victim = it; - ++it; - node_facts.erase(victim); // if it ain't true, nix it - continue; - } - propagated.push_back(fact); - } - ++it; - } - slvr.pop(); - for(unsigned i = 0; i < propagated.size(); i++){ - root->Annotation.Formula = propagated[i]; - UpdateNodeToNode(node,root); - } - delete checker; - } - for(std::set ::iterator it = conjs.begin(), en = conjs.end(); it != en; ++it){ - expr foo = *it; - node_facts.insert(foo); - } - } - timer_stop("Propagate"); - } - - /** This class represents a derivation tree. */ - class DerivationTree { - public: - - virtual ~DerivationTree(){} - - DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) - : slvr(rpfp->slvr()), - ctx(rpfp->ctx) - { - duality = _duality; - reporter = _reporter; - heuristic = _heuristic; - full_expand = _full_expand; - } - - Duality *duality; - Reporter *reporter; - Heuristic *heuristic; - solver &slvr; - context &ctx; - RPFP *tree; - RPFP::Node *top; - std::list leaves; - bool full_expand; - bool underapprox; - bool constrained; - bool false_approx; - std::vector underapprox_core; - int start_decs, last_decs; - - /* We build derivation trees in one of three modes: - - 1) In normal mode, we build the full tree without considering - underapproximations. - - 2) In underapprox mode, we use underapproximations to cut off - the tree construction. THis means the resulting tree may not - be complete. - - 3) In constrained mode, we build the full tree but use - underapproximations as upper bounds. This mode is used to - complete the partial derivation constructed in underapprox - mode. - */ - - bool Derive(RPFP *rpfp, RPFP::Node *root, bool _underapprox, bool _constrained = false, RPFP *_tree = 0){ - underapprox = _underapprox; - constrained = _constrained; - false_approx = true; - timer_start("Derive"); -#ifndef USE_CACHING_RPFP - tree = _tree ? _tree : new RPFP(rpfp->ls); -#else - RPFP::LogicSolver *cache_ls = new RPFP::iZ3LogicSolver(ctx); - cache_ls->slvr->push(); - tree = _tree ? _tree : new RPFP_caching(cache_ls); -#endif - tree->HornClauses = rpfp->HornClauses; - tree->Push(); // so we can clear out the solver later when finished - top = CreateApproximatedInstance(root); - tree->AssertNode(top); // assert the negation of the top-level spec - timer_start("Build"); - bool res = Build(); - heuristic->Done(); - timer_stop("Build"); - timer_start("Pop"); - tree->Pop(1); - timer_stop("Pop"); -#ifdef USE_CACHING_RPFP - cache_ls->slvr->pop(1); - delete cache_ls; - tree->ls = rpfp->ls; -#endif - timer_stop("Derive"); - return res; - } - -#define WITH_CHILDREN - - void InitializeApproximatedInstance(RPFP::Node *to){ - to->Annotation = to->map->Annotation; -#ifndef WITH_CHILDREN - tree->CreateLowerBoundEdge(to); -#endif - leaves.push_back(to); - } - - Node *CreateApproximatedInstance(RPFP::Node *from){ - Node *to = tree->CloneNode(from); - InitializeApproximatedInstance(to); - return to; - } - - bool CheckWithUnderapprox(){ - timer_start("CheckWithUnderapprox"); - std::vector leaves_vector(leaves.size()); - std::copy(leaves.begin(),leaves.end(),leaves_vector.begin()); - check_result res = tree->Check(top,leaves_vector); - timer_stop("CheckWithUnderapprox"); - return res != unsat; - } - - virtual bool Build(){ -#ifdef EFFORT_BOUNDED_STRAT - start_decs = tree->CumulativeDecisions(); -#endif - while(ExpandSomeNodes(true)); // do high-priority expansions - while (true) - { -#ifndef WITH_CHILDREN - timer_start("asserting leaves"); - timer_start("pushing"); - tree->Push(); - timer_stop("pushing"); - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - tree->AssertEdge((*it)->Outgoing,1); // assert the overapproximation, and keep it past pop - timer_stop("asserting leaves"); - lbool res = tree->Solve(top, 2); // incremental solve, keep interpolants for two pops - timer_start("popping leaves"); - tree->Pop(1); - timer_stop("popping leaves"); -#else - lbool res; - if((underapprox || false_approx) && top->Outgoing && CheckWithUnderapprox()){ - if(constrained) goto expand_some_nodes; // in constrained mode, keep expanding - goto we_are_sat; // else if underapprox is sat, we stop - } - // tree->Check(top); - res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop -#endif - if (res == l_false) - return false; - - expand_some_nodes: - if(ExpandSomeNodes()) - continue; - - we_are_sat: - if(underapprox && !constrained){ - timer_start("ComputeUnderapprox"); - tree->ComputeUnderapprox(top,1); - timer_stop("ComputeUnderapprox"); - } - else { -#ifdef UNDERAPPROX_NODES -#ifndef SKIP_UNDERAPPROX_NODES - timer_start("ComputeUnderapprox"); - tree->ComputeUnderapprox(top,1); - timer_stop("ComputeUnderapprox"); -#endif -#endif - } - return true; - } - } - - virtual void ExpandNode(RPFP::Node *p){ - // tree->RemoveEdge(p->Outgoing); - Edge *ne = p->Outgoing; - if(ne) { - // reporter->Message("Recycling edge..."); - std::vector &cs = ne->Children; - for(unsigned i = 0; i < cs.size(); i++) - InitializeApproximatedInstance(cs[i]); - // ne->dual = expr(); - } - else { - Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); - std::vector &cs = edge->Children; - std::vector children(cs.size()); - for(unsigned i = 0; i < cs.size(); i++) - children[i] = CreateApproximatedInstance(cs[i]); - ne = tree->CreateEdge(p, p->map->Outgoing->F, children); - ne->map = p->map->Outgoing->map; - } -#ifndef WITH_CHILDREN - tree->AssertEdge(ne); // assert the edge in the solver -#else - tree->AssertEdge(ne,0,!full_expand,(underapprox || false_approx)); // assert the edge in the solver -#endif - reporter->Expand(ne); - } - -#define UNDERAPPROXCORE -#ifndef UNDERAPPROXCORE - void ExpansionChoices(std::set &best){ - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - if (!tree->Empty(*it)) // if used in the counter-model - choices.insert(*it); - heuristic->ChooseExpand(choices, best); - } -#else -#if 0 - - void ExpansionChoices(std::set &best){ - std::vector unused_set, used_set; - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ - Node *n = *it; - if (!tree->Empty(n)) - used_set.push_back(n); - else - unused_set.push_back(n); - } - if(tree->Check(top,unused_set) == unsat) - throw "error in ExpansionChoices"; - for(unsigned i = 0; i < used_set.size(); i++){ - Node *n = used_set[i]; - unused_set.push_back(n); - if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ - unused_set.pop_back(); - choices.insert(n); - } - else - std::cout << "Using underapprox of " << n->number << std::endl; - } - heuristic->ChooseExpand(choices, best); - } -#else - void ExpansionChoicesFull(std::set &best, bool high_priority, bool best_only = false){ - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - if (high_priority || !tree->Empty(*it)) // if used in the counter-model - choices.insert(*it); - heuristic->ChooseExpand(choices, best, high_priority, best_only); - } - - void ExpansionChoicesRec(std::vector &unused_set, std::vector &used_set, - std::set &choices, int from, int to){ - if(from == to) return; - int orig_unused = unused_set.size(); - unused_set.resize(orig_unused + (to - from)); - std::copy(used_set.begin()+from,used_set.begin()+to,unused_set.begin()+orig_unused); - if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ - unused_set.resize(orig_unused); - if(to - from == 1){ -#if 1 - std::cout << "Not using underapprox of " << used_set[from] ->number << std::endl; -#endif - choices.insert(used_set[from]); - } - else { - int mid = from + (to - from)/2; - ExpansionChoicesRec(unused_set, used_set, choices, from, mid); - ExpansionChoicesRec(unused_set, used_set, choices, mid, to); - } - } - else { -#if 1 - std::cout << "Using underapprox of "; - for(int i = from; i < to; i++){ - std::cout << used_set[i]->number << " "; - if(used_set[i]->map->Underapprox.IsEmpty()) - std::cout << "(false!) "; - } - std::cout << std::endl; -#endif - } - } - - std::set old_choices; - - void ExpansionChoices(std::set &best, bool high_priority, bool best_only = false){ - if(!underapprox || constrained || high_priority){ - ExpansionChoicesFull(best, high_priority,best_only); - return; - } - std::vector unused_set, used_set; - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ - Node *n = *it; - if (!tree->Empty(n)){ - if(old_choices.find(n) != old_choices.end() || n->map->Underapprox.IsEmpty()) - choices.insert(n); - else - used_set.push_back(n); - } - else - unused_set.push_back(n); - } - if(tree->Check(top,unused_set) == unsat) - throw "error in ExpansionChoices"; - ExpansionChoicesRec(unused_set, used_set, choices, 0, used_set.size()); - old_choices = choices; - heuristic->ChooseExpand(choices, best, high_priority); - } -#endif -#endif - - bool ExpandSomeNodes(bool high_priority = false, int max = INT_MAX){ -#ifdef EFFORT_BOUNDED_STRAT - last_decs = tree->CumulativeDecisions() - start_decs; -#endif - timer_start("ExpandSomeNodes"); - timer_start("ExpansionChoices"); - std::set choices; - ExpansionChoices(choices,high_priority,max != INT_MAX); - timer_stop("ExpansionChoices"); - std::list leaves_copy = leaves; // copy so can modify orig - leaves.clear(); - int count = 0; - for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ - if(choices.find(*it) != choices.end() && count < max){ - count++; - ExpandNode(*it); - } - else leaves.push_back(*it); - } - timer_stop("ExpandSomeNodes"); - return !choices.empty(); - } - - void RemoveExpansion(RPFP::Node *p){ - Edge *edge = p->Outgoing; - Node *parent = edge->Parent; -#ifndef KEEP_EXPANSIONS - std::vector cs = edge->Children; - tree->DeleteEdge(edge); - for(unsigned i = 0; i < cs.size(); i++) - tree->DeleteNode(cs[i]); -#endif - leaves.push_back(parent); - } - - // remove all the descendants of tree root (but not root itself) - void RemoveTree(RPFP *tree, RPFP::Node *root){ - Edge *edge = root->Outgoing; - std::vector cs = edge->Children; - tree->DeleteEdge(edge); - for(unsigned i = 0; i < cs.size(); i++){ - RemoveTree(tree,cs[i]); - tree->DeleteNode(cs[i]); - } - } - }; - - class DerivationTreeSlow : public DerivationTree { - public: - - struct stack_entry { - unsigned level; // SMT solver stack level - std::vector expansions; - }; - - std::vector stack; - - hash_map updates; - - DerivationTreeSlow(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) - : DerivationTree(_duality, rpfp, _reporter, _heuristic, _full_expand) { - stack.push_back(stack_entry()); - } - - struct DoRestart {}; - - virtual bool Build(){ - while (true) { - try { - return BuildMain(); - } - catch (const DoRestart &) { - // clear the statck and try again - updated_nodes.clear(); - while(stack.size() > 1) - PopLevel(); - reporter->Message("restarted"); - } - } - } - - bool BuildMain(){ - - stack.back().level = tree->slvr().get_scope_level(); - bool was_sat = true; - int update_failures = 0; - - while (true) - { - lbool res; - - 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); - res = foo == unsat ? l_false : l_true; - - if (res == l_false) { - if (stack.empty()) // should never happen - return false; - - { - std::vector &expansions = stack.back().expansions; - int update_count = 0; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - tree->SolveSingleNode(top,node); -#ifdef NO_GENERALIZE - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); -#else - try { - if(expansions.size() == 1 && NodeTooComplicated(node)) - SimplifyNode(node); - else - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); - Generalize(node); - } - catch(const RPFP::Bad &){ - // bad interpolants can get us here - throw DoRestart(); - } -#endif - if(RecordUpdate(node)) - update_count++; - else - heuristic->Update(node->map); // make it less likely to expand this node in future - } - if(update_count == 0){ - if(was_sat){ - update_failures++; - if(update_failures > 10) - throw Incompleteness(); - } - reporter->Message("backtracked without learning"); - } - else update_failures = 0; - } - tree->ComputeProofCore(); // need to compute the proof core before popping solver - bool propagated = false; - while(1) { - bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop - PopLevel(); - if(stack.size() == 1)break; - if(prev_level_used){ - Node *node = stack.back().expansions[0]; -#ifndef NO_PROPAGATE - if(!Propagate(node)) break; -#endif - if(!RecordUpdate(node)) break; // shouldn't happen! - RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list - propagated = true; - continue; - } - if(propagated) break; // propagation invalidates the proof core, so disable non-chron backtrack - RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list - std::vector &unused_ex = stack.back().expansions; - for(unsigned i = 0; i < unused_ex.size(); i++) - heuristic->Update(unused_ex[i]->map); // make it less likely to expand this node in future - } - HandleUpdatedNodes(); - if(stack.size() == 1){ - if(top->Outgoing) - tree->DeleteEdge(top->Outgoing); // in case we kept the tree - return false; - } - was_sat = false; - } - else { - was_sat = true; - tree->Push(); - std::vector &expansions = stack.back().expansions; -#ifndef NO_DECISIONS - for(unsigned i = 0; i < expansions.size(); i++){ - tree->FixCurrentState(expansions[i]->Outgoing); - } -#endif -#if 0 - if(tree->slvr().check() == unsat) - throw "help!"; -#endif - int expand_max = 1; - if(0&&duality->BatchExpand){ - int thing = stack.size() * 0.1; - expand_max = std::max(1,thing); - if(expand_max > 1) - std::cout << "foo!\n"; - } - - if(ExpandSomeNodes(false,expand_max)) - continue; - tree->Pop(1); - while(stack.size() > 1){ - tree->Pop(1); - stack.pop_back(); - } - return true; - } - } - } - - void PopLevel(){ - std::vector &expansions = stack.back().expansions; - tree->Pop(1); - hash_set leaves_to_remove; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - // if(node != top) - // tree->ConstrainParent(node->Incoming[0],node); - std::vector &cs = node->Outgoing->Children; - for(unsigned i = 0; i < cs.size(); i++){ - leaves_to_remove.insert(cs[i]); - UnmapNode(cs[i]); - if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) - throw "help!"; - } - } - RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - RemoveExpansion(node); - } - stack.pop_back(); - } - - bool NodeTooComplicated(Node *node){ - int ops = tree->CountOperators(node->Annotation.Formula); - if(ops > 10) return true; - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); - return tree->CountOperators(node->Annotation.Formula) > 3; - } - - void SimplifyNode(Node *node){ - // have to destroy the old proof to get a new interpolant - timer_start("SimplifyNode"); - tree->PopPush(); - try { - tree->InterpolateByCases(top,node); - } - catch(const RPFP::Bad&){ - timer_stop("SimplifyNode"); - throw RPFP::Bad(); - } - timer_stop("SimplifyNode"); - } - - bool LevelUsedInProof(unsigned level){ - std::vector &expansions = stack[level].expansions; - for(unsigned i = 0; i < expansions.size(); i++) - if(tree->EdgeUsedInProof(expansions[i]->Outgoing)) - return true; - return false; - } - - void RemoveUpdateNodesAtCurrentLevel() { - for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ - Node *node = *it; - if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ - std::list::iterator victim = it; - ++it; - updated_nodes.erase(victim); - } - else - ++it; - } - } - - void RemoveLeaves(hash_set &leaves_to_remove){ - std::list leaves_copy; - leaves_copy.swap(leaves); - for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ - if(leaves_to_remove.find(*it) == leaves_to_remove.end()) - leaves.push_back(*it); - } - } - - hash_map > node_map; - std::list updated_nodes; - - virtual void ExpandNode(RPFP::Node *p){ - stack.push_back(stack_entry()); - stack.back().level = tree->slvr().get_scope_level(); - stack.back().expansions.push_back(p); - DerivationTree::ExpandNode(p); - std::vector &new_nodes = p->Outgoing->Children; - for(unsigned i = 0; i < new_nodes.size(); i++){ - Node *n = new_nodes[i]; - node_map[n->map].push_back(n); - } - } - - bool RecordUpdate(Node *node){ - bool res = duality->UpdateNodeToNode(node->map,node); - if(res){ - std::vector to_update = node_map[node->map]; - for(unsigned i = 0; i < to_update.size(); i++){ - Node *node2 = to_update[i]; - // maintain invariant that no nodes on updated list are created at current stack level - if(node2 == node || !(node->Incoming.size() > 0 && AtCurrentStackLevel(node2->Incoming[0]->Parent))){ - updated_nodes.push_back(node2); - if(node2 != node) - node2->Annotation = node->Annotation; - } - } - } - return res; - } - - void HandleUpdatedNodes(){ - for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ - Node *node = *it; - node->Annotation = node->map->Annotation; - if(node->Incoming.size() > 0) - tree->ConstrainParent(node->Incoming[0],node); - if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ - std::list::iterator victim = it; - ++it; - updated_nodes.erase(victim); - } - else - ++it; - } - } - - bool AtCurrentStackLevel(Node *node){ - std::vector vec = stack.back().expansions; - for(unsigned i = 0; i < vec.size(); i++) - if(vec[i] == node) - return true; - return false; - } - - void UnmapNode(Node *node){ - std::vector &vec = node_map[node->map]; - for(unsigned i = 0; i < vec.size(); i++){ - if(vec[i] == node){ - std::swap(vec[i],vec.back()); - vec.pop_back(); - return; - } - } - throw "can't unmap node"; - } - - void Generalize(Node *node){ -#ifndef USE_RPFP_CLONE - tree->Generalize(top,node); -#else - RPFP_caching *clone_rpfp = duality->clone_rpfp; - if(!node->Outgoing->map) return; - Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); - Node *clone_node = clone_edge->Parent; - clone_node->Annotation = node->Annotation; - for(unsigned i = 0; i < clone_edge->Children.size(); i++) - clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; - clone_rpfp->GeneralizeCache(clone_edge); - node->Annotation = clone_node->Annotation; -#endif - } - - bool Propagate(Node *node){ -#ifdef USE_RPFP_CLONE - RPFP_caching *clone_rpfp = duality->clone_rpfp; - Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); - Node *clone_node = clone_edge->Parent; - clone_node->Annotation = node->map->Annotation; - for(unsigned i = 0; i < clone_edge->Children.size(); i++) - clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; - bool res = clone_rpfp->PropagateCache(clone_edge); - if(res) - node->Annotation = clone_node->Annotation; - return res; -#else - return false; -#endif - } - - }; - - - class Covering { - - struct cover_info { - Node *covered_by; - std::list covers; - bool dominated; - std::set dominates; - cover_info(){ - covered_by = 0; - dominated = false; - } - }; - - typedef hash_map cover_map; - cover_map cm; - Duality *parent; - bool some_updates; - -#define NO_CONJ_ON_SIMPLE_LOOPS -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - hash_set simple_loops; -#endif - - Node *&covered_by(Node *node){ - return cm[node].covered_by; - } - - std::list &covers(Node *node){ - return cm[node].covers; - } - - std::vector &insts_of_node(Node *node){ - return parent->insts_of_node[node]; - } - - Reporter *reporter(){ - return parent->reporter; - } - - std::set &dominates(Node *x){ - return cm[x].dominates; - } - - bool dominates(Node *x, Node *y){ - std::set &d = cm[x].dominates; - return d.find(y) != d.end(); - } - - bool &dominated(Node *x){ - return cm[x].dominated; - } - - public: - - Covering(Duality *_parent){ - parent = _parent; - some_updates = false; - -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - hash_map > outgoing; - for(unsigned i = 0; i < parent->rpfp->edges.size(); i++) - outgoing[parent->rpfp->edges[i]->Parent].push_back(parent->rpfp->edges[i]); - for(unsigned i = 0; i < parent->rpfp->nodes.size(); i++){ - Node * node = parent->rpfp->nodes[i]; - std::vector &outs = outgoing[node]; - if(outs.size() == 2){ - for(int j = 0; j < 2; j++){ - Edge *loop_edge = outs[j]; - if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent) - simple_loops.insert(node); - } - } - } -#endif - - } - - bool IsCoveredRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return false; - memo.insert(node); - if(covered_by(node)) return true; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) - if(IsCoveredRec(memo,node->Outgoing->Children[i])) - return true; - return false; - } - - bool IsCovered(Node *node){ - hash_set memo; - return IsCoveredRec(memo,node); - } - -#ifndef UNDERAPPROX_NODES - void RemoveCoveringsBy(Node *node){ - std::list &cs = covers(node); - for(std::list::iterator it = cs.begin(), en = cs.end(); it != en; it++){ - covered_by(*it) = 0; - reporter()->RemoveCover(*it,node); - } - cs.clear(); - } -#else - void RemoveCoveringsBy(Node *node){ - std::vector &cs = parent->all_of_node[node->map]; - for(std::vector::iterator it = cs.begin(), en = cs.end(); it != en; it++){ - Node *other = *it; - if(covered_by(other) && CoverOrder(node,other)){ - covered_by(other) = 0; - reporter()->RemoveCover(*it,node); - } - } - } -#endif - - void RemoveAscendantCoveringsRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return; - memo.insert(node); - RemoveCoveringsBy(node); - for(std::vector::iterator it = node->Incoming.begin(), en = node->Incoming.end(); it != en; ++it) - RemoveAscendantCoveringsRec(memo,(*it)->Parent); - } - - void RemoveAscendantCoverings(Node *node){ - hash_set memo; - RemoveAscendantCoveringsRec(memo,node); - } - - bool CoverOrder(Node *covering, Node *covered){ -#ifdef UNDERAPPROX_NODES - if(parent->underapprox_map.find(covered) != parent->underapprox_map.end()) - return false; - if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) - return covering->number < covered->number || parent->underapprox_map[covering] == covered; -#endif - return covering->number < covered->number; - } - - bool CheckCover(Node *covered, Node *covering){ - return - CoverOrder(covering,covered) - && covered->Annotation.SubsetEq(covering->Annotation) - && !IsCovered(covering); - } - - bool CoverByNode(Node *covered, Node *covering){ - if(CheckCover(covered,covering)){ - covered_by(covered) = covering; - covers(covering).push_back(covered); - std::vector others; others.push_back(covering); - reporter()->AddCover(covered,others); - RemoveAscendantCoverings(covered); - return true; - } - else - return false; - } - -#ifdef UNDERAPPROX_NODES - bool CoverByAll(Node *covered){ - RPFP::Transformer all = covered->Annotation; - all.SetEmpty(); - std::vector &insts = parent->insts_of_node[covered->map]; - std::vector others; - for(unsigned i = 0; i < insts.size(); i++){ - Node *covering = insts[i]; - if(CoverOrder(covering,covered) && !IsCovered(covering)){ - others.push_back(covering); - all.UnionWith(covering->Annotation); - } - } - if(others.size() && covered->Annotation.SubsetEq(all)){ - covered_by(covered) = covered; // anything non-null will do - reporter()->AddCover(covered,others); - RemoveAscendantCoverings(covered); - return true; - } - else - return false; - } -#endif - - bool Close(Node *node){ - if(covered_by(node)) - return true; -#ifndef UNDERAPPROX_NODES - std::vector &insts = insts_of_node(node->map); - for(unsigned i = 0; i < insts.size(); i++) - if(CoverByNode(node,insts[i])) - return true; -#else - if(CoverByAll(node)) - return true; -#endif - return false; - } - - bool CloseDescendantsRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return false; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) - if(CloseDescendantsRec(memo,node->Outgoing->Children[i])) - return true; - if(Close(node)) - return true; - memo.insert(node); - return false; - } - - bool CloseDescendants(Node *node){ - timer_start("CloseDescendants"); - hash_set memo; - bool res = CloseDescendantsRec(memo,node); - timer_stop("CloseDescendants"); - return res; - } - - bool Contains(Node *node){ - timer_start("Contains"); - bool res = !IsCovered(node); - timer_stop("Contains"); - return res; - } - - bool Candidate(Node *node){ - timer_start("Candidate"); - bool res = !IsCovered(node) && !dominated(node); - timer_stop("Candidate"); - return res; - } - - void SetDominated(Node *node){ - dominated(node) = true; - } - - bool CouldCover(Node *covered, Node *covering){ -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - // Forsimple loops, we rely on propagation, not covering - if(simple_loops.find(covered->map) != simple_loops.end()) - return false; -#endif -#ifdef UNDERAPPROX_NODES - // if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) - // return parent->underapprox_map[covering] == covered; -#endif - if(CoverOrder(covering,covered) - && !IsCovered(covering)){ - RPFP::Transformer f(covering->Annotation); f.SetEmpty(); -#if defined(TOP_DOWN) || defined(EFFORT_BOUNDED_STRAT) - if(parent->StratifiedInlining) - return true; -#endif - return !covering->Annotation.SubsetEq(f); - } - return false; - } - - bool ContainsCex(Node *node, Counterexample &cex){ - expr val = cex.get_tree()->Eval(cex.get_root()->Outgoing,node->Annotation.Formula); - return eq(val,parent->ctx.bool_val(true)); - } - - /** We conjecture that the annotations of similar nodes may be - true of this one. We start with later nodes, on the - principle that their annotations are likely weaker. We save - a counterexample -- if annotations of other nodes are true - in this counterexample, we don't need to check them. - */ - -#ifndef UNDERAPPROX_NODES - bool Conjecture(Node *node){ - std::vector &insts = insts_of_node(node->map); - Counterexample cex; - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CouldCover(node,other)){ - reporter()->Forcing(node,other); - if(cex.get_tree() && !ContainsCex(other,cex)) - continue; - cex.clear(); - if(parent->ProveConjecture(node,other->Annotation,other,&cex)) - if(CloseDescendants(node)) - return true; - } - } - cex.clear(); - return false; - } -#else - bool Conjecture(Node *node){ - std::vector &insts = insts_of_node(node->map); - Counterexample cex; - RPFP::Transformer Bound = node->Annotation; - Bound.SetEmpty(); - bool some_other = false; - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CouldCover(node,other)){ - reporter()->Forcing(node,other); - Bound.UnionWith(other->Annotation); - some_other = true; - } - } - if(some_other && parent->ProveConjecture(node,Bound)){ - CloseDescendants(node); - return true; - } - return false; - } -#endif - - void Update(Node *node, const RPFP::Transformer &update){ - RemoveCoveringsBy(node); - some_updates = true; - } - -#ifndef UNDERAPPROX_NODES - Node *GetSimilarNode(Node *node){ - if(!some_updates) - return 0; - std::vector &insts = insts_of_node(node->map); - for(int i = insts.size()-1; i >= 0; i--){ - Node *other = insts[i]; - if(dominates(node,other)) - if(CoverOrder(other,node) - && !IsCovered(other)) - return other; - } - return 0; - } -#else - Node *GetSimilarNode(Node *node){ - if(!some_updates) - return 0; - std::vector &insts = insts_of_node(node->map); - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CoverOrder(other,node) - && !IsCovered(other)) - return other; - } - return 0; - } -#endif - - bool Dominates(Node * node, Node *other){ - if(node == other) return false; - if(other->Outgoing->map == 0) return true; - if(node->Outgoing->map == other->Outgoing->map){ - assert(node->Outgoing->Children.size() == other->Outgoing->Children.size()); - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *nc = node->Outgoing->Children[i]; - Node *oc = other->Outgoing->Children[i]; - if(!(nc == oc || oc->Outgoing->map ==0 || dominates(nc,oc))) - return false; - } - return true; - } - return false; - } - - void Add(Node *node){ - std::vector &insts = insts_of_node(node->map); - for(unsigned i = 0; i < insts.size(); i++){ - Node *other = insts[i]; - if(Dominates(node,other)){ - cm[node].dominates.insert(other); - cm[other].dominated = true; - reporter()->Dominates(node, other); - } - } - } - - }; - - /* This expansion heuristic makes use of a previuosly obtained - counterexample as a guide. This is for use in abstraction - refinement schemes.*/ - - class ReplayHeuristic : public Heuristic { - - Counterexample old_cex; - public: - ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) - : Heuristic(_rpfp) - { - old_cex.swap(_old_cex); // take ownership from caller - } - - ~ReplayHeuristic(){ - } - - // Maps nodes of derivation tree into old cex - hash_map cex_map; - - void Done() { - cex_map.clear(); - old_cex.clear(); - } - - void ShowNodeAndChildren(Node *n){ - std::cout << n->Name.name() << ": "; - std::vector &chs = n->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++) - std::cout << chs[i]->Name.name() << " " ; - std::cout << std::endl; - } - - // HACK: When matching relation names, we drop suffixes used to - // make the names unique between runs. For compatibility - // with boggie, we drop suffixes beginning with @@ - std::string BaseName(const std::string &name){ - int pos = name.find("@@"); - if(pos >= 1) - return name.substr(0,pos); - return name; - } - - virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ - if(!high_priority || !old_cex.get_tree()){ - Heuristic::ChooseExpand(choices,best,false); - return; - } - // first, try to match the derivatino tree nodes to the old cex - std::set matched, unmatched; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it); - if(cex_map.empty()) - cex_map[node] = old_cex.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()) - throw "catastrophe in ReplayHeuristic::ChooseExpand"; - Node *old_parent = cex_map[parent]; - std::vector &chs = parent->Outgoing->Children; - if(old_parent && old_parent->Outgoing){ - std::vector &old_chs = old_parent->Outgoing->Children; - for(unsigned i = 0, j=0; i < chs.size(); i++){ - if(j < old_chs.size() && BaseName(chs[i]->Name.name().str()) == BaseName(old_chs[j]->Name.name().str())) - cex_map[chs[i]] = old_chs[j++]; - else { - std::cerr << "WARNING: duality: unmatched child: " << chs[i]->Name.name() << std::endl; - cex_map[chs[i]] = 0; - } - } - goto matching_done; - } - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = 0; - } - matching_done: - Node *old_node = cex_map[node]; - if(!old_node) - unmatched.insert(node); - else if(old_cex.get_tree()->Empty(old_node)) - unmatched.insert(node); - else - matched.insert(node); - } - if (matched.empty() && !high_priority) - Heuristic::ChooseExpand(unmatched,best,false); - else - Heuristic::ChooseExpand(matched,best,false); - } - }; - - - class LocalHeuristic : public Heuristic { - - RPFP::Node *old_node; - public: - LocalHeuristic(RPFP *_rpfp) - : Heuristic(_rpfp) - { - old_node = 0; - } - - void SetOldNode(RPFP::Node *_old_node){ - old_node = _old_node; - cex_map.clear(); - } - - // Maps nodes of derivation tree into old subtree - hash_map cex_map; - - virtual void ChooseExpand(const std::set &choices, std::set &best){ - if(old_node == 0){ - Heuristic::ChooseExpand(choices,best); - return; - } - // first, try to match the derivatino tree nodes to the old cex - std::set matched, unmatched; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it); - if(cex_map.empty()) - cex_map[node] = old_node; // 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()) - throw "catastrophe in ReplayHeuristic::ChooseExpand"; - Node *old_parent = cex_map[parent]; - std::vector &chs = parent->Outgoing->Children; - if(old_parent && old_parent->Outgoing){ - std::vector &old_chs = old_parent->Outgoing->Children; - if(chs.size() == old_chs.size()){ - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = old_chs[i]; - goto matching_done; - } - else - std::cout << "derivation tree does not match old cex" << std::endl; - } - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = 0; - } - matching_done: - Node *old_node = cex_map[node]; - if(!old_node) - unmatched.insert(node); - else if(old_node != node->map) - unmatched.insert(node); - else - matched.insert(node); - } - Heuristic::ChooseExpand(unmatched,best); - } - }; - - /** This proposer class generates conjectures based on the - unwinding generated by a previous solver. The assumption is - that the provious solver was working on a different - abstraction of the same system. The trick is to adapt the - annotations in the old unwinding to the new predicates. We - start by generating a map from predicates and parameters in - the old problem to the new. - - HACK: mapping is done by cheesy name comparison. - */ - - class HistoryProposer : public Proposer - { - Duality *old_solver; - Duality *new_solver; - hash_map > conjectures; - - public: - /** Construct a history solver. */ - HistoryProposer(Duality *_old_solver, Duality *_new_solver) - : old_solver(_old_solver), new_solver(_new_solver) { - - // tricky: names in the axioms may have changed -- map them - hash_set &old_constants = old_solver->unwinding->ls->get_constants(); - hash_set &new_constants = new_solver->rpfp->ls->get_constants(); - hash_map cmap; - for(hash_set::iterator it = new_constants.begin(), en = new_constants.end(); it != en; ++it) - cmap[GetKey(*it)] = *it; - hash_map bckg_map; - for(hash_set::iterator it = old_constants.begin(), en = old_constants.end(); it != en; ++it){ - func_decl f = new_solver->ctx.translate(*it); // move to new context - if(cmap.find(GetKey(f)) != cmap.end()) - bckg_map[f] = cmap[GetKey(f)]; - // else - // std::cout << "constant not matched\n"; - } - - RPFP *old_unwinding = old_solver->unwinding; - hash_map > pred_match; - - // index all the predicates in the old unwinding - for(unsigned i = 0; i < old_unwinding->nodes.size(); i++){ - Node *node = old_unwinding->nodes[i]; - std::string key = GetKey(node); - pred_match[key].push_back(node); - } - - // match with predicates in the new RPFP - RPFP *rpfp = new_solver->rpfp; - for(unsigned i = 0; i < rpfp->nodes.size(); i++){ - Node *node = rpfp->nodes[i]; - std::string key = GetKey(node); - std::vector &matches = pred_match[key]; - for(unsigned j = 0; j < matches.size(); j++) - MatchNodes(node,matches[j],bckg_map); - } - } - - virtual std::vector &Propose(Node *node){ - return conjectures[node->map]; - } - - virtual ~HistoryProposer(){ - }; - - private: - void MatchNodes(Node *new_node, Node *old_node, hash_map &bckg_map){ - if(old_node->Annotation.IsFull()) - return; // don't conjecture true! - hash_map var_match; - std::vector &new_params = new_node->Annotation.IndParams; - // Index the new parameters by their keys - for(unsigned i = 0; i < new_params.size(); i++) - var_match[GetKey(new_params[i])] = new_params[i]; - RPFP::Transformer &old = old_node->Annotation; - std::vector from_params = old.IndParams; - for(unsigned j = 0; j < from_params.size(); j++) - from_params[j] = new_solver->ctx.translate(from_params[j]); // get in new context - std::vector to_params = from_params; - for(unsigned j = 0; j < to_params.size(); j++){ - std::string key = GetKey(to_params[j]); - if(var_match.find(key) == var_match.end()){ - // std::cout << "unmatched parameter!\n"; - return; - } - to_params[j] = var_match[key]; - } - expr fmla = new_solver->ctx.translate(old.Formula); // get in new context - fmla = new_solver->rpfp->SubstParams(old.IndParams,to_params,fmla); // substitute parameters - hash_map memo; - fmla = new_solver->rpfp->SubstRec(memo,bckg_map,fmla); // substitute background constants - RPFP::Transformer new_annot = new_node->Annotation; - new_annot.Formula = fmla; - conjectures[new_node].push_back(new_annot); - } - - // We match names by removing suffixes beginning with double at sign - - std::string GetKey(Node *node){ - return GetKey(node->Name); - } - - std::string GetKey(const expr &var){ - return GetKey(var.decl()); - } - - std::string GetKey(const func_decl &f){ - std::string name = f.name().str(); - int idx = name.find("@@"); - if(idx >= 0) - name.erase(idx); - return name; - } - }; - }; - - - class StreamReporter : public Reporter { - std::ostream &s; - public: - StreamReporter(RPFP *_rpfp, std::ostream &_s) - : Reporter(_rpfp), s(_s) {event = 0; depth = -1;} - int event; - int depth; - void ev(){ - s << "[" << event++ << "]" ; - } - virtual void Extend(RPFP::Node *node){ - ev(); s << "node " << node->number << ": " << node->Name.name(); - std::vector &rps = node->Outgoing->Children; - for(unsigned i = 0; i < rps.size(); i++) - s << " " << rps[i]->number; - s << std::endl; - } - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ - ev(); s << "update " << node->number << " " << node->Name.name() << ": "; - rpfp->Summarize(update.Formula); - 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(); - if(depth > 0) s << " (depth=" << depth << ")"; - s << std::endl; - } - virtual void Depth(int d){ - depth = d; - } - virtual void AddCover(RPFP::Node *covered, std::vector &covering){ - ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; - for(unsigned i = 0; i < covering.size(); i++) - 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; - } - virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){ - ev(); s << "forcing " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; - } - virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){ - ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": "; - rpfp->Summarize(t.Formula); - s << std::endl; - } - virtual void Dominates(RPFP::Node *node, RPFP::Node *other){ - ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; - } - virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){ - ev(); s << "induction failure: " << edge->Parent->Name.name() << ", children ="; - for(unsigned i = 0; i < children.size(); i++) - s << " " << children[i]->number; - s << std::endl; - } - virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){ - ev(); s << "underapprox " << node->number << " " << node->Name.name() << ": " << update.Formula << std::endl; - } - virtual void Reject(RPFP::Edge *edge, const std::vector &children){ - ev(); s << "reject " << edge->Parent->number << " " << edge->Parent->Name.name() << ": "; - for(unsigned i = 0; i < children.size(); i++) - s << " " << children[i]->number; - s << std::endl; - } - virtual void Message(const std::string &msg){ - ev(); s << "msg " << msg << std::endl; - } - - }; - - Solver *Solver::Create(const std::string &solver_class, RPFP *rpfp){ - Duality *s = alloc(Duality,rpfp); - return s; - } - - Reporter *CreateStdoutReporter(RPFP *rpfp){ - return new StreamReporter(rpfp, std::cout); - } -} +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + duality_solver.h + +Abstract: + + implements relational post-fixedpoint problem + (RPFP) solver + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + + +--*/ + +#ifdef _WINDOWS +#pragma warning(disable:4996) +#pragma warning(disable:4800) +#pragma warning(disable:4267) +#endif + +#include "duality.h" +#include "duality_profiling.h" + +#include +#include +#include +#include +#include + +// TODO: make these official options or get rid of them + +#define NEW_CAND_SEL +// #define LOCALIZE_CONJECTURES +// #define CANDS_FROM_UPDATES +#define CANDS_FROM_COVER_FAIL +#define DEPTH_FIRST_EXPAND +#define MINIMIZE_CANDIDATES +// #define MINIMIZE_CANDIDATES_HARDER +#define BOUNDED +// #define CHECK_CANDS_FROM_IND_SET +#define UNDERAPPROX_NODES +#define NEW_EXPAND +#define EARLY_EXPAND +// #define TOP_DOWN +// #define EFFORT_BOUNDED_STRAT +#define SKIP_UNDERAPPROX_NODES +// #define KEEP_EXPANSIONS +// #define USE_CACHING_RPFP +// #define PROPAGATE_BEFORE_CHECK +#define NEW_STRATIFIED_INLINING + +#define USE_RPFP_CLONE +#define USE_NEW_GEN_CANDS + +//#define NO_PROPAGATE +//#define NO_GENERALIZE +//#define NO_DECISIONS + +namespace Duality { + + // TODO: must be a better place for this... + static char string_of_int_buffer[20]; + + static const char *string_of_int(int n){ + sprintf(string_of_int_buffer,"%d",n); + return string_of_int_buffer; + } + + /** Generic object for producing diagnostic output. */ + + class Reporter { + protected: + RPFP *rpfp; + public: + Reporter(RPFP *_rpfp){ + rpfp = _rpfp; + } + virtual void Extend(RPFP::Node *node){} + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){} + virtual void Bound(RPFP::Node *node){} + virtual void Expand(RPFP::Edge *edge){} + virtual void AddCover(RPFP::Node *covered, std::vector &covering){} + virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){} + virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){} + virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){} + virtual void Dominates(RPFP::Node *node, RPFP::Node *other){} + virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){} + virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} + virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} + virtual void Message(const std::string &msg){} + virtual void Depth(int){} + virtual ~Reporter(){} + }; + + Reporter *CreateStdoutReporter(RPFP *rpfp); + + /** Object we throw in case of catastrophe. */ + + struct InternalError { + std::string msg; + InternalError(const std::string _msg) + : msg(_msg) {} + }; + + + /** This is the main solver. It takes anarbitrary (possibly cyclic) + RPFP and either annotates it with a solution, or returns a + counterexample derivation in the form of an embedd RPFP tree. */ + + class Duality : public Solver { + + public: + Duality(RPFP *_rpfp) + : ctx(_rpfp->ctx), + slvr(_rpfp->slvr()), + nodes(_rpfp->nodes), + edges(_rpfp->edges) + { + rpfp = _rpfp; + reporter = 0; + heuristic = 0; + unwinding = 0; + FullExpand = false; + NoConj = false; + FeasibleEdges = true; + UseUnderapprox = true; + Report = false; + StratifiedInlining = false; + RecursionBound = -1; + BatchExpand = false; + { + scoped_no_proof no_proofs_please(ctx.m()); +#ifdef USE_RPFP_CLONE + clone_rpfp = new RPFP_caching(rpfp->ls); + clone_rpfp->Clone(rpfp); +#endif +#ifdef USE_NEW_GEN_CANDS + gen_cands_rpfp = new RPFP_caching(rpfp->ls); + gen_cands_rpfp->Clone(rpfp); +#endif + } + } + + ~Duality(){ +#ifdef USE_RPFP_CLONE + delete clone_rpfp; +#endif +#ifdef USE_NEW_GEN_CANDS + delete gen_cands_rpfp; +#endif + if(unwinding) delete unwinding; + } + +#ifdef USE_RPFP_CLONE + RPFP_caching *clone_rpfp; +#endif +#ifdef USE_NEW_GEN_CANDS + RPFP_caching *gen_cands_rpfp; +#endif + + + typedef RPFP::Node Node; + typedef RPFP::Edge Edge; + + /** This struct represents a candidate for extending the + unwinding. It consists of an edge to instantiate + and a vector of children for the new instance. */ + + struct Candidate { + Edge *edge; std::vector + Children; + }; + + /** Comparison operator, allowing us to sort Nodes + by their number field. */ + + struct lnode + { + bool operator()(const Node* s1, const Node* s2) const + { + return s1->number < s2->number; + } + }; + + typedef std::set Unexpanded; // sorted set of Nodes + + /** This class provides a heuristic for expanding a derivation + tree. */ + + class Heuristic { + RPFP *rpfp; + + /** Heuristic score for unwinding nodes. Currently this + counts the number of updates. */ + struct score { + int updates; + score() : updates(0) {} + }; + hash_map scores; + + public: + Heuristic(RPFP *_rpfp){ + rpfp = _rpfp; + } + + virtual ~Heuristic(){} + + virtual void Update(RPFP::Node *node){ + scores[node].updates++; + } + + /** Heuristic choice of nodes to expand. Takes a set "choices" + and returns a subset "best". We currently choose the + nodes with the fewest updates. + */ +#if 0 + virtual void ChooseExpand(const std::set &choices, std::set &best){ + int best_score = INT_MAX; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it)->map; + int score = scores[node].updates; + best_score = std::min(best_score,score); + } + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) + if(scores[(*it)->map].updates == best_score) + best.insert(*it); + } +#else + virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority=false, bool best_only=false){ + if(high_priority) return; + int best_score = INT_MAX; + int worst_score = 0; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it)->map; + int score = scores[node].updates; + best_score = std::min(best_score,score); + worst_score = std::max(worst_score,score); + } + int cutoff = best_only ? best_score : (best_score + (worst_score-best_score)/2); + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) + if(scores[(*it)->map].updates <= cutoff) + best.insert(*it); + } +#endif + + /** Called when done expanding a tree */ + virtual void Done() {} + }; + + /** The Proposer class proposes conjectures eagerly. These can come + from any source, including predicate abstraction, templates, or + previous solver runs. The proposed conjectures are checked + with low effort when the unwinding is expanded. + */ + + class Proposer { + public: + /** Given a node in the unwinding, propose some conjectures */ + virtual std::vector &Propose(Node *node) = 0; + virtual ~Proposer(){}; + }; + + + class Covering; // see below + + // These members represent the state of the algorithm. + + RPFP *rpfp; // the input RPFP + Reporter *reporter; // object for logging + Heuristic *heuristic; // expansion heuristic + context &ctx; // Z3 context + solver &slvr; // Z3 solver + std::vector &nodes; // Nodes of input RPFP + std::vector &edges; // Edges of input RPFP + std::vector leaves; // leaf nodes of unwinding (unused) + Unexpanded unexpanded; // unexpanded nodes + std::list candidates; // candidates for expansion + // maps children to edges in input RPFP + hash_map > edges_by_child; + // maps each node in input RPFP to its expanded instances + hash_map > insts_of_node; + // maps each node in input RPFP to all its instances + hash_map > all_of_node; + RPFP *unwinding; // the unwinding + Covering *indset; // proposed inductive subset + Counterexample cex; // counterexample + std::list to_expand; + hash_set updated_nodes; + hash_map underapprox_map; // maps underapprox nodes to the nodes they approximate + int last_decisions; + hash_set overapproxes; + std::vector proposers; + +#ifdef BOUNDED + struct Counter { + int val; + Counter(){val = 0;} + }; + typedef std::map NodeToCounter; + hash_map back_edges; // counts of back edges +#endif + + /** Solve the problem. */ + virtual bool Solve(){ + reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); +#ifndef LOCALIZE_CONJECTURES + heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); +#else + heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp)) + : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); +#endif + 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(); +#ifndef TOP_DOWN + CreateInitialUnwinding(); +#else + CreateLeaves(); + for(unsigned i = 0; i < leaves.size(); i++) + if(!SatisfyUpperBound(leaves[i])) + return false; +#endif + StratifiedLeafCount = -1; + timer_start("SolveMain"); + bool res = SolveMain(); // does the actual work + timer_stop("SolveMain"); + // print_profile(std::cout); + delete indset; + delete heuristic; + // 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 + } + +#if 0 + virtual void Restart(RPFP *_rpfp){ + rpfp = _rpfp; + delete unwinding; + nodes = _rpfp->nodes; + edges = _rpfp->edges; + leaves.clear(); + unexpanded.clear(); // unexpanded nodes + candidates.clear(); // candidates for expansion + edges_by_child.clear(); + insts_of_node.clear(); + all_of_node.clear(); + to_expand.clear(); + } +#endif + + virtual void LearnFrom(Solver *other_solver){ + // get the counterexample as a guide + cex.swap(other_solver->GetCounterexample()); + + // propose conjectures based on old unwinding + Duality *old_duality = dynamic_cast(other_solver); + if(old_duality) + proposers.push_back(new HistoryProposer(old_duality,this)); + } + + /** Return a reference to the counterexample */ + virtual Counterexample &GetCounterexample(){ + return cex; + } + + // options + bool FullExpand; // do not use partial expansion of derivation tree + bool NoConj; // do not use conjectures (no forced covering) + bool FeasibleEdges; // use only feasible edges in unwinding + bool UseUnderapprox; // use underapproximations + bool Report; // spew on stdout + bool StratifiedInlining; // Do stratified inlining as preprocessing step + int RecursionBound; // Recursion bound for bounded verification + bool BatchExpand; + + bool SetBoolOption(bool &opt, const std::string &value){ + if(value == "0") { + opt = false; + return true; + } + if(value == "1") { + opt = true; + return true; + } + return false; + } + + bool SetIntOption(int &opt, const std::string &value){ + opt = atoi(value.c_str()); + return true; + } + + /** Set options (not currently used) */ + virtual bool SetOption(const std::string &option, const std::string &value){ + if(option == "full_expand"){ + return SetBoolOption(FullExpand,value); + } + if(option == "no_conj"){ + return SetBoolOption(NoConj,value); + } + if(option == "feasible_edges"){ + return SetBoolOption(FeasibleEdges,value); + } + if(option == "use_underapprox"){ + return SetBoolOption(UseUnderapprox,value); + } + if(option == "report"){ + return SetBoolOption(Report,value); + } + if(option == "stratified_inlining"){ + return SetBoolOption(StratifiedInlining,value); + } + if(option == "batch_expand"){ + return SetBoolOption(BatchExpand,value); + } + if(option == "recursion_bound"){ + return SetIntOption(RecursionBound,value); + } + return false; + } + + /** Create an instance of a node in the unwinding. Set its + annotation to true, and mark it unexpanded. */ + Node* CreateNodeInstance(Node *node, int number = 0){ + RPFP::Node *inst = unwinding->CloneNode(node); + inst->Annotation.SetFull(); + if(number < 0) inst->number = number; + unexpanded.insert(inst); + all_of_node[node].push_back(inst); + return inst; + } + + /** Create an instance of an edge in the unwinding, with given + parent and children. */ + void CreateEdgeInstance(Edge *edge, Node *parent, const std::vector &children){ + RPFP::Edge *inst = unwinding->CreateEdge(parent,edge->F,children); + inst->map = edge; + } + + void MakeLeaf(Node *node, bool do_not_expand = false){ + node->Annotation.SetEmpty(); + Edge *e = unwinding->CreateLowerBoundEdge(node); +#ifdef TOP_DOWN + node->Annotation.SetFull(); // allow this node to cover others +#endif + if(StratifiedInlining) + node->Annotation.SetFull(); // allow this node to cover others + else + updated_nodes.insert(node); + e->map = 0; + reporter->Extend(node); +#ifdef EARLY_EXPAND + if(!do_not_expand) + TryExpandNode(node); +#endif + // e->F.SetEmpty(); + } + + void MakeOverapprox(Node *node){ + node->Annotation.SetFull(); + Edge *e = unwinding->CreateLowerBoundEdge(node); + overapproxes.insert(node); + e->map = 0; + } + + /** We start the unwinding with leaves that under-approximate + each relation with false. */ + void CreateLeaves(){ + unexpanded.clear(); + leaves.clear(); + for(unsigned i = 0; i < nodes.size(); i++){ + RPFP::Node *node = CreateNodeInstance(nodes[i]); + if(0 && nodes[i]->Outgoing->Children.size() == 0) + CreateEdgeInstance(nodes[i]->Outgoing,node,std::vector()); + else { + if(!StratifiedInlining) + MakeLeaf(node); + else { + MakeOverapprox(node); + LeafMap[nodes[i]] = node; + } + } + leaves.push_back(node); + } + } + + /** Create the map from children to edges in the input RPFP. This + is used to generate candidates for expansion. */ + void CreateEdgesByChildMap(){ + edges_by_child.clear(); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *e = edges[i]; + std::set done; + for(unsigned j = 0; j < e->Children.size(); j++){ + Node *c = e->Children[j]; + if(done.find(c) == done.end()) // avoid duplicates + edges_by_child[c].push_back(e); + done.insert(c); + } + } + } + + void NullaryCandidates(){ + for(unsigned i = 0; i < edges.size(); i++){ + RPFP::Edge *edge = edges[i]; + if(edge->Children.size() == 0){ + Candidate cand; + cand.edge = edge; + candidates.push_back(cand); + } + } + } + + void InstantiateAllEdges(){ + hash_map leaf_map; + for(unsigned i = 0; i < leaves.size(); i++){ + leaf_map[leaves[i]->map] = leaves[i]; + insts_of_node[leaves[i]->map].push_back(leaves[i]); + } + unexpanded.clear(); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *edge = edges[i]; + Candidate c; c.edge = edge; + c.Children.resize(edge->Children.size()); + for(unsigned j = 0; j < c.Children.size(); j++) + c.Children[j] = leaf_map[edge->Children[j]]; + 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); + for(unsigned i = 0; i < leaves.size(); i++){ + std::vector &foo = insts_of_node[leaves[i]->map]; + foo.erase(foo.begin()); + } + } + + bool ProducedBySI(Edge *edge, std::vector &children){ + if(LeafMap.find(edge->Parent) == LeafMap.end()) return false; + Node *other = LeafMap[edge->Parent]; + if(other->Outgoing->map != edge) return false; + std::vector &ochs = other->Outgoing->Children; + for(unsigned i = 0; i < children.size(); i++) + if(ochs[i] != children[i]) return false; + return true; + } + + /** Add a candidate for expansion, but not if Stratified inlining has already + produced it */ + + void AddCandidate(Edge *edge, std::vector &children){ + if(StratifiedInlining && ProducedBySI(edge,children)) + return; + candidates.push_back(Candidate()); + candidates.back().edge = edge; + candidates.back().Children = children; + } + + /** Generate candidates for expansion, given a vector of candidate + sets for each argument position. This recursively produces + the cross product. + */ + void GenCandidatesRec(int pos, Edge *edge, + const std::vector > &vec, + std::vector &children){ + if(pos == (int)vec.size()){ + AddCandidate(edge,children); + } + else { + for(unsigned i = 0; i < vec[pos].size(); i++){ + children[pos] = vec[pos][i]; + GenCandidatesRec(pos+1,edge,vec,children); + } + } + } + + /** Setup for above recursion. */ + void GenCandidates(int pos, Edge *edge, + const std::vector > &vec){ + std::vector children(vec.size()); + GenCandidatesRec(0,edge,vec,children); + } + + /** Expand a node. We find all the candidates for expansion using + this node and other already expanded nodes. This is a little + tricky, since a node may be used for multiple argument + positions of an edge, and we don't want to produce duplicates. + */ + +#ifndef NEW_EXPAND + void ExpandNode(Node *node){ + std::vector &nedges = edges_by_child[node->map]; + for(unsigned i = 0; i < nedges.size(); i++){ + Edge *edge = nedges[i]; + for(unsigned npos = 0; npos < edge->Children.size(); ++npos){ + if(edge->Children[npos] == node->map){ + std::vector > vec(edge->Children.size()); + vec[npos].push_back(node); + for(unsigned j = 0; j < edge->Children.size(); j++){ + if(j != npos){ + std::vector &insts = insts_of_node[edge->Children[j]]; + for(unsigned k = 0; k < insts.size(); k++) + if(indset->Candidate(insts[k])) + vec[j].push_back(insts[k]); + } + if(j < npos && edge->Children[j] == node->map) + vec[j].push_back(node); + } + GenCandidates(0,edge,vec); + } + } + } + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + } +#else + /** If the current proposed solution is not inductive, + use the induction failure to generate candidates for extension. */ + void ExpandNode(Node *node){ + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + timer_start("GenCandIndFailUsing"); + std::vector &nedges = edges_by_child[node->map]; + for(unsigned i = 0; i < nedges.size(); i++){ + Edge *edge = nedges[i]; + slvr.push(); + RPFP *checker = new RPFP(rpfp->ls); + Node *root = CheckerJustForEdge(edge,checker,true); + if(root){ + expr using_cond = ctx.bool_val(false); + for(unsigned npos = 0; npos < edge->Children.size(); ++npos) + if(edge->Children[npos] == node->map) + using_cond = using_cond || checker->Localize(root->Outgoing->Children[npos]->Outgoing,NodeMarker(node)); + slvr.add(using_cond); + if(checker->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,checker,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + } + slvr.pop(1); + delete checker; + } + timer_stop("GenCandIndFailUsing"); + } +#endif + + void ExpandNodeFromOther(Node *node, Node *other){ + std::vector &in = other->Incoming; + for(unsigned i = 0; i < in.size(); i++){ + Edge *edge = in[i]; + Candidate cand; + cand.edge = edge->map; + cand.Children = edge->Children; + for(unsigned j = 0; j < cand.Children.size(); j++) + if(cand.Children[j] == other) + cand.Children[j] = node; + candidates.push_front(cand); + } + // unexpanded.erase(node); + // insts_of_node[node->map].push_back(node); + } + + /** Expand a node based on some uncovered node it dominates. + This pushes cahdidates onto the *front* of the candidate + queue, so these expansions are done depth-first. */ + bool ExpandNodeFromCoverFail(Node *node){ + if(!node->Outgoing || node->Outgoing->Children.size() == 0) + return false; + Node *other = indset->GetSimilarNode(node); + if(!other) + return false; +#ifdef UNDERAPPROX_NODES + Node *under_node = CreateUnderapproxNode(node); + underapprox_map[under_node] = node; + indset->CoverByNode(node,under_node); + ExpandNodeFromOther(under_node,other); + ExpandNode(under_node); +#else + ExpandNodeFromOther(node,other); + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); +#endif + return true; + } + + + /** Make a boolean variable to act as a "marker" for a node. */ + expr NodeMarker(Node *node){ + std::string name = std::string("@m_") + string_of_int(node->number); + return ctx.constant(name.c_str(),ctx.bool_sort()); + } + + /** Union the annotation of dst into src. If with_markers is + true, we conjoin the annotation formula of dst with its + marker. This allows us to discover which disjunct is + true in a satisfying assignment. */ + void UnionAnnotations(RPFP::Transformer &dst, Node *src, bool with_markers = false){ + if(!with_markers) + dst.UnionWith(src->Annotation); + else { + RPFP::Transformer t = src->Annotation; + t.Formula = t.Formula && NodeMarker(src); + dst.UnionWith(t); + } + } + + void GenNodeSolutionFromIndSet(Node *node, RPFP::Transformer &annot, bool with_markers = false){ + annot.SetEmpty(); + std::vector &insts = insts_of_node[node]; + for(unsigned j = 0; j < insts.size(); j++) + if(indset->Contains(insts[j])) + UnionAnnotations(annot,insts[j],with_markers); + annot.Simplify(); + } + + /** Generate a proposed solution of the input RPFP from + the unwinding, by unioning the instances of each node. */ + void GenSolutionFromIndSet(bool with_markers = false){ + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + GenNodeSolutionFromIndSet(node,node->Annotation,with_markers); + } + } + +#ifdef BOUNDED + bool NodePastRecursionBound(Node *node){ + if(RecursionBound < 0) return false; + NodeToCounter &backs = back_edges[node]; + for(NodeToCounter::iterator it = backs.begin(), en = backs.end(); it != en; ++it){ + if(it->second.val > RecursionBound) + return true; + } + return false; + } +#endif + + /** Test whether a given extension candidate actually represents + an induction failure. Right now we approximate this: if + the resulting node in the unwinding could be labeled false, + it clearly is not an induction failure. */ + + bool CandidateFeasible(const Candidate &cand){ + if(!FeasibleEdges) return true; + timer_start("CandidateFeasible"); + RPFP *checker = new RPFP(rpfp->ls); + // std::cout << "Checking feasibility of extension " << cand.edge->Parent->number << std::endl; + checker->Push(); + std::vector chs(cand.Children.size()); + Node *root = checker->CloneNode(cand.edge->Parent); +#ifdef BOUNDED + for(unsigned i = 0; i < cand.Children.size(); i++) + if(NodePastRecursionBound(cand.Children[i])){ + timer_stop("CandidateFeasible"); + return false; + } +#endif +#ifdef NEW_CAND_SEL + GenNodeSolutionFromIndSet(cand.edge->Parent,root->Bound); +#else + root->Bound.SetEmpty(); +#endif + checker->AssertNode(root); + for(unsigned i = 0; i < cand.Children.size(); i++) + chs[i] = checker->CloneNode(cand.Children[i]); + Edge *e = checker->CreateEdge(root,cand.edge->F,chs); + checker->AssertEdge(e,0,true); + // std::cout << "Checking SAT: " << e->dual << std::endl; + bool res = checker->Check(root) != unsat; + // std::cout << "Result: " << res << std::endl; + if(!res)reporter->Reject(cand.edge,cand.Children); + checker->Pop(1); + delete checker; + timer_stop("CandidateFeasible"); + return res; + } + + + hash_map TopoSort; + int TopoSortCounter; + std::vector SortedEdges; + + void DoTopoSortRec(Node *node){ + if(TopoSort.find(node) != TopoSort.end()) + return; + TopoSort[node] = INT_MAX; // just to break cycles + Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge + if(edge){ + std::vector &chs = edge->Children; + for(unsigned i = 0; i < chs.size(); i++) + DoTopoSortRec(chs[i]); + } + TopoSort[node] = TopoSortCounter++; + SortedEdges.push_back(edge); + } + + void DoTopoSort(){ + TopoSort.clear(); + SortedEdges.clear(); + TopoSortCounter = 0; + for(unsigned i = 0; i < nodes.size(); i++) + DoTopoSortRec(nodes[i]); + } + + + int StratifiedLeafCount; + +#ifdef NEW_STRATIFIED_INLINING + + /** Stratified inlining builds an initial layered unwinding before + switching to the LAWI strategy. Currently the number of layers + is one. Only nodes that are the targets of back edges are + consider to be leaves. This assumes we have already computed a + topological sort. + */ + + bool DoStratifiedInlining(){ + DoTopoSort(); + int depth = 1; // TODO: make this an option + std::vector > unfolding_levels(depth+1); + for(int level = 1; level <= depth; level++) + for(unsigned i = 0; i < SortedEdges.size(); i++){ + Edge *edge = SortedEdges[i]; + Node *parent = edge->Parent; + std::vector &chs = edge->Children; + std::vector my_chs(chs.size()); + for(unsigned j = 0; j < chs.size(); j++){ + Node *child = chs[j]; + int ch_level = TopoSort[child] >= TopoSort[parent] ? level-1 : level; + if(unfolding_levels[ch_level].find(child) == unfolding_levels[ch_level].end()){ + if(ch_level == 0) + unfolding_levels[0][child] = CreateLeaf(child); + else + throw InternalError("in levelized unwinding"); + } + my_chs[j] = unfolding_levels[ch_level][child]; + } + Candidate cand; cand.edge = edge; cand.Children = my_chs; + Node *new_node; + bool ok = Extend(cand,new_node); + MarkExpanded(new_node); // we don't expand here -- just mark it done + if(!ok) return false; // got a counterexample + unfolding_levels[level][parent] = new_node; + } + return true; + } + + Node *CreateLeaf(Node *node){ + RPFP::Node *nchild = CreateNodeInstance(node); + MakeLeaf(nchild, /* do_not_expand = */ true); + nchild->Annotation.SetEmpty(); + return nchild; + } + + void MarkExpanded(Node *node){ + if(unexpanded.find(node) != unexpanded.end()){ + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + } + } + +#else + + /** In stratified inlining, we build the unwinding from the bottom + down, trying to satisfy the node bounds. We do this as a pre-pass, + limiting the expansion. If we get a counterexample, we are done, + else we continue as usual expanding the unwinding upward. + */ + + bool DoStratifiedInlining(){ + timer_start("StratifiedInlining"); + DoTopoSort(); + for(unsigned i = 0; i < leaves.size(); i++){ + Node *node = leaves[i]; + bool res = SatisfyUpperBound(node); + if(!res){ + timer_stop("StratifiedInlining"); + return false; + } + } + // don't leave any dangling nodes! +#ifndef EFFORT_BOUNDED_STRAT + for(unsigned i = 0; i < leaves.size(); i++) + if(!leaves[i]->Outgoing) + MakeLeaf(leaves[i],true); +#endif + timer_stop("StratifiedInlining"); + return true; + } + +#endif + + /** Here, we do the downward expansion for stratified inlining */ + + hash_map LeafMap, StratifiedLeafMap; + + Edge *GetNodeOutgoing(Node *node, int last_decs = 0){ + if(overapproxes.find(node) == overapproxes.end()) return node->Outgoing; /* already expanded */ + overapproxes.erase(node); +#ifdef EFFORT_BOUNDED_STRAT + if(last_decs > 5000){ + // RPFP::Transformer save = node->Annotation; + node->Annotation.SetEmpty(); + Edge *e = unwinding->CreateLowerBoundEdge(node); + // node->Annotation = save; + insts_of_node[node->map].push_back(node); + // std::cout << "made leaf: " << node->number << std::endl; + return e; + } +#endif + Edge *edge = node->map->Outgoing; + std::vector &chs = edge->Children; + + // make sure we don't create a covered node in this process! + + for(unsigned i = 0; i < chs.size(); i++){ + Node *child = chs[i]; + if(TopoSort[child] < TopoSort[node->map]){ + Node *leaf = LeafMap[child]; + if(!indset->Contains(leaf)){ + node->Outgoing->F.Formula = ctx.bool_val(false); // make this a proper leaf, else bogus cex + return node->Outgoing; + } + } + } + + std::vector nchs(chs.size()); + for(unsigned i = 0; i < chs.size(); i++){ + Node *child = chs[i]; + if(TopoSort[child] < TopoSort[node->map]){ + Node *leaf = LeafMap[child]; + nchs[i] = leaf; + if(unexpanded.find(leaf) != unexpanded.end()){ + unexpanded.erase(leaf); + insts_of_node[child].push_back(leaf); + } + } + else { + if(StratifiedLeafMap.find(child) == StratifiedLeafMap.end()){ + RPFP::Node *nchild = CreateNodeInstance(child,StratifiedLeafCount--); + MakeLeaf(nchild); + nchild->Annotation.SetEmpty(); + StratifiedLeafMap[child] = nchild; + indset->SetDominated(nchild); + } + nchs[i] = StratifiedLeafMap[child]; + } + } + CreateEdgeInstance(edge,node,nchs); + reporter->Extend(node); + return node->Outgoing; + } + + void SetHeuristicOldNode(Node *node){ + LocalHeuristic *h = dynamic_cast(heuristic); + if(h) + h->SetOldNode(node); + } + + /** This does the actual solving work. We try to generate + candidates for extension. If we succed, we extend the + unwinding. If we fail, we have a solution. */ + bool SolveMain(){ + if(StratifiedInlining && !DoStratifiedInlining()) + return false; +#ifdef BOUNDED + DoTopoSort(); +#endif + while(true){ + timer_start("ProduceCandidatesForExtension"); + ProduceCandidatesForExtension(); + timer_stop("ProduceCandidatesForExtension"); + if(candidates.empty()){ + GenSolutionFromIndSet(); + return true; + } + Candidate cand = candidates.front(); + candidates.pop_front(); + if(CandidateFeasible(cand)){ + Node *new_node; + if(!Extend(cand,new_node)) + return false; +#ifdef EARLY_EXPAND + TryExpandNode(new_node); +#endif + } + } + } + + // hack: put something local into the underapproximation formula + // without this, interpolants can be pretty bad + void AddThing(expr &conj){ + std::string name = "@thing"; + expr thing = ctx.constant(name.c_str(),ctx.bool_sort()); + if(conj.is_app() && conj.decl().get_decl_kind() == And){ + std::vector conjs(conj.num_args()+1); + for(unsigned i = 0; i+1 < conjs.size(); i++) + conjs[i] = conj.arg(i); + conjs[conjs.size()-1] = thing; + conj = rpfp->conjoin(conjs); + } + } + + + Node *CreateUnderapproxNode(Node *node){ + // cex.get_tree()->ComputeUnderapprox(cex.get_root(),0); + RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */); + 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 + back_edges[under_node] = back_edges[node]; + e->map = 0; + reporter->Extend(under_node); + return under_node; + } + + /** Try to prove a conjecture about a node. If successful + update the unwinding annotation appropriately. */ + bool ProveConjecture(Node *node, const RPFP::Transformer &t,Node *other = 0, Counterexample *_cex = 0){ + reporter->Conjecture(node,t); + timer_start("ProveConjecture"); + RPFP::Transformer save = node->Bound; + node->Bound.IntersectWith(t); + +#ifndef LOCALIZE_CONJECTURES + bool ok = SatisfyUpperBound(node); +#else + SetHeuristicOldNode(other); + bool ok = SatisfyUpperBound(node); + SetHeuristicOldNode(0); +#endif + + if(ok){ + timer_stop("ProveConjecture"); + return true; + } +#ifdef UNDERAPPROX_NODES + if(UseUnderapprox && last_decisions > 500){ + std::cout << "making an underapprox\n"; + ExpandNodeFromCoverFail(node); + } +#endif + 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; + } + + /** If a node is part of the inductive subset, expand it. + We ask the inductive subset to exclude the node if possible. + */ + void TryExpandNode(RPFP::Node *node){ + if(indset->Close(node)) return; + if(!NoConj && indset->Conjecture(node)){ +#ifdef UNDERAPPROX_NODES + /* TODO: temporary fix. this prevents an infinite loop in case + the node is covered by multiple others. This should be + removed when covering by a set is implemented. + */ + if(indset->Contains(node)){ + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + } +#endif + return; + } +#ifdef UNDERAPPROX_NODES + if(!indset->Contains(node)) + return; // could be covered by an underapprox node +#endif + indset->Add(node); +#if defined(CANDS_FROM_COVER_FAIL) && !defined(UNDERAPPROX_NODES) + if(ExpandNodeFromCoverFail(node)) + return; +#endif + ExpandNode(node); + } + + /** Make the conjunction of markers for all (expanded) instances of + a node in the input RPFP. */ + expr AllNodeMarkers(Node *node){ + expr res = ctx.bool_val(true); + std::vector &insts = insts_of_node[node]; + for(int k = insts.size()-1; k >= 0; k--) + res = res && NodeMarker(insts[k]); + return res; + } + + void RuleOutNodesPastBound(Node *node, RPFP::Transformer &t){ +#ifdef BOUNDED + if(RecursionBound < 0)return; + std::vector &insts = insts_of_node[node]; + for(unsigned i = 0; i < insts.size(); i++) + if(NodePastRecursionBound(insts[i])) + t.Formula = t.Formula && !NodeMarker(insts[i]); +#endif + } + + + void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction){ +#ifdef BOUNDED + if(RecursionBound >= 0 && NodePastRecursionBound(node)) + return; +#endif + RPFP::Transformer temp = node->Annotation; + expr marker = NodeMarker(node); + temp.Formula = (!marker || temp.Formula); + annot.IntersectWith(temp); + marker_disjunction = marker_disjunction || marker; + } + + bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false){ + bool res = false; + annot.SetFull(); + expr marker_disjunction = ctx.bool_val(false); + std::vector &insts = expanded_only ? insts_of_node[node] : all_of_node[node]; + for(unsigned j = 0; j < insts.size(); j++){ + Node *node = insts[j]; + if(indset->Contains(insts[j])){ + GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction); res = true; + } + } + annot.Formula = annot.Formula && marker_disjunction; + annot.Simplify(); + return res; + } + + /** Make a checker to determine if an edge in the input RPFP + is satisfied. */ + Node *CheckerJustForEdge(Edge *edge, RPFP *checker, bool expanded_only = false){ + Node *root = checker->CloneNode(edge->Parent); + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); + if(root->Bound.IsFull()) + return 0; + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + if(!GenNodeSolutionWithMarkers(oc,nc->Annotation,expanded_only)) + return 0; + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } + +#ifndef MINIMIZE_CANDIDATES_HARDER + +#if 0 + /** Make a checker to detheermine if an edge in the input RPFP + is satisfied. */ + Node *CheckerForEdge(Edge *edge, RPFP *checker){ + Node *root = checker->CloneNode(edge->Parent); + root->Bound = edge->Parent->Annotation; + root->Bound.Formula = (!AllNodeMarkers(edge->Parent)) || root->Bound.Formula; + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + nc->Annotation = oc->Annotation; + RuleOutNodesPastBound(oc,nc->Annotation); + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } + +#else + /** Make a checker to determine if an edge in the input RPFP + is satisfied. */ + Node *CheckerForEdge(Edge *edge, RPFP *checker){ + Node *root = checker->CloneNode(edge->Parent); + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); +#if 0 + if(root->Bound.IsFull()) + return = 0; +#endif + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } +#endif + + /** If an edge is not satisfied, produce an extension candidate + using instances of its children that violate the parent annotation. + We find these using the marker predicates. */ + void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ + candidate.edge = edge; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *node = root->Outgoing->Children[j]; + Edge *lb = node->Outgoing; + std::vector &insts = insts_of_node[edge->Children[j]]; +#ifndef MINIMIZE_CANDIDATES + for(int k = insts.size()-1; k >= 0; k--) +#else + for(unsigned k = 0; k < insts.size(); k++) +#endif + { + Node *inst = insts[k]; + if(indset->Contains(inst)){ + if(checker->Empty(node) || + eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst)),ctx.bool_val(true))){ + candidate.Children.push_back(inst); + goto next_child; + } + } + } + throw InternalError("No candidate from induction failure"); + next_child:; + } + } +#else + + + /** Make a checker to determine if an edge in the input RPFP + is satisfied. */ + Node *CheckerForEdge(Edge *edge, RPFP *checker){ + Node *root = checker->CloneNode(edge->Parent); + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); + if(root->Bound.IsFull()) + return = 0; + checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + Edge *e = checker->CreateLowerBoundEdge(nc); + checker->AssertEdge(e); + cs.push_back(nc); + } + checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); + return root; + } + + /** If an edge is not satisfied, produce an extension candidate + using instances of its children that violate the parent annotation. + We find these using the marker predicates. */ + void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ + candidate.edge = edge; + std::vector assumps; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Edge *lb = root->Outgoing->Children[j]->Outgoing; + std::vector &insts = insts_of_node[edge->Children[j]]; + for(unsigned k = 0; k < insts.size(); k++) + { + Node *inst = insts[k]; + expr marker = NodeMarker(inst); + if(indset->Contains(inst)){ + if(checker->Empty(lb->Parent) || + eq(checker->Eval(lb,marker),ctx.bool_val(true))){ + candidate.Children.push_back(inst); + assumps.push_back(checker->Localize(lb,marker)); + goto next_child; + } + assumps.push_back(checker->Localize(lb,marker)); + if(checker->CheckUpdateModel(root,assumps) != unsat){ + candidate.Children.push_back(inst); + goto next_child; + } + assumps.pop_back(); + } + } + throw InternalError("No candidate from induction failure"); + next_child:; + } + } + +#endif + + + Node *CheckerForEdgeClone(Edge *edge, RPFP_caching *checker){ + Edge *gen_cands_edge = checker->GetEdgeClone(edge); + Node *root = gen_cands_edge->Parent; + root->Outgoing = gen_cands_edge; + GenNodeSolutionFromIndSet(edge->Parent, root->Bound); +#if 0 + if(root->Bound.IsFull()) + return = 0; +#endif + checker->AssertNode(root); + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = gen_cands_edge->Children[j]; + GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + } + checker->AssertEdge(gen_cands_edge,1,true); + return root; + } + + /** If the current proposed solution is not inductive, + use the induction failure to generate candidates for extension. */ + void GenCandidatesFromInductionFailure(bool full_scan = false){ + timer_start("GenCandIndFail"); + GenSolutionFromIndSet(true /* add markers */); + for(unsigned i = 0; i < edges.size(); i++){ + Edge *edge = edges[i]; + if(!full_scan && updated_nodes.find(edge->Parent) == updated_nodes.end()) + continue; +#ifndef USE_NEW_GEN_CANDS + slvr.push(); + RPFP *checker = new RPFP(rpfp->ls); + Node *root = CheckerForEdge(edge,checker); + if(checker->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,checker,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + slvr.pop(1); + delete checker; +#else + RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); + gen_cands_rpfp->Push(); + Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); + if(gen_cands_rpfp->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,gen_cands_rpfp,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + gen_cands_rpfp->Pop(1); +#endif + } + updated_nodes.clear(); + timer_stop("GenCandIndFail"); +#ifdef CHECK_CANDS_FROM_IND_SET + for(std::list::iterator it = candidates.begin(), en = candidates.end(); it != en; ++it){ + if(!CandidateFeasible(*it)) + throw "produced infeasible candidate"; + } +#endif + if(!full_scan && candidates.empty()){ + reporter->Message("No candidates from updates. Trying full scan."); + GenCandidatesFromInductionFailure(true); + } + } + +#ifdef CANDS_FROM_UPDATES + /** If the given edge is not inductive in the current proposed solution, + use the induction failure to generate candidates for extension. */ + void GenCandidatesFromEdgeInductionFailure(RPFP::Edge *edge){ + GenSolutionFromIndSet(true /* add markers */); + for(unsigned i = 0; i < edges.size(); i++){ + slvr.push(); + Edge *edge = edges[i]; + RPFP *checker = new RPFP(rpfp->ls); + Node *root = CheckerForEdge(edge,checker); + if(checker->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,checker,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + slvr.pop(1); + delete checker; + } + } +#endif + + /** Find the unexpanded nodes in the inductive subset. */ + void FindNodesToExpand(){ + for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it){ + Node *node = *it; + if(indset->Candidate(node)) + to_expand.push_back(node); + } + } + + /** Try to create some extension candidates from the unexpanded + nodes. */ + void ProduceSomeCandidates(){ + while(candidates.empty() && !to_expand.empty()){ + Node *node = to_expand.front(); + to_expand.pop_front(); + TryExpandNode(node); + } + } + + std::list postponed_candidates; + + /** Try to produce some extension candidates, first from unexpanded + nides, and if this fails, from induction failure. */ + void ProduceCandidatesForExtension(){ + if(candidates.empty()) + ProduceSomeCandidates(); + while(candidates.empty()){ + FindNodesToExpand(); + if(to_expand.empty()) break; + ProduceSomeCandidates(); + } + if(candidates.empty()){ +#ifdef DEPTH_FIRST_EXPAND + if(postponed_candidates.empty()){ + GenCandidatesFromInductionFailure(); + postponed_candidates.swap(candidates); + } + if(!postponed_candidates.empty()){ + candidates.push_back(postponed_candidates.front()); + postponed_candidates.pop_front(); + } +#else + GenCandidatesFromInductionFailure(); +#endif + } + } + + 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(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. */ + void UpdateWithInterpolant(Node *node, RPFP *tree, Node *top){ + if(top->Outgoing) + for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) + UpdateWithInterpolant(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); + UpdateNodeToNode(node, top); + heuristic->Update(node); + } + + /** Update unwinding lower bounds, using a counterexample. */ + + void UpdateWithCounterexample(Node *node, RPFP *tree, Node *top){ + if(top->Outgoing) + for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) + UpdateWithCounterexample(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); + if(!top->Underapprox.SubsetEq(node->Underapprox)){ + reporter->UpdateUnderapprox(node,top->Underapprox); + // indset->Update(node,top->Annotation); + node->Underapprox.UnionWith(top->Underapprox); + heuristic->Update(node); + } + } + + /** Try to update the unwinding to satisfy the upper bound of a + node. */ + bool SatisfyUpperBound(Node *node){ + if(node->Bound.IsFull()) return true; +#ifdef PROPAGATE_BEFORE_CHECK + Propagate(); +#endif + reporter->Bound(node); + int start_decs = rpfp->CumulativeDecisions(); + DerivationTree *dtp = new DerivationTreeSlow(this,unwinding,reporter,heuristic,FullExpand); + DerivationTree &dt = *dtp; + bool res = dt.Derive(unwinding,node,UseUnderapprox); + int end_decs = rpfp->CumulativeDecisions(); + // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; + last_decisions = end_decs - start_decs; + if(res){ + cex.set(dt.tree,dt.top); // note tree is now owned by cex + if(UseUnderapprox){ + UpdateWithCounterexample(node,dt.tree,dt.top); + } + } + else { + UpdateWithInterpolant(node,dt.tree,dt.top); + delete dt.tree; + } + delete dtp; + return !res; + } + + /* For a given nod in the unwinding, get conjectures from the + proposers and check them locally. Update the node with any true + conjectures. + */ + + void DoEagerDeduction(Node *node){ + for(unsigned i = 0; i < proposers.size(); i++){ + const std::vector &conjectures = proposers[i]->Propose(node); + for(unsigned j = 0; j < conjectures.size(); j++){ + const RPFP::Transformer &conjecture = conjectures[j]; + RPFP::Transformer bound(conjecture); + std::vector conj_vec; + unwinding->CollectConjuncts(bound.Formula,conj_vec); + for(unsigned k = 0; k < conj_vec.size(); k++){ + bound.Formula = conj_vec[k]; + if(CheckEdgeCaching(node->Outgoing,bound) == unsat) + Update(node,bound, /* eager = */ true); + //else + //std::cout << "conjecture failed\n"; + } + } + } + } + + + check_result CheckEdge(RPFP *checker, Edge *edge){ + Node *root = edge->Parent; + checker->Push(); + checker->AssertNode(root); + checker->AssertEdge(edge,1,true); + check_result res = checker->Check(root); + checker->Pop(1); + return res; + } + + check_result CheckEdgeCaching(Edge *unwinding_edge, const RPFP::Transformer &bound){ + + // use a dedicated solver for this edge + // TODO: can this mess be hidden somehow? + + RPFP_caching *checker = gen_cands_rpfp; // TODO: a good choice? + Edge *edge = unwinding_edge->map; // get the edge in the original RPFP + RPFP_caching::scoped_solver_for_edge ssfe(checker,edge,true /* models */, true /*axioms*/); + Edge *checker_edge = checker->GetEdgeClone(edge); + + // copy the annotations and bound to the clone + Node *root = checker_edge->Parent; + root->Bound = bound; + for(unsigned j = 0; j < checker_edge->Children.size(); j++){ + Node *oc = unwinding_edge->Children[j]; + Node *nc = checker_edge->Children[j]; + nc->Annotation = oc->Annotation; + } + + return CheckEdge(checker,checker_edge); + } + + + /* If the counterexample derivation is partial due to + use of underapproximations, complete it. */ + + void BuildFullCex(Node *node){ + 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"; + cex.set(dt.tree,dt.top); + } + + void UpdateBackEdges(Node *node){ +#ifdef BOUNDED + std::vector &chs = node->Outgoing->Children; + for(unsigned i = 0; i < chs.size(); i++){ + Node *child = chs[i]; + bool is_back = TopoSort[child->map] >= TopoSort[node->map]; + NodeToCounter &nov = back_edges[node]; + NodeToCounter chv = back_edges[child]; + if(is_back) + chv[child->map].val++; + for(NodeToCounter::iterator it = chv.begin(), en = chv.end(); it != en; ++it){ + Node *back = it->first; + Counter &c = nov[back]; + c.val = std::max(c.val,it->second.val); + } + } +#endif + } + + /** Extend the unwinding, keeping it solved. */ + bool Extend(Candidate &cand, Node *&node){ + timer_start("Extend"); + node = CreateNodeInstance(cand.edge->Parent); + CreateEdgeInstance(cand.edge,node,cand.Children); + UpdateBackEdges(node); + reporter->Extend(node); + DoEagerDeduction(node); // first be eager... + bool res = SatisfyUpperBound(node); // then be lazy + if(res) indset->CloseDescendants(node); + else { +#ifdef UNDERAPPROX_NODES + ExpandUnderapproxNodes(cex.get_tree(), cex.get_root()); +#endif + if(UseUnderapprox) BuildFullCex(node); + timer_stop("Extend"); + return res; + } + timer_stop("Extend"); + return res; + } + + void ExpandUnderapproxNodes(RPFP *tree, Node *root){ + Node *node = root->map; + if(underapprox_map.find(node) != underapprox_map.end()){ + RPFP::Transformer cnst = root->Annotation; + tree->EvalNodeAsConstraint(root, cnst); + cnst.Complement(); + Node *orig = underapprox_map[node]; + RPFP::Transformer save = orig->Bound; + orig->Bound = cnst; + DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); + bool res = dt.Derive(unwinding,orig,UseUnderapprox,true,tree); + if(!res){ + UpdateWithInterpolant(orig,dt.tree,dt.top); + throw "bogus underapprox!"; + } + ExpandUnderapproxNodes(tree,dt.top); + } + else if(root->Outgoing){ + std::vector &chs = root->Outgoing->Children; + for(unsigned i = 0; i < chs.size(); i++) + ExpandUnderapproxNodes(tree,chs[i]); + } + } + + // Propagate conjuncts up the unwinding + void Propagate(){ + reporter->Message("beginning propagation"); + timer_start("Propagate"); + std::vector sorted_nodes = unwinding->nodes; + std::sort(sorted_nodes.begin(),sorted_nodes.end(),std::less()); // sorts by sequence number + hash_map > facts; + for(unsigned i = 0; i < sorted_nodes.size(); i++){ + Node *node = sorted_nodes[i]; + std::set &node_facts = facts[node->map]; + if(!(node->Outgoing && indset->Contains(node))) + continue; + std::vector conj_vec; + unwinding->CollectConjuncts(node->Annotation.Formula,conj_vec); + std::set conjs; + std::copy(conj_vec.begin(),conj_vec.end(),std::inserter(conjs,conjs.begin())); + if(!node_facts.empty()){ + RPFP *checker = new RPFP(rpfp->ls); + slvr.push(); + Node *root = checker->CloneNode(node); + Edge *edge = node->Outgoing; + // checker->AssertNode(root); + std::vector cs; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *oc = edge->Children[j]; + Node *nc = checker->CloneNode(oc); + nc->Annotation = oc->Annotation; // is this needed? + cs.push_back(nc); + } + Edge *checker_edge = checker->CreateEdge(root,edge->F,cs); + checker->AssertEdge(checker_edge, 0, true, false); + std::vector propagated; + for(std::set ::iterator it = node_facts.begin(), en = node_facts.end(); it != en;){ + const expr &fact = *it; + if(conjs.find(fact) == conjs.end()){ + root->Bound.Formula = fact; + slvr.push(); + checker->AssertNode(root); + check_result res = checker->Check(root); + slvr.pop(); + if(res != unsat){ + std::set ::iterator victim = it; + ++it; + node_facts.erase(victim); // if it ain't true, nix it + continue; + } + propagated.push_back(fact); + } + ++it; + } + slvr.pop(); + for(unsigned i = 0; i < propagated.size(); i++){ + root->Annotation.Formula = propagated[i]; + UpdateNodeToNode(node,root); + } + delete checker; + } + for(std::set ::iterator it = conjs.begin(), en = conjs.end(); it != en; ++it){ + expr foo = *it; + node_facts.insert(foo); + } + } + timer_stop("Propagate"); + } + + /** This class represents a derivation tree. */ + class DerivationTree { + public: + + virtual ~DerivationTree(){} + + DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) + : slvr(rpfp->slvr()), + ctx(rpfp->ctx) + { + duality = _duality; + reporter = _reporter; + heuristic = _heuristic; + full_expand = _full_expand; + } + + Duality *duality; + Reporter *reporter; + Heuristic *heuristic; + solver &slvr; + context &ctx; + RPFP *tree; + RPFP::Node *top; + std::list leaves; + bool full_expand; + bool underapprox; + bool constrained; + bool false_approx; + std::vector underapprox_core; + int start_decs, last_decs; + + /* We build derivation trees in one of three modes: + + 1) In normal mode, we build the full tree without considering + underapproximations. + + 2) In underapprox mode, we use underapproximations to cut off + the tree construction. THis means the resulting tree may not + be complete. + + 3) In constrained mode, we build the full tree but use + underapproximations as upper bounds. This mode is used to + complete the partial derivation constructed in underapprox + mode. + */ + + bool Derive(RPFP *rpfp, RPFP::Node *root, bool _underapprox, bool _constrained = false, RPFP *_tree = 0){ + underapprox = _underapprox; + constrained = _constrained; + false_approx = true; + timer_start("Derive"); +#ifndef USE_CACHING_RPFP + tree = _tree ? _tree : new RPFP(rpfp->ls); +#else + RPFP::LogicSolver *cache_ls = new RPFP::iZ3LogicSolver(ctx); + cache_ls->slvr->push(); + tree = _tree ? _tree : new RPFP_caching(cache_ls); +#endif + tree->HornClauses = rpfp->HornClauses; + tree->Push(); // so we can clear out the solver later when finished + top = CreateApproximatedInstance(root); + tree->AssertNode(top); // assert the negation of the top-level spec + timer_start("Build"); + bool res = Build(); + heuristic->Done(); + timer_stop("Build"); + timer_start("Pop"); + tree->Pop(1); + timer_stop("Pop"); +#ifdef USE_CACHING_RPFP + cache_ls->slvr->pop(1); + delete cache_ls; + tree->ls = rpfp->ls; +#endif + timer_stop("Derive"); + return res; + } + +#define WITH_CHILDREN + + void InitializeApproximatedInstance(RPFP::Node *to){ + to->Annotation = to->map->Annotation; +#ifndef WITH_CHILDREN + tree->CreateLowerBoundEdge(to); +#endif + leaves.push_back(to); + } + + Node *CreateApproximatedInstance(RPFP::Node *from){ + Node *to = tree->CloneNode(from); + InitializeApproximatedInstance(to); + return to; + } + + bool CheckWithUnderapprox(){ + timer_start("CheckWithUnderapprox"); + std::vector leaves_vector(leaves.size()); + std::copy(leaves.begin(),leaves.end(),leaves_vector.begin()); + check_result res = tree->Check(top,leaves_vector); + timer_stop("CheckWithUnderapprox"); + return res != unsat; + } + + virtual bool Build(){ +#ifdef EFFORT_BOUNDED_STRAT + start_decs = tree->CumulativeDecisions(); +#endif + while(ExpandSomeNodes(true)); // do high-priority expansions + while (true) + { +#ifndef WITH_CHILDREN + timer_start("asserting leaves"); + timer_start("pushing"); + tree->Push(); + timer_stop("pushing"); + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) + tree->AssertEdge((*it)->Outgoing,1); // assert the overapproximation, and keep it past pop + timer_stop("asserting leaves"); + lbool res = tree->Solve(top, 2); // incremental solve, keep interpolants for two pops + timer_start("popping leaves"); + tree->Pop(1); + timer_stop("popping leaves"); +#else + lbool res; + if((underapprox || false_approx) && top->Outgoing && CheckWithUnderapprox()){ + if(constrained) goto expand_some_nodes; // in constrained mode, keep expanding + goto we_are_sat; // else if underapprox is sat, we stop + } + // tree->Check(top); + res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop +#endif + if (res == l_false) + return false; + + expand_some_nodes: + if(ExpandSomeNodes()) + continue; + + we_are_sat: + if(underapprox && !constrained){ + timer_start("ComputeUnderapprox"); + tree->ComputeUnderapprox(top,1); + timer_stop("ComputeUnderapprox"); + } + else { +#ifdef UNDERAPPROX_NODES +#ifndef SKIP_UNDERAPPROX_NODES + timer_start("ComputeUnderapprox"); + tree->ComputeUnderapprox(top,1); + timer_stop("ComputeUnderapprox"); +#endif +#endif + } + return true; + } + } + + virtual void ExpandNode(RPFP::Node *p){ + // tree->RemoveEdge(p->Outgoing); + Edge *ne = p->Outgoing; + if(ne) { + // reporter->Message("Recycling edge..."); + std::vector &cs = ne->Children; + for(unsigned i = 0; i < cs.size(); i++) + InitializeApproximatedInstance(cs[i]); + // ne->dual = expr(); + } + else { + Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); + std::vector &cs = edge->Children; + std::vector children(cs.size()); + for(unsigned i = 0; i < cs.size(); i++) + children[i] = CreateApproximatedInstance(cs[i]); + ne = tree->CreateEdge(p, p->map->Outgoing->F, children); + ne->map = p->map->Outgoing->map; + } +#ifndef WITH_CHILDREN + tree->AssertEdge(ne); // assert the edge in the solver +#else + tree->AssertEdge(ne,0,!full_expand,(underapprox || false_approx)); // assert the edge in the solver +#endif + reporter->Expand(ne); + } + +#define UNDERAPPROXCORE +#ifndef UNDERAPPROXCORE + void ExpansionChoices(std::set &best){ + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) + if (!tree->Empty(*it)) // if used in the counter-model + choices.insert(*it); + heuristic->ChooseExpand(choices, best); + } +#else +#if 0 + + void ExpansionChoices(std::set &best){ + std::vector unused_set, used_set; + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ + Node *n = *it; + if (!tree->Empty(n)) + used_set.push_back(n); + else + unused_set.push_back(n); + } + if(tree->Check(top,unused_set) == unsat) + throw "error in ExpansionChoices"; + for(unsigned i = 0; i < used_set.size(); i++){ + Node *n = used_set[i]; + unused_set.push_back(n); + if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ + unused_set.pop_back(); + choices.insert(n); + } + else + std::cout << "Using underapprox of " << n->number << std::endl; + } + heuristic->ChooseExpand(choices, best); + } +#else + void ExpansionChoicesFull(std::set &best, bool high_priority, bool best_only = false){ + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) + if (high_priority || !tree->Empty(*it)) // if used in the counter-model + choices.insert(*it); + heuristic->ChooseExpand(choices, best, high_priority, best_only); + } + + void ExpansionChoicesRec(std::vector &unused_set, std::vector &used_set, + std::set &choices, int from, int to){ + if(from == to) return; + int orig_unused = unused_set.size(); + unused_set.resize(orig_unused + (to - from)); + std::copy(used_set.begin()+from,used_set.begin()+to,unused_set.begin()+orig_unused); + if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ + unused_set.resize(orig_unused); + if(to - from == 1){ +#if 1 + std::cout << "Not using underapprox of " << used_set[from] ->number << std::endl; +#endif + choices.insert(used_set[from]); + } + else { + int mid = from + (to - from)/2; + ExpansionChoicesRec(unused_set, used_set, choices, from, mid); + ExpansionChoicesRec(unused_set, used_set, choices, mid, to); + } + } + else { +#if 1 + std::cout << "Using underapprox of "; + for(int i = from; i < to; i++){ + std::cout << used_set[i]->number << " "; + if(used_set[i]->map->Underapprox.IsEmpty()) + std::cout << "(false!) "; + } + std::cout << std::endl; +#endif + } + } + + std::set old_choices; + + void ExpansionChoices(std::set &best, bool high_priority, bool best_only = false){ + if(!underapprox || constrained || high_priority){ + ExpansionChoicesFull(best, high_priority,best_only); + return; + } + std::vector unused_set, used_set; + std::set choices; + for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ + Node *n = *it; + if (!tree->Empty(n)){ + if(old_choices.find(n) != old_choices.end() || n->map->Underapprox.IsEmpty()) + choices.insert(n); + else + used_set.push_back(n); + } + else + unused_set.push_back(n); + } + if(tree->Check(top,unused_set) == unsat) + throw "error in ExpansionChoices"; + ExpansionChoicesRec(unused_set, used_set, choices, 0, used_set.size()); + old_choices = choices; + heuristic->ChooseExpand(choices, best, high_priority); + } +#endif +#endif + + bool ExpandSomeNodes(bool high_priority = false, int max = INT_MAX){ +#ifdef EFFORT_BOUNDED_STRAT + last_decs = tree->CumulativeDecisions() - start_decs; +#endif + timer_start("ExpandSomeNodes"); + timer_start("ExpansionChoices"); + std::set choices; + ExpansionChoices(choices,high_priority,max != INT_MAX); + timer_stop("ExpansionChoices"); + std::list leaves_copy = leaves; // copy so can modify orig + leaves.clear(); + int count = 0; + for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ + if(choices.find(*it) != choices.end() && count < max){ + count++; + ExpandNode(*it); + } + else leaves.push_back(*it); + } + timer_stop("ExpandSomeNodes"); + return !choices.empty(); + } + + void RemoveExpansion(RPFP::Node *p){ + Edge *edge = p->Outgoing; + Node *parent = edge->Parent; +#ifndef KEEP_EXPANSIONS + std::vector cs = edge->Children; + tree->DeleteEdge(edge); + for(unsigned i = 0; i < cs.size(); i++) + tree->DeleteNode(cs[i]); +#endif + leaves.push_back(parent); + } + + // remove all the descendants of tree root (but not root itself) + void RemoveTree(RPFP *tree, RPFP::Node *root){ + Edge *edge = root->Outgoing; + std::vector cs = edge->Children; + tree->DeleteEdge(edge); + for(unsigned i = 0; i < cs.size(); i++){ + RemoveTree(tree,cs[i]); + tree->DeleteNode(cs[i]); + } + } + }; + + class DerivationTreeSlow : public DerivationTree { + public: + + struct stack_entry { + unsigned level; // SMT solver stack level + std::vector expansions; + }; + + std::vector stack; + + hash_map updates; + + DerivationTreeSlow(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) + : DerivationTree(_duality, rpfp, _reporter, _heuristic, _full_expand) { + stack.push_back(stack_entry()); + } + + struct DoRestart {}; + + virtual bool Build(){ + while (true) { + try { + return BuildMain(); + } + catch (const DoRestart &) { + // clear the statck and try again + updated_nodes.clear(); + while(stack.size() > 1) + PopLevel(); + reporter->Message("restarted"); + } + } + } + + bool BuildMain(){ + + stack.back().level = tree->slvr().get_scope_level(); + bool was_sat = true; + int update_failures = 0; + + while (true) + { + lbool res; + + 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); + res = foo == unsat ? l_false : l_true; + + if (res == l_false) { + if (stack.empty()) // should never happen + return false; + + { + std::vector &expansions = stack.back().expansions; + int update_count = 0; + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + tree->SolveSingleNode(top,node); +#ifdef NO_GENERALIZE + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); +#else + try { + if(expansions.size() == 1 && NodeTooComplicated(node)) + SimplifyNode(node); + else + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); + Generalize(node); + } + catch(const RPFP::Bad &){ + // bad interpolants can get us here + throw DoRestart(); + } +#endif + if(RecordUpdate(node)) + update_count++; + else + heuristic->Update(node->map); // make it less likely to expand this node in future + } + if(update_count == 0){ + if(was_sat){ + update_failures++; + if(update_failures > 10) + throw Incompleteness(); + } + reporter->Message("backtracked without learning"); + } + else update_failures = 0; + } + tree->ComputeProofCore(); // need to compute the proof core before popping solver + bool propagated = false; + while(1) { + bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop + PopLevel(); + if(stack.size() == 1)break; + if(prev_level_used){ + Node *node = stack.back().expansions[0]; +#ifndef NO_PROPAGATE + if(!Propagate(node)) break; +#endif + if(!RecordUpdate(node)) break; // shouldn't happen! + RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list + propagated = true; + continue; + } + if(propagated) break; // propagation invalidates the proof core, so disable non-chron backtrack + RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list + std::vector &unused_ex = stack.back().expansions; + for(unsigned i = 0; i < unused_ex.size(); i++) + heuristic->Update(unused_ex[i]->map); // make it less likely to expand this node in future + } + HandleUpdatedNodes(); + if(stack.size() == 1){ + if(top->Outgoing) + tree->DeleteEdge(top->Outgoing); // in case we kept the tree + return false; + } + was_sat = false; + } + else { + was_sat = true; + tree->Push(); + std::vector &expansions = stack.back().expansions; +#ifndef NO_DECISIONS + for(unsigned i = 0; i < expansions.size(); i++){ + tree->FixCurrentState(expansions[i]->Outgoing); + } +#endif +#if 0 + if(tree->slvr().check() == unsat) + throw "help!"; +#endif + int expand_max = 1; + if(0&&duality->BatchExpand){ + int thing = stack.size() * 0.1; + expand_max = std::max(1,thing); + if(expand_max > 1) + std::cout << "foo!\n"; + } + + if(ExpandSomeNodes(false,expand_max)) + continue; + tree->Pop(1); + while(stack.size() > 1){ + tree->Pop(1); + stack.pop_back(); + } + return true; + } + } + } + + void PopLevel(){ + std::vector &expansions = stack.back().expansions; + tree->Pop(1); + hash_set leaves_to_remove; + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + // if(node != top) + // tree->ConstrainParent(node->Incoming[0],node); + std::vector &cs = node->Outgoing->Children; + for(unsigned i = 0; i < cs.size(); i++){ + leaves_to_remove.insert(cs[i]); + UnmapNode(cs[i]); + if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) + throw "help!"; + } + } + RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + RemoveExpansion(node); + } + stack.pop_back(); + } + + bool NodeTooComplicated(Node *node){ + int ops = tree->CountOperators(node->Annotation.Formula); + if(ops > 10) return true; + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); + return tree->CountOperators(node->Annotation.Formula) > 3; + } + + void SimplifyNode(Node *node){ + // have to destroy the old proof to get a new interpolant + timer_start("SimplifyNode"); + tree->PopPush(); + try { + tree->InterpolateByCases(top,node); + } + catch(const RPFP::Bad&){ + timer_stop("SimplifyNode"); + throw RPFP::Bad(); + } + timer_stop("SimplifyNode"); + } + + bool LevelUsedInProof(unsigned level){ + std::vector &expansions = stack[level].expansions; + for(unsigned i = 0; i < expansions.size(); i++) + if(tree->EdgeUsedInProof(expansions[i]->Outgoing)) + return true; + return false; + } + + void RemoveUpdateNodesAtCurrentLevel() { + for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ + Node *node = *it; + if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ + std::list::iterator victim = it; + ++it; + updated_nodes.erase(victim); + } + else + ++it; + } + } + + void RemoveLeaves(hash_set &leaves_to_remove){ + std::list leaves_copy; + leaves_copy.swap(leaves); + for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ + if(leaves_to_remove.find(*it) == leaves_to_remove.end()) + leaves.push_back(*it); + } + } + + hash_map > node_map; + std::list updated_nodes; + + virtual void ExpandNode(RPFP::Node *p){ + stack.push_back(stack_entry()); + stack.back().level = tree->slvr().get_scope_level(); + stack.back().expansions.push_back(p); + DerivationTree::ExpandNode(p); + std::vector &new_nodes = p->Outgoing->Children; + for(unsigned i = 0; i < new_nodes.size(); i++){ + Node *n = new_nodes[i]; + node_map[n->map].push_back(n); + } + } + + bool RecordUpdate(Node *node){ + bool res = duality->UpdateNodeToNode(node->map,node); + if(res){ + std::vector to_update = node_map[node->map]; + for(unsigned i = 0; i < to_update.size(); i++){ + Node *node2 = to_update[i]; + // maintain invariant that no nodes on updated list are created at current stack level + if(node2 == node || !(node->Incoming.size() > 0 && AtCurrentStackLevel(node2->Incoming[0]->Parent))){ + updated_nodes.push_back(node2); + if(node2 != node) + node2->Annotation = node->Annotation; + } + } + } + return res; + } + + void HandleUpdatedNodes(){ + for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ + Node *node = *it; + node->Annotation = node->map->Annotation; + if(node->Incoming.size() > 0) + tree->ConstrainParent(node->Incoming[0],node); + if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ + std::list::iterator victim = it; + ++it; + updated_nodes.erase(victim); + } + else + ++it; + } + } + + bool AtCurrentStackLevel(Node *node){ + std::vector vec = stack.back().expansions; + for(unsigned i = 0; i < vec.size(); i++) + if(vec[i] == node) + return true; + return false; + } + + void UnmapNode(Node *node){ + std::vector &vec = node_map[node->map]; + for(unsigned i = 0; i < vec.size(); i++){ + if(vec[i] == node){ + std::swap(vec[i],vec.back()); + vec.pop_back(); + return; + } + } + throw "can't unmap node"; + } + + void Generalize(Node *node){ +#ifndef USE_RPFP_CLONE + tree->Generalize(top,node); +#else + RPFP_caching *clone_rpfp = duality->clone_rpfp; + if(!node->Outgoing->map) return; + Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); + Node *clone_node = clone_edge->Parent; + clone_node->Annotation = node->Annotation; + for(unsigned i = 0; i < clone_edge->Children.size(); i++) + clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; + clone_rpfp->GeneralizeCache(clone_edge); + node->Annotation = clone_node->Annotation; +#endif + } + + bool Propagate(Node *node){ +#ifdef USE_RPFP_CLONE + RPFP_caching *clone_rpfp = duality->clone_rpfp; + Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); + Node *clone_node = clone_edge->Parent; + clone_node->Annotation = node->map->Annotation; + for(unsigned i = 0; i < clone_edge->Children.size(); i++) + clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; + bool res = clone_rpfp->PropagateCache(clone_edge); + if(res) + node->Annotation = clone_node->Annotation; + return res; +#else + return false; +#endif + } + + }; + + + class Covering { + + struct cover_info { + Node *covered_by; + std::list covers; + bool dominated; + std::set dominates; + cover_info(){ + covered_by = 0; + dominated = false; + } + }; + + typedef hash_map cover_map; + cover_map cm; + Duality *parent; + bool some_updates; + +#define NO_CONJ_ON_SIMPLE_LOOPS +#ifdef NO_CONJ_ON_SIMPLE_LOOPS + hash_set simple_loops; +#endif + + Node *&covered_by(Node *node){ + return cm[node].covered_by; + } + + std::list &covers(Node *node){ + return cm[node].covers; + } + + std::vector &insts_of_node(Node *node){ + return parent->insts_of_node[node]; + } + + Reporter *reporter(){ + return parent->reporter; + } + + std::set &dominates(Node *x){ + return cm[x].dominates; + } + + bool dominates(Node *x, Node *y){ + std::set &d = cm[x].dominates; + return d.find(y) != d.end(); + } + + bool &dominated(Node *x){ + return cm[x].dominated; + } + + public: + + Covering(Duality *_parent){ + parent = _parent; + some_updates = false; + +#ifdef NO_CONJ_ON_SIMPLE_LOOPS + hash_map > outgoing; + for(unsigned i = 0; i < parent->rpfp->edges.size(); i++) + outgoing[parent->rpfp->edges[i]->Parent].push_back(parent->rpfp->edges[i]); + for(unsigned i = 0; i < parent->rpfp->nodes.size(); i++){ + Node * node = parent->rpfp->nodes[i]; + std::vector &outs = outgoing[node]; + if(outs.size() == 2){ + for(int j = 0; j < 2; j++){ + Edge *loop_edge = outs[j]; + if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent) + simple_loops.insert(node); + } + } + } +#endif + + } + + bool IsCoveredRec(hash_set &memo, Node *node){ + if(memo.find(node) != memo.end()) + return false; + memo.insert(node); + if(covered_by(node)) return true; + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) + if(IsCoveredRec(memo,node->Outgoing->Children[i])) + return true; + return false; + } + + bool IsCovered(Node *node){ + hash_set memo; + return IsCoveredRec(memo,node); + } + +#ifndef UNDERAPPROX_NODES + void RemoveCoveringsBy(Node *node){ + std::list &cs = covers(node); + for(std::list::iterator it = cs.begin(), en = cs.end(); it != en; it++){ + covered_by(*it) = 0; + reporter()->RemoveCover(*it,node); + } + cs.clear(); + } +#else + void RemoveCoveringsBy(Node *node){ + std::vector &cs = parent->all_of_node[node->map]; + for(std::vector::iterator it = cs.begin(), en = cs.end(); it != en; it++){ + Node *other = *it; + if(covered_by(other) && CoverOrder(node,other)){ + covered_by(other) = 0; + reporter()->RemoveCover(*it,node); + } + } + } +#endif + + void RemoveAscendantCoveringsRec(hash_set &memo, Node *node){ + if(memo.find(node) != memo.end()) + return; + memo.insert(node); + RemoveCoveringsBy(node); + for(std::vector::iterator it = node->Incoming.begin(), en = node->Incoming.end(); it != en; ++it) + RemoveAscendantCoveringsRec(memo,(*it)->Parent); + } + + void RemoveAscendantCoverings(Node *node){ + hash_set memo; + RemoveAscendantCoveringsRec(memo,node); + } + + bool CoverOrder(Node *covering, Node *covered){ +#ifdef UNDERAPPROX_NODES + if(parent->underapprox_map.find(covered) != parent->underapprox_map.end()) + return false; + if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) + return covering->number < covered->number || parent->underapprox_map[covering] == covered; +#endif + return covering->number < covered->number; + } + + bool CheckCover(Node *covered, Node *covering){ + return + CoverOrder(covering,covered) + && covered->Annotation.SubsetEq(covering->Annotation) + && !IsCovered(covering); + } + + bool CoverByNode(Node *covered, Node *covering){ + if(CheckCover(covered,covering)){ + covered_by(covered) = covering; + covers(covering).push_back(covered); + std::vector others; others.push_back(covering); + reporter()->AddCover(covered,others); + RemoveAscendantCoverings(covered); + return true; + } + else + return false; + } + +#ifdef UNDERAPPROX_NODES + bool CoverByAll(Node *covered){ + RPFP::Transformer all = covered->Annotation; + all.SetEmpty(); + std::vector &insts = parent->insts_of_node[covered->map]; + std::vector others; + for(unsigned i = 0; i < insts.size(); i++){ + Node *covering = insts[i]; + if(CoverOrder(covering,covered) && !IsCovered(covering)){ + others.push_back(covering); + all.UnionWith(covering->Annotation); + } + } + if(others.size() && covered->Annotation.SubsetEq(all)){ + covered_by(covered) = covered; // anything non-null will do + reporter()->AddCover(covered,others); + RemoveAscendantCoverings(covered); + return true; + } + else + return false; + } +#endif + + bool Close(Node *node){ + if(covered_by(node)) + return true; +#ifndef UNDERAPPROX_NODES + std::vector &insts = insts_of_node(node->map); + for(unsigned i = 0; i < insts.size(); i++) + if(CoverByNode(node,insts[i])) + return true; +#else + if(CoverByAll(node)) + return true; +#endif + return false; + } + + bool CloseDescendantsRec(hash_set &memo, Node *node){ + if(memo.find(node) != memo.end()) + return false; + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) + if(CloseDescendantsRec(memo,node->Outgoing->Children[i])) + return true; + if(Close(node)) + return true; + memo.insert(node); + return false; + } + + bool CloseDescendants(Node *node){ + timer_start("CloseDescendants"); + hash_set memo; + bool res = CloseDescendantsRec(memo,node); + timer_stop("CloseDescendants"); + return res; + } + + bool Contains(Node *node){ + timer_start("Contains"); + bool res = !IsCovered(node); + timer_stop("Contains"); + return res; + } + + bool Candidate(Node *node){ + timer_start("Candidate"); + bool res = !IsCovered(node) && !dominated(node); + timer_stop("Candidate"); + return res; + } + + void SetDominated(Node *node){ + dominated(node) = true; + } + + bool CouldCover(Node *covered, Node *covering){ +#ifdef NO_CONJ_ON_SIMPLE_LOOPS + // Forsimple loops, we rely on propagation, not covering + if(simple_loops.find(covered->map) != simple_loops.end()) + return false; +#endif +#ifdef UNDERAPPROX_NODES + // if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) + // return parent->underapprox_map[covering] == covered; +#endif + if(CoverOrder(covering,covered) + && !IsCovered(covering)){ + RPFP::Transformer f(covering->Annotation); f.SetEmpty(); +#if defined(TOP_DOWN) || defined(EFFORT_BOUNDED_STRAT) + if(parent->StratifiedInlining) + return true; +#endif + return !covering->Annotation.SubsetEq(f); + } + return false; + } + + bool ContainsCex(Node *node, Counterexample &cex){ + expr val = cex.get_tree()->Eval(cex.get_root()->Outgoing,node->Annotation.Formula); + return eq(val,parent->ctx.bool_val(true)); + } + + /** We conjecture that the annotations of similar nodes may be + true of this one. We start with later nodes, on the + principle that their annotations are likely weaker. We save + a counterexample -- if annotations of other nodes are true + in this counterexample, we don't need to check them. + */ + +#ifndef UNDERAPPROX_NODES + bool Conjecture(Node *node){ + std::vector &insts = insts_of_node(node->map); + Counterexample cex; + for(int i = insts.size() - 1; i >= 0; i--){ + Node *other = insts[i]; + if(CouldCover(node,other)){ + reporter()->Forcing(node,other); + if(cex.get_tree() && !ContainsCex(other,cex)) + continue; + cex.clear(); + if(parent->ProveConjecture(node,other->Annotation,other,&cex)) + if(CloseDescendants(node)) + return true; + } + } + cex.clear(); + return false; + } +#else + bool Conjecture(Node *node){ + std::vector &insts = insts_of_node(node->map); + Counterexample cex; + RPFP::Transformer Bound = node->Annotation; + Bound.SetEmpty(); + bool some_other = false; + for(int i = insts.size() - 1; i >= 0; i--){ + Node *other = insts[i]; + if(CouldCover(node,other)){ + reporter()->Forcing(node,other); + Bound.UnionWith(other->Annotation); + some_other = true; + } + } + if(some_other && parent->ProveConjecture(node,Bound)){ + CloseDescendants(node); + return true; + } + return false; + } +#endif + + void Update(Node *node, const RPFP::Transformer &update){ + RemoveCoveringsBy(node); + some_updates = true; + } + +#ifndef UNDERAPPROX_NODES + Node *GetSimilarNode(Node *node){ + if(!some_updates) + return 0; + std::vector &insts = insts_of_node(node->map); + for(int i = insts.size()-1; i >= 0; i--){ + Node *other = insts[i]; + if(dominates(node,other)) + if(CoverOrder(other,node) + && !IsCovered(other)) + return other; + } + return 0; + } +#else + Node *GetSimilarNode(Node *node){ + if(!some_updates) + return 0; + std::vector &insts = insts_of_node(node->map); + for(int i = insts.size() - 1; i >= 0; i--){ + Node *other = insts[i]; + if(CoverOrder(other,node) + && !IsCovered(other)) + return other; + } + return 0; + } +#endif + + bool Dominates(Node * node, Node *other){ + if(node == other) return false; + if(other->Outgoing->map == 0) return true; + if(node->Outgoing->map == other->Outgoing->map){ + assert(node->Outgoing->Children.size() == other->Outgoing->Children.size()); + for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ + Node *nc = node->Outgoing->Children[i]; + Node *oc = other->Outgoing->Children[i]; + if(!(nc == oc || oc->Outgoing->map ==0 || dominates(nc,oc))) + return false; + } + return true; + } + return false; + } + + void Add(Node *node){ + std::vector &insts = insts_of_node(node->map); + for(unsigned i = 0; i < insts.size(); i++){ + Node *other = insts[i]; + if(Dominates(node,other)){ + cm[node].dominates.insert(other); + cm[other].dominated = true; + reporter()->Dominates(node, other); + } + } + } + + }; + + /* This expansion heuristic makes use of a previuosly obtained + counterexample as a guide. This is for use in abstraction + refinement schemes.*/ + + class ReplayHeuristic : public Heuristic { + + Counterexample old_cex; + public: + ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) + : Heuristic(_rpfp) + { + old_cex.swap(_old_cex); // take ownership from caller + } + + ~ReplayHeuristic(){ + } + + // Maps nodes of derivation tree into old cex + hash_map cex_map; + + void Done() { + cex_map.clear(); + old_cex.clear(); + } + + void ShowNodeAndChildren(Node *n){ + std::cout << n->Name.name() << ": "; + std::vector &chs = n->Outgoing->Children; + for(unsigned i = 0; i < chs.size(); i++) + std::cout << chs[i]->Name.name() << " " ; + std::cout << std::endl; + } + + // HACK: When matching relation names, we drop suffixes used to + // make the names unique between runs. For compatibility + // with boggie, we drop suffixes beginning with @@ + std::string BaseName(const std::string &name){ + int pos = name.find("@@"); + if(pos >= 1) + return name.substr(0,pos); + return name; + } + + virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ + if(!high_priority || !old_cex.get_tree()){ + Heuristic::ChooseExpand(choices,best,false); + return; + } + // first, try to match the derivatino tree nodes to the old cex + std::set matched, unmatched; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it); + if(cex_map.empty()) + cex_map[node] = old_cex.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()) + throw "catastrophe in ReplayHeuristic::ChooseExpand"; + Node *old_parent = cex_map[parent]; + std::vector &chs = parent->Outgoing->Children; + if(old_parent && old_parent->Outgoing){ + std::vector &old_chs = old_parent->Outgoing->Children; + for(unsigned i = 0, j=0; i < chs.size(); i++){ + if(j < old_chs.size() && BaseName(chs[i]->Name.name().str()) == BaseName(old_chs[j]->Name.name().str())) + cex_map[chs[i]] = old_chs[j++]; + else { + std::cerr << "WARNING: duality: unmatched child: " << chs[i]->Name.name() << std::endl; + cex_map[chs[i]] = 0; + } + } + goto matching_done; + } + for(unsigned i = 0; i < chs.size(); i++) + cex_map[chs[i]] = 0; + } + matching_done: + Node *old_node = cex_map[node]; + if(!old_node) + unmatched.insert(node); + else if(old_cex.get_tree()->Empty(old_node)) + unmatched.insert(node); + else + matched.insert(node); + } + if (matched.empty() && !high_priority) + Heuristic::ChooseExpand(unmatched,best,false); + else + Heuristic::ChooseExpand(matched,best,false); + } + }; + + + class LocalHeuristic : public Heuristic { + + RPFP::Node *old_node; + public: + LocalHeuristic(RPFP *_rpfp) + : Heuristic(_rpfp) + { + old_node = 0; + } + + void SetOldNode(RPFP::Node *_old_node){ + old_node = _old_node; + cex_map.clear(); + } + + // Maps nodes of derivation tree into old subtree + hash_map cex_map; + + virtual void ChooseExpand(const std::set &choices, std::set &best){ + if(old_node == 0){ + Heuristic::ChooseExpand(choices,best); + return; + } + // first, try to match the derivatino tree nodes to the old cex + std::set matched, unmatched; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it); + if(cex_map.empty()) + cex_map[node] = old_node; // 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()) + throw "catastrophe in ReplayHeuristic::ChooseExpand"; + Node *old_parent = cex_map[parent]; + std::vector &chs = parent->Outgoing->Children; + if(old_parent && old_parent->Outgoing){ + std::vector &old_chs = old_parent->Outgoing->Children; + if(chs.size() == old_chs.size()){ + for(unsigned i = 0; i < chs.size(); i++) + cex_map[chs[i]] = old_chs[i]; + goto matching_done; + } + else + std::cout << "derivation tree does not match old cex" << std::endl; + } + for(unsigned i = 0; i < chs.size(); i++) + cex_map[chs[i]] = 0; + } + matching_done: + Node *old_node = cex_map[node]; + if(!old_node) + unmatched.insert(node); + else if(old_node != node->map) + unmatched.insert(node); + else + matched.insert(node); + } + Heuristic::ChooseExpand(unmatched,best); + } + }; + + /** This proposer class generates conjectures based on the + unwinding generated by a previous solver. The assumption is + that the provious solver was working on a different + abstraction of the same system. The trick is to adapt the + annotations in the old unwinding to the new predicates. We + start by generating a map from predicates and parameters in + the old problem to the new. + + HACK: mapping is done by cheesy name comparison. + */ + + class HistoryProposer : public Proposer + { + Duality *old_solver; + Duality *new_solver; + hash_map > conjectures; + + public: + /** Construct a history solver. */ + HistoryProposer(Duality *_old_solver, Duality *_new_solver) + : old_solver(_old_solver), new_solver(_new_solver) { + + // tricky: names in the axioms may have changed -- map them + hash_set &old_constants = old_solver->unwinding->ls->get_constants(); + hash_set &new_constants = new_solver->rpfp->ls->get_constants(); + hash_map cmap; + for(hash_set::iterator it = new_constants.begin(), en = new_constants.end(); it != en; ++it) + cmap[GetKey(*it)] = *it; + hash_map bckg_map; + for(hash_set::iterator it = old_constants.begin(), en = old_constants.end(); it != en; ++it){ + func_decl f = new_solver->ctx.translate(*it); // move to new context + if(cmap.find(GetKey(f)) != cmap.end()) + bckg_map[f] = cmap[GetKey(f)]; + // else + // std::cout << "constant not matched\n"; + } + + RPFP *old_unwinding = old_solver->unwinding; + hash_map > pred_match; + + // index all the predicates in the old unwinding + for(unsigned i = 0; i < old_unwinding->nodes.size(); i++){ + Node *node = old_unwinding->nodes[i]; + std::string key = GetKey(node); + pred_match[key].push_back(node); + } + + // match with predicates in the new RPFP + RPFP *rpfp = new_solver->rpfp; + for(unsigned i = 0; i < rpfp->nodes.size(); i++){ + Node *node = rpfp->nodes[i]; + std::string key = GetKey(node); + std::vector &matches = pred_match[key]; + for(unsigned j = 0; j < matches.size(); j++) + MatchNodes(node,matches[j],bckg_map); + } + } + + virtual std::vector &Propose(Node *node){ + return conjectures[node->map]; + } + + virtual ~HistoryProposer(){ + }; + + private: + void MatchNodes(Node *new_node, Node *old_node, hash_map &bckg_map){ + if(old_node->Annotation.IsFull()) + return; // don't conjecture true! + hash_map var_match; + std::vector &new_params = new_node->Annotation.IndParams; + // Index the new parameters by their keys + for(unsigned i = 0; i < new_params.size(); i++) + var_match[GetKey(new_params[i])] = new_params[i]; + RPFP::Transformer &old = old_node->Annotation; + std::vector from_params = old.IndParams; + for(unsigned j = 0; j < from_params.size(); j++) + from_params[j] = new_solver->ctx.translate(from_params[j]); // get in new context + std::vector to_params = from_params; + for(unsigned j = 0; j < to_params.size(); j++){ + std::string key = GetKey(to_params[j]); + if(var_match.find(key) == var_match.end()){ + // std::cout << "unmatched parameter!\n"; + return; + } + to_params[j] = var_match[key]; + } + expr fmla = new_solver->ctx.translate(old.Formula); // get in new context + fmla = new_solver->rpfp->SubstParams(old.IndParams,to_params,fmla); // substitute parameters + hash_map memo; + fmla = new_solver->rpfp->SubstRec(memo,bckg_map,fmla); // substitute background constants + RPFP::Transformer new_annot = new_node->Annotation; + new_annot.Formula = fmla; + conjectures[new_node].push_back(new_annot); + } + + // We match names by removing suffixes beginning with double at sign + + std::string GetKey(Node *node){ + return GetKey(node->Name); + } + + std::string GetKey(const expr &var){ + return GetKey(var.decl()); + } + + std::string GetKey(const func_decl &f){ + std::string name = f.name().str(); + int idx = name.find("@@"); + if(idx >= 0) + name.erase(idx); + return name; + } + }; + }; + + + class StreamReporter : public Reporter { + std::ostream &s; + public: + StreamReporter(RPFP *_rpfp, std::ostream &_s) + : Reporter(_rpfp), s(_s) {event = 0; depth = -1;} + int event; + int depth; + void ev(){ + s << "[" << event++ << "]" ; + } + virtual void Extend(RPFP::Node *node){ + ev(); s << "node " << node->number << ": " << node->Name.name(); + std::vector &rps = node->Outgoing->Children; + for(unsigned i = 0; i < rps.size(); i++) + s << " " << rps[i]->number; + s << std::endl; + } + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ + ev(); s << "update " << node->number << " " << node->Name.name() << ": "; + rpfp->Summarize(update.Formula); + 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(); + if(depth > 0) s << " (depth=" << depth << ")"; + s << std::endl; + } + virtual void Depth(int d){ + depth = d; + } + virtual void AddCover(RPFP::Node *covered, std::vector &covering){ + ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; + for(unsigned i = 0; i < covering.size(); i++) + 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; + } + virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){ + ev(); s << "forcing " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; + } + virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){ + ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": "; + rpfp->Summarize(t.Formula); + s << std::endl; + } + virtual void Dominates(RPFP::Node *node, RPFP::Node *other){ + ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; + } + virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){ + ev(); s << "induction failure: " << edge->Parent->Name.name() << ", children ="; + for(unsigned i = 0; i < children.size(); i++) + s << " " << children[i]->number; + s << std::endl; + } + virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){ + ev(); s << "underapprox " << node->number << " " << node->Name.name() << ": " << update.Formula << std::endl; + } + virtual void Reject(RPFP::Edge *edge, const std::vector &children){ + ev(); s << "reject " << edge->Parent->number << " " << edge->Parent->Name.name() << ": "; + for(unsigned i = 0; i < children.size(); i++) + s << " " << children[i]->number; + s << std::endl; + } + virtual void Message(const std::string &msg){ + ev(); s << "msg " << msg << std::endl; + } + + }; + + Solver *Solver::Create(const std::string &solver_class, RPFP *rpfp){ + Duality *s = alloc(Duality,rpfp); + return s; + } + + Reporter *CreateStdoutReporter(RPFP *rpfp){ + return new StreamReporter(rpfp, std::cout); + } +} diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index 6bf09bb85..d82e721ae 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -1,195 +1,195 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - iz3base.h - -Abstract: - - Base class for interpolators. Includes an AST manager and a scoping - object as bases. - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - ---*/ - -#ifndef IZ3BASE_H -#define IZ3BASE_H - -#include "iz3mgr.h" -#include "iz3scopes.h" - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(func_decl * const &s) const { - return (size_t) s; - } - }; -} - -/* Base class for interpolators. Includes an AST manager and a scoping - object as bases. */ - -class iz3base : public iz3mgr, public scopes { - - public: - - /** Get the range in which an expression occurs. This is the - smallest subtree containing all occurrences of the - expression. */ - range &ast_range(ast); - - /** Get the scope of an expression. This is the set of tree nodes in - which all of the expression's symbols are in scope. */ - range &ast_scope(ast); - - /** Get the range of a symbol. This is the smallest subtree containing - all occurrences of the symbol. */ - range &sym_range(symb); - - /** Is an expression local (in scope in some frame)? */ - - bool is_local(ast node){ - return !range_is_empty(ast_scope(node)); - } - - /** Simplify an expression */ - - ast simplify(ast); - - /** Constructor */ - - iz3base(ast_manager &_m_manager, - const std::vector &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(_m_manager), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other, - const std::vector &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(other), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other, - const std::vector > &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(other), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other) - : iz3mgr(other), scopes() { - weak = false; - } - - /* Set our options */ - void set_option(const std::string &name, const std::string &value){ - if(name == "weak" && value == "1") weak = true; - } - - /* Are we doing weak interpolants? */ - bool weak_mode(){return weak;} - - /** Print interpolation problem to an SMTLIB format file */ - void print(const std::string &filename); - - /** Check correctness of a solutino to this problem. */ - void check_interp(const std::vector &itps, std::vector &theory); - - /** For convenience -- is this formula SAT? */ - bool is_sat(const std::vector &consts, ast &_proof); - - /** Interpolator for clauses, to be implemented */ - virtual void interpolate_clause(std::vector &lits, std::vector &itps){ - throw "no interpolator"; - } - - ast get_proof_check_assump(range &rng){ - std::vector cs(theory); - cs.push_back(cnsts[rng.hi]); - return make(And,cs); - } - - int frame_of_assertion(const ast &ass){ - stl_ext::hash_map::iterator it = frame_map.find(ass); - if(it == frame_map.end()) - throw "unknown assertion"; - return it->second; - } - - - void to_parents_vec_representation(const std::vector &_cnsts, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &theory, - std::vector &pos_map, - bool merge = false - ); - - protected: - std::vector cnsts; - std::vector theory; - - private: - - struct ranges { - range rng; - range scp; - bool scope_computed; - ranges(){scope_computed = false;} - }; - - stl_ext::hash_map sym_range_hash; - stl_ext::hash_map ast_ranges_hash; - stl_ext::hash_map simplify_memo; - stl_ext::hash_map frame_map; // map assertions to frames - - int frames; // number of frames - - protected: - void add_frame_range(int frame, ast t); - - private: - void initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory); - - void initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory); - - bool is_literal(ast n); - void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); - void gather_conjuncts(ast n, std::vector &conjuncts); - ast simplify_and(std::vector &conjuncts); - ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); - ast simplify_with_lit(ast n, ast lit); - void find_children(const stl_ext::hash_set &cnsts_set, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &conjuncts, - std::vector &children, - std::vector &pos_map, - bool merge - ); - bool weak; - -}; - - - -#endif +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3base.h + +Abstract: + + Base class for interpolators. Includes an AST manager and a scoping + object as bases. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#ifndef IZ3BASE_H +#define IZ3BASE_H + +#include "iz3mgr.h" +#include "iz3scopes.h" + +namespace hash_space { + template <> + class hash { + public: + size_t operator()(func_decl * const &s) const { + return (size_t) s; + } + }; +} + +/* Base class for interpolators. Includes an AST manager and a scoping + object as bases. */ + +class iz3base : public iz3mgr, public scopes { + + public: + + /** Get the range in which an expression occurs. This is the + smallest subtree containing all occurrences of the + expression. */ + range &ast_range(ast); + + /** Get the scope of an expression. This is the set of tree nodes in + which all of the expression's symbols are in scope. */ + range &ast_scope(ast); + + /** Get the range of a symbol. This is the smallest subtree containing + all occurrences of the symbol. */ + range &sym_range(symb); + + /** Is an expression local (in scope in some frame)? */ + + bool is_local(ast node){ + return !range_is_empty(ast_scope(node)); + } + + /** Simplify an expression */ + + ast simplify(ast); + + /** Constructor */ + + iz3base(ast_manager &_m_manager, + const std::vector &_cnsts, + const std::vector &_parents, + const std::vector &_theory) + : iz3mgr(_m_manager), scopes(_parents) { + initialize(_cnsts,_parents,_theory); + weak = false; + } + + iz3base(const iz3mgr& other, + const std::vector &_cnsts, + const std::vector &_parents, + const std::vector &_theory) + : iz3mgr(other), scopes(_parents) { + initialize(_cnsts,_parents,_theory); + weak = false; + } + + iz3base(const iz3mgr& other, + const std::vector > &_cnsts, + const std::vector &_parents, + const std::vector &_theory) + : iz3mgr(other), scopes(_parents) { + initialize(_cnsts,_parents,_theory); + weak = false; + } + + iz3base(const iz3mgr& other) + : iz3mgr(other), scopes() { + weak = false; + } + + /* Set our options */ + void set_option(const std::string &name, const std::string &value){ + if(name == "weak" && value == "1") weak = true; + } + + /* Are we doing weak interpolants? */ + bool weak_mode(){return weak;} + + /** Print interpolation problem to an SMTLIB format file */ + void print(const std::string &filename); + + /** Check correctness of a solutino to this problem. */ + void check_interp(const std::vector &itps, std::vector &theory); + + /** For convenience -- is this formula SAT? */ + bool is_sat(const std::vector &consts, ast &_proof); + + /** Interpolator for clauses, to be implemented */ + virtual void interpolate_clause(std::vector &lits, std::vector &itps){ + throw "no interpolator"; + } + + ast get_proof_check_assump(range &rng){ + std::vector cs(theory); + cs.push_back(cnsts[rng.hi]); + return make(And,cs); + } + + int frame_of_assertion(const ast &ass){ + stl_ext::hash_map::iterator it = frame_map.find(ass); + if(it == frame_map.end()) + throw "unknown assertion"; + return it->second; + } + + + void to_parents_vec_representation(const std::vector &_cnsts, + const ast &tree, + std::vector &cnsts, + std::vector &parents, + std::vector &theory, + std::vector &pos_map, + bool merge = false + ); + + protected: + std::vector cnsts; + std::vector theory; + + private: + + struct ranges { + range rng; + range scp; + bool scope_computed; + ranges(){scope_computed = false;} + }; + + stl_ext::hash_map sym_range_hash; + stl_ext::hash_map ast_ranges_hash; + stl_ext::hash_map simplify_memo; + stl_ext::hash_map frame_map; // map assertions to frames + + int frames; // number of frames + + protected: + void add_frame_range(int frame, ast t); + + private: + void initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory); + + void initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory); + + bool is_literal(ast n); + void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); + void gather_conjuncts(ast n, std::vector &conjuncts); + ast simplify_and(std::vector &conjuncts); + ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); + ast simplify_with_lit(ast n, ast lit); + void find_children(const stl_ext::hash_set &cnsts_set, + const ast &tree, + std::vector &cnsts, + std::vector &parents, + std::vector &conjuncts, + std::vector &children, + std::vector &pos_map, + bool merge + ); + bool weak; + +}; + + + +#endif diff --git a/src/interp/iz3scopes.cpp b/src/interp/iz3scopes.cpp index 198ecffe3..389a64b6c 100755 --- a/src/interp/iz3scopes.cpp +++ b/src/interp/iz3scopes.cpp @@ -1,321 +1,321 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - iz3scopes.cpp - -Abstract: - - Calculations with scopes, for both sequence and tree interpolation. - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - ---*/ - -#include - -#include - -#include "iz3scopes.h" - - -/** computes the least common ancestor of two nodes in the tree, or SHRT_MAX if none */ -int scopes::tree_lca(int n1, int n2){ - if(!tree_mode()) - return std::max(n1,n2); - if(n1 == SHRT_MIN) return n2; - if(n2 == SHRT_MIN) return n1; - if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; - while(n1 != n2){ - if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; - assert(n1 >= 0 && n2 >= 0 && n1 < (int)parents.size() && n2 < (int)parents.size()); - if(n1 < n2) n1 = parents[n1]; - else n2 = parents[n2]; - } - return n1; -} - -/** computes the greatest common descendant two nodes in the tree, or SHRT_MIN if none */ -int scopes::tree_gcd(int n1, int n2){ - if(!tree_mode()) - return std::min(n1,n2); - int foo = tree_lca(n1,n2); - if(foo == n1) return n2; - if(foo == n2) return n1; - return SHRT_MIN; -} - -#ifndef FULL_TREE - -/** test whether a tree node is contained in a range */ -bool scopes::in_range(int n, const range &rng){ - return tree_lca(rng.lo,n) == n && tree_gcd(rng.hi,n) == n; -} - -/** test whether two ranges of tree nodes intersect */ -bool scopes::ranges_intersect(const range &rng1, const range &rng2){ - return tree_lca(rng1.lo,rng2.hi) == rng2.hi && tree_lca(rng1.hi,rng2.lo) == rng1.hi; -} - - -bool scopes::range_contained(const range &rng1, const range &rng2){ - return tree_lca(rng2.lo,rng1.lo) == rng1.lo - && tree_lca(rng1.hi,rng2.hi) == rng2.hi; -} - -scopes::range scopes::range_lub(const range &rng1, const range &rng2){ - range res; - res.lo = tree_gcd(rng1.lo,rng2.lo); - res.hi = tree_lca(rng1.hi,rng2.hi); - return res; -} - -scopes::range scopes::range_glb(const range &rng1, const range &rng2){ - range res; - res.lo = tree_lca(rng1.lo,rng2.lo); - res.hi = tree_gcd(rng1.hi,rng2.hi); - return res; -} - -#else - - - namespace std { - template <> - class hash { - public: - size_t operator()(const scopes::range_lo &p) const { - return p.lo + (size_t)p.next; - } - }; - } - - template <> inline - size_t stdext::hash_value(const scopes::range_lo& p) - { - std::hash h; - return h(p); - } - - namespace std { - template <> - class less { - public: - bool operator()(const scopes::range_lo &x, const scopes::range_lo &y) const { - return x.lo < y.lo || x.lo == y.lo && (size_t)x.next < (size_t)y.next; - } - }; - } - - - struct range_op { - scopes::range_lo *x, *y; - int hi; - range_op(scopes::range_lo *_x, scopes::range_lo *_y, int _hi){ - x = _x; y = _y; hi = _hi; - } - }; - - namespace std { - template <> - class hash { - public: - size_t operator()(const range_op &p) const { - return (size_t) p.x + (size_t)p.y + p.hi; - } - }; - } - - template <> inline - size_t stdext::hash_value(const range_op& p) - { - std::hash h; - return h(p); - } - - namespace std { - template <> - class less { - public: - bool operator()(const range_op &x, const range_op &y) const { - return (size_t)x.x < (size_t)y.x || x.x == y.x && - ((size_t)x.y < (size_t)y.y || x.y == y.y && x.hi < y.hi); - } - }; - } - - struct range_tables { - hash_map unique; - hash_map lub; - hash_map glb; - }; - - - scopes::range_lo *scopes::find_range_lo(int lo, range_lo *next){ - range_lo foo(lo,next); - std::pair baz(foo,(range_lo *)0); - std::pair::iterator,bool> bar = rt->unique.insert(baz); - if(bar.second) - bar.first->second = new range_lo(lo,next); - return bar.first->second; - //std::pair::iterator,bool> bar = rt->unique.insert(foo); - // const range_lo *baz = &*(bar.first); - // return (range_lo *)baz; // exit const hell - } - - scopes::range_lo *scopes::range_lub_lo(range_lo *rng1, range_lo *rng2){ - if(!rng1) return rng2; - if(!rng2) return rng1; - if(rng1->lo > rng2->lo) - std::swap(rng1,rng2); - std::pair foo(range_op(rng1,rng2,0),(range_lo *)0); - std::pair::iterator,bool> bar = rt->lub.insert(foo); - range_lo *&res = bar.first->second; - if(!bar.second) return res; - if(!(rng1->next && rng1->next->lo <= rng2->lo)){ - for(int lo = rng1->lo; lo <= rng2->lo; lo = parents[lo]) - if(lo == rng2->lo) - {rng2 = rng2->next; break;} - } - range_lo *baz = range_lub_lo(rng1->next,rng2); - res = find_range_lo(rng1->lo,baz); - return res; - } - - - scopes::range_lo *scopes::range_glb_lo(range_lo *rng1, range_lo *rng2, int hi){ - if(!rng1) return rng1; - if(!rng2) return rng2; - if(rng1->lo > rng2->lo) - std::swap(rng1,rng2); - std::pair cand(range_op(rng1,rng2,hi),(range_lo *)0); - std::pair::iterator,bool> bar = rt->glb.insert(cand); - range_lo *&res = bar.first->second; - if(!bar.second) return res; - range_lo *foo; - if(!(rng1->next && rng1->next->lo <= rng2->lo)){ - int lim = hi; - if(rng1->next) lim = std::min(lim,rng1->next->lo); - int a = rng1->lo, b = rng2->lo; - while(a != b && b <= lim){ - a = parents[a]; - if(a > b)std::swap(a,b); - } - if(a == b && b <= lim){ - foo = range_glb_lo(rng1->next,rng2->next,hi); - foo = find_range_lo(b,foo); - } - else - foo = range_glb_lo(rng2,rng1->next,hi); - } - else foo = range_glb_lo(rng1->next,rng2,hi); - res = foo; - return res; - } - - /** computes the lub (smallest containing subtree) of two ranges */ - scopes::range scopes::range_lub(const range &rng1, const range &rng2){ - int hi = tree_lca(rng1.hi,rng2.hi); - if(hi == SHRT_MAX) return range_full(); - range_lo *lo = range_lub_lo(rng1.lo,rng2.lo); - return range(hi,lo); - } - - /** computes the glb (intersection) of two ranges */ - scopes::range scopes::range_glb(const range &rng1, const range &rng2){ - if(rng1.hi == SHRT_MAX) return rng2; - if(rng2.hi == SHRT_MAX) return rng1; - int hi = tree_gcd(rng1.hi,rng2.hi); - range_lo *lo = hi == SHRT_MIN ? 0 : range_glb_lo(rng1.lo,rng2.lo,hi); - if(!lo) hi = SHRT_MIN; - return range(hi,lo); - } - - /** is this range empty? */ - bool scopes::range_is_empty(const range &rng){ - return rng.hi == SHRT_MIN; - } - - /** return an empty range */ - scopes::range scopes::range_empty(){ - return range(SHRT_MIN,0); - } - - /** return a full range */ - scopes::range scopes::range_full(){ - return range(SHRT_MAX,0); - } - - /** return the maximal element of a range */ - int scopes::range_max(const range &rng){ - return rng.hi; - } - - /** return a minimal (not necessarily unique) element of a range */ - int scopes::range_min(const range &rng){ - if(rng.hi == SHRT_MAX) return SHRT_MIN; - return rng.lo ? rng.lo->lo : SHRT_MAX; - } - - - /** return range consisting of downward closure of a point */ - scopes::range scopes::range_downward(int _hi){ - std::vector descendants(parents.size()); - for(int i = descendants.size() - 1; i >= 0 ; i--) - descendants[i] = i == _hi || parents[i] < parents.size() && descendants[parents[i]]; - for(unsigned i = 0; i < descendants.size() - 1; i++) - if(parents[i] < parents.size()) - descendants[parents[i]] = false; - range_lo *foo = 0; - for(int i = descendants.size() - 1; i >= 0; --i) - if(descendants[i]) foo = find_range_lo(i,foo); - return range(_hi,foo); - } - - /** add an element to a range */ - void scopes::range_add(int i, range &n){ - range foo = range(i, find_range_lo(i,0)); - n = range_lub(foo,n); - } - - /** Choose an element of rng1 that is near to rng2 */ - int scopes::range_near(const range &rng1, const range &rng2){ - - int frame; - int thing = tree_lca(rng1.hi,rng2.hi); - if(thing != rng1.hi) return rng1.hi; - range line = range(rng1.hi,find_range_lo(rng2.hi,(range_lo *)0)); - line = range_glb(line,rng1); - return range_min(line); - } - - - /** test whether a tree node is contained in a range */ - bool scopes::in_range(int n, const range &rng){ - range r = range_empty(); - range_add(n,r); - r = range_glb(rng,r); - return !range_is_empty(r); - } - - /** test whether two ranges of tree nodes intersect */ - bool scopes::ranges_intersect(const range &rng1, const range &rng2){ - range r = range_glb(rng1,rng2); - return !range_is_empty(r); - } - - - bool scopes::range_contained(const range &rng1, const range &rng2){ - range r = range_glb(rng1,rng2); - return r.hi == rng1.hi && r.lo == rng1.lo; - } - - -#endif - - +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3scopes.cpp + +Abstract: + + Calculations with scopes, for both sequence and tree interpolation. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + +#include + +#include + +#include "iz3scopes.h" + + +/** computes the least common ancestor of two nodes in the tree, or SHRT_MAX if none */ +int scopes::tree_lca(int n1, int n2){ + if(!tree_mode()) + return std::max(n1,n2); + if(n1 == SHRT_MIN) return n2; + if(n2 == SHRT_MIN) return n1; + if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; + while(n1 != n2){ + if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; + assert(n1 >= 0 && n2 >= 0 && n1 < (int)parents.size() && n2 < (int)parents.size()); + if(n1 < n2) n1 = parents[n1]; + else n2 = parents[n2]; + } + return n1; +} + +/** computes the greatest common descendant two nodes in the tree, or SHRT_MIN if none */ +int scopes::tree_gcd(int n1, int n2){ + if(!tree_mode()) + return std::min(n1,n2); + int foo = tree_lca(n1,n2); + if(foo == n1) return n2; + if(foo == n2) return n1; + return SHRT_MIN; +} + +#ifndef FULL_TREE + +/** test whether a tree node is contained in a range */ +bool scopes::in_range(int n, const range &rng){ + return tree_lca(rng.lo,n) == n && tree_gcd(rng.hi,n) == n; +} + +/** test whether two ranges of tree nodes intersect */ +bool scopes::ranges_intersect(const range &rng1, const range &rng2){ + return tree_lca(rng1.lo,rng2.hi) == rng2.hi && tree_lca(rng1.hi,rng2.lo) == rng1.hi; +} + + +bool scopes::range_contained(const range &rng1, const range &rng2){ + return tree_lca(rng2.lo,rng1.lo) == rng1.lo + && tree_lca(rng1.hi,rng2.hi) == rng2.hi; +} + +scopes::range scopes::range_lub(const range &rng1, const range &rng2){ + range res; + res.lo = tree_gcd(rng1.lo,rng2.lo); + res.hi = tree_lca(rng1.hi,rng2.hi); + return res; +} + +scopes::range scopes::range_glb(const range &rng1, const range &rng2){ + range res; + res.lo = tree_lca(rng1.lo,rng2.lo); + res.hi = tree_gcd(rng1.hi,rng2.hi); + return res; +} + +#else + + + namespace std { + template <> + class hash { + public: + size_t operator()(const scopes::range_lo &p) const { + return p.lo + (size_t)p.next; + } + }; + } + + template <> inline + size_t stdext::hash_value(const scopes::range_lo& p) + { + std::hash h; + return h(p); + } + + namespace std { + template <> + class less { + public: + bool operator()(const scopes::range_lo &x, const scopes::range_lo &y) const { + return x.lo < y.lo || x.lo == y.lo && (size_t)x.next < (size_t)y.next; + } + }; + } + + + struct range_op { + scopes::range_lo *x, *y; + int hi; + range_op(scopes::range_lo *_x, scopes::range_lo *_y, int _hi){ + x = _x; y = _y; hi = _hi; + } + }; + + namespace std { + template <> + class hash { + public: + size_t operator()(const range_op &p) const { + return (size_t) p.x + (size_t)p.y + p.hi; + } + }; + } + + template <> inline + size_t stdext::hash_value(const range_op& p) + { + std::hash h; + return h(p); + } + + namespace std { + template <> + class less { + public: + bool operator()(const range_op &x, const range_op &y) const { + return (size_t)x.x < (size_t)y.x || x.x == y.x && + ((size_t)x.y < (size_t)y.y || x.y == y.y && x.hi < y.hi); + } + }; + } + + struct range_tables { + hash_map unique; + hash_map lub; + hash_map glb; + }; + + + scopes::range_lo *scopes::find_range_lo(int lo, range_lo *next){ + range_lo foo(lo,next); + std::pair baz(foo,(range_lo *)0); + std::pair::iterator,bool> bar = rt->unique.insert(baz); + if(bar.second) + bar.first->second = new range_lo(lo,next); + return bar.first->second; + //std::pair::iterator,bool> bar = rt->unique.insert(foo); + // const range_lo *baz = &*(bar.first); + // return (range_lo *)baz; // exit const hell + } + + scopes::range_lo *scopes::range_lub_lo(range_lo *rng1, range_lo *rng2){ + if(!rng1) return rng2; + if(!rng2) return rng1; + if(rng1->lo > rng2->lo) + std::swap(rng1,rng2); + std::pair foo(range_op(rng1,rng2,0),(range_lo *)0); + std::pair::iterator,bool> bar = rt->lub.insert(foo); + range_lo *&res = bar.first->second; + if(!bar.second) return res; + if(!(rng1->next && rng1->next->lo <= rng2->lo)){ + for(int lo = rng1->lo; lo <= rng2->lo; lo = parents[lo]) + if(lo == rng2->lo) + {rng2 = rng2->next; break;} + } + range_lo *baz = range_lub_lo(rng1->next,rng2); + res = find_range_lo(rng1->lo,baz); + return res; + } + + + scopes::range_lo *scopes::range_glb_lo(range_lo *rng1, range_lo *rng2, int hi){ + if(!rng1) return rng1; + if(!rng2) return rng2; + if(rng1->lo > rng2->lo) + std::swap(rng1,rng2); + std::pair cand(range_op(rng1,rng2,hi),(range_lo *)0); + std::pair::iterator,bool> bar = rt->glb.insert(cand); + range_lo *&res = bar.first->second; + if(!bar.second) return res; + range_lo *foo; + if(!(rng1->next && rng1->next->lo <= rng2->lo)){ + int lim = hi; + if(rng1->next) lim = std::min(lim,rng1->next->lo); + int a = rng1->lo, b = rng2->lo; + while(a != b && b <= lim){ + a = parents[a]; + if(a > b)std::swap(a,b); + } + if(a == b && b <= lim){ + foo = range_glb_lo(rng1->next,rng2->next,hi); + foo = find_range_lo(b,foo); + } + else + foo = range_glb_lo(rng2,rng1->next,hi); + } + else foo = range_glb_lo(rng1->next,rng2,hi); + res = foo; + return res; + } + + /** computes the lub (smallest containing subtree) of two ranges */ + scopes::range scopes::range_lub(const range &rng1, const range &rng2){ + int hi = tree_lca(rng1.hi,rng2.hi); + if(hi == SHRT_MAX) return range_full(); + range_lo *lo = range_lub_lo(rng1.lo,rng2.lo); + return range(hi,lo); + } + + /** computes the glb (intersection) of two ranges */ + scopes::range scopes::range_glb(const range &rng1, const range &rng2){ + if(rng1.hi == SHRT_MAX) return rng2; + if(rng2.hi == SHRT_MAX) return rng1; + int hi = tree_gcd(rng1.hi,rng2.hi); + range_lo *lo = hi == SHRT_MIN ? 0 : range_glb_lo(rng1.lo,rng2.lo,hi); + if(!lo) hi = SHRT_MIN; + return range(hi,lo); + } + + /** is this range empty? */ + bool scopes::range_is_empty(const range &rng){ + return rng.hi == SHRT_MIN; + } + + /** return an empty range */ + scopes::range scopes::range_empty(){ + return range(SHRT_MIN,0); + } + + /** return a full range */ + scopes::range scopes::range_full(){ + return range(SHRT_MAX,0); + } + + /** return the maximal element of a range */ + int scopes::range_max(const range &rng){ + return rng.hi; + } + + /** return a minimal (not necessarily unique) element of a range */ + int scopes::range_min(const range &rng){ + if(rng.hi == SHRT_MAX) return SHRT_MIN; + return rng.lo ? rng.lo->lo : SHRT_MAX; + } + + + /** return range consisting of downward closure of a point */ + scopes::range scopes::range_downward(int _hi){ + std::vector descendants(parents.size()); + for(int i = descendants.size() - 1; i >= 0 ; i--) + descendants[i] = i == _hi || parents[i] < parents.size() && descendants[parents[i]]; + for(unsigned i = 0; i < descendants.size() - 1; i++) + if(parents[i] < parents.size()) + descendants[parents[i]] = false; + range_lo *foo = 0; + for(int i = descendants.size() - 1; i >= 0; --i) + if(descendants[i]) foo = find_range_lo(i,foo); + return range(_hi,foo); + } + + /** add an element to a range */ + void scopes::range_add(int i, range &n){ + range foo = range(i, find_range_lo(i,0)); + n = range_lub(foo,n); + } + + /** Choose an element of rng1 that is near to rng2 */ + int scopes::range_near(const range &rng1, const range &rng2){ + + int frame; + int thing = tree_lca(rng1.hi,rng2.hi); + if(thing != rng1.hi) return rng1.hi; + range line = range(rng1.hi,find_range_lo(rng2.hi,(range_lo *)0)); + line = range_glb(line,rng1); + return range_min(line); + } + + + /** test whether a tree node is contained in a range */ + bool scopes::in_range(int n, const range &rng){ + range r = range_empty(); + range_add(n,r); + r = range_glb(rng,r); + return !range_is_empty(r); + } + + /** test whether two ranges of tree nodes intersect */ + bool scopes::ranges_intersect(const range &rng1, const range &rng2){ + range r = range_glb(rng1,rng2); + return !range_is_empty(r); + } + + + bool scopes::range_contained(const range &rng1, const range &rng2){ + range r = range_glb(rng1,rng2); + return r.hi == rng1.hi && r.lo == rng1.lo; + } + + +#endif + + diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h index 54cf89aba..cd7203eeb 100755 --- a/src/interp/iz3scopes.h +++ b/src/interp/iz3scopes.h @@ -1,197 +1,197 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - iz3scopes.h - -Abstract: - - Calculations with scopes, for both sequence and tree interpolation. - -Author: - - Ken McMillan (kenmcmil) - -Revision History: - ---*/ - - -#ifndef IZ3SOPES_H -#define IZ3SOPES_H - -#include -#include - -class scopes { - - public: - /** Construct from parents vector. */ - scopes(const std::vector &_parents){ - parents = _parents; - } - - scopes(){ - } - - void initialize(const std::vector &_parents){ - parents = _parents; - } - - /** The parents vector defining the tree structure */ - std::vector parents; - - // #define FULL_TREE -#ifndef FULL_TREE - struct range { - range(){ - lo = SHRT_MAX; - hi = SHRT_MIN; - } - short lo, hi; - }; - - /** computes the lub (smallest containing subtree) of two ranges */ - range range_lub(const range &rng1, const range &rng2); - - /** computes the glb (intersection) of two ranges */ - range range_glb(const range &rng1, const range &rng2); - - /** is this range empty? */ - bool range_is_empty(const range &rng){ - return rng.hi < rng.lo; - } - - /** return an empty range */ - range range_empty(){ - range res; - res.lo = SHRT_MAX; - res.hi = SHRT_MIN; - return res; - } - - /** return an empty range */ - range range_full(){ - range res; - res.lo = SHRT_MIN; - res.hi = SHRT_MAX; - return res; - } - - /** return the maximal element of a range */ - int range_max(const range &rng){ - return rng.hi; - } - - /** return a minimal (not necessarily unique) element of a range */ - int range_min(const range &rng){ - return rng.lo; - } - - /** return range consisting of downward closure of a point */ - range range_downward(int _hi){ - range foo; - foo.lo = SHRT_MIN; - foo.hi = _hi; - return foo; - } - - void range_add(int i, range &n){ -#if 0 - if(i < n.lo) n.lo = i; - if(i > n.hi) n.hi = i; -#else - range rng; rng.lo = i; rng.hi = i; - n = range_lub(rng,n); -#endif - } - - /** Choose an element of rng1 that is near to rng2 */ - int range_near(const range &rng1, const range &rng2){ - int frame; - int thing = tree_lca(rng1.lo,rng2.hi); - if(thing == rng1.lo) frame = rng1.lo; - else frame = tree_gcd(thing,rng1.hi); - return frame; - } -#else - - struct range_lo { - int lo; - range_lo *next; - range_lo(int _lo, range_lo *_next){ - lo = _lo; - next = _next; - } - }; - - struct range { - int hi; - range_lo *lo; - range(int _hi, range_lo *_lo){ - hi = _hi; - lo = _lo; - } - range(){ - hi = SHRT_MIN; - lo = 0; - } - }; - - range_tables *rt; - - /** computes the lub (smallest containing subtree) of two ranges */ - range range_lub(const range &rng1, const range &rng2); - - /** computes the glb (intersection) of two ranges */ - range range_glb(const range &rng1, const range &rng2); - - /** is this range empty? */ - bool range_is_empty(const range &rng); - - /** return an empty range */ - range range_empty(); - - /** return a full range */ - range range_full(); - - /** return the maximal element of a range */ - int range_max(const range &rng); - - /** return a minimal (not necessarily unique) element of a range */ - int range_min(const range &rng); - - /** return range consisting of downward closure of a point */ - range range_downward(int _hi); - - /** add an element to a range */ - void range_add(int i, range &n); - - /** Choose an element of rng1 that is near to rng2 */ - int range_near(const range &rng1, const range &rng2); - - range_lo *find_range_lo(int lo, range_lo *next); - range_lo *range_lub_lo(range_lo *rng1, range_lo *rng2); - range_lo *range_glb_lo(range_lo *rng1, range_lo *rng2, int lim); - -#endif - - /** test whether a tree node is contained in a range */ - bool in_range(int n, const range &rng); - - /** test whether two ranges of tree nodes intersect */ - bool ranges_intersect(const range &rng1, const range &rng2); - - /** test whether range rng1 contained in range rng2 */ - bool range_contained(const range &rng1, const range &rng2); - - private: - int tree_lca(int n1, int n2); - int tree_gcd(int n1, int n2); - bool tree_mode(){return parents.size() != 0;} - - - -}; -#endif +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + iz3scopes.h + +Abstract: + + Calculations with scopes, for both sequence and tree interpolation. + +Author: + + Ken McMillan (kenmcmil) + +Revision History: + +--*/ + + +#ifndef IZ3SOPES_H +#define IZ3SOPES_H + +#include +#include + +class scopes { + + public: + /** Construct from parents vector. */ + scopes(const std::vector &_parents){ + parents = _parents; + } + + scopes(){ + } + + void initialize(const std::vector &_parents){ + parents = _parents; + } + + /** The parents vector defining the tree structure */ + std::vector parents; + + // #define FULL_TREE +#ifndef FULL_TREE + struct range { + range(){ + lo = SHRT_MAX; + hi = SHRT_MIN; + } + short lo, hi; + }; + + /** computes the lub (smallest containing subtree) of two ranges */ + range range_lub(const range &rng1, const range &rng2); + + /** computes the glb (intersection) of two ranges */ + range range_glb(const range &rng1, const range &rng2); + + /** is this range empty? */ + bool range_is_empty(const range &rng){ + return rng.hi < rng.lo; + } + + /** return an empty range */ + range range_empty(){ + range res; + res.lo = SHRT_MAX; + res.hi = SHRT_MIN; + return res; + } + + /** return an empty range */ + range range_full(){ + range res; + res.lo = SHRT_MIN; + res.hi = SHRT_MAX; + return res; + } + + /** return the maximal element of a range */ + int range_max(const range &rng){ + return rng.hi; + } + + /** return a minimal (not necessarily unique) element of a range */ + int range_min(const range &rng){ + return rng.lo; + } + + /** return range consisting of downward closure of a point */ + range range_downward(int _hi){ + range foo; + foo.lo = SHRT_MIN; + foo.hi = _hi; + return foo; + } + + void range_add(int i, range &n){ +#if 0 + if(i < n.lo) n.lo = i; + if(i > n.hi) n.hi = i; +#else + range rng; rng.lo = i; rng.hi = i; + n = range_lub(rng,n); +#endif + } + + /** Choose an element of rng1 that is near to rng2 */ + int range_near(const range &rng1, const range &rng2){ + int frame; + int thing = tree_lca(rng1.lo,rng2.hi); + if(thing == rng1.lo) frame = rng1.lo; + else frame = tree_gcd(thing,rng1.hi); + return frame; + } +#else + + struct range_lo { + int lo; + range_lo *next; + range_lo(int _lo, range_lo *_next){ + lo = _lo; + next = _next; + } + }; + + struct range { + int hi; + range_lo *lo; + range(int _hi, range_lo *_lo){ + hi = _hi; + lo = _lo; + } + range(){ + hi = SHRT_MIN; + lo = 0; + } + }; + + range_tables *rt; + + /** computes the lub (smallest containing subtree) of two ranges */ + range range_lub(const range &rng1, const range &rng2); + + /** computes the glb (intersection) of two ranges */ + range range_glb(const range &rng1, const range &rng2); + + /** is this range empty? */ + bool range_is_empty(const range &rng); + + /** return an empty range */ + range range_empty(); + + /** return a full range */ + range range_full(); + + /** return the maximal element of a range */ + int range_max(const range &rng); + + /** return a minimal (not necessarily unique) element of a range */ + int range_min(const range &rng); + + /** return range consisting of downward closure of a point */ + range range_downward(int _hi); + + /** add an element to a range */ + void range_add(int i, range &n); + + /** Choose an element of rng1 that is near to rng2 */ + int range_near(const range &rng1, const range &rng2); + + range_lo *find_range_lo(int lo, range_lo *next); + range_lo *range_lub_lo(range_lo *rng1, range_lo *rng2); + range_lo *range_glb_lo(range_lo *rng1, range_lo *rng2, int lim); + +#endif + + /** test whether a tree node is contained in a range */ + bool in_range(int n, const range &rng); + + /** test whether two ranges of tree nodes intersect */ + bool ranges_intersect(const range &rng1, const range &rng2); + + /** test whether range rng1 contained in range rng2 */ + bool range_contained(const range &rng1, const range &rng2); + + private: + int tree_lca(int n1, int n2); + int tree_gcd(int n1, int n2); + bool tree_mode(){return parents.size() != 0;} + + + +}; +#endif diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 2c4a22724..a7c2125e7 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -464,7 +464,7 @@ public: for(int i = 0; i < 2; i++){ // try the second equality both ways if(match_op(eq_ops_r[0],Select,sel_ops,2)) if(match_op(sel_ops[0],Store,sto_ops,3)) - if(match_op(eq_ops_r[1],Select,sel_ops2,2)) + if(match_op(eq_ops_r[1],Select,sel_ops2,2)) for(int j = 0; j < 2; j++){ // try the first equality both ways if(eq_ops_l[0] == sto_ops[1] && eq_ops_l[1] == sel_ops[1] @@ -482,8 +482,8 @@ public: // int frame = range_min(ast_scope(res)); TODO // antes.push_back(std::pair(res,frame)); return; - } - std::swap(eq_ops_l[0],eq_ops_l[1]); + } + std::swap(eq_ops_l[0],eq_ops_l[1]); } std::swap(eq_ops_r[0],eq_ops_r[1]); } diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index e5b8a5fca..9cef3a686 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -484,7 +484,7 @@ public: for(int i = 0; i < 2; i++){ // try the second equality both ways if(match_op(eq_ops_r[0],Select,sel_ops,2)) if(match_op(sel_ops[0],Store,sto_ops,3)) - if(match_op(eq_ops_r[1],Select,sel_ops2,2)) + if(match_op(eq_ops_r[1],Select,sel_ops2,2)) for(int j = 0; j < 2; j++){ // try the first equality both ways if(eq_ops_l[0] == sto_ops[1] && eq_ops_l[1] == sel_ops[1] @@ -502,8 +502,8 @@ public: int frame = range_min(ast_scope(res)); antes.push_back(std::pair(res,frame)); return; - } - std::swap(eq_ops_l[0],eq_ops_l[1]); + } + std::swap(eq_ops_l[0],eq_ops_l[1]); } std::swap(eq_ops_r[0],eq_ops_r[1]); } From 43644fc2cbe8da8c74bdced967b8b9b9db50f677 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 4 Apr 2014 01:28:09 +0100 Subject: [PATCH 320/509] g++ pedantry --- src/interp/iz3hash.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index 355c03817..a12cfb990 100644 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -464,7 +464,9 @@ namespace hash_space { Value &operator[](const Key& key) { std::pair kvp(key,Value()); - return lookup(kvp,true)->val.second; + return + hashtable,Key,HashFun,proj1,EqFun>:: + lookup(kvp,true)->val.second; } }; From 9c052f589d7a535ea46837f1feff095e57cfc878 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 4 Apr 2014 17:57:50 +0100 Subject: [PATCH 321/509] C API bugfix (Stackoverflow #22864146) Signed-off-by: Christoph M. Wintersteiger --- src/api/api_bv.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/api/api_bv.cpp b/src/api/api_bv.cpp index 07d4fda18..0109d6e6b 100644 --- a/src/api/api_bv.cpp +++ b/src/api/api_bv.cpp @@ -117,6 +117,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_sort int_s = Z3_mk_int_sort(c); if (is_signed) { Z3_ast r = Z3_mk_bv2int(c, n, false); + Z3_inc_ref(c, r); Z3_sort s = Z3_get_sort(c, n); unsigned sz = Z3_get_bv_sort_size(c, s); rational max_bound = power(rational(2), sz); @@ -135,6 +136,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_dec_ref(c, pred); Z3_dec_ref(c, sub); Z3_dec_ref(c, zero); + Z3_dec_ref(c, r); RETURN_Z3(res); } else { From bdc7bfde875a53fcc782bc71bd26610013c2c04d Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 4 Apr 2014 13:10:18 -0700 Subject: [PATCH 322/509] duality quantifier simplification fix --- src/duality/duality_rpfp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 8040310ec..ef37dd8a2 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -561,7 +561,7 @@ namespace Duality { } decl_kind op = is_forall ? Or : And; if(free.empty()) - return SimplifyAndOr(not_free,op == And); + return DeleteBound(0,1,SimplifyAndOr(not_free,op == And)); expr q = clone_quantifier(is_forall ? Forall : Exists,t, SimplifyAndOr(free, op == And)); if(!not_free.empty()) q = ctx.make(op,q,DeleteBound(0,1,SimplifyAndOr(not_free, op == And))); From 58ffffe4d4f3756381910e62c1fc4a5f169cb4c0 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 6 Apr 2014 13:01:20 -0700 Subject: [PATCH 323/509] hack to filter out Boogie axioms with large "distinct" predicates that cause legacy solver death --- src/duality/duality.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/duality/duality.h b/src/duality/duality.h index fc70ffa70..1445bc881 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -253,6 +253,10 @@ protected: } void assert_axiom(const expr &axiom){ + // HACK: large "distict" predicates can kill the legacy SMT solver -- ignore them + if(axiom.is_app() && axiom.decl().get_decl_kind() == Distinct) + if(axiom.num_args() > 10) + return; islvr->AssertInterpolationAxiom(axiom); } From b6c0b8c9ff3dc85f1e2841e3a88c1569d09584f3 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 7 Apr 2014 16:09:22 +0100 Subject: [PATCH 324/509] Compilation fix for FreeBSD --- scripts/mk_util.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 883492614..89cabe87e 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -54,6 +54,7 @@ CPP_COMPONENT='cpp' IS_WINDOWS=False IS_LINUX=False IS_OSX=False +IS_FREEBSD=False VERBOSE=True DEBUG_MODE=False SHOW_CPPS = True @@ -98,6 +99,9 @@ def is_windows(): def is_linux(): return IS_LINUX +def is_freebsd(): + return IS_FREEBSD + def is_osx(): return IS_OSX @@ -426,6 +430,8 @@ elif os.name == 'posix': IS_OSX=True elif os.uname()[0] == 'Linux': IS_LINUX=True + elif os.uname()[0] == 'FreeBSD': + IS_FREEBSD=True def display_help(exit_code): print("mk_make.py: Z3 Makefile generator\n") @@ -1181,6 +1187,8 @@ class JavaDLLComponent(Component): t = t.replace('PLATFORM', 'darwin') elif IS_LINUX: t = t.replace('PLATFORM', 'linux') + elif IS_FREEBSD: + t = t.replace('PLATFORM', 'freebsd') else: t = t.replace('PLATFORM', 'win32') out.write(t) From a3b89a8af34b987a368a118790917795f2607581 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 9 Apr 2014 11:24:42 +0100 Subject: [PATCH 325/509] .NET API documentation fixes Signed-off-by: Christoph M. Wintersteiger --- src/api/dotnet/Context.cs | 20 ++++++++++---------- src/api/dotnet/Global.cs | 2 +- src/api/dotnet/Solver.cs | 6 ++++-- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 47294244a..41ac1fa31 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -302,11 +302,11 @@ namespace Microsoft.Z3 } /// - /// Create a new finite domain sort. - /// The name used to identify the sort - /// The size of the sort - /// The result is a sort + /// Create a new finite domain sort. + /// The result is a sort /// + /// The name used to identify the sort + /// The size of the sort public FiniteDomainSort MkFiniteDomainSort(Symbol name, ulong size) { Contract.Requires(name != null); @@ -317,13 +317,13 @@ namespace Microsoft.Z3 } /// - /// Create a new finite domain sort. - /// The name used to identify the sort - /// The size of the sort - /// The result is a sort - /// Elements of the sort are created using , - /// and the elements range from 0 to size-1. + /// Create a new finite domain sort. + /// The result is a sort + /// Elements of the sort are created using , + /// and the elements range from 0 to size-1. /// + /// The name used to identify the sort + /// The size of the sort public FiniteDomainSort MkFiniteDomainSort(string name, ulong size) { Contract.Ensures(Contract.Result() != null); diff --git a/src/api/dotnet/Global.cs b/src/api/dotnet/Global.cs index 707264c82..787c9b788 100644 --- a/src/api/dotnet/Global.cs +++ b/src/api/dotnet/Global.cs @@ -43,7 +43,7 @@ namespace Microsoft.Z3 /// The parameter names are case-insensitive. The character '-' should be viewed as an "alias" for '_'. /// Thus, the following parameter names are considered equivalent: "pp.decimal-precision" and "PP.DECIMAL_PRECISION". /// This function can be used to set parameters for a specific Z3 module. - /// This can be done by using .. + /// This can be done by using [module-name].[parameter-name]. /// For example: /// Z3_global_param_set('pp.decimal', 'true') /// will set the parameter "decimal" in the module "pp" to true. diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index 555a2466f..9de515e86 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -132,7 +132,8 @@ namespace Microsoft.Z3 /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination - /// of the Boolean variables provided using and the Boolean literals + /// of the Boolean variables provided using + /// and the Boolean literals /// provided using with assumptions. /// public void AssertAndTrack(BoolExpr[] constraints, BoolExpr[] ps) @@ -156,7 +157,8 @@ namespace Microsoft.Z3 /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination - /// of the Boolean variables provided using and the Boolean literals + /// of the Boolean variables provided using + /// and the Boolean literals /// provided using with assumptions. /// public void AssertAndTrack(BoolExpr constraint, BoolExpr p) From aa006fa2372bf8b17e0db7d1ae78672b8767c812 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 9 Apr 2014 11:31:44 +0100 Subject: [PATCH 326/509] added dotnet generated files to .gitignore Signed-off-by: Christoph M. Wintersteiger --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 77f4d1896..647529b4f 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,8 @@ src/api/api_log_macros.cpp src/api/dll/api_dll.def src/api/dotnet/Enumerations.cs src/api/dotnet/Native.cs +src/api/dotnet/Properties/AssemblyInfo.cs +src/api/dotnet/Microsoft.Z3.xml src/api/python/z3consts.py src/api/python/z3core.py src/ast/pattern/database.h From 64bfbb657c77c57390d6f5c1603693d77ed230c2 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 9 Apr 2014 11:39:05 +0100 Subject: [PATCH 327/509] .NET API documentation XML build fix Signed-off-by: Christoph M. Wintersteiger --- src/api/dotnet/Microsoft.Z3.csproj | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/api/dotnet/Microsoft.Z3.csproj b/src/api/dotnet/Microsoft.Z3.csproj index 9eb9eb660..0a062054d 100644 --- a/src/api/dotnet/Microsoft.Z3.csproj +++ b/src/api/dotnet/Microsoft.Z3.csproj @@ -24,8 +24,7 @@ prompt 4 true - - + ..\Debug\Microsoft.Z3.XML False False True @@ -140,6 +139,7 @@ Full %28none%29 0 + ..\x64\Debug\Microsoft.Z3.XML ..\x64\external_64\ @@ -193,7 +193,7 @@ ..\x64\external\ true - ..\external\Microsoft.Z3.xml + ..\x64\external\Microsoft.Z3.XML true pdbonly x64 @@ -220,7 +220,7 @@ ..\Release_delaysign\ true - ..\Release_delaysign\Microsoft.Z3.xml + ..\Release_delaysign\Microsoft.Z3.XML true pdbonly AnyCPU @@ -238,7 +238,7 @@ bin\x64\Release_delaysign\ true - ..\x64\external_64\Microsoft.Z3.xml + bin\x64\Release_delaysign\Microsoft.Z3.XML true pdbonly x64 @@ -266,11 +266,12 @@ MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + bin\x86\Debug\Microsoft.Z3.XML bin\x86\Release\ true - ..\external\Microsoft.Z3.xml + bin\x86\Release\Microsoft.Z3.xml true pdbonly x86 @@ -285,7 +286,7 @@ bin\x86\external\ true - ..\external\Microsoft.Z3.xml + bin\x86\external\Microsoft.Z3.XML true pdbonly x86 @@ -303,7 +304,7 @@ bin\x86\Release_delaysign\ DELAYSIGN true - ..\Release_delaysign\Microsoft.Z3.xml + bin\x86\Release_delaysign\Microsoft.Z3.XML true pdbonly x86 @@ -399,4 +400,4 @@ --> - + \ No newline at end of file From 52b54f395b88926703ed390b04ea7767bbadacd3 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 10 Apr 2014 19:33:34 +0100 Subject: [PATCH 328/509] FPA division bugfix Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 2d1d6705f..533988689 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -872,8 +872,19 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(extra_bits-2, 0, quotient)); res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(extra_bits+sbits+1, extra_bits-1, quotient), sticky); - SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); + SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); + expr_ref res_sig_lz(m); + mk_leading_zeros(res_sig, sbits + 4, res_sig_lz); + dbg_decouple("fpa2bv_div_res_sig_lz", res_sig_lz); + expr_ref res_sig_shift_amount(m); + res_sig_shift_amount = m_bv_util.mk_bv_sub(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); + dbg_decouple("fpa2bv_div_res_sig_shift_amount", res_sig_shift_amount); + expr_ref shift_cond(m); + shift_cond = m_bv_util.mk_ule(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); + m_simp.mk_ite(shift_cond, res_sig, m_bv_util.mk_bv_shl(res_sig, res_sig_shift_amount), res_sig); + m_simp.mk_ite(shift_cond, res_exp, m_bv_util.mk_bv_sub(res_exp, m_bv_util.mk_extract(ebits+1, 0, res_sig_shift_amount)), res_exp); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, v8); // And finally, we tie them together. From f7d589fc49d1ed30761705732dcfaf7338777085 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 10 Apr 2014 17:53:00 -0700 Subject: [PATCH 329/509] changed fixedpoint output format for easier parsing in Boogie --- src/muz/duality/duality_dl_interface.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 587a184bc..99ac2ee1c 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -350,7 +350,9 @@ void dl_interface::display_certificate_non_const(std::ostream& out) { if(_d->status == StatusModel){ ast_manager &m = m_ctx.get_manager(); model_ref md = get_model(); + out << "(fixedpoint \n"; model_smt2_pp(out, m, *md.get(), 0); + out << ")\n"; } else if(_d->status == StatusRefutation){ out << "(derivation\n"; From de81db9a3bb451fb70826b5bee3c28a8adb88dd3 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 10 Apr 2014 17:53:17 -0700 Subject: [PATCH 330/509] fixed several interpolation problems --- src/interp/iz3base.cpp | 11 +++- src/interp/iz3base.h | 2 +- src/interp/iz3translate.cpp | 122 ++++++++++++++++++++++++++++++++++-- 3 files changed, 128 insertions(+), 7 deletions(-) diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 32fe33566..8f98a23f9 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -260,7 +260,7 @@ void iz3base::check_interp(const std::vector &itps, std::vector &theor #endif } -bool iz3base::is_sat(const std::vector &q, ast &_proof){ +bool iz3base::is_sat(const std::vector &q, ast &_proof, std::vector &vars){ params_ref p; p.set_bool("proof", true); // this is currently useless @@ -277,6 +277,15 @@ bool iz3base::is_sat(const std::vector &q, ast &_proof){ ::ast *proof = s.get_proof(); _proof = cook(proof); } + else if(vars.size()) { + model_ref(_m); + s.get_model(_m); + for(unsigned i = 0; i < vars.size(); i++){ + expr_ref r(m()); + _m.get()->eval(to_expr(vars[i].raw()),r,true); + vars[i] = cook(r.get()); + } + } dealloc(m_solver); return res != l_false; } diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index d82e721ae..bca9b8518 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -113,7 +113,7 @@ class iz3base : public iz3mgr, public scopes { void check_interp(const std::vector &itps, std::vector &theory); /** For convenience -- is this formula SAT? */ - bool is_sat(const std::vector &consts, ast &_proof); + bool is_sat(const std::vector &consts, ast &_proof, std::vector &vars); /** Interpolator for clauses, to be implemented */ virtual void interpolate_clause(std::vector &lits, std::vector &itps){ diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index a7c2125e7..060bb74ad 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1071,6 +1071,8 @@ public: ast AssignBounds2Farkas(const ast &proof, const ast &con){ std::vector farkas_coeffs; get_assign_bounds_coeffs(proof,farkas_coeffs); + if(farkas_coeffs[0] != make_int(rational(1))) + farkas_coeffs[0] = make_int(rational(1)); std::vector lits; int nargs = num_args(con); if(nargs != (int)(farkas_coeffs.size())) @@ -1107,6 +1109,8 @@ public: ast AssignBoundsRule2Farkas(const ast &proof, const ast &con, std::vector prems){ std::vector farkas_coeffs; get_assign_bounds_rule_coeffs(proof,farkas_coeffs); + if(farkas_coeffs[0] != make_int(rational(1))) + farkas_coeffs[0] = make_int(rational(1)); std::vector lits; int nargs = num_prems(proof)+1; if(nargs != (int)(farkas_coeffs.size())) @@ -1278,6 +1282,17 @@ public: return make(Plus,args); } + void get_sum_as_vector(const ast &t, std::vector &coeffs, std::vector &vars){ + if(!(op(t) == Plus)){ + coeffs.push_back(get_coeff(t)); + vars.push_back(get_linear_var(t)); + } + else { + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + get_sum_as_vector(arg(t,i),coeffs,vars); + } + } ast replace_summands_with_fresh_vars(const ast &t, hash_map &map){ if(op(t) == Plus){ @@ -1294,6 +1309,94 @@ public: return map[t]; } + rational lcd(const std::vector &rats){ + rational res = rational(1); + for(unsigned i = 0; i < rats.size(); i++){ + res = lcm(res,denominator(rats[i])); + } + return res; + } + + Iproof::node reconstruct_farkas_with_dual(const std::vector &prems, const std::vector &pfs, const ast &con){ + int nprems = prems.size(); + std::vector npcons(nprems); + hash_map pain_map; // not needed + for(int i = 0; i < nprems; i++) + npcons[i] = painfully_normalize_ineq(conc(prems[i]),pain_map); + ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); + npcons.push_back(ncon); + + hash_map dual_map; + std::vector cvec, vars_seen; + ast rhs = make_real(rational(0)); + for(unsigned i = 0; i < npcons.size(); i++){ + ast c= mk_fresh_constant("@c",real_type()); + cvec.push_back(c); + ast lhs = arg(npcons[i],0); + std::vector coeffs; + std::vector vars; + get_sum_as_vector(lhs,coeffs,vars); + for(unsigned j = 0; j < coeffs.size(); j++){ + rational coeff = coeffs[j]; + ast var = vars[j]; + if(dual_map.find(var) == dual_map.end()){ + dual_map[var] = make_real(rational(0)); + vars_seen.push_back(var); + } + ast foo = make(Plus,dual_map[var],make(Times,make_real(coeff),c)); + dual_map[var] = foo; + } + rhs = make(Plus,rhs,make(Times,c,arg(npcons[i],1))); + } + std::vector cnstrs; + for(unsigned i = 0; i < vars_seen.size(); i++) + cnstrs.push_back(make(Equal,dual_map[vars_seen[i]],make_real(rational(0)))); + cnstrs.push_back(make(Leq,rhs,make_real(rational(0)))); + for(unsigned i = 0; i < cvec.size() - 1; i++) + cnstrs.push_back(make(Geq,cvec[i],make_real(rational(0)))); + cnstrs.push_back(make(Equal,cvec.back(),make_real(rational(1)))); + ast new_proof; + + // greedily reduce the core + for(unsigned i = 0; i < cvec.size() - 1; i++){ + std::vector dummy; + cnstrs.push_back(make(Equal,cvec[i],make_real(rational(0)))); + if(!is_sat(cnstrs,new_proof,dummy)) + cnstrs.pop_back(); + } + + std::vector vals = cvec; + if(!is_sat(cnstrs,new_proof,vals)) + throw "Proof error!"; + std::vector rat_farkas_coeffs; + for(unsigned i = 0; i < cvec.size(); i++){ + ast bar = vals[i]; + rational r; + if(is_numeral(bar,r)) + rat_farkas_coeffs.push_back(r); + else + throw "Proof error!"; + } + rational the_lcd = lcd(rat_farkas_coeffs); + std::vector farkas_coeffs; + std::vector my_prems; + std::vector my_pcons; + for(unsigned i = 0; i < prems.size(); i++){ + ast fc = make_int(rat_farkas_coeffs[i] * the_lcd); + if(!(fc == make_int(rational(0)))){ + farkas_coeffs.push_back(fc); + my_prems.push_back(pfs[i]); + my_pcons.push_back(conc(prems[i])); + } + } + farkas_coeffs.push_back(make_int(the_lcd)); + my_prems.push_back(iproof->make_hypothesis(mk_not(con))); + my_pcons.push_back(mk_not(con)); + + Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); + return res; + } + ast painfully_normalize_ineq(const ast &ineq, hash_map &map){ ast res = normalize_inequality(ineq); ast lhs = arg(res,0); @@ -1318,7 +1421,8 @@ public: npcons.push_back(ncon); // ast assumps = make(And,pcons); ast new_proof; - if(is_sat(npcons,new_proof)) + std::vector dummy; + if(is_sat(npcons,new_proof,dummy)) throw "Proof error!"; pfrule dk = pr(new_proof); int nnp = num_prems(new_proof); @@ -1334,7 +1438,7 @@ public: farkas_coeffs.push_back(make_int(rational(1))); } else - throw "cannot reconstruct farkas proof"; + return reconstruct_farkas_with_dual(prems,pfs,con); for(int i = 0; i < nnp; i++){ ast p = conc(prem(new_proof,i)); @@ -1348,7 +1452,7 @@ public: my_pcons.push_back(mk_not(con)); } else - throw "cannot reconstruct farkas proof"; + return reconstruct_farkas_with_dual(prems,pfs,con); } Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); return res; @@ -1378,7 +1482,8 @@ public: npcons.push_back(ncon); // ast assumps = make(And,pcons); ast new_proof; - if(is_sat(npcons,new_proof)) + std::vector dummy; + if(is_sat(npcons,new_proof,dummy)) throw "Proof error!"; pfrule dk = pr(new_proof); int nnp = num_prems(new_proof); @@ -1408,7 +1513,7 @@ public: my_pcons.push_back(mk_not(con)); } else - throw "cannot reconstruct farkas proof"; + return painfully_reconstruct_farkas(prems,pfs,con); } Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); return res; @@ -1552,6 +1657,13 @@ public: if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or) std::cout << "foo!\n"; + // no idea why this shows up + if(dk == PR_MODUS_PONENS_OEQ) + if(conc(prem(proof,0)) == con){ + res = translate_main(prem(proof,0),expect_clause); + return res; + } + #if 0 if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,1)) == PR_COMMUTATIVITY){ Iproof::node clause = translate_main(prem(proof,0),true); From 60ef669fbc8b3fd3a1aaa10e179c2edee41eadbe Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 10 Apr 2014 17:54:49 -0700 Subject: [PATCH 331/509] removed distinct predicate hack --- src/duality/duality.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/duality/duality.h b/src/duality/duality.h index 1445bc881..572ce427b 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -253,10 +253,12 @@ protected: } void assert_axiom(const expr &axiom){ +#if 0 // HACK: large "distict" predicates can kill the legacy SMT solver -- ignore them if(axiom.is_app() && axiom.decl().get_decl_kind() == Distinct) if(axiom.num_args() > 10) return; +#endif islvr->AssertInterpolationAxiom(axiom); } From 77f8aa9f6b53114fea43d5c5f76b3724533079a7 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 22 Apr 2014 13:28:11 -0700 Subject: [PATCH 332/509] fix for quantifiers in interpolants --- src/interp/iz3proof_itp.cpp | 15 +++++++++++++-- src/interp/iz3scopes.h | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 5fe3338dc..8913920ac 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2655,8 +2655,13 @@ class iz3proof_itp_impl : public iz3proof_itp { }; std::vector localization_vars; // localization vars in order of creation - hash_map localization_map; // maps terms to their localization vars - hash_map localization_pf_map; // maps terms to proofs of their localizations + + struct locmaps { + hash_map localization_map; // maps terms to their localization vars + hash_map localization_pf_map; // maps terms to proofs of their localizations + }; + + hash_map localization_maps_per_range; /* "localize" a term e to a given frame range, creating new symbols to represent non-local subterms. This returns the localized version e_l, @@ -2678,6 +2683,12 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast localize_term(ast e, const prover::range &rng, ast &pf){ + + // we need to memoize this function per range + locmaps &maps = localization_maps_per_range[rng]; + hash_map &localization_map = maps.localization_map; + hash_map &localization_pf_map = maps.localization_pf_map; + ast orig_e = e; pf = make_refl(e); // proof that e = e diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h index cd7203eeb..23ab1c714 100755 --- a/src/interp/iz3scopes.h +++ b/src/interp/iz3scopes.h @@ -23,6 +23,7 @@ Revision History: #include #include +#include "iz3hash.h" class scopes { @@ -194,4 +195,23 @@ class scopes { }; + +// let us hash on ranges + +#ifndef FULL_TREE +namespace hash_space { + template <> + class hash { + public: + size_t operator()(const scopes::range &p) const { + return (size_t)p.lo + (size_t)p.hi; + } + }; +} + +inline bool operator==(const scopes::range &x, const scopes::range &y){ + return x.lo == y.lo && x.hi == y.hi; +} +#endif + #endif From 2755854c81e9cf6ec3da1d4b9ae0685e24252d67 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 22 Apr 2014 16:42:35 -0700 Subject: [PATCH 333/509] trying alternate encoding of distint --- src/duality/duality.h | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 572ce427b..77fbdfd55 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -253,11 +253,26 @@ protected: } void assert_axiom(const expr &axiom){ -#if 0 - // HACK: large "distict" predicates can kill the legacy SMT solver -- ignore them +#if 1 + // HACK: large "distict" predicates can kill the legacy SMT solver. + // encode them with a UIF if(axiom.is_app() && axiom.decl().get_decl_kind() == Distinct) - if(axiom.num_args() > 10) + if(axiom.num_args() > 10){ + sort s = axiom.arg(0).get_sort(); + std::vector sv; + sv.push_back(s); + int nargs = axiom.num_args(); + std::vector args(nargs); + func_decl f = ctx->fresh_func_decl("@distinct",sv,ctx->int_sort()); + for(int i = 0; i < nargs; i++){ + expr a = axiom.arg(i); + expr new_cnstr = f(a) == ctx->int_val(i); + args[i] = new_cnstr; + } + expr cnstr = ctx->make(And,args); + islvr->AssertInterpolationAxiom(cnstr); return; + } #endif islvr->AssertInterpolationAxiom(axiom); } From 7d16ed9fdceb73af43843404834f8daee459f85d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Apr 2014 14:13:01 +0200 Subject: [PATCH 334/509] fix exception class in python API Signed-off-by: Nikolaj Bjorner --- src/api/python/z3printer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3printer.py b/src/api/python/z3printer.py index d3dc43d3e..d1d85d30e 100644 --- a/src/api/python/z3printer.py +++ b/src/api/python/z3printer.py @@ -386,7 +386,7 @@ def seq3(args, lp='(', rp=')'): else: return group(indent(len(lp), compose(to_format(lp), seq(args), to_format(rp)))) -class StopPPException: +class StopPPException(Exception): def __str__(self): return 'pp-interrupted' From fb4c07a2ea336ad62fbfce3e4f49579fa088cdef Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 23 Apr 2014 18:36:38 +0100 Subject: [PATCH 335/509] FPA refactoring in preparation for FPA support in the kernel. Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_project.py | 7 +- src/{tactic => ast}/fpa/fpa2bv_converter.cpp | 212 ----------------- src/{tactic => ast}/fpa/fpa2bv_converter.h | 85 ------- src/{tactic => ast}/fpa/fpa2bv_rewriter.h | 0 src/smt/theory_fpa.cpp | 46 ++++ src/smt/theory_fpa.h | 45 ++++ src/tactic/fpa/fpa2bv_model_converter.cpp | 232 +++++++++++++++++++ src/tactic/fpa/fpa2bv_model_converter.h | 106 +++++++++ src/tactic/fpa/fpa2bv_tactic.cpp | 1 + 9 files changed, 434 insertions(+), 300 deletions(-) rename src/{tactic => ast}/fpa/fpa2bv_converter.cpp (92%) rename src/{tactic => ast}/fpa/fpa2bv_converter.h (72%) rename src/{tactic => ast}/fpa/fpa2bv_rewriter.h (100%) create mode 100644 src/smt/theory_fpa.cpp create mode 100644 src/smt/theory_fpa.h create mode 100644 src/tactic/fpa/fpa2bv_model_converter.cpp create mode 100644 src/tactic/fpa/fpa2bv_model_converter.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index f2c688525..6f8fe1f7f 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -42,17 +42,18 @@ def init_project_def(): # Simplifier module will be deleted in the future. # It has been replaced with rewriter module. add_lib('simplifier', ['rewriter'], 'ast/simplifier') + add_lib('fpa', ['ast', 'util', 'simplifier'], 'ast/fpa') add_lib('macros', ['simplifier'], 'ast/macros') add_lib('pattern', ['normal_forms', 'smt2parser', 'simplifier'], 'ast/pattern') add_lib('bit_blaster', ['rewriter', 'simplifier'], 'ast/rewriter/bit_blaster') add_lib('smt_params', ['ast', 'simplifier', 'pattern', 'bit_blaster'], 'smt/params') add_lib('proto_model', ['model', 'simplifier', 'smt_params'], 'smt/proto_model') add_lib('smt', ['bit_blaster', 'macros', 'normal_forms', 'cmd_context', 'proto_model', - 'substitution', 'grobner', 'euclid', 'proof_checker', 'pattern', 'parser_util']) + 'substitution', 'grobner', 'euclid', 'proof_checker', 'pattern', 'parser_util', 'fpa']) add_lib('user_plugin', ['smt'], 'smt/user_plugin') add_lib('bv_tactics', ['tactic', 'bit_blaster'], 'tactic/bv') add_lib('fuzzing', ['ast'], 'test/fuzzing') - add_lib('fpa', ['core_tactics', 'bv_tactics', 'sat_tactic'], 'tactic/fpa') + add_lib('fpa_tactics', ['fpa', 'core_tactics', 'bv_tactics', 'sat_tactic'], 'tactic/fpa') add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') add_lib('qe', ['smt','sat'], 'qe') @@ -68,7 +69,7 @@ def init_project_def(): add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf'], 'muz/fp') add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') - add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') + add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa_tactics', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') # add_dll('foci2', ['util'], 'interp/foci2stub', # dll_name='foci2', diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp similarity index 92% rename from src/tactic/fpa/fpa2bv_converter.cpp rename to src/ast/fpa/fpa2bv_converter.cpp index 533988689..20925a0f9 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -2754,215 +2754,3 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & TRACE("fpa2bv_round", tout << "ROUND = " << mk_ismt2_pp(result, m) << std::endl; ); } - -void fpa2bv_model_converter::display(std::ostream & out) { - out << "(fpa2bv-model-converter"; - for (obj_map::iterator it = m_const2bv.begin(); - it != m_const2bv.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value, m, indent) << ")"; - } - for (obj_map::iterator it = m_rm_const2bv.begin(); - it != m_rm_const2bv.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value, m, indent) << ")"; - } - for (obj_map::iterator it = m_uf2bvuf.begin(); - it != m_uf2bvuf.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value, m, indent) << ")"; - } - for (obj_map::iterator it = m_uf23bvuf.begin(); - it != m_uf23bvuf.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value.f_sgn, m, indent) << " ; " << - mk_ismt2_pp(it->m_value.f_sig, m, indent) << " ; " << - mk_ismt2_pp(it->m_value.f_exp, m, indent) << " ; " << - ")"; - } - out << ")" << std::endl; -} - -model_converter * fpa2bv_model_converter::translate(ast_translation & translator) { - fpa2bv_model_converter * res = alloc(fpa2bv_model_converter, translator.to()); - for (obj_map::iterator it = m_const2bv.begin(); - it != m_const2bv.end(); - it++) - { - func_decl * k = translator(it->m_key); - expr * v = translator(it->m_value); - res->m_const2bv.insert(k, v); - translator.to().inc_ref(k); - translator.to().inc_ref(v); - } - for (obj_map::iterator it = m_rm_const2bv.begin(); - it != m_rm_const2bv.end(); - it++) - { - func_decl * k = translator(it->m_key); - expr * v = translator(it->m_value); - res->m_rm_const2bv.insert(k, v); - translator.to().inc_ref(k); - translator.to().inc_ref(v); - } - return res; -} - -void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { - float_util fu(m); - bv_util bu(m); - mpf fp_val; - unsynch_mpz_manager & mpzm = fu.fm().mpz_manager(); - unsynch_mpq_manager & mpqm = fu.fm().mpq_manager(); - - TRACE("fpa2bv_mc", tout << "BV Model: " << std::endl; - for (unsigned i = 0 ; i < bv_mdl->get_num_constants(); i++) - tout << bv_mdl->get_constant(i)->get_name() << " --> " << - mk_ismt2_pp(bv_mdl->get_const_interp(bv_mdl->get_constant(i)), m) << std::endl; - ); - - obj_hashtable seen; - - for (obj_map::iterator it = m_const2bv.begin(); - it != m_const2bv.end(); - it++) - { - func_decl * var = it->m_key; - app * a = to_app(it->m_value); - SASSERT(fu.is_float(var->get_range())); - SASSERT(var->get_range()->get_num_parameters() == 2); - - unsigned ebits = fu.get_ebits(var->get_range()); - unsigned sbits = fu.get_sbits(var->get_range()); - - expr_ref sgn(m), sig(m), exp(m); - sgn = bv_mdl->get_const_interp(to_app(a->get_arg(0))->get_decl()); - sig = bv_mdl->get_const_interp(to_app(a->get_arg(1))->get_decl()); - exp = bv_mdl->get_const_interp(to_app(a->get_arg(2))->get_decl()); - - seen.insert(to_app(a->get_arg(0))->get_decl()); - seen.insert(to_app(a->get_arg(1))->get_decl()); - seen.insert(to_app(a->get_arg(2))->get_decl()); - - if (!sgn && !sig && !exp) - continue; - - unsigned sgn_sz = bu.get_bv_size(m.get_sort(a->get_arg(0))); - unsigned sig_sz = bu.get_bv_size(m.get_sort(a->get_arg(1))) - 1; - unsigned exp_sz = bu.get_bv_size(m.get_sort(a->get_arg(2))); - - rational sgn_q(0), sig_q(0), exp_q(0); - - if (sgn) bu.is_numeral(sgn, sgn_q, sgn_sz); - if (sig) bu.is_numeral(sig, sig_q, sig_sz); - if (exp) bu.is_numeral(exp, exp_q, exp_sz); - - // un-bias exponent - rational exp_unbiased_q; - exp_unbiased_q = exp_q - fu.fm().m_powers2.m1(ebits-1); - - mpz sig_z; mpf_exp_t exp_z; - mpzm.set(sig_z, sig_q.to_mpq().numerator()); - exp_z = mpzm.get_int64(exp_unbiased_q.to_mpq().numerator()); - - TRACE("fpa2bv_mc", tout << var->get_name() << " == [" << sgn_q.to_string() << " " << - mpzm.to_string(sig_z) << " " << exp_z << "(" << exp_q.to_string() << ")]" << std::endl; ); - - fu.fm().set(fp_val, ebits, sbits, !mpqm.is_zero(sgn_q.to_mpq()), sig_z, exp_z); - - float_mdl->register_decl(var, fu.mk_value(fp_val)); - - mpzm.del(sig_z); - } - - for (obj_map::iterator it = m_rm_const2bv.begin(); - it != m_rm_const2bv.end(); - it++) - { - func_decl * var = it->m_key; - app * a = to_app(it->m_value); - SASSERT(fu.is_rm(var->get_range())); - rational val(0); - unsigned sz = 0; - if (a && bu.is_numeral(a, val, sz)) { - TRACE("fpa2bv_mc", tout << var->get_name() << " == " << val.to_string() << std::endl; ); - SASSERT(val.is_uint64()); - switch (val.get_uint64()) - { - case BV_RM_TIES_TO_AWAY: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_away()); break; - case BV_RM_TIES_TO_EVEN: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_even()); break; - case BV_RM_TO_NEGATIVE: float_mdl->register_decl(var, fu.mk_round_toward_negative()); break; - case BV_RM_TO_POSITIVE: float_mdl->register_decl(var, fu.mk_round_toward_positive()); break; - case BV_RM_TO_ZERO: - default: float_mdl->register_decl(var, fu.mk_round_toward_zero()); - } - seen.insert(var); - } - } - - for (obj_map::iterator it = m_uf2bvuf.begin(); - it != m_uf2bvuf.end(); - it++) - seen.insert(it->m_value); - - for (obj_map::iterator it = m_uf23bvuf.begin(); - it != m_uf23bvuf.end(); - it++) - { - seen.insert(it->m_value.f_sgn); - seen.insert(it->m_value.f_sig); - seen.insert(it->m_value.f_exp); - } - - fu.fm().del(fp_val); - - // Keep all the non-float constants. - unsigned sz = bv_mdl->get_num_constants(); - for (unsigned i = 0; i < sz; i++) - { - func_decl * c = bv_mdl->get_constant(i); - if (!seen.contains(c)) - float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); - } - - // And keep everything else - sz = bv_mdl->get_num_functions(); - for (unsigned i = 0; i < sz; i++) - { - func_decl * f = bv_mdl->get_function(i); - if (!seen.contains(f)) - { - TRACE("fpa2bv_mc", tout << "Keeping: " << mk_ismt2_pp(f, m) << std::endl; ); - func_interp * val = bv_mdl->get_func_interp(f); - float_mdl->register_decl(f, val); - } - } - - sz = bv_mdl->get_num_uninterpreted_sorts(); - for (unsigned i = 0; i < sz; i++) - { - sort * s = bv_mdl->get_uninterpreted_sort(i); - ptr_vector u = bv_mdl->get_universe(s); - float_mdl->register_usort(s, u.size(), u.c_ptr()); - } -} - -model_converter * mk_fpa2bv_model_converter(ast_manager & m, - obj_map const & const2bv, - obj_map const & rm_const2bv, - obj_map const & uf2bvuf, - obj_map const & uf23bvuf) { - return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv, uf2bvuf, uf23bvuf); -} diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h similarity index 72% rename from src/tactic/fpa/fpa2bv_converter.h rename to src/ast/fpa/fpa2bv_converter.h index 79c8039ef..2ccdac6a9 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -24,13 +24,10 @@ Notes: #include"ref_util.h" #include"float_decl_plugin.h" #include"bv_decl_plugin.h" -#include"model_converter.h" #include"basic_simplifier_plugin.h" typedef enum { BV_RM_TIES_TO_AWAY=0, BV_RM_TIES_TO_EVEN=1, BV_RM_TO_NEGATIVE=2, BV_RM_TO_POSITIVE=3, BV_RM_TO_ZERO=4 } BV_RM_VAL; -class fpa2bv_model_converter; - struct func_decl_triple { func_decl_triple () { f_sgn = 0; f_sig = 0; f_exp = 0; } func_decl_triple (func_decl * sgn, func_decl * sig, func_decl * exp) @@ -173,86 +170,4 @@ protected: expr_ref & res_sgn, expr_ref & res_sig, expr_ref & res_exp); }; - -class fpa2bv_model_converter : public model_converter { - ast_manager & m; - obj_map m_const2bv; - obj_map m_rm_const2bv; - obj_map m_uf2bvuf; - obj_map m_uf23bvuf; - -public: - fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv, - obj_map const & uf2bvuf, - obj_map const & uf23bvuf) : - m(m) { - // Just create a copy? - for (obj_map::iterator it = const2bv.begin(); - it != const2bv.end(); - it++) - { - m_const2bv.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - m.inc_ref(it->m_value); - } - for (obj_map::iterator it = rm_const2bv.begin(); - it != rm_const2bv.end(); - it++) - { - m_rm_const2bv.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - m.inc_ref(it->m_value); - } - for (obj_map::iterator it = uf2bvuf.begin(); - it != uf2bvuf.end(); - it++) - { - m_uf2bvuf.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - m.inc_ref(it->m_value); - } - for (obj_map::iterator it = uf23bvuf.begin(); - it != uf23bvuf.end(); - it++) - { - m_uf23bvuf.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - } - } - - virtual ~fpa2bv_model_converter() { - dec_ref_map_key_values(m, m_const2bv); - dec_ref_map_key_values(m, m_rm_const2bv); - } - - virtual void operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); - model * new_model = alloc(model, m); - obj_hashtable bits; - convert(md.get(), new_model); - md = new_model; - } - - virtual void operator()(model_ref & md) { - operator()(md, 0); - } - - void display(std::ostream & out); - - virtual model_converter * translate(ast_translation & translator); - -protected: - fpa2bv_model_converter(ast_manager & m) : m(m) { } - - void convert(model * bv_mdl, model * float_mdl); -}; - - -model_converter * mk_fpa2bv_model_converter(ast_manager & m, - obj_map const & const2bv, - obj_map const & rm_const2bv, - obj_map const & uf2bvuf, - obj_map const & uf23bvuf); - #endif diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/ast/fpa/fpa2bv_rewriter.h similarity index 100% rename from src/tactic/fpa/fpa2bv_rewriter.h rename to src/ast/fpa/fpa2bv_rewriter.h diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp new file mode 100644 index 000000000..147d87496 --- /dev/null +++ b/src/smt/theory_fpa.cpp @@ -0,0 +1,46 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + theory_fpa.cpp + +Abstract: + + Floating-Point Theory Plugin + +Author: + + Christoph (cwinter) 2014-04-23 + +Revision History: + +--*/ +#include"ast_smt2_pp.h" +#include"theory_fpa.h" + +namespace smt { + + bool theory_fpa::internalize_atom(app * atom, bool gate_ctx) { + TRACE("bv", tout << "internalizing atom: " << mk_ismt2_pp(atom, get_manager()) << "\n";); + SASSERT(atom->get_family_id() == get_family_id()); + NOT_IMPLEMENTED_YET(); + return true; + } + + void theory_fpa::new_eq_eh(theory_var, theory_var) { + NOT_IMPLEMENTED_YET(); + } + + void theory_fpa::new_diseq_eh(theory_var, theory_var) { + NOT_IMPLEMENTED_YET(); + } + + void theory_fpa::push_scope_eh() { + NOT_IMPLEMENTED_YET(); + } + + void theory_fpa::pop_scope_eh(unsigned num_scopes) { + NOT_IMPLEMENTED_YET(); + } +}; diff --git a/src/smt/theory_fpa.h b/src/smt/theory_fpa.h new file mode 100644 index 000000000..3716247d3 --- /dev/null +++ b/src/smt/theory_fpa.h @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + theory_fpa.h + +Abstract: + + Floating-Point Theory Plugin + +Author: + + Christoph (cwinter) 2014-04-23 + +Revision History: + +--*/ +#ifndef _THEORY_FPA_H_ +#define _THEORY_FPA_H_ + +#include"smt_theory.h" +#include"fpa2bv_converter.h" + +namespace smt { + class theory_fpa : public theory { + fpa2bv_converter m_converter; + + virtual final_check_status final_check_eh() { return FC_DONE; } + virtual bool internalize_atom(app*, bool); + virtual bool internalize_term(app*) { return internalize_atom(0, false); } + virtual void new_eq_eh(theory_var, theory_var); + virtual void new_diseq_eh(theory_var, theory_var); + virtual void push_scope_eh(); + virtual void pop_scope_eh(unsigned num_scopes); + virtual theory* mk_fresh(context*) { return alloc(theory_fpa, get_manager()); } + virtual char const * get_name() const { return "fpa"; } + public: + theory_fpa(ast_manager& m) : theory(m.mk_family_id("fpa")), m_converter(m) {} + }; + +}; + +#endif /* _THEORY_FPA_H_ */ + diff --git a/src/tactic/fpa/fpa2bv_model_converter.cpp b/src/tactic/fpa/fpa2bv_model_converter.cpp new file mode 100644 index 000000000..9b4b5a70f --- /dev/null +++ b/src/tactic/fpa/fpa2bv_model_converter.cpp @@ -0,0 +1,232 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + fpa2bv_model_converter.h + +Abstract: + + Model conversion for fpa2bv_converter + +Author: + + Christoph (cwinter) 2012-02-09 + +Notes: + +--*/ +#include"ast_smt2_pp.h" +#include"fpa2bv_model_converter.h" + +void fpa2bv_model_converter::display(std::ostream & out) { + out << "(fpa2bv-model-converter"; + for (obj_map::iterator it = m_const2bv.begin(); + it != m_const2bv.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_rm_const2bv.begin(); + it != m_rm_const2bv.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_uf23bvuf.begin(); + it != m_uf23bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value.f_sgn, m, indent) << " ; " << + mk_ismt2_pp(it->m_value.f_sig, m, indent) << " ; " << + mk_ismt2_pp(it->m_value.f_exp, m, indent) << " ; " << + ")"; + } + out << ")" << std::endl; +} + +model_converter * fpa2bv_model_converter::translate(ast_translation & translator) { + fpa2bv_model_converter * res = alloc(fpa2bv_model_converter, translator.to()); + for (obj_map::iterator it = m_const2bv.begin(); + it != m_const2bv.end(); + it++) + { + func_decl * k = translator(it->m_key); + expr * v = translator(it->m_value); + res->m_const2bv.insert(k, v); + translator.to().inc_ref(k); + translator.to().inc_ref(v); + } + for (obj_map::iterator it = m_rm_const2bv.begin(); + it != m_rm_const2bv.end(); + it++) + { + func_decl * k = translator(it->m_key); + expr * v = translator(it->m_value); + res->m_rm_const2bv.insert(k, v); + translator.to().inc_ref(k); + translator.to().inc_ref(v); + } + return res; +} + +void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { + float_util fu(m); + bv_util bu(m); + mpf fp_val; + unsynch_mpz_manager & mpzm = fu.fm().mpz_manager(); + unsynch_mpq_manager & mpqm = fu.fm().mpq_manager(); + + TRACE("fpa2bv_mc", tout << "BV Model: " << std::endl; + for (unsigned i = 0; i < bv_mdl->get_num_constants(); i++) + tout << bv_mdl->get_constant(i)->get_name() << " --> " << + mk_ismt2_pp(bv_mdl->get_const_interp(bv_mdl->get_constant(i)), m) << std::endl; + ); + + obj_hashtable seen; + + for (obj_map::iterator it = m_const2bv.begin(); + it != m_const2bv.end(); + it++) + { + func_decl * var = it->m_key; + app * a = to_app(it->m_value); + SASSERT(fu.is_float(var->get_range())); + SASSERT(var->get_range()->get_num_parameters() == 2); + + unsigned ebits = fu.get_ebits(var->get_range()); + unsigned sbits = fu.get_sbits(var->get_range()); + + expr_ref sgn(m), sig(m), exp(m); + sgn = bv_mdl->get_const_interp(to_app(a->get_arg(0))->get_decl()); + sig = bv_mdl->get_const_interp(to_app(a->get_arg(1))->get_decl()); + exp = bv_mdl->get_const_interp(to_app(a->get_arg(2))->get_decl()); + + seen.insert(to_app(a->get_arg(0))->get_decl()); + seen.insert(to_app(a->get_arg(1))->get_decl()); + seen.insert(to_app(a->get_arg(2))->get_decl()); + + if (!sgn && !sig && !exp) + continue; + + unsigned sgn_sz = bu.get_bv_size(m.get_sort(a->get_arg(0))); + unsigned sig_sz = bu.get_bv_size(m.get_sort(a->get_arg(1))) - 1; + unsigned exp_sz = bu.get_bv_size(m.get_sort(a->get_arg(2))); + + rational sgn_q(0), sig_q(0), exp_q(0); + + if (sgn) bu.is_numeral(sgn, sgn_q, sgn_sz); + if (sig) bu.is_numeral(sig, sig_q, sig_sz); + if (exp) bu.is_numeral(exp, exp_q, exp_sz); + + // un-bias exponent + rational exp_unbiased_q; + exp_unbiased_q = exp_q - fu.fm().m_powers2.m1(ebits - 1); + + mpz sig_z; mpf_exp_t exp_z; + mpzm.set(sig_z, sig_q.to_mpq().numerator()); + exp_z = mpzm.get_int64(exp_unbiased_q.to_mpq().numerator()); + + TRACE("fpa2bv_mc", tout << var->get_name() << " == [" << sgn_q.to_string() << " " << + mpzm.to_string(sig_z) << " " << exp_z << "(" << exp_q.to_string() << ")]" << std::endl;); + + fu.fm().set(fp_val, ebits, sbits, !mpqm.is_zero(sgn_q.to_mpq()), sig_z, exp_z); + + float_mdl->register_decl(var, fu.mk_value(fp_val)); + + mpzm.del(sig_z); + } + + for (obj_map::iterator it = m_rm_const2bv.begin(); + it != m_rm_const2bv.end(); + it++) + { + func_decl * var = it->m_key; + app * a = to_app(it->m_value); + SASSERT(fu.is_rm(var->get_range())); + rational val(0); + unsigned sz = 0; + if (a && bu.is_numeral(a, val, sz)) { + TRACE("fpa2bv_mc", tout << var->get_name() << " == " << val.to_string() << std::endl;); + SASSERT(val.is_uint64()); + switch (val.get_uint64()) + { + case BV_RM_TIES_TO_AWAY: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_away()); break; + case BV_RM_TIES_TO_EVEN: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_even()); break; + case BV_RM_TO_NEGATIVE: float_mdl->register_decl(var, fu.mk_round_toward_negative()); break; + case BV_RM_TO_POSITIVE: float_mdl->register_decl(var, fu.mk_round_toward_positive()); break; + case BV_RM_TO_ZERO: + default: float_mdl->register_decl(var, fu.mk_round_toward_zero()); + } + seen.insert(var); + } + } + + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) + seen.insert(it->m_value); + + for (obj_map::iterator it = m_uf23bvuf.begin(); + it != m_uf23bvuf.end(); + it++) + { + seen.insert(it->m_value.f_sgn); + seen.insert(it->m_value.f_sig); + seen.insert(it->m_value.f_exp); + } + + fu.fm().del(fp_val); + + // Keep all the non-float constants. + unsigned sz = bv_mdl->get_num_constants(); + for (unsigned i = 0; i < sz; i++) + { + func_decl * c = bv_mdl->get_constant(i); + if (!seen.contains(c)) + float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); + } + + // And keep everything else + sz = bv_mdl->get_num_functions(); + for (unsigned i = 0; i < sz; i++) + { + func_decl * f = bv_mdl->get_function(i); + if (!seen.contains(f)) + { + TRACE("fpa2bv_mc", tout << "Keeping: " << mk_ismt2_pp(f, m) << std::endl;); + func_interp * val = bv_mdl->get_func_interp(f); + float_mdl->register_decl(f, val); + } + } + + sz = bv_mdl->get_num_uninterpreted_sorts(); + for (unsigned i = 0; i < sz; i++) + { + sort * s = bv_mdl->get_uninterpreted_sort(i); + ptr_vector u = bv_mdl->get_universe(s); + float_mdl->register_usort(s, u.size(), u.c_ptr()); + } +} + +model_converter * mk_fpa2bv_model_converter(ast_manager & m, + obj_map const & const2bv, + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf) { + return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv, uf2bvuf, uf23bvuf); +} diff --git a/src/tactic/fpa/fpa2bv_model_converter.h b/src/tactic/fpa/fpa2bv_model_converter.h new file mode 100644 index 000000000..7b9598740 --- /dev/null +++ b/src/tactic/fpa/fpa2bv_model_converter.h @@ -0,0 +1,106 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + fpa2bv_model_converter.h + +Abstract: + + Model conversion for fpa2bv_converter + +Author: + + Christoph (cwinter) 2012-02-09 + +Notes: + +--*/ +#ifndef _FPA2BV_MODEL_CONVERTER_H_ +#define _FPA2BV_MODEL_CONVERTER_H_ + +#include"fpa2bv_converter.h" +#include"model_converter.h" + +class fpa2bv_model_converter : public model_converter { + ast_manager & m; + obj_map m_const2bv; + obj_map m_rm_const2bv; + obj_map m_uf2bvuf; + obj_map m_uf23bvuf; + +public: + fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf) : + m(m) { + // Just create a copy? + for (obj_map::iterator it = const2bv.begin(); + it != const2bv.end(); + it++) + { + m_const2bv.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = rm_const2bv.begin(); + it != rm_const2bv.end(); + it++) + { + m_rm_const2bv.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = uf2bvuf.begin(); + it != uf2bvuf.end(); + it++) + { + m_uf2bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = uf23bvuf.begin(); + it != uf23bvuf.end(); + it++) + { + m_uf23bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + } + } + + virtual ~fpa2bv_model_converter() { + dec_ref_map_key_values(m, m_const2bv); + dec_ref_map_key_values(m, m_rm_const2bv); + } + + virtual void operator()(model_ref & md, unsigned goal_idx) { + SASSERT(goal_idx == 0); + model * new_model = alloc(model, m); + obj_hashtable bits; + convert(md.get(), new_model); + md = new_model; + } + + virtual void operator()(model_ref & md) { + operator()(md, 0); + } + + void display(std::ostream & out); + + virtual model_converter * translate(ast_translation & translator); + +protected: + fpa2bv_model_converter(ast_manager & m) : m(m) { } + + void convert(model * bv_mdl, model * float_mdl); +}; + + +model_converter * mk_fpa2bv_model_converter(ast_manager & m, + obj_map const & const2bv, + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf); + +#endif \ No newline at end of file diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index 9bb35eea6..3a6b7ea45 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -20,6 +20,7 @@ Notes: #include"fpa2bv_rewriter.h" #include"simplify_tactic.h" #include"fpa2bv_tactic.h" +#include"fpa2bv_model_converter.h" class fpa2bv_tactic : public tactic { struct imp { From f4790a183d42bbfdd6ec8e345439cca0965ef9f9 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 24 Apr 2014 16:18:20 -0700 Subject: [PATCH 336/509] strarting on conjecture printing in duality --- src/duality/duality_solver.cpp | 41 ++++++++++++++++++++++++++++++++++ src/duality/duality_wrapper.h | 5 +++++ 2 files changed, 46 insertions(+) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index ff3bc190b..f973b16e5 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -100,6 +100,7 @@ namespace Duality { }; Reporter *CreateStdoutReporter(RPFP *rpfp); + Reporter *CreateConjectureFileReporter(RPFP *rpfp, const std::string &fname); /** Object we throw in case of catastrophe. */ @@ -125,6 +126,7 @@ namespace Duality { { rpfp = _rpfp; reporter = 0; + conj_reporter = 0; heuristic = 0; unwinding = 0; FullExpand = false; @@ -274,6 +276,7 @@ namespace Duality { RPFP *rpfp; // the input RPFP Reporter *reporter; // object for logging + Reporter *conj_reporter; // object for logging conjectures Heuristic *heuristic; // expansion heuristic context &ctx; // Z3 context solver &slvr; // Z3 solver @@ -297,6 +300,7 @@ namespace Duality { int last_decisions; hash_set overapproxes; std::vector proposers; + std::string ConjectureFile; #ifdef BOUNDED struct Counter { @@ -310,6 +314,7 @@ namespace Duality { /** Solve the problem. */ virtual bool Solve(){ reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); + conj_reporter = ConjectureFile.empty() ? 0 : CreateConjectureFileReporter(rpfp,ConjectureFile); #ifndef LOCALIZE_CONJECTURES heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); #else @@ -340,6 +345,8 @@ namespace Duality { delete heuristic; // delete unwinding; // keep the unwinding for future mining of predicates delete reporter; + if(conj_reporter) + delete conj_reporter; for(unsigned i = 0; i < proposers.size(); i++) delete proposers[i]; return res; @@ -449,6 +456,9 @@ namespace Duality { if(option == "recursion_bound"){ return SetIntOption(RecursionBound,value); } + if(option == "conjecture_file"){ + ConjectureFile = value; + } return false; } @@ -1462,6 +1472,8 @@ namespace Duality { bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){ if(!node->Annotation.SubsetEq(fact)){ reporter->Update(node,fact,eager); + if(conj_reporter) + conj_reporter->Update(node,fact,eager); indset->Update(node,fact); updated_nodes.insert(node->map); node->Annotation.IntersectWith(fact); @@ -3043,6 +3055,10 @@ namespace Duality { }; }; + void stop(int event){ + if(event == 1932) + std::cout << "foo!\n"; + } class StreamReporter : public Reporter { std::ostream &s; @@ -3052,6 +3068,7 @@ namespace Duality { int event; int depth; void ev(){ + stop(event); s << "[" << event++ << "]" ; } virtual void Extend(RPFP::Node *node){ @@ -3129,4 +3146,28 @@ namespace Duality { Reporter *CreateStdoutReporter(RPFP *rpfp){ return new StreamReporter(rpfp, std::cout); } + + class ConjectureFileReporter : public Reporter { + std::ofstream s; + public: + ConjectureFileReporter(RPFP *_rpfp, const std::string &fname) + : Reporter(_rpfp), s(fname.c_str()) {} + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ + s << "(define-fun " << node->Name.name() << " ("; + for(unsigned i = 0; i < update.IndParams.size(); i++){ + if(i != 0) + s << " "; + s << "(" << update.IndParams[i] << " " << update.IndParams[i].get_sort() << ")"; + } + s << ") Bool \n"; + s << update.Formula << ")\n"; + s << std::endl; + } + }; + + Reporter *CreateConjectureFileReporter(RPFP *rpfp, const std::string &fname){ + return new ConjectureFileReporter(rpfp, fname); + } + } + diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 3ee7c3882..979717580 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -397,6 +397,11 @@ namespace Duality { sort array_domain() const; sort array_range() const; + + friend std::ostream & operator<<(std::ostream & out, sort const & m){ + m.ctx().print_expr(out,m); + return out; + } }; From a5ce28d82ab2e3cf71f59be61d1ef78c346c0738 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 25 Apr 2014 22:10:53 +0100 Subject: [PATCH 337/509] bugfix Signed-off-by: Christoph M. Wintersteiger --- src/math/polynomial/upolynomial_factorization.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/polynomial/upolynomial_factorization.cpp b/src/math/polynomial/upolynomial_factorization.cpp index 230d352f3..7d5a23d99 100644 --- a/src/math/polynomial/upolynomial_factorization.cpp +++ b/src/math/polynomial/upolynomial_factorization.cpp @@ -530,7 +530,7 @@ bool check_hansel_lift(z_manager & upm, numeral_vector const & C, upm.mul(A_lifted.size(), A_lifted.c_ptr(), B_lifted.size(), B_lifted.c_ptr(), test1); upm.sub(C.size(), C.c_ptr(), test1.size(), test1.c_ptr(), test1); to_zp_manager(br_upm, test1); - if (!test1.size() == 0) { + if (test1.size() != 0) { TRACE("polynomial::factorization::bughunt", tout << "sage: R. = ZZ['x']" << endl; tout << "sage: A = "; upm.display(tout, A); tout << endl; From 769b2b585bcab0d82b783acf1214f38ccf153724 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 2 May 2014 16:43:32 +0100 Subject: [PATCH 338/509] fixed typo Signed-off-by: Christoph M. Wintersteiger --- src/ast/scoped_proof.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/scoped_proof.h b/src/ast/scoped_proof.h index e37290c03..e23a6d92a 100644 --- a/src/ast/scoped_proof.h +++ b/src/ast/scoped_proof.h @@ -17,7 +17,7 @@ Revision History: --*/ #ifndef _SCOPED_PROOF__H_ -#define _SCOPED_PROOF_H_ +#define _SCOPED_PROOF__H_ #include "ast.h" From 8150bd56174177a3bfda6b554bcff9e294ce07c8 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 2 May 2014 17:58:17 +0100 Subject: [PATCH 339/509] OSX timeout handling bugfix --- src/util/scoped_timer.cpp | 64 ++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index d0a79cec6..66c321267 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -70,7 +70,9 @@ struct scoped_timer::imp { pthread_t m_thread_id; pthread_attr_t m_attributes; unsigned m_interval; + pthread_mutex_t m_mutex; pthread_cond_t m_condition_var; + struct timespec m_end_time; #elif defined(_LINUX_) || defined(_FREEBSD_) // Linux & FreeBSD timer_t m_timerid; @@ -93,35 +95,15 @@ struct scoped_timer::imp { static void * thread_func(void * arg) { scoped_timer::imp * st = static_cast(arg); - pthread_mutex_t mutex; - clock_serv_t host_clock; - struct timespec abstime; - mach_timespec_t now; - unsigned long long nano = static_cast(st->m_interval) * 1000000ull; + pthread_mutex_lock(&st->m_mutex); - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &host_clock); - - if (pthread_mutex_init(&mutex, NULL) != 0) - throw default_exception("failed to initialize timer mutex"); - if (pthread_cond_init(&st->m_condition_var, NULL) != 0) - throw default_exception("failed to initialize timer condition variable"); - - abstime.tv_sec = nano / 1000000000ull; - abstime.tv_nsec = nano % 1000000000ull; - - pthread_mutex_lock(&mutex); - clock_get_time(host_clock, &now); - ADD_MACH_TIMESPEC(&abstime, &now); - int e = pthread_cond_timedwait(&st->m_condition_var, &mutex, &abstime); + int e = pthread_cond_timedwait(&st->m_condition_var, &st->m_mutex, &st->m_end_time); if (e != 0 && e != ETIMEDOUT) throw default_exception("failed to start timed wait"); st->m_eh->operator()(); - pthread_mutex_unlock(&mutex); - if (pthread_mutex_destroy(&mutex) != 0) - throw default_exception("failed to destroy pthread mutex"); - if (pthread_cond_destroy(&st->m_condition_var) != 0) - throw default_exception("failed to destroy pthread condition variable"); + pthread_mutex_unlock(&st->m_mutex); + return st; } #elif defined(_LINUX_) || defined(_FREEBSD_) @@ -150,6 +132,22 @@ struct scoped_timer::imp { m_interval = ms; if (pthread_attr_init(&m_attributes) != 0) throw default_exception("failed to initialize timer thread attributes"); + if (pthread_cond_init(&m_condition_var, NULL) != 0) + throw default_exception("failed to initialize timer condition variable"); + if (pthread_mutex_init(&m_mutex, NULL) != 0) + throw default_exception("failed to initialize timer mutex"); + + clock_serv_t host_clock; + mach_timespec_t now; + unsigned long long nano = static_cast(m_interval) * 1000000ull; + + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &host_clock); + m_end_time.tv_sec = nano / 1000000000ull; + m_end_time.tv_nsec = nano % 1000000000ull; + clock_get_time(host_clock, &now); + ADD_MACH_TIMESPEC(&m_end_time, &now); + + if (pthread_create(&m_thread_id, &m_attributes, &thread_func, this) != 0) throw default_exception("failed to start timer thread"); #elif defined(_LINUX_) || defined(_FREEBSD_) @@ -183,9 +181,25 @@ struct scoped_timer::imp { INVALID_HANDLE_VALUE); #elif defined(__APPLE__) && defined(__MACH__) // Mac OS X - pthread_cond_signal(&m_condition_var); // this is okay to fail + + // If the waiting-thread is not up and waiting yet, + // we can make sure that it finishes quickly by + // setting the end-time to zero. + m_end_time.tv_sec = 0; + m_end_time.tv_nsec = 0; + + // Otherwise it's already up and waiting, and + // we can send a signal on m_condidion_var: + pthread_mutex_lock(&m_mutex); + pthread_cond_signal(&m_condition_var); + pthread_mutex_unlock(&m_mutex); + if (pthread_join(m_thread_id, NULL) != 0) throw default_exception("failed to join thread"); + if (pthread_mutex_destroy(&m_mutex) != 0) + throw default_exception("failed to destroy pthread mutex"); + if (pthread_cond_destroy(&m_condition_var) != 0) + throw default_exception("failed to destroy pthread condition variable"); if (pthread_attr_destroy(&m_attributes) != 0) throw default_exception("failed to destroy pthread attributes object"); #elif defined(_LINUX_) || defined(_FREEBSD_) From 581bbb58fb97f0735e01e28781d5c0ef9e762f60 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 2 May 2014 18:04:32 +0100 Subject: [PATCH 340/509] typo Signed-off-by: Christoph M. Wintersteiger --- src/util/scoped_timer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index 66c321267..c4a640009 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -189,7 +189,7 @@ struct scoped_timer::imp { m_end_time.tv_nsec = 0; // Otherwise it's already up and waiting, and - // we can send a signal on m_condidion_var: + // we can send a signal on m_condition_var: pthread_mutex_lock(&m_mutex); pthread_cond_signal(&m_condition_var); pthread_mutex_unlock(&m_mutex); From a4f3afd70db0f33c35f760d22b803168d1e51423 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 5 May 2014 14:29:54 -0700 Subject: [PATCH 341/509] added fixedpoint.conjecture_file option --- src/muz/base/fixedpoint_params.pyg | 1 + src/muz/duality/duality_dl_interface.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index e201c2a19..5bfbba6b2 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -77,6 +77,7 @@ def_module_params('fixedpoint', ('mbqi', BOOL, True, 'DUALITY: use model-based quantifier instantion'), ('batch_expand', BOOL, False, 'DUALITY: use batch expansion'), ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), + ('conjecture_file', STRING, '', 'DUALITY: save conjectures to file'), )) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 99ac2ee1c..e23f3f7ef 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -211,6 +211,7 @@ lbool dl_interface::query(::expr * query) { rs->SetOption("use_underapprox",m_ctx.get_params().use_underapprox() ? "1" : "0"); rs->SetOption("stratified_inlining",m_ctx.get_params().stratified_inlining() ? "1" : "0"); rs->SetOption("batch_expand",m_ctx.get_params().batch_expand() ? "1" : "0"); + rs->SetOption("conjecture_file",m_ctx.get_params().conjecture_file()); unsigned rb = m_ctx.get_params().recursion_bound(); if(rb != UINT_MAX){ std::ostringstream os; os << rb; From 2a887a7608d97fb79d7e7c6c0e7e06d1e95bd6bf Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 9 May 2014 13:08:39 -0700 Subject: [PATCH 342/509] interp localization hack --- src/interp/iz3proof_itp.cpp | 30 ++++++++++++++++++++++++++++-- src/interp/iz3scopes.h | 5 +++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 8913920ac..8df83a79f 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2682,10 +2682,21 @@ class iz3proof_itp_impl : public iz3proof_itp { return make(Equal,x,y); } + bool range_is_global(const prover::range &r){ + if(pv->range_contained(r,rng)) + return false; + if(!pv->ranges_intersect(r,rng)) + return false; + return true; + } + ast localize_term(ast e, const prover::range &rng, ast &pf){ - // we need to memoize this function per range - locmaps &maps = localization_maps_per_range[rng]; + // we need to memoize this function separately for A, B and global + prover::range map_range = rng; + if(range_is_global(map_range)) + map_range = pv->range_full(); + locmaps &maps = localization_maps_per_range[map_range]; hash_map &localization_map = maps.localization_map; hash_map &localization_pf_map = maps.localization_pf_map; @@ -2775,6 +2786,21 @@ class iz3proof_itp_impl : public iz3proof_itp { ast bar = make_assumption(frame,foo); pf = make_transitivity(new_var,e,orig_e,bar,pf); localization_pf_map[orig_e] = pf; + + // HACK: try to bias this term in the future + if(!pv->range_is_full(rng)){ + prover::range rf = pv->range_full(); + locmaps &fmaps = localization_maps_per_range[rf]; + hash_map &flocalization_map = fmaps.localization_map; + hash_map &flocalization_pf_map = fmaps.localization_pf_map; + // if(flocalization_map.find(orig_e) == flocalization_map.end()) + { + flocalization_map[orig_e] = new_var; + flocalization_pf_map[orig_e] = pf; + } + } + + return new_var; } diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h index 23ab1c714..bf93f4726 100755 --- a/src/interp/iz3scopes.h +++ b/src/interp/iz3scopes.h @@ -64,6 +64,11 @@ class scopes { return rng.hi < rng.lo; } + /** is this range full? */ + bool range_is_full(const range &rng){ + return rng.lo == SHRT_MIN && rng.hi == SHRT_MAX; + } + /** return an empty range */ range range_empty(){ range res; From 90ca1b95c0ab1acaf9323074be7908f1acfd5cc8 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 9 May 2014 13:10:03 -0700 Subject: [PATCH 343/509] debugging code in duality --- src/duality/duality_solver.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index ff3bc190b..326d51dc8 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -3043,6 +3043,7 @@ namespace Duality { }; }; + static int stop_event = -1; class StreamReporter : public Reporter { std::ostream &s; @@ -3052,6 +3053,9 @@ namespace Duality { int event; int depth; void ev(){ + if(stop_event == event){ + std::cout << "stop!"; + } s << "[" << event++ << "]" ; } virtual void Extend(RPFP::Node *node){ From aa35f988fc09506780522c3d790db99411e0dd2f Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 13 May 2014 14:58:32 -0700 Subject: [PATCH 344/509] fix for bad coefficient in AssignBounds --- src/interp/iz3translate.cpp | 85 ++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 060bb74ad..5aafa94f7 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1058,38 +1058,66 @@ public: } + rational get_first_coefficient(const ast &t, ast &v){ + if(op(t) == Plus){ + unsigned best_id = UINT_MAX; + rational best_coeff(0); + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + if(op(arg(t,i)) != Numeral){ + ast lv = get_linear_var(arg(t,i)); + unsigned id = ast_id(lv); + if(id < best_id) { + v = lv; + best_id = id; + best_coeff = get_coeff(arg(t,i)); + } + } + return best_coeff; + } + else + if(op(t) != Numeral) + return(get_coeff(t)); + return rational(0); + } + ast divide_inequalities(const ast &x, const ast&y){ - std::vector xcoeffs,ycoeffs; - get_linear_coefficients(arg(x,1),xcoeffs); - get_linear_coefficients(arg(y,1),ycoeffs); - if(xcoeffs.size() != ycoeffs.size() || xcoeffs.size() == 0) + ast xvar, yvar; + rational xcoeff = get_first_coefficient(arg(x,0),xvar); + rational ycoeff = get_first_coefficient(arg(y,0),yvar); + if(xcoeff == rational(0) || ycoeff == rational(0) || xvar != yvar) + throw "bad assign-bounds lemma"; + rational ratio = xcoeff/ycoeff; + if(denominator(ratio) != rational(1)) throw "bad assign-bounds lemma"; - rational ratio = xcoeffs[0]/ycoeffs[0]; return make_int(ratio); // better be integer! } ast AssignBounds2Farkas(const ast &proof, const ast &con){ std::vector farkas_coeffs; get_assign_bounds_coeffs(proof,farkas_coeffs); - if(farkas_coeffs[0] != make_int(rational(1))) - farkas_coeffs[0] = make_int(rational(1)); - std::vector lits; int nargs = num_args(con); if(nargs != (int)(farkas_coeffs.size())) throw "bad assign-bounds theory lemma"; #if 0 - for(int i = 1; i < nargs; i++) - lits.push_back(mk_not(arg(con,i))); - ast sum = sum_inequalities(farkas_coeffs,lits); - ast conseq = rhs_normalize_inequality(arg(con,0)); - ast d = divide_inequalities(sum,conseq); - std::vector my_coeffs; - my_coeffs.push_back(d); - for(unsigned i = 0; i < farkas_coeffs.size(); i++) - my_coeffs.push_back(farkas_coeffs[i]); + if(farkas_coeffs[0] != make_int(rational(1))) + farkas_coeffs[0] = make_int(rational(1)); #else - std::vector my_coeffs; + std::vector lits, lit_coeffs; + for(int i = 1; i < nargs; i++){ + lits.push_back(mk_not(arg(con,i))); + lit_coeffs.push_back(farkas_coeffs[i]); + } + ast sum = normalize_inequality(sum_inequalities(lit_coeffs,lits)); + ast conseq = normalize_inequality(arg(con,0)); + ast d = divide_inequalities(sum,conseq); +#if 0 + if(d != farkas_coeffs[0]) + std::cout << "wow!\n"; #endif + farkas_coeffs[0] = d; +#endif + std::vector my_coeffs; std::vector my_cons; for(int i = 1; i < nargs; i++){ my_cons.push_back(mk_not(arg(con,i))); @@ -1109,12 +1137,27 @@ public: ast AssignBoundsRule2Farkas(const ast &proof, const ast &con, std::vector prems){ std::vector farkas_coeffs; get_assign_bounds_rule_coeffs(proof,farkas_coeffs); - if(farkas_coeffs[0] != make_int(rational(1))) - farkas_coeffs[0] = make_int(rational(1)); - std::vector lits; int nargs = num_prems(proof)+1; if(nargs != (int)(farkas_coeffs.size())) throw "bad assign-bounds theory lemma"; +#if 0 + if(farkas_coeffs[0] != make_int(rational(1))) + farkas_coeffs[0] = make_int(rational(1)); +#else + std::vector lits, lit_coeffs; + for(int i = 1; i < nargs; i++){ + lits.push_back(conc(prem(proof,i-1))); + lit_coeffs.push_back(farkas_coeffs[i]); + } + ast sum = normalize_inequality(sum_inequalities(lit_coeffs,lits)); + ast conseq = normalize_inequality(con); + ast d = divide_inequalities(sum,conseq); +#if 0 + if(d != farkas_coeffs[0]) + std::cout << "wow!\n"; +#endif + farkas_coeffs[0] = d; +#endif std::vector my_coeffs; std::vector my_cons; for(int i = 1; i < nargs; i++){ From 669fded98aabf26b5b6a4915b8081164847d3ad5 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 13 May 2014 14:59:09 -0700 Subject: [PATCH 345/509] fix for possible problem in Farkas proofs in interp --- src/interp/iz3proof_itp.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 8df83a79f..01c252381 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -607,7 +607,29 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } + ast distribute_coeff(const ast &coeff, const ast &s){ + if(sym(s) == sum){ + if(sym(arg(s,2)) == sum) + return make(sum, + distribute_coeff(coeff,arg(s,0)), + make_int(rational(1)), + distribute_coeff(make(Times,coeff,arg(s,1)), arg(s,2))); + else + return make(sum, + distribute_coeff(coeff,arg(s,0)), + make(Times,coeff,arg(s,1)), + arg(s,2)); + } + if(op(s) == Leq && arg(s,1) == make_int(rational(0)) && arg(s,2) == make_int(rational(0))) + return s; + return make(sum,make(Leq,make_int(rational(0)),make_int(rational(0))),coeff,s); + } + ast simplify_sum(std::vector &args){ + if(args[1] != make_int(rational(1))){ + if(sym(args[2]) == sum) + return make(sum,args[0],make_int(rational(1)),distribute_coeff(args[1],args[2])); + } ast Aproves = mk_true(), Bproves = mk_true(); ast ineq = destruct_cond_ineq(args[0],Aproves,Bproves); if(!is_normal_ineq(ineq)) throw cannot_simplify(); From c9e9b30af6a9ff699188a1f7e7004a4feabec398 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 13 May 2014 17:28:22 -0700 Subject: [PATCH 346/509] interp handle mystery arith lemmas --- src/interp/iz3mgr.h | 5 ++++- src/interp/iz3translate.cpp | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 4ef594cee..3ec2c42d1 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -387,10 +387,13 @@ class iz3mgr { return UnknownTheory; } - enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,UnknownKind}; + enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,ArithMysteryKind,UnknownKind}; lemma_kind get_theory_lemma_kind(const ast &proof){ symb s = sym(proof); + if(s->get_num_parameters() < 2) { + return ArithMysteryKind; // Bad -- Z3 hasn't told us + } ::symbol p0; bool ok = s->get_parameter(1).is_symbol(p0); if(!ok) return UnknownKind; diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 5aafa94f7..e467da1fc 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1581,6 +1581,12 @@ public: return res; } + ast ArithMysteryRule(const ast &con, const std::vector &prems, const std::vector &args){ + // Hope for the best! + Iproof::node guess = reconstruct_farkas(prems,args,con); + return guess; + } + struct CannotCombineEqPropagate {}; void CombineEqPropagateRec(const ast &proof, std::vector &prems, std::vector &args, ast &eqprem){ @@ -1892,6 +1898,14 @@ public: res = EqPropagate(con,prems,args); break; } + case ArithMysteryKind: { + // Z3 hasn't told us what kind of lemma this is -- maybe we can guess + std::vector prems(nprems); + for(unsigned i = 0; i < nprems; i++) + prems[i] = prem(proof,i); + res = ArithMysteryRule(con,prems,args); + break; + } default: throw unsupported(); } From 95146483eaace2638eb6916af490184cc2342494 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 13 May 2014 18:15:51 -0700 Subject: [PATCH 347/509] add round-off to farkas resconstruction in interp --- src/interp/iz3translate.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index e467da1fc..26786d57a 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1364,8 +1364,13 @@ public: int nprems = prems.size(); std::vector npcons(nprems); hash_map pain_map; // not needed - for(int i = 0; i < nprems; i++) + for(int i = 0; i < nprems; i++){ npcons[i] = painfully_normalize_ineq(conc(prems[i]),pain_map); + if(op(npcons[i]) == Lt){ + ast constval = z3_simplify(make(Sub,arg(npcons[i],1),make_int(rational(1)))); + npcons[i] = make(Leq,arg(npcons[i],0),constval); + } + } ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); npcons.push_back(ncon); From 3d1ca5ecc91a498a36cfbc39ff6a0d3a03f80d89 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 15 May 2014 21:12:16 -0700 Subject: [PATCH 348/509] make eval cache sensitive to model completion. Bug 110 reported by cipher1024 Signed-off-by: Nikolaj Bjorner --- src/model/model.cpp | 3 ++- src/smt/proto_model/proto_model.cpp | 3 +++ src/smt/smt_model_finder.cpp | 21 +++++++++++++-------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/model/model.cpp b/src/model/model.cpp index 4a9767cb0..f87992d05 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -133,7 +133,8 @@ bool model::eval(expr * e, expr_ref & result, bool model_completion) { ev(e, result); return true; } - catch (model_evaluator_exception &) { + catch (model_evaluator_exception & ex) { + TRACE("model_evaluator", tout << ex.msg() << "\n";); return false; } } diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index 70287728e..26b35f0ed 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -247,6 +247,7 @@ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) { new_t = mk_some_interp_for(f); } else { + TRACE("model_eval", tout << f->get_name() << " is uninterpreted\n";); is_ok = false; } } @@ -294,6 +295,7 @@ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) { // f is an uninterpreted function, there is no need to use m_simplifier.mk_app new_t = m_manager.mk_app(f, num_args, args.c_ptr()); trail.push_back(new_t); + TRACE("model_eval", tout << f->get_name() << " is uninterpreted\n";); is_ok = false; } } @@ -326,6 +328,7 @@ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) { todo.pop_back(); break; case AST_QUANTIFIER: + TRACE("model_eval", tout << "found quantifier\n" << mk_pp(a, m_manager) << "\n";); is_ok = false; // evaluator does not handle quantifiers. SASSERT(a != 0); eval_cache.insert(a, a); diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 8303d05ac..568e9fca9 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -396,7 +396,7 @@ namespace smt { // Support for evaluating expressions in the current model. proto_model * m_model; - obj_map m_eval_cache; + obj_map m_eval_cache[2]; expr_ref_vector m_eval_cache_range; ptr_vector m_root_nodes; @@ -409,7 +409,8 @@ namespace smt { } void reset_eval_cache() { - m_eval_cache.reset(); + m_eval_cache[0].reset(); + m_eval_cache[1].reset(); m_eval_cache_range.reset(); } @@ -468,6 +469,7 @@ namespace smt { ~auf_solver() { flush_nodes(); + reset_eval_cache(); } void set_context(context * ctx) { @@ -547,7 +549,7 @@ namespace smt { for (obj_map::iterator it = elems.begin(); it != elems.end(); it++) { expr * n = it->m_key; expr * n_val = eval(n, true); - if (!m_manager.is_value(n_val)) + if (!n_val || !m_manager.is_value(n_val)) to_delete.push_back(n); } for (ptr_vector::iterator it = to_delete.begin(); it != to_delete.end(); it++) { @@ -569,16 +571,19 @@ namespace smt { virtual expr * eval(expr * n, bool model_completion) { expr * r = 0; - if (m_eval_cache.find(n, r)) { + if (m_eval_cache[model_completion].find(n, r)) { return r; } expr_ref tmp(m_manager); - if (!m_model->eval(n, tmp, model_completion)) + if (!m_model->eval(n, tmp, model_completion)) { r = 0; - else + TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n-----> null\n";); + } + else { r = tmp; - TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n----->\n" << mk_pp(r, m_manager) << "\n";); - m_eval_cache.insert(n, r); + TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n----->\n" << mk_pp(r, m_manager) << "\n";); + } + m_eval_cache[model_completion].insert(n, r); m_eval_cache_range.push_back(r); return r; } From 2ca14b49fe45a09aa39c36b1856e32f5ac8843ea Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 16 May 2014 09:45:32 -0700 Subject: [PATCH 349/509] fix AV in debug assertion, address warnings Signed-off-by: Nikolaj Bjorner --- src/cmd_context/pdecl.cpp | 2 +- src/duality/duality.h | 2 +- src/duality/duality_solver.cpp | 2 +- src/muz/transforms/dl_mk_coi_filter.cpp | 18 +++++++++++++++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp index 4a51e4943..08a29e381 100644 --- a/src/cmd_context/pdecl.cpp +++ b/src/cmd_context/pdecl.cpp @@ -189,7 +189,7 @@ class psort_app : public psort { m.inc_ref(d); m.inc_ref(num_args, args); SASSERT(num_args == m_decl->get_num_params() || m_decl->has_var_params()); - DEBUG_CODE(for (unsigned i = 0; i < num_params; i++) args[i]->check_num_params(this);); + DEBUG_CODE(if (num_args == num_params) { for (unsigned i = 0; i < num_params; i++) args[i]->check_num_params(this); }); } virtual void finalize(pdecl_manager & m) { diff --git a/src/duality/duality.h b/src/duality/duality.h index fc70ffa70..4005adc1a 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -29,7 +29,7 @@ using namespace stl_ext; namespace Duality { - class implicant_solver; + struct implicant_solver; /* Generic operations on Z3 formulas */ diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index ff3bc190b..79055b43a 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -2201,7 +2201,7 @@ namespace Duality { #endif int expand_max = 1; if(0&&duality->BatchExpand){ - int thing = stack.size() * 0.1; + int thing = stack.size() / 10; // * 0.1; expand_max = std::max(1,thing); if(expand_max > 1) std::cout << "foo!\n"; diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index 31af7a53f..c7a8d5aa0 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -200,7 +200,23 @@ namespace datalog { func_decl_set::iterator it = pruned_preds.begin(); extension_model_converter* mc0 = alloc(extension_model_converter, m); for (; it != end; ++it) { - mc0->insert(*it, m.mk_true()); + const rule_vector& rules = source.get_predicate_rules(*it); + expr_ref_vector fmls(m); + for (unsigned i = 0; i < rules.size(); ++i) { + app* head = rules[i]->get_head(); + expr_ref_vector conj(m); + unsigned n = head->get_num_args()-1; + for (unsigned j = 0; j < head->get_num_args(); ++j) { + expr* arg = head->get_arg(j); + if (!is_var(arg)) { + conj.push_back(m.mk_eq(m.mk_var(j, m.get_sort(arg)), arg)); + } + } + fmls.push_back(m.mk_and(conj.size(), conj.c_ptr())); + } + expr_ref fml(m); + fml = m.mk_or(fmls.size(), fmls.c_ptr()); + mc0->insert(*it, fml); } m_context.add_model_converter(mc0); } From 6f0155ce94a6124caedad154f00b8389351f7f1d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 20 May 2014 10:14:40 -0700 Subject: [PATCH 350/509] avoid compiler warning Signed-off-by: Nikolaj Bjorner --- src/model/model.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/model/model.cpp b/src/model/model.cpp index f87992d05..a6b9695b4 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -134,6 +134,7 @@ bool model::eval(expr * e, expr_ref & result, bool model_completion) { return true; } catch (model_evaluator_exception & ex) { + (void)ex; TRACE("model_evaluator", tout << ex.msg() << "\n";); return false; } From e3098b0ec59ddf46c8f25402828d62d2fe21c122 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 20 May 2014 11:20:15 -0700 Subject: [PATCH 351/509] add documentation comment to bind_variables Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_context.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 51b54bc01..1952cc42f 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -278,6 +278,12 @@ namespace datalog { void register_variable(func_decl* var); + /* + Replace constants that have been registered as + variables by de-Bruijn indices and corresponding + universal (if is_forall is true) or existential + quantifier. + */ expr_ref bind_variables(expr* fml, bool is_forall); /** From b91cca8db9b95a9e5ef3240181a7fea0447febe3 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 20 May 2014 15:10:16 -0700 Subject: [PATCH 352/509] fix unbound variables bug in duality_dl_interface --- src/muz/duality/duality_dl_interface.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index e23f3f7ef..0584e6b58 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -35,6 +35,7 @@ Revision History: #include "model_smt2_pp.h" #include "model_v2_pp.h" #include "fixedpoint_params.hpp" +#include "used_vars.h" // template class symbol_table; @@ -164,6 +165,20 @@ lbool dl_interface::query(::expr * query) { clauses.push_back(e); } + std::vector b_sorts; + std::vector b_names; + used_vars uv; + uv.process(query); + unsigned nuv = uv.get_max_found_var_idx_plus_1(); + for(int i = nuv-1; i >= 0; i--){ // var indices are backward + ::sort * s = uv.get(i); + if(!s) + s = _d->ctx.m().mk_bool_sort(); // missing var, whatever + b_sorts.push_back(sort(_d->ctx,s)); + b_names.push_back(symbol(_d->ctx,::symbol(i))); // names? + } + +#if 0 // turn the query into a clause expr q(_d->ctx,m_ctx.bind_variables(query,false)); @@ -177,6 +192,9 @@ lbool dl_interface::query(::expr * query) { } q = q.arg(0); } +#else + expr q(_d->ctx,query); +#endif expr qc = implies(q,_d->ctx.bool_val(false)); qc = _d->ctx.make_quant(Forall,b_sorts,b_names,qc); From 01c6fe39ab73ce2dfb1d1aba4f0b7aba154a170b Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 20 May 2014 15:10:31 -0700 Subject: [PATCH 353/509] fix markers aliasing bug in Duality::CheckerForEdgeClone --- src/duality/duality_solver.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 73fc986b1..26acf7e7c 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -738,6 +738,13 @@ namespace Duality { return ctx.constant(name.c_str(),ctx.bool_sort()); } + /** Make a boolean variable to act as a "marker" for a pair of nodes. */ + expr NodeMarker(Node *node1, Node *node2){ + std::string name = std::string("@m_") + string_of_int(node1->number); + name += std::string("_") + string_of_int(node2->number); + return ctx.constant(name.c_str(),ctx.bool_sort()); + } + /** Union the annotation of dst into src. If with_markers is true, we conjoin the annotation formula of dst with its marker. This allows us to discover which disjunct is @@ -1146,19 +1153,19 @@ namespace Duality { } - void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction){ + void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction, Node *other_node){ #ifdef BOUNDED if(RecursionBound >= 0 && NodePastRecursionBound(node)) return; #endif RPFP::Transformer temp = node->Annotation; - expr marker = NodeMarker(node); + expr marker = (!other_node) ? NodeMarker(node) : NodeMarker(node, other_node); temp.Formula = (!marker || temp.Formula); annot.IntersectWith(temp); marker_disjunction = marker_disjunction || marker; } - bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false){ + bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false, Node *other_node = 0){ bool res = false; annot.SetFull(); expr marker_disjunction = ctx.bool_val(false); @@ -1166,7 +1173,7 @@ namespace Duality { for(unsigned j = 0; j < insts.size(); j++){ Node *node = insts[j]; if(indset->Contains(insts[j])){ - GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction); res = true; + GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction, other_node); res = true; } } annot.Formula = annot.Formula && marker_disjunction; @@ -1263,7 +1270,7 @@ namespace Duality { Node *inst = insts[k]; if(indset->Contains(inst)){ if(checker->Empty(node) || - eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst)),ctx.bool_val(true))){ + eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst,node)),ctx.bool_val(true))){ candidate.Children.push_back(inst); goto next_child; } @@ -1346,7 +1353,7 @@ namespace Duality { for(unsigned j = 0; j < edge->Children.size(); j++){ Node *oc = edge->Children[j]; Node *nc = gen_cands_edge->Children[j]; - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + GenNodeSolutionWithMarkers(oc,nc->Annotation,true,nc); } checker->AssertEdge(gen_cands_edge,1,true); return root; From 2ee416fc8ffb069a309bc36cd992b71be68f43cc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 21 May 2014 10:23:31 -0700 Subject: [PATCH 354/509] deal with infinite loop in diagonalization attempt in datatype factory Signed-off-by: Nikolaj Bjorner --- src/smt/proto_model/datatype_factory.cpp | 37 +++++++++++++++++++++--- src/smt/proto_model/datatype_factory.h | 2 ++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/smt/proto_model/datatype_factory.cpp b/src/smt/proto_model/datatype_factory.cpp index a45b53155..5e66ab738 100644 --- a/src/smt/proto_model/datatype_factory.cpp +++ b/src/smt/proto_model/datatype_factory.cpp @@ -20,6 +20,7 @@ Revision History: #include"proto_model.h" #include"ast_pp.h" #include"ast_ll_pp.h" +#include"expr_functors.h" datatype_factory::datatype_factory(ast_manager & m, proto_model & md): struct_factory(m, m.mk_family_id("datatype"), md), @@ -47,8 +48,10 @@ expr * datatype_factory::get_some_value(sort * s) { */ expr * datatype_factory::get_last_fresh_value(sort * s) { expr * val = 0; - if (m_last_fresh_value.find(s, val)) + if (m_last_fresh_value.find(s, val)) { + TRACE("datatype_factory", tout << "cached fresh value: " << mk_pp(val, m_manager) << "\n";); return val; + } value_set * set = get_value_set(s); if (set->empty()) val = get_some_value(s); @@ -59,6 +62,17 @@ expr * datatype_factory::get_last_fresh_value(sort * s) { return val; } +bool datatype_factory::is_subterm_of_last_value(app* e) { + expr* last; + if (!m_last_fresh_value.find(m_manager.get_sort(e), last)) { + return false; + } + contains_app contains(m_manager, e); + bool result = contains(last); + TRACE("datatype_factory", tout << mk_pp(e, m_manager) << " in " << mk_pp(last, m_manager) << " " << result << "\n";); + return result; +} + /** \brief Create an almost fresh value. If s is recursive, then the result is not 0. It also updates m_last_fresh_value @@ -105,11 +119,18 @@ expr * datatype_factory::get_almost_fresh_value(sort * s) { } } if (recursive || found_fresh_arg) { - expr * new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); + app * new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); SASSERT(!found_fresh_arg || !set->contains(new_value)); register_value(new_value); - if (m_util.is_recursive(s)) - m_last_fresh_value.insert(s, new_value); + if (m_util.is_recursive(s)) { + if (is_subterm_of_last_value(new_value)) { + new_value = static_cast(m_last_fresh_value.find(s)); + } + else { + m_last_fresh_value.insert(s, new_value); + } + } + TRACE("datatype_factory", tout << "almost fresh: " << mk_pp(new_value, m_manager) << "\n";); return new_value; } } @@ -181,12 +202,20 @@ expr * datatype_factory::get_fresh_value(sort * s) { expr_ref_vector args(m_manager); bool found_sibling = false; unsigned num = constructor->get_arity(); + TRACE("datatype_factory", tout << "checking constructor: " << constructor->get_name() << "\n";); for (unsigned i = 0; i < num; i++) { sort * s_arg = constructor->get_domain(i); + TRACE("datatype_factory", tout << mk_pp(s, m_manager) << " " + << mk_pp(s_arg, m_manager) << " are_siblings " + << m_util.are_siblings(s, s_arg) << " is_datatype " + << m_util.is_datatype(s_arg) << " found_sibling " + << found_sibling << "\n";); if (!found_sibling && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) { found_sibling = true; expr * maybe_new_arg = get_almost_fresh_value(s_arg); if (!maybe_new_arg) { + TRACE("datatype_factory", + tout << "no argument found for " << mk_pp(s_arg, m_manager) << "\n";); maybe_new_arg = m_model.get_some_value(s_arg); found_sibling = false; } diff --git a/src/smt/proto_model/datatype_factory.h b/src/smt/proto_model/datatype_factory.h index e8e1f2589..f70604ca8 100644 --- a/src/smt/proto_model/datatype_factory.h +++ b/src/smt/proto_model/datatype_factory.h @@ -29,6 +29,8 @@ class datatype_factory : public struct_factory { expr * get_last_fresh_value(sort * s); expr * get_almost_fresh_value(sort * s); + bool is_subterm_of_last_value(app* e); + public: datatype_factory(ast_manager & m, proto_model & md); virtual ~datatype_factory() {} From a1ee1ec4cce4ae978c2325dd76424c55edd9c882 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 21 May 2014 12:28:07 -0700 Subject: [PATCH 355/509] add virtal destructor to qe_sat Signed-off-by: Nikolaj Bjorner --- src/qe/qe_sat_tactic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qe/qe_sat_tactic.cpp b/src/qe/qe_sat_tactic.cpp index 854e15493..b4a1a6a8b 100644 --- a/src/qe/qe_sat_tactic.cpp +++ b/src/qe/qe_sat_tactic.cpp @@ -226,7 +226,7 @@ namespace qe { return alloc(sat_tactic, m); } - ~sat_tactic() { + virtual ~sat_tactic() { for (unsigned i = 0; i < m_solvers.size(); ++i) { dealloc(m_solvers[i]); } From 06b79cd9ea7827936e67bb2908e99f61355b2b3a Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 21 May 2014 13:30:54 -0700 Subject: [PATCH 356/509] trying to prevent quantifier in interp (leq2eq rule) --- src/interp/iz3proof_itp.cpp | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 01c252381..e89aa1d6d 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -779,6 +779,22 @@ class iz3proof_itp_impl : public iz3proof_itp { ast Bcond = my_implies(Bproves1,my_and(Aproves1,z3_simplify(ineq2))); // if(!is_true(Aproves1) || !is_true(Bproves1)) // std::cout << "foo!\n";; + if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ + if(get_term_type(arg(x,0)) == LitA){ + ast iter = z3_simplify(make(Plus,arg(x,0),get_ineq_rhs(xleqy))); + ast rewrite1 = make_rewrite(LitA,pos_add(0,top_pos),Acond,make(Equal,arg(x,0),iter)); + iter = make(Plus,iter,arg(x,1)); + ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); + return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); + } + if(get_term_type(arg(x,1)) == LitA){ + ast iter = z3_simplify(make(Plus,arg(x,1),get_ineq_rhs(xleqy))); + ast rewrite1 = make_rewrite(LitA,pos_add(1,top_pos),Acond,make(Equal,arg(x,1),iter)); + iter = make(Plus,arg(x,0),iter); + ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); + return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); + } + } if(get_term_type(x) == LitA){ ast iter = z3_simplify(make(Plus,x,get_ineq_rhs(xleqy))); ast rewrite1 = make_rewrite(LitA,top_pos,Acond,make(Equal,x,iter)); @@ -1036,6 +1052,7 @@ class iz3proof_itp_impl : public iz3proof_itp { coeff = argpos ? make_int(rational(-1)) : make_int(rational(1)); break; case Not: + coeff = make_int(rational(-1)); case Plus: break; case Times: @@ -2590,12 +2607,17 @@ class iz3proof_itp_impl : public iz3proof_itp { break; default: { // mixed equality if(get_term_type(x) == LitMixed || get_term_type(y) == LitMixed){ - // std::cerr << "WARNING: mixed term in leq2eq\n"; - std::vector lits; - lits.push_back(con); - lits.push_back(make(Not,xleqy)); - lits.push_back(make(Not,yleqx)); - return make_axiom(lits); + if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ + // std::cerr << "WARNING: untested case in leq2eq\n"; + } + else { + // std::cerr << "WARNING: mixed term in leq2eq\n"; + std::vector lits; + lits.push_back(con); + lits.push_back(make(Not,xleqy)); + lits.push_back(make(Not,yleqx)); + return make_axiom(lits); + } } std::vector conjs; conjs.resize(3); conjs[0] = mk_not(con); From 97c5d09de1ffb10b2847cba765c2ec165cf9efc0 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 21 May 2014 16:56:18 -0700 Subject: [PATCH 357/509] turn off a windows warning --- src/interp/iz3hash.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index a12cfb990..cd94f0e04 100644 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -29,6 +29,10 @@ Revision History: #ifndef IZ3_HASH_H #define IZ3_HASH_H +#ifdef _WINDOWS +#pragma warning(disable:4267) +#endif + #include #include #include From aba79802cfb5a4b2e1a0e64efa4e4ab52058f946 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 25 May 2014 21:01:10 -0700 Subject: [PATCH 358/509] fix warning about unused variable Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_coi_filter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index c7a8d5aa0..cdb4a5b9e 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -205,7 +205,6 @@ namespace datalog { for (unsigned i = 0; i < rules.size(); ++i) { app* head = rules[i]->get_head(); expr_ref_vector conj(m); - unsigned n = head->get_num_args()-1; for (unsigned j = 0; j < head->get_num_args(); ++j) { expr* arg = head->get_arg(j); if (!is_var(arg)) { From 4da56aa4dff9b7830ffcb7d393d8b367c0d3774d Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 29 May 2014 12:41:07 +0100 Subject: [PATCH 359/509] added debug assertion Signed-off-by: Christoph M. Wintersteiger --- src/api/python/z3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 9233a41dc..4eae61c3d 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -4128,6 +4128,7 @@ class Datatype: """ if __debug__: _z3_assert(isinstance(name, str), "String expected") + _z3_assert(name != "") return self.declare_core(name, "is_" + name, *args) def __repr__(self): From 756645326bb2c0578b340e05e03c8276cacd1dd9 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 29 May 2014 17:33:03 +0100 Subject: [PATCH 360/509] Bugfix for And/Or operators in Python. Signed-off-by: Christoph M. Wintersteiger --- src/api/python/z3.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 4eae61c3d..ff0bd6641 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -899,6 +899,7 @@ def _coerce_expr_merge(s, a): return s else: if __debug__: + _z3_assert(s1.ctx == s.ctx, "context mismatch") _z3_assert(False, "sort mismatch") else: return s @@ -1459,9 +1460,18 @@ def And(*args): >>> And(P) And(p__0, p__1, p__2, p__3, p__4) """ - args = _get_args(args) - ctx = _ctx_from_ast_arg_list(args) + last_arg = None + if len(args) > 0: + last_arg = args[len(args)-1] + if isinstance(last_arg, Context): + ctx = args[len(args)-1] + args = args[:len(args)-1] + else: + ctx = main_ctx() + args = _get_args(args) + ctx_args = _ctx_from_ast_arg_list(args, ctx) if __debug__: + _z3_assert(ctx_args == None or ctx_args == ctx, "context mismatch") _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression or probe") if _has_probe(args): return _probe_and(args, ctx) @@ -1480,9 +1490,18 @@ def Or(*args): >>> Or(P) Or(p__0, p__1, p__2, p__3, p__4) """ - args = _get_args(args) - ctx = _ctx_from_ast_arg_list(args) + last_arg = None + if len(args) > 0: + last_arg = args[len(args)-1] + if isinstance(last_arg, Context): + ctx = args[len(args)-1] + args = args[:len(args)-1] + else: + ctx = main_ctx() + args = _get_args(args) + ctx_args = _ctx_from_ast_arg_list(args, ctx) if __debug__: + _z3_assert(ctx_args == None or ctx_args == ctx, "context mismatch") _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression or probe") if _has_probe(args): return _probe_or(args, ctx) From 1e774064bc9fbfde8c873c6664044b7e28645363 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 30 May 2014 12:26:55 +0100 Subject: [PATCH 361/509] assertion bug fix in z3py Signed-off-by: Christoph M. Wintersteiger --- src/api/python/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index ff0bd6641..ce53a4c1f 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -4147,7 +4147,7 @@ class Datatype: """ if __debug__: _z3_assert(isinstance(name, str), "String expected") - _z3_assert(name != "") + _z3_assert(name != "", "Constructor name cannot be empty") return self.declare_core(name, "is_" + name, *args) def __repr__(self): From 49f9f4b3b53191e965e7a59dfd3b4868caa719ca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 30 May 2014 20:52:39 +0530 Subject: [PATCH 362/509] fix crash in model construction from finite domain theory Signed-off-by: Nikolaj Bjorner --- src/ast/dl_decl_plugin.cpp | 18 +++++++++++++++++- src/ast/dl_decl_plugin.h | 2 ++ src/smt/smt_model_generator.cpp | 2 ++ src/smt/theory_dl.cpp | 7 +++---- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index 6d0823a24..badf8a59d 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -599,7 +599,23 @@ namespace datalog { return 0; } result = mk_compare(OP_DL_LT, m_lt_sym, domain); - break; + break; + + case OP_DL_REP: { + if (!check_domain(0, 0, num_parameters) || + !check_domain(1, 1, arity)) return 0; + func_decl_info info(m_family_id, k, 0, 0); + result = m_manager->mk_func_decl(symbol("rep"), 1, domain, range, info); + break; + } + + case OP_DL_ABS: { + if (!check_domain(0, 0, num_parameters) || + !check_domain(1, 1, arity)) return 0; + func_decl_info info(m_family_id, k, 0, 0); + result = m_manager->mk_func_decl(symbol("abs"), 1, domain, range, info); + break; + } default: m_manager->raise_exception("operator not recognized"); diff --git a/src/ast/dl_decl_plugin.h b/src/ast/dl_decl_plugin.h index a662de578..65b00235c 100644 --- a/src/ast/dl_decl_plugin.h +++ b/src/ast/dl_decl_plugin.h @@ -48,6 +48,8 @@ namespace datalog { OP_RA_CLONE, OP_DL_CONSTANT, OP_DL_LT, + OP_DL_REP, + OP_DL_ABS, LAST_RA_OP }; diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index cf22c3e3a..6c138fd57 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -102,6 +102,7 @@ namespace smt { if (th && th->build_models()) { if (r->get_th_var(th->get_id()) != null_theory_var) { proc = th->mk_value(r, *this); + SASSERT(proc); } else { TRACE("model_bug", tout << "creating fresh value for #" << r->get_owner_id() << "\n";); @@ -110,6 +111,7 @@ namespace smt { } else { proc = mk_model_value(r); + SASSERT(proc); } } SASSERT(proc); diff --git a/src/smt/theory_dl.cpp b/src/smt/theory_dl.cpp index 4ef1bd8e0..fc9138bf6 100644 --- a/src/smt/theory_dl.cpp +++ b/src/smt/theory_dl.cpp @@ -162,7 +162,7 @@ namespace smt { m.register_factory(alloc(dl_factory, m_util, m.get_model())); } - virtual smt::model_value_proc * mk_value(smt::enode * n) { + virtual smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator&) { return alloc(dl_value_proc, *this, n); } @@ -201,9 +201,8 @@ namespace smt { if(!m_reps.find(s, r) || !m_vals.find(s,v)) { SASSERT(!m_reps.contains(s)); sort* bv = b().mk_sort(64); - // TBD: filter these from model. - r = m().mk_fresh_func_decl("rep",1, &s,bv); - v = m().mk_fresh_func_decl("val",1, &bv,s); + r = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_REP, 0, 0, 1, &s, bv); + v = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_ABS, 0, 0, 1, &bv, s); m_reps.insert(s, r); m_vals.insert(s, v); add_trail(r); From bc25ea404fc251bb3343bb724abf8cef24356947 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 30 May 2014 18:14:33 +0100 Subject: [PATCH 363/509] Fixed potential bug (warning on OSX). Signed-off-by: Christoph M. Wintersteiger --- src/smt/theory_arith_eq.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/theory_arith_eq.h b/src/smt/theory_arith_eq.h index 04e16e778..368ccb87d 100644 --- a/src/smt/theory_arith_eq.h +++ b/src/smt/theory_arith_eq.h @@ -193,7 +193,7 @@ namespace smt { return true; } - if (!r.get_base_var() == x && x > y) { + if (r.get_base_var() != x && x > y) { std::swap(x, y); k.neg(); } From f76b343bfa4d65417e2366da199189617604fa3a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 31 May 2014 11:25:54 +0530 Subject: [PATCH 364/509] expose parameter settings for controlling injectivity axiom. rquested by Jasmin Blanchette Signed-off-by: Nikolaj Bjorner --- src/smt/params/preprocessor_params.cpp | 1 + src/smt/params/smt_params_helper.pyg | 1 + 2 files changed, 2 insertions(+) diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index 4799b8b9f..9ad787c2a 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -23,6 +23,7 @@ void preprocessor_params::updt_local_params(params_ref const & _p) { smt_params_helper p(_p); m_macro_finder = p.macro_finder(); m_pull_nested_quantifiers = p.pull_nested_quantifiers(); + m_refine_inj_axiom = p.refine_inj_axioms(); } void preprocessor_params::updt_params(params_ref const & p) { diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 50bb6422b..869b8cdc4 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -14,6 +14,7 @@ def_module_params(module_name='smt', ('delay_units', BOOL, False, 'if true then z3 will not restart when a unit clause is learned'), ('delay_units_threshold', UINT, 32, 'maximum number of learned unit clauses before restarting, ingored if delay_units is false'), ('pull_nested_quantifiers', BOOL, False, 'pull nested quantifiers'), + ('refine_inj_axioms', BOOL, True, 'refine injectivity axioms'), ('soft_timeout', UINT, 0, 'soft timeout (0 means no timeout)'), ('mbqi', BOOL, True, 'model based quantifier instantiation (MBQI)'), ('mbqi.max_cexs', UINT, 1, 'initial maximal number of counterexamples used in MBQI, each counterexample generates a quantifier instantiation'), From 88bd01bc4fabb2800b3abfe6badf2bd3ae0fa68e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 3 Jun 2014 23:03:34 +0530 Subject: [PATCH 365/509] patching non-termination bug in datatype factory, reported by Tiago Signed-off-by: Nikolaj Bjorner --- src/smt/proto_model/datatype_factory.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/smt/proto_model/datatype_factory.cpp b/src/smt/proto_model/datatype_factory.cpp index 5e66ab738..3b0ec8e5f 100644 --- a/src/smt/proto_model/datatype_factory.cpp +++ b/src/smt/proto_model/datatype_factory.cpp @@ -191,8 +191,10 @@ expr * datatype_factory::get_fresh_value(sort * s) { // Approach 2) // For recursive datatypes. // search for constructor... + unsigned num_iterations = 0; if (m_util.is_recursive(s)) { while(true) { + ++num_iterations; TRACE("datatype_factory", tout << mk_pp(get_last_fresh_value(s), m_manager) << "\n";); ptr_vector const * constructors = m_util.get_datatype_constructors(s); ptr_vector::const_iterator it = constructors->begin(); @@ -212,7 +214,13 @@ expr * datatype_factory::get_fresh_value(sort * s) { << found_sibling << "\n";); if (!found_sibling && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) { found_sibling = true; - expr * maybe_new_arg = get_almost_fresh_value(s_arg); + expr * maybe_new_arg = 0; + if (num_iterations <= 1) { + maybe_new_arg = get_almost_fresh_value(s_arg); + } + else { + maybe_new_arg = get_fresh_value(s_arg); + } if (!maybe_new_arg) { TRACE("datatype_factory", tout << "no argument found for " << mk_pp(s_arg, m_manager) << "\n";); @@ -231,6 +239,7 @@ expr * datatype_factory::get_fresh_value(sort * s) { if (found_sibling) { expr_ref new_value(m_manager); new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); + TRACE("datatype_factory", tout << "potential new value: " << mk_pp(new_value, m_manager) << "\n";); m_last_fresh_value.insert(s, new_value); if (!set->contains(new_value)) { register_value(new_value); From 8ef4ec7009abaeefa415b1a5b93f020bec3e3ae2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Jun 2014 12:46:23 +0100 Subject: [PATCH 366/509] fix bit-vector rotation left bug Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp | 3 +++ src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h | 10 ++++++++-- src/ast/rewriter/bv_rewriter.cpp | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp index 80d319377..63843d31e 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp @@ -323,6 +323,9 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; + TRACE("bit_blaster", tout << f->get_name() << " "; + for (unsigned i = 0; i < num; ++i) tout << mk_pp(args[i], m()) << " "; + tout << "\n";); if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) { mk_const(f, result); return BR_DONE; diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h index 72c0a447e..b41aa2238 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h @@ -828,6 +828,12 @@ void bit_blaster_tpl::mk_eq(unsigned sz, expr * const * a_bits, expr * cons template void bit_blaster_tpl::mk_rotate_left(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { + TRACE("bit_blaster", tout << n << ": " << sz << " "; + for (unsigned i = 0; i < sz; ++i) { + tout << mk_pp(a_bits[i], m()) << " "; + } + tout << "\n"; + ); n = n % sz; for (unsigned i = sz - n; i < sz; i++) out_bits.push_back(a_bits[i]); @@ -974,7 +980,7 @@ void bit_blaster_tpl::mk_ashr(unsigned sz, expr * const * a_bits, expr * co mk_ite(eqs.get(i - j), a_bits[sz - j - 1], out, new_out); out = new_out; } - TRACE("bit_blaster_tpl", tout << (sz - i - 1) << " :\n" << mk_pp(out, m()) << "\n";); + TRACE("bit_blaster", tout << (sz - i - 1) << " :\n" << mk_pp(out, m()) << "\n";); out_bits.set(sz - i - 1, out); } } @@ -1004,7 +1010,7 @@ void bit_blaster_tpl::mk_ext_rotate_left_right(unsigned sz, expr * const * out = a_bits[i]; for (unsigned j = 1; j < sz; j++) { expr_ref new_out(m()); - unsigned src = (Left ? (i - j) : (i + j)) % sz; + unsigned src = (Left ? (sz + i - j) : (i + j)) % sz; mk_ite(eqs.get(j), a_bits[src], out, new_out); out = new_out; } diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 11b09003b..1115663d2 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -21,6 +21,7 @@ Notes: #include"poly_rewriter_def.h" #include"ast_smt2_pp.h" + mk_extract_proc::mk_extract_proc(bv_util & u): m_util(u), m_high(0), From a5e3713c2c5e1065510d927a9c07ab240b3bf870 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 14 Jun 2014 05:47:42 -0700 Subject: [PATCH 367/509] fix unmatched parenthsis and code odor Signed-off-by: Nikolaj Bjorner --- examples/tptp/tptp5.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/tptp/tptp5.cpp b/examples/tptp/tptp5.cpp index d0e174914..00d03a8b0 100644 --- a/examples/tptp/tptp5.cpp +++ b/examples/tptp/tptp5.cpp @@ -1630,10 +1630,8 @@ public: unsigned parent_id = Z3_get_ast_id(ctx, p.arg(0)); std::set const& hyps = m_proof_hypotheses.find(parent_id)->second; print_hypotheses(out, hyps); - out << ").\n"; + out << "))).\n"; break; - display_inference(out, "lemma", "thm", p); - break; } case Z3_OP_PR_UNIT_RESOLUTION: display_inference(out, "unit_resolution", "thm", p); From 1ed7643d3223c88f80e72135553e6cb5743158e5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 18 Jun 2014 09:52:59 -0700 Subject: [PATCH 368/509] Add option to control explosion of cofactor-term-ite following example by Anvesh Signed-off-by: Nikolaj Bjorner --- src/tactic/core/cofactor_elim_term_ite.cpp | 54 ++++++++++++-------- src/tactic/core/cofactor_elim_term_ite.h | 2 +- src/tactic/core/cofactor_term_ite_tactic.cpp | 3 +- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/tactic/core/cofactor_elim_term_ite.cpp b/src/tactic/core/cofactor_elim_term_ite.cpp index d81b4fa13..bba6db2b6 100644 --- a/src/tactic/core/cofactor_elim_term_ite.cpp +++ b/src/tactic/core/cofactor_elim_term_ite.cpp @@ -29,14 +29,15 @@ struct cofactor_elim_term_ite::imp { ast_manager & m; params_ref m_params; unsigned long long m_max_memory; - volatile bool m_cancel; + bool m_cofactor_equalities; + volatile bool m_cancel; void checkpoint() { cooperate("cofactor ite"); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); if (m_cancel) - throw tactic_exception(TACTIC_MAX_MEMORY_MSG); + throw tactic_exception(TACTIC_CANCELED_MSG); } // Collect atoms that contain term if-then-else @@ -111,7 +112,7 @@ struct cofactor_elim_term_ite::imp { frame & fr = m_frame_stack.back(); expr * t = fr.m_t; bool form_ctx = fr.m_form_ctx; - TRACE("cofactor_ite_analyzer", tout << "processing, form_ctx: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); + TRACE("cofactor", tout << "processing, form_ctx: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); m_owner.checkpoint(); @@ -150,7 +151,7 @@ struct cofactor_elim_term_ite::imp { } if (i < num_args) { m_has_term_ite.mark(t); - TRACE("cofactor_ite_analyzer", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); + TRACE("cofactor", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); save_candidate(t, form_ctx); } } @@ -167,6 +168,7 @@ struct cofactor_elim_term_ite::imp { }; expr * get_first(expr * t) { + TRACE("cofactor", tout << mk_ismt2_pp(t, m) << "\n";); typedef std::pair frame; expr_fast_mark1 visited; sbuffer stack; @@ -225,6 +227,7 @@ struct cofactor_elim_term_ite::imp { \brief Fuctor for selecting the term if-then-else condition with the most number of occurrences. */ expr * get_best(expr * t) { + TRACE("cofactor", tout << mk_ismt2_pp(t, m) << "\n";); typedef std::pair frame; obj_map occs; expr_fast_mark1 visited; @@ -299,12 +302,17 @@ struct cofactor_elim_term_ite::imp { } } visited.reset(); - CTRACE("cofactor_ite_get_best", best != 0, tout << "best num-occs: " << best_occs << "\n" << mk_ismt2_pp(best, m) << "\n";); + CTRACE("cofactor", best != 0, tout << "best num-occs: " << best_occs << "\n" << mk_ismt2_pp(best, m) << "\n";); return best; } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_cofactor_equalities = p.get_bool("cofactor_equalities", true); + } + + void collect_param_descrs(param_descrs & r) { + r.insert("cofactor_equalities", CPK_BOOL, "(default: true) use equalities to rewrite bodies of ite-expressions. This is potentially expensive."); } void set_cancel(bool f) { @@ -354,16 +362,16 @@ struct cofactor_elim_term_ite::imp { m_term = 0; expr * lhs; expr * rhs; - if (m.is_eq(t, lhs, rhs)) { + if (m_owner.m_cofactor_equalities && m.is_eq(t, lhs, rhs)) { if (m.is_unique_value(lhs)) { m_term = rhs; m_value = to_app(lhs); - TRACE("set_cofactor_atom", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); + TRACE("cofactor", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); } else if (m.is_unique_value(rhs)) { m_term = lhs; m_value = to_app(rhs); - TRACE("set_cofactor_atom", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); + TRACE("cofactor", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); } } // TODO: bounds @@ -467,7 +475,7 @@ struct cofactor_elim_term_ite::imp { m_cofactor.set_cofactor_atom(neg_c); m_cofactor(curr, neg_cofactor); curr = m.mk_ite(c, pos_cofactor, neg_cofactor); - TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); + TRACE("cofactor", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); } } return false; @@ -522,7 +530,7 @@ struct cofactor_elim_term_ite::imp { void cofactor(expr * t, expr_ref & r) { unsigned step = 0; - TRACE("cofactor_ite", tout << "cofactor target:\n" << mk_ismt2_pp(t, m) << "\n";); + TRACE("cofactor", tout << "cofactor target:\n" << mk_ismt2_pp(t, m) << "\n";); expr_ref curr(m); curr = t; while (true) { @@ -543,21 +551,20 @@ struct cofactor_elim_term_ite::imp { m_cofactor(curr, neg_cofactor); if (pos_cofactor == neg_cofactor) { curr = pos_cofactor; - TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); - continue; } - if (m.is_true(pos_cofactor) && m.is_false(neg_cofactor)) { + else if (m.is_true(pos_cofactor) && m.is_false(neg_cofactor)) { curr = c; - TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); - continue; } - if (m.is_false(pos_cofactor) && m.is_true(neg_cofactor)) { + else if (m.is_false(pos_cofactor) && m.is_true(neg_cofactor)) { curr = neg_c; - TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); - continue; } - curr = m.mk_ite(c, pos_cofactor, neg_cofactor); - TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); + else { + curr = m.mk_ite(c, pos_cofactor, neg_cofactor); + } + TRACE("cofactor", + tout << "cofactor_ite step: " << step << "\n"; + tout << "co-factor: " << mk_ismt2_pp(c, m) << "\n"; + tout << mk_ismt2_pp(curr, m) << "\n";); } } @@ -570,6 +577,7 @@ struct cofactor_elim_term_ite::imp { void operator()(expr * t, expr_ref & r) { ptr_vector new_args; + SASSERT(m_frames.empty()); m_frames.push_back(frame(t, true)); while (!m_frames.empty()) { m_owner.checkpoint(); @@ -649,7 +657,8 @@ struct cofactor_elim_term_ite::imp { imp(ast_manager & _m, params_ref const & p): m(_m), - m_params(p) { + m_params(p), + m_cofactor_equalities(true) { m_cancel = false; updt_params(p); } @@ -686,7 +695,8 @@ void cofactor_elim_term_ite::updt_params(params_ref const & p) { m_imp->updt_params(p); } -void cofactor_elim_term_ite::get_param_descrs(param_descrs & r) { +void cofactor_elim_term_ite::collect_param_descrs(param_descrs & r) { + m_imp->collect_param_descrs(r); } void cofactor_elim_term_ite::operator()(expr * t, expr_ref & r) { diff --git a/src/tactic/core/cofactor_elim_term_ite.h b/src/tactic/core/cofactor_elim_term_ite.h index 9b325b1f0..ce2f31ea0 100644 --- a/src/tactic/core/cofactor_elim_term_ite.h +++ b/src/tactic/core/cofactor_elim_term_ite.h @@ -31,7 +31,7 @@ public: virtual ~cofactor_elim_term_ite(); void updt_params(params_ref const & p); - static void get_param_descrs(param_descrs & r); + void collect_param_descrs(param_descrs & r); void operator()(expr * t, expr_ref & r); diff --git a/src/tactic/core/cofactor_term_ite_tactic.cpp b/src/tactic/core/cofactor_term_ite_tactic.cpp index 16b4d1ad4..bc719a85e 100644 --- a/src/tactic/core/cofactor_term_ite_tactic.cpp +++ b/src/tactic/core/cofactor_term_ite_tactic.cpp @@ -52,8 +52,7 @@ public: virtual ~cofactor_term_ite_tactic() {} virtual void updt_params(params_ref const & p) { m_params = p; m_elim_ite.updt_params(p); } - static void get_param_descrs(param_descrs & r) { cofactor_elim_term_ite::get_param_descrs(r); } - virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } + virtual void collect_param_descrs(param_descrs & r) { m_elim_ite.collect_param_descrs(r); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, From 103e49d9b4906237a9f520d27861675e222f2c7d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 18 Jun 2014 09:53:47 -0700 Subject: [PATCH 369/509] Add option to control explosion of cofactor-term-ite following example by Anvesh Signed-off-by: Nikolaj Bjorner --- src/tactic/core/cofactor_elim_term_ite.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/core/cofactor_elim_term_ite.cpp b/src/tactic/core/cofactor_elim_term_ite.cpp index bba6db2b6..1f560ef62 100644 --- a/src/tactic/core/cofactor_elim_term_ite.cpp +++ b/src/tactic/core/cofactor_elim_term_ite.cpp @@ -563,7 +563,7 @@ struct cofactor_elim_term_ite::imp { } TRACE("cofactor", tout << "cofactor_ite step: " << step << "\n"; - tout << "co-factor: " << mk_ismt2_pp(c, m) << "\n"; + tout << "cofactor: " << mk_ismt2_pp(c, m) << "\n"; tout << mk_ismt2_pp(curr, m) << "\n";); } } From a26ae624e0d33a738707d86afd819cca668f2429 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 19 Jun 2014 15:50:18 +0100 Subject: [PATCH 370/509] Fixed dependencies of install target in Makefile Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_util.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 89cabe87e..423898ad8 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -810,6 +810,9 @@ class Component: def require_mem_initializer(self): return False + def mk_install_deps(self, out): + return + def mk_install(self, out): return @@ -853,6 +856,9 @@ class LibComponent(Component): out.write('\n') out.write('%s: %s\n\n' % (self.name, libfile)) + def mk_install_dep(self, out): + out.write('%s' % libfile) + def mk_install(self, out): for include in self.includes2install: out.write('\t@cp %s %s\n' % (os.path.join(self.to_src_dir, include), os.path.join('$(PREFIX)', 'include', include))) @@ -935,6 +941,9 @@ class ExeComponent(Component): def main_component(self): return self.install + def mk_install_dep(self, out): + out.write('%s' % exefile) + def mk_install(self, out): if self.install: exefile = '%s$(EXE_EXT)' % self.exe_name @@ -1076,6 +1085,11 @@ class DLLComponent(Component): def require_def_file(self): return IS_WINDOWS and self.export_files + def mk_install_dep(self, out): + out.write('%s$(SO_EXT)' % self.dll_name) + if self.static: + out.write(' %s$(LIB_EXT)' % self.dll_name) + def mk_install(self, out): if self.install: dllfile = '%s$(SO_EXT)' % self.dll_name @@ -1611,7 +1625,11 @@ def mk_config(): print('Java Compiler: %s' % JAVAC) def mk_install(out): - out.write('install:\n') + out.write('install: ') + for c in get_components(): + c.mk_install_deps(out) + out.write(' ') + out.write('\n') out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'bin')) out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'include')) out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'lib')) From 91b32206fde2cf0ab6fc85ff3452370d24cd22f4 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 20 Jun 2014 13:59:35 -0700 Subject: [PATCH 371/509] fix(scripts/mk_make): python3 compatibility Signed-off-by: Leonardo de Moura --- scripts/mk_util.py | 124 ++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 423898ad8..653e4588b 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1,7 +1,7 @@ ############################################ # Copyright (c) 2012 Microsoft Corporation -# -# Auxiliary scripts for generating Makefiles +# +# Auxiliary scripts for generating Makefiles # and Visual Studio project files. # # Author: Leonardo de Moura (leonardo) @@ -42,7 +42,7 @@ BUILD_DIR='build' REV_BUILD_DIR='..' SRC_DIR='src' EXAMPLE_DIR='examples' -# Required Components +# Required Components Z3_DLL_COMPONENT='api_dll' PATTERN_COMPONENT='pattern' UTIL_COMPONENT='util' @@ -80,7 +80,7 @@ GPROF=False GIT_HASH=False def check_output(cmd): - return subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0].rstrip('\r\n') + return str(subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]).rstrip('\r\n') def git_hash(): try: @@ -222,7 +222,7 @@ def test_openmp(cc): return exec_compiler_cmd([cc, CPPFLAGS, 'tstomp.cpp', LDFLAGS, '-fopenmp']) == 0 def find_jni_h(path): - for root, dirs, files in os.walk(path): + for root, dirs, files in os.walk(path): for f in files: if f == 'jni.h': return root @@ -234,7 +234,7 @@ def check_java(): global JAR JDK_HOME = getenv('JDK_HOME', None) # we only need to check this locally. - + if is_verbose(): print("Finding javac ...") @@ -283,7 +283,7 @@ def check_java(): oo = TempFile('output') eo = TempFile('errout') - try: + try: subprocess.call([JAVAC, 'Hello.java', '-verbose'], stdout=oo.fname, stderr=eo.fname) oo.commit() eo.commit() @@ -298,7 +298,7 @@ def check_java(): if JNI_HOME != None: if not os.path.exists(os.path.join(JNI_HOME, 'jni.h')): raise MKException("Failed to detect jni.h '%s'; the environment variable JNI_HOME is probably set to the wrong path." % os.path.join(JNI_HOME)) - else: + else: # Search for jni.h in the library directories... t = open('errout', 'r') open_pat = re.compile("\[search path for class files: (.*)\]") @@ -314,22 +314,22 @@ def check_java(): # ... plus some heuristic ones. extra_dirs = [] - - # For the libraries, even the JDK usually uses a JRE that comes with it. To find the + + # For the libraries, even the JDK usually uses a JRE that comes with it. To find the # headers we have to go a little bit higher up. for dir in cdirs: extra_dirs.append(os.path.abspath(os.path.join(dir, '..'))) if IS_OSX: # Apparently Apple knows best where to put stuff... extra_dirs.append('/System/Library/Frameworks/JavaVM.framework/Headers/') - + cdirs[len(cdirs):] = extra_dirs for dir in cdirs: q = find_jni_h(dir) if q != False: JNI_HOME = q - + if JNI_HOME == None: raise MKException("Failed to detect jni.h. Possible solution: set JNI_HOME with the path to JDK.") @@ -351,7 +351,7 @@ def find_cxx_compiler(): if test_cxx_compiler(cxx): CXX = cxx return CXX - raise MKException('C++ compiler was not found. Try to set the environment variable CXX with the C++ compiler available in your system.') + raise MKException('C++ compiler was not found. Try to set the environment variable CXX with the C++ compiler available in your system.') def find_c_compiler(): global CC, C_COMPILERS @@ -362,7 +362,7 @@ def find_c_compiler(): if test_c_compiler(c): CC = c return CC - raise MKException('C compiler was not found. Try to set the environment variable CC with the C compiler available in your system.') + raise MKException('C compiler was not found. Try to set the environment variable CC with the C compiler available in your system.') def set_version(major, minor, build, revision): global VER_MAJOR, VER_MINOR, VER_BUILD, VER_REVISION @@ -478,8 +478,8 @@ def parse_options(): global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM global DOTNET_ENABLED, JAVA_ENABLED, STATIC_LIB, PREFIX, GMP, FOCI2, FOCI2LIB, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH try: - options, remainder = getopt.gnu_getopt(sys.argv[1:], - 'b:df:sxhmcvtnp:gj', + options, remainder = getopt.gnu_getopt(sys.argv[1:], + 'b:df:sxhmcvtnp:gj', ['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj', 'trace', 'nodotnet', 'staticlib', 'prefix=', 'gmp', 'foci2=', 'java', 'parallel=', 'gprof', 'githash=']) @@ -534,7 +534,7 @@ def parse_options(): elif opt == '--gprof': GPROF = True elif opt == '--githash': - GIT_HASH=arg + GIT_HASH=arg else: print("ERROR: Invalid command line option '%s'" % opt) display_help(1) @@ -548,7 +548,7 @@ def extract_c_includes(fname): system_inc_pat = re.compile("[ \t]*#include[ \t]*\<.*\>[ \t]*") # We should generate and error for any occurrence of #include that does not match the previous pattern. non_std_inc_pat = re.compile(".*#include.*") - + f = open(fname, 'r') linenum = 1 for line in f: @@ -622,7 +622,7 @@ def is_java_enabled(): return JAVA_ENABLED def is_compiler(given, expected): - """ + """ Return True if the 'given' compiler is the expected one. >>> is_compiler('g++', 'g++') True @@ -741,7 +741,7 @@ class Component: self.add_rule_for_each_include(out, include) include_node = '%s.node' % os.path.join(self.build_dir, include) out.write('%s: ' % include_node) - self.add_cpp_h_deps(out, include) + self.add_cpp_h_deps(out, include) out.write('\n') out.write('\t@echo done > %s\n' % include_node) @@ -801,7 +801,7 @@ class Component: # Return true if the component needs builder to generate an install_tactics.cpp file def require_install_tactics(self): return False - + # Return true if the component needs a def file def require_def_file(self): return False @@ -821,7 +821,7 @@ class Component: def is_example(self): return False - + # Invoked when creating a (windows) distribution package using components at build_path, and # storing them at dist_path def mk_win_dist(self, build_path, dist_path): @@ -862,7 +862,7 @@ class LibComponent(Component): def mk_install(self, out): for include in self.includes2install: out.write('\t@cp %s %s\n' % (os.path.join(self.to_src_dir, include), os.path.join('$(PREFIX)', 'include', include))) - + def mk_uninstall(self, out): for include in self.includes2install: out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'include', include)) @@ -871,7 +871,7 @@ class LibComponent(Component): mk_dir(os.path.join(dist_path, 'include')) for include in self.includes2install: shutil.copy(os.path.join(self.src_dir, include), - os.path.join(dist_path, 'include', include)) + os.path.join(dist_path, 'include', include)) def mk_unix_dist(self, build_path, dist_path): self.mk_win_dist(build_path, dist_path) @@ -948,7 +948,7 @@ class ExeComponent(Component): if self.install: exefile = '%s$(EXE_EXT)' % self.exe_name out.write('\t@cp %s %s\n' % (exefile, os.path.join('$(PREFIX)', 'bin', exefile))) - + def mk_uninstall(self, out): exefile = '%s$(EXE_EXT)' % self.exe_name out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'bin', exefile)) @@ -1072,7 +1072,7 @@ class DLLComponent(Component): out.write(' ') out.write(obj) out.write('\n') - + def main_component(self): return self.install @@ -1098,7 +1098,7 @@ class DLLComponent(Component): if self.static: libfile = '%s$(LIB_EXT)' % self.dll_name out.write('\t@cp %s %s\n' % (libfile, os.path.join('$(PREFIX)', 'lib', libfile))) - + def mk_uninstall(self, out): dllfile = '%s$(SO_EXT)' % self.dll_name @@ -1132,7 +1132,7 @@ class DotNetDLLComponent(Component): if assembly_info_dir == None: assembly_info_dir = "." self.dll_name = dll_name - self.assembly_info_dir = assembly_info_dir + self.assembly_info_dir = assembly_info_dir def mk_makefile(self, out): if DOTNET_ENABLED: @@ -1161,7 +1161,7 @@ class DotNetDLLComponent(Component): out.write('\n') out.write('%s: %s\n\n' % (self.name, dllfile)) return - + def main_component(self): return DOTNET_ENABLED @@ -1194,7 +1194,7 @@ class JavaDLLComponent(Component): if is_java_enabled(): mk_dir(os.path.join(BUILD_DIR, 'api', 'java', 'classes')) - dllfile = '%s$(SO_EXT)' % self.dll_name + dllfile = '%s$(SO_EXT)' % self.dll_name out.write('libz3java$(SO_EXT): libz3$(SO_EXT) %s\n' % os.path.join(self.to_src_dir, 'Native.cpp')) t = '\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)api/java/Native$(OBJ_EXT) -I"%s" -I"%s/PLATFORM" -I%s %s/Native.cpp\n' % (JNI_HOME, JNI_HOME, get_component('api').to_src_dir, self.to_src_dir) if IS_OSX: @@ -1225,16 +1225,16 @@ class JavaDLLComponent(Component): JAR = '"%s"' % JAR t = ('\t%s %s.java -d %s\n' % (JAVAC, os.path.join(self.to_src_dir, 'enumerations', '*'), os.path.join('api', 'java', 'classes'))) out.write(t) - t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC, - os.path.join('api', 'java', 'classes'), - os.path.join(self.to_src_dir, '*'), + t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC, + os.path.join('api', 'java', 'classes'), + os.path.join(self.to_src_dir, '*'), os.path.join('api', 'java', 'classes'))) out.write(t) - out.write('\t%s cfm %s.jar %s -C %s .\n' % (JAR, self.package_name, - os.path.join(self.to_src_dir, 'manifest'), + out.write('\t%s cfm %s.jar %s -C %s .\n' % (JAR, self.package_name, + os.path.join(self.to_src_dir, 'manifest'), os.path.join('api', 'java', 'classes'))) out.write('java: %s.jar\n\n' % self.package_name) - + def main_component(self): return is_java_enabled() @@ -1243,7 +1243,7 @@ class JavaDLLComponent(Component): mk_dir(os.path.join(dist_path, 'bin')) shutil.copy('%s.jar' % os.path.join(build_path, self.package_name), '%s.jar' % os.path.join(dist_path, 'bin', self.package_name)) - shutil.copy(os.path.join(build_path, 'libz3java.dll'), + shutil.copy(os.path.join(build_path, 'libz3java.dll'), os.path.join(dist_path, 'bin', 'libz3java.dll')) shutil.copy(os.path.join(build_path, 'libz3java.lib'), os.path.join(dist_path, 'bin', 'libz3java.lib')) @@ -1254,7 +1254,7 @@ class JavaDLLComponent(Component): shutil.copy('%s.jar' % os.path.join(build_path, self.package_name), '%s.jar' % os.path.join(dist_path, 'bin', self.package_name)) so = get_so_ext() - shutil.copy(os.path.join(build_path, 'libz3java.%s' % so), + shutil.copy(os.path.join(build_path, 'libz3java.%s' % so), os.path.join(dist_path, 'bin', 'libz3java.%s' % so)) class ExampleComponent(Component): @@ -1310,7 +1310,7 @@ class CExampleComponent(CppExampleComponent): def src_files(self): return get_c_files(self.ex_dir) - + class DotNetExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) @@ -1591,7 +1591,7 @@ def mk_config(): config.write('CC=%s\n' % CC) config.write('CXX=%s\n' % CXX) config.write('CXXFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS)) - config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG) + config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG) config.write('CXX_OUT_FLAG=-o \n') config.write('OBJ_EXT=.o\n') config.write('LIB_EXT=.a\n') @@ -1650,7 +1650,7 @@ def mk_install(out): out.write('\t@echo Z3 shared libraries were installed at \'%s\', make sure this directory is in your %s environment variable.\n' % (os.path.join(PREFIX, 'lib'), LD_LIBRARY_PATH)) out.write('\t@echo Z3Py was installed at \'%s\', make sure this directory is in your PYTHONPATH environment variable.' % PYTHON_PACKAGE_DIR) - out.write('\n') + out.write('\n') def mk_uninstall(out): out.write('uninstall:\n') @@ -1660,7 +1660,7 @@ def mk_uninstall(out): out.write('\t@rm -f %s*.pyc\n' % os.path.join(PYTHON_PACKAGE_DIR, 'z3')) out.write('\t@rm -f %s*.pyc\n' % os.path.join(PYTHON_PACKAGE_DIR, '__pycache__', 'z3')) out.write('\t@echo Z3 was successfully uninstalled.\n') - out.write('\n') + out.write('\n') # Generate the Z3 makefile def mk_makefile(): @@ -1715,7 +1715,7 @@ def mk_makefile(): print('Remark: to open a Visual Studio Command Prompt, go to: "Start > All Programs > Visual Studio > Visual Studio Tools"') else: print("Type 'cd %s; make' to build Z3" % BUILD_DIR) - + # Generate automatically generated source code def mk_auto_src(): if not ONLY_MAKEFILES: @@ -1801,10 +1801,10 @@ def def_module_params(module_name, export, params, class_name=None, description= # Generated accessors for param in params: if export: - out.write(' %s %s() const { return p.%s("%s", g, %s); }\n' % + out.write(' %s %s() const { return p.%s("%s", g, %s); }\n' % (TYPE2CTYPE[param[1]], to_c_method(param[0]), TYPE2GETTER[param[1]], param[0], pyg_default_as_c_literal(param))) else: - out.write(' %s %s() const { return p.%s("%s", %s); }\n' % + out.write(' %s %s() const { return p.%s("%s", %s); }\n' % (TYPE2CTYPE[param[1]], to_c_method(param[0]), TYPE2GETTER[param[1]], param[0], pyg_default_as_c_literal(param))) out.write('};\n') out.write('#endif\n') @@ -1817,8 +1817,8 @@ def max_memory_param(): def max_steps_param(): return ('max_steps', UINT, UINT_MAX, 'maximum number of steps') -PYG_GLOBALS = { 'UINT' : UINT, 'BOOL' : BOOL, 'DOUBLE' : DOUBLE, 'STRING' : STRING, 'SYMBOL' : SYMBOL, - 'UINT_MAX' : UINT_MAX, +PYG_GLOBALS = { 'UINT' : UINT, 'BOOL' : BOOL, 'DOUBLE' : DOUBLE, 'STRING' : STRING, 'SYMBOL' : SYMBOL, + 'UINT_MAX' : UINT_MAX, 'max_memory_param' : max_memory_param, 'max_steps_param' : max_steps_param, 'def_module_params' : def_module_params } @@ -1833,7 +1833,7 @@ def _execfile(file, globals=globals(), locals=locals()): # Execute python auxiliary scripts that generate extra code for Z3. def exec_pyg_scripts(): global CURR_PYG - for root, dirs, files in os.walk('src'): + for root, dirs, files in os.walk('src'): for f in files: if f.endswith('.pyg'): script = os.path.join(root, f) @@ -1849,7 +1849,7 @@ def mk_pat_db(): fout.write('char const * g_pattern_database =\n') for line in fin: fout.write('"%s\\n"\n' % line.strip('\n')) - fout.write(';\n') + fout.write(';\n') if VERBOSE: print("Generated '%s'" % os.path.join(c.src_dir, 'database.h')) @@ -1865,7 +1865,7 @@ def update_version(): mk_version_dot_h(major, minor, build, revision) mk_all_assembly_infos(major, minor, build, revision) mk_def_files() - + # Update files with the version number def mk_version_dot_h(major, minor, build, revision): c = get_component(UTIL_COMPONENT) @@ -1888,8 +1888,8 @@ def mk_all_assembly_infos(major, minor, build, revision): mk_assembly_info_version(assembly, major, minor, build, revision) else: raise MKException("Failed to find assembly info file 'AssemblyInfo' at '%s'" % os.path.join(c.src_dir, c.assembly_info_dir)) - - + + # Generate version number in the given 'AssemblyInfo.cs' file using 'AssemblyInfo' as a template. def mk_assembly_info_version(assemblyinfo, major, minor, build, revision): ver_pat = re.compile('[assembly: AssemblyVersion\("[\.\d]*"\) *') @@ -1954,7 +1954,7 @@ def mk_install_tactic_cpp(cnames, path): if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) - try: + try: exec(line.strip('\n '), globals()) except: raise MKException("Failed processing ADD_TACTIC command at '%s'\n%s" % (fullname, line)) @@ -1962,7 +1962,7 @@ def mk_install_tactic_cpp(cnames, path): if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) - try: + try: exec(line.strip('\n '), globals()) except: raise MKException("Failed processing ADD_PROBE command at '%s'\n%s" % (fullname, line)) @@ -2146,7 +2146,7 @@ def mk_def_files(): def cp_z3py_to_build(): mk_dir(BUILD_DIR) # Erase existing .pyc files - for root, dirs, files in os.walk(Z3PY_SRC_DIR): + for root, dirs, files in os.walk(Z3PY_SRC_DIR): for f in files: if f.endswith('.pyc'): rmf(os.path.join(root, f)) @@ -2189,7 +2189,7 @@ def mk_bindings(api_files): mk_z3consts_java(api_files) _execfile(os.path.join('scripts', 'update_api.py'), g) # HACK cp_z3py_to_build() - + # Extract enumeration types from API files, and add python definitions. def mk_z3consts_py(api_files): if Z3PY_SRC_DIR == None: @@ -2226,7 +2226,7 @@ def mk_z3consts_py(api_files): m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments - linenum = linenum + 1 + linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: @@ -2267,7 +2267,7 @@ def mk_z3consts_py(api_files): linenum = linenum + 1 if VERBOSE: print("Generated '%s'" % os.path.join(Z3PY_SRC_DIR, 'z3consts.py')) - + # Extract enumeration types from z3_api.h, and add .Net definitions def mk_z3consts_dotnet(api_files): @@ -2308,7 +2308,7 @@ def mk_z3consts_dotnet(api_files): m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments - linenum = linenum + 1 + linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: @@ -2391,7 +2391,7 @@ def mk_z3consts_java(api_files): m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments - linenum = linenum + 1 + linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: @@ -2429,7 +2429,7 @@ def mk_z3consts_java(api_files): for k in decls: i = decls[k] if first: - first = False + first = False else: efile.write(',\n') efile.write(' %s (%s)' % (k, i)) @@ -2440,7 +2440,7 @@ def mk_z3consts_java(api_files): efile.write(' }\n\n') efile.write(' public static final %s fromInt(int v) {\n' % name) efile.write(' for (%s k: values()) \n' % name) - efile.write(' if (k.intValue == v) return k;\n') + efile.write(' if (k.intValue == v) return k;\n') efile.write(' return values()[0];\n') efile.write(' }\n\n') efile.write(' public final int toInt() { return this.intValue; }\n') From 000db81b4f6811328fb9309c7af1a5d702eb1291 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 23 Jun 2014 15:47:47 +0100 Subject: [PATCH 372/509] Fixed bug where the random seed wasn't passed through to theory_arith. Thanks to Carsten! (Stackoverflow #24327987) Signed-off-by: Christoph M. Wintersteiger --- src/smt/params/theory_arith_params.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index 7281a5daa..7287cac2f 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -22,6 +22,7 @@ Revision History: void theory_arith_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_arith_random_initial_value = p.arith_random_initial_value(); + m_arith_random_seed = p.random_seed(); m_arith_mode = static_cast(p.arith_solver()); m_nl_arith = p.arith_nl(); m_nl_arith_gb = p.arith_nl_gb(); From 3209cd2ded8bfd952cfd99d0398b4c0bd4a0c04d Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 23 Jun 2014 16:40:49 +0100 Subject: [PATCH 373/509] Disabled construction of partial model on theory failure as it caused buggy behavior. Thanks to parno (Codeplex Issue #117)! Signed-off-by: Christoph M. Wintersteiger --- src/smt/smt_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 02ee06985..4f3c73ce6 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3945,7 +3945,7 @@ namespace smt { m_fingerprints.display(tout); ); failure fl = get_last_search_failure(); - if (fl == TIMEOUT || fl == MEMOUT || fl == CANCELED || fl == NUM_CONFLICTS) { + if (fl == TIMEOUT || fl == MEMOUT || fl == CANCELED || fl == NUM_CONFLICTS || fl == THEORY) { // don't generate model. return; } From 7158e814d1dc22eabaace0f8da6f8950e9bca1d9 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 25 Jun 2014 13:25:23 +0100 Subject: [PATCH 374/509] Bugfix for quasi-macros, many thanks to Nuno Lopez finding this bug and for suggesting a fix! Signed-off-by: Christoph M. Wintersteiger --- src/ast/macros/quasi_macros.cpp | 17 +++++++++++------ src/ast/macros/quasi_macros.h | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/ast/macros/quasi_macros.cpp b/src/ast/macros/quasi_macros.cpp index 084a33ebf..b26b7faba 100644 --- a/src/ast/macros/quasi_macros.cpp +++ b/src/ast/macros/quasi_macros.cpp @@ -41,7 +41,7 @@ void quasi_macros::find_occurrences(expr * e) { // we remember whether we have seen an expr once, or more than once; // when we see it the second time, we don't have to visit it another time, - // as we are only intersted in finding unique function applications. + // as we are only interested in finding unique function applications. m_visited_once.reset(); m_visited_more.reset(); @@ -61,7 +61,7 @@ void quasi_macros::find_occurrences(expr * e) { case AST_VAR: break; case AST_QUANTIFIER: m_todo.push_back(to_quantifier(cur)->get_expr()); break; case AST_APP: - if (is_uninterp(cur) && !is_ground(cur)) { + if (is_non_ground_uninterp(cur)) { func_decl * f = to_app(cur)->get_decl(); m_occurrences.insert_if_not_there(f, 0); occurrences_map::iterator it = m_occurrences.find_iterator(f); @@ -76,6 +76,10 @@ void quasi_macros::find_occurrences(expr * e) { } }; +bool quasi_macros::is_non_ground_uninterp(expr const * e) const { + return is_non_ground(e) && is_uninterp(e); +} + bool quasi_macros::is_unique(func_decl * f) const { return m_occurrences.find(f) == 1; } @@ -149,6 +153,7 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const { // Our definition of a quasi-macro: // Forall X. f[X] = T[X], where f[X] is a term starting with symbol f, f is uninterpreted, // f[X] contains all universally quantified variables, and f does not occur in T[X]. + TRACE("quasi_macros", tout << "Checking for quasi macro: " << mk_pp(e, m_manager) << std::endl;); if (is_quantifier(e) && to_quantifier(e)->is_forall()) { quantifier * q = to_quantifier(e); @@ -157,23 +162,23 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const { expr * lhs = to_app(qe)->get_arg(0); expr * rhs = to_app(qe)->get_arg(1); - if (is_uninterp(lhs) && is_unique(to_app(lhs)->get_decl()) && + if (is_non_ground_uninterp(lhs) && is_unique(to_app(lhs)->get_decl()) && !depends_on(rhs, to_app(lhs)->get_decl()) && fully_depends_on(to_app(lhs), q)) { a = to_app(lhs); t = rhs; return true; - } else if (is_uninterp(rhs) && is_unique(to_app(rhs)->get_decl()) && + } else if (is_non_ground_uninterp(rhs) && is_unique(to_app(rhs)->get_decl()) && !depends_on(lhs, to_app(rhs)->get_decl()) && fully_depends_on(to_app(rhs), q)) { a = to_app(rhs); t = lhs; return true; } - } else if (m_manager.is_not(qe) && is_uninterp(to_app(qe)->get_arg(0)) && + } else if (m_manager.is_not(qe) && is_non_ground_uninterp(to_app(qe)->get_arg(0)) && is_unique(to_app(to_app(qe)->get_arg(0))->get_decl())) { // this is like f(...) = false a = to_app(to_app(qe)->get_arg(0)); t = m_manager.mk_false(); return true; - } else if (is_uninterp(qe) && is_unique(to_app(qe)->get_decl())) { // this is like f(...) = true + } else if (is_non_ground_uninterp(qe) && is_unique(to_app(qe)->get_decl())) { // this is like f(...) = true a = to_app(qe); t = m_manager.mk_true(); return true; diff --git a/src/ast/macros/quasi_macros.h b/src/ast/macros/quasi_macros.h index c5e6b6d4f..64e72e348 100644 --- a/src/ast/macros/quasi_macros.h +++ b/src/ast/macros/quasi_macros.h @@ -45,6 +45,7 @@ class quasi_macros { expr_mark m_visited_more; bool is_unique(func_decl * f) const; + bool is_non_ground_uninterp(expr const * e) const; bool fully_depends_on(app * a, quantifier * q) const; bool depends_on(expr * e, func_decl * f) const; From 311fed47602fbd10e4e8313081328f0864215060 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 1 Jul 2014 13:12:10 +0100 Subject: [PATCH 375/509] Changed python distribution to include *.py files to enable use with Python 2.7 and 3.4 out of the box. Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_util.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 653e4588b..6a9ace762 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2591,16 +2591,17 @@ def mk_vs_proj(name, components): def mk_win_dist(build_path, dist_path): for c in get_components(): c.mk_win_dist(build_path, dist_path) - # Add Z3Py to lib directory - for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(build_path)): + # Add Z3Py to bin directory + print "Adding to %s\n" % dist_path + for pyc in filter(lambda f: f.endswith('.pyc') or f.endswith('.py'), os.listdir(build_path)): shutil.copy(os.path.join(build_path, pyc), os.path.join(dist_path, 'bin', pyc)) def mk_unix_dist(build_path, dist_path): for c in get_components(): c.mk_unix_dist(build_path, dist_path) - # Add Z3Py to lib directory - for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(build_path)): + # Add Z3Py to bin directory + for pyc in filter(lambda f: f.endswith('.pyc') or f.endswith('.py'), os.listdir(build_path)): shutil.copy(os.path.join(build_path, pyc), os.path.join(dist_path, 'bin', pyc)) From 3533a090105a5e26064bae50635e3f52a0c7846a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 2 Jul 2014 23:48:49 +0200 Subject: [PATCH 376/509] bit2bool bug reported by Sagar Chaki Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_bit_blast.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 112541541..90d410ab1 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -88,11 +88,12 @@ namespace datalog { for (unsigned j = 0; j < arity_q; ++j) { sort* s = q->get_domain(j); arg = m.mk_var(j, s); + expr* t = arg; if (m_bv.is_bv_sort(s)) { - expr* args[1] = { arg }; unsigned sz = m_bv.get_bv_size(s); for (unsigned k = 0; k < sz; ++k) { - proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, args); + parameter p(k); + proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t); sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj); } } From d6de73a2d10135d5fb036787888380352c2bc3a0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 6 Jul 2014 18:11:57 +0200 Subject: [PATCH 377/509] fix model converter in inliner. Bug reported by Sagar Chaki Signed-off-by: Nikolaj Bjorner --- src/api/z3_api.h | 3 ++- src/muz/base/dl_util.cpp | 2 +- src/muz/transforms/dl_mk_rule_inliner.cpp | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index b29a9c87d..94e20eceb 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -5668,7 +5668,8 @@ END_MLAPI_EXCLUDE Each conjunct encodes values of the bound variables of the query that are satisfied. In PDR mode, the returned answer is a single conjunction. - The previous call to Z3_fixedpoint_query must have returned Z3_L_TRUE. + When used in Datalog mode the previous call to Z3_fixedpoint_query must have returned Z3_L_TRUE. + When used with the PDR engine, the previous call must have been either Z3_L_TRUE or Z3_L_FALSE. def_API('Z3_fixedpoint_get_answer', AST, (_in(CONTEXT), _in(FIXEDPOINT))) */ diff --git a/src/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index 218f9906a..a6647a1d2 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -290,7 +290,7 @@ namespace datalog { } } TRACE("dl_dr", - tout << r.get_decl()->get_name() << "\n"; + tout << mk_pp(r.get_head(), m) << " :- \n"; for (unsigned i = 0; i < body.size(); ++i) { tout << mk_pp(body[i].get(), m) << "\n"; }); diff --git a/src/muz/transforms/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp index d9dad5c56..712c82feb 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -423,6 +423,11 @@ namespace datalog { } TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; ); + + for (unsigned i = 0; i < m_inlined_rules.get_num_rules(); ++i) { + rule* r = m_inlined_rules.get_rule(i); + datalog::del_rule(m_mc, *r); + } } bool mk_rule_inliner::transform_rule(rule_set const& orig, rule * r0, rule_set& tgt) { From 4f7d872d591cf1f9d23807898026e4516b686dc3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 8 Jul 2014 11:21:19 +0200 Subject: [PATCH 378/509] fix model transformation bug in bit blaster rule transformer, reported by Sagar Chaki Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_bit_blast.cpp | 2 +- src/tactic/filter_model_converter.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 90d410ab1..15d3b2f5f 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -71,7 +71,7 @@ namespace datalog { unsigned arity_p = p->get_arity(); unsigned arity_q = q->get_arity(); SASSERT(0 < arity_p); - model->register_decl(p, f); + model->register_decl(p, f->copy()); func_interp* g = alloc(func_interp, m, arity_q); if (f) { diff --git a/src/tactic/filter_model_converter.cpp b/src/tactic/filter_model_converter.cpp index da4c71e54..ba6ee4f0d 100644 --- a/src/tactic/filter_model_converter.cpp +++ b/src/tactic/filter_model_converter.cpp @@ -43,6 +43,7 @@ void filter_model_converter::operator()(model_ref & old_model, unsigned goal_idx if (fs.is_marked(f)) continue; func_interp * fi = old_model->get_func_interp(f); + SASSERT(fi); new_model->register_decl(f, fi->copy()); } new_model->copy_usort_interps(*old_model); From e4dedbbefca8ac3fcf21e9f4dac0cf2018b8100a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 Jul 2014 15:38:22 +0200 Subject: [PATCH 379/509] fix quantifier elimination bugs reported by Berdine and Bornat Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/arith_rewriter.cpp | 2 +- src/qe/qe.cpp | 115 ++++++++++++++++++++------- src/qe/qe_arith_plugin.cpp | 73 ++++++++++------- src/smt/theory_bv.cpp | 5 +- src/tactic/arith/bv2int_rewriter.cpp | 2 + 5 files changed, 137 insertions(+), 60 deletions(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index b040efc0c..e67300970 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -704,7 +704,7 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul } // propagate mod inside only if not all arguments are not already mod. - if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos() && (is_add(arg1) || is_mul(arg1))) { + if (false && m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos() && (is_add(arg1) || is_mul(arg1))) { TRACE("mod_bug", tout << "mk_mod:\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";); unsigned num_args = to_app(arg1)->get_num_args(); unsigned i; diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index d63f0ae00..770a49c07 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -1120,6 +1120,7 @@ namespace qe { st->init(fml); st->m_vars.append(m_vars.size(), m_vars.c_ptr()); SASSERT(invariant()); + TRACE("qe", tout << mk_pp(m_fml, m) << " child: " << mk_pp(fml, m) << "\n";); return st; } @@ -1133,6 +1134,7 @@ namespace qe { m_branch_index.insert(branch_id, index); st->m_vars.append(m_vars.size(), m_vars.c_ptr()); SASSERT(invariant()); + //TRACE("qe", tout << mk_pp(m_fml, m) << " child: " << mk_pp(st->fml(), m) << "\n";); return st; } @@ -1164,6 +1166,16 @@ namespace qe { } } + expr_ref abstract_variable(app* x, expr* fml) const { + expr_ref result(m); + expr* y = x; + expr_abstract(m, 0, 1, &y, fml, result); + symbol X(x->get_decl()->get_name()); + sort* s = m.get_sort(x); + result = m.mk_exists(1, &s, &X, result); + return result; + } + void display_validate(std::ostream& out) const { if (m_children.empty()) { return; @@ -1171,25 +1183,53 @@ namespace qe { expr_ref q(m); expr* x = m_var; if (x) { - expr_abstract(m, 0, 1, &x, m_fml, q); - ptr_vector fmls; + q = abstract_variable(m_var, m_fml); + + expr_ref_vector fmls(m); + expr_ref fml(m); for (unsigned i = 0; i < m_children.size(); ++i) { - expr* fml = m_children[i]->fml(); + search_tree const& child = *m_children[i]; + fml = child.fml(); if (fml) { + // abstract free variables in children. + ptr_vector child_vars, new_vars; + child_vars.append(child.m_vars.size(), child.m_vars.c_ptr()); + if (child.m_var) { + child_vars.push_back(child.m_var); + } + for (unsigned j = 0; j < child_vars.size(); ++j) { + if (!m_vars.contains(child_vars[j]) && + !new_vars.contains(child_vars[j])) { + fml = abstract_variable(child_vars[j], fml); + new_vars.push_back(child_vars[j]); + } + } fmls.push_back(fml); } } - symbol X(m_var->get_decl()->get_name()); - sort* s = m.get_sort(x); - q = m.mk_exists(1, &s, &X, q); - expr_ref tmp(m); - bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), tmp); - expr_ref f(m.mk_not(m.mk_iff(q, tmp)), m); + bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), fml); + + fml = m.mk_not(m.mk_iff(q, fml)); ast_smt_pp pp(m); - out << "(echo " << m_var->get_decl()->get_name() << ")\n"; + out << "; eliminate " << mk_pp(m_var, m) << "\n"; out << "(push)\n"; - pp.display_smt2(out, f); + pp.display_smt2(out, fml); out << "(pop)\n\n"; + DEBUG_CODE( + smt_params params; + params.m_simplify_bit2int = true; + params.m_arith_enum_const_mod = true; + params.m_bv_enable_int2bv2int = true; + params.m_relevancy_lvl = 0; + smt::kernel ctx(m, params); + ctx.assert_expr(fml); + lbool is_sat = ctx.check(); + if (is_sat == l_true) { + std::cout << "; Validation failed:\n"; + std::cout << mk_pp(fml, m) << "\n"; + } +); + } for (unsigned i = 0; i < m_children.size(); ++i) { if (m_children[i]->fml()) { @@ -1410,13 +1450,9 @@ namespace qe { m_solver.assert_expr(m_fml); if (assumption) m_solver.assert_expr(assumption); bool is_sat = false; - while (l_false != m_solver.check()) { + while (l_true == m_solver.check()) { is_sat = true; - model_ref model; - m_solver.get_model(model); - TRACE("qe", model_v2_pp(tout, *model);); - model_evaluator model_eval(*model); - final_check(model_eval); + final_check(); } if (!is_sat) { @@ -1466,14 +1502,30 @@ namespace qe { private: - void final_check(model_evaluator& model_eval) { - TRACE("qe", tout << "\n";); - while (can_propagate_assignment(model_eval)) { - propagate_assignment(model_eval); - } - VERIFY(CHOOSE_VAR == update_current(model_eval, true)); - SASSERT(m_current->fml()); - pop(model_eval); + void final_check() { + model_ref model; + m_solver.get_model(model); + scoped_ptr model_eval = alloc(model_evaluator, *model); + + while (true) { + TRACE("qe", model_v2_pp(tout, *model);); + while (can_propagate_assignment(*model_eval)) { + propagate_assignment(*model_eval); + } + VERIFY(CHOOSE_VAR == update_current(*model_eval, true)); + SASSERT(m_current->fml()); + if (l_true != m_solver.check()) { + return; + } + m_solver.get_model(model); + model_eval = alloc(model_evaluator, *model); + search_tree* st = m_current; + update_current(*model_eval, false); + if (st == m_current) { + break; + } + } + pop(*model_eval); } ast_manager& get_manager() { return m; } @@ -1633,6 +1685,7 @@ namespace qe { nb = m_current->get_num_branches(); expr_ref fml(m_current->fml(), m); if (!eval(model_eval, b, branch) || branch >= nb) { + TRACE("qe", tout << "evaluation failed: setting branch to 0\n";); branch = rational::zero(); } SASSERT(!branch.is_neg()); @@ -1694,11 +1747,12 @@ namespace qe { } // - // The current state is satisfiable - // and the closed portion of the formula - // can be used as the quantifier-free portion. + // The closed portion of the formula + // can be used as the quantifier-free portion, + // unless the current state is unsatisfiable. // if (m.is_true(fml_mixed)) { + SASSERT(l_true == m_solver.check()); m_current = m_current->add_child(fml_closed); for (unsigned i = 0; m_defs && i < m_current->num_free_vars(); ++i) { expr_ref val(m); @@ -1708,6 +1762,7 @@ namespace qe { if (val == x) { model_ref model; lbool is_sat = m_solver.check(); + if (is_sat == l_undef) return; m_solver.get_model(model); SASSERT(is_sat == l_true); model_evaluator model_eval2(*model); @@ -1890,7 +1945,7 @@ namespace qe { vars.reset(); closed = closed && (r != l_undef); } - TRACE("qe", tout << mk_ismt2_pp(fml, m) << "\n";); + TRACE("qe", tout << mk_pp(fml, m) << "\n";); m_current->add_child(fml)->reset_free_vars(); block_assignment(); } @@ -1959,7 +2014,7 @@ namespace qe { class quant_elim_new : public quant_elim { ast_manager& m; - smt_params& m_fparams; + smt_params& m_fparams; expr_ref m_assumption; bool m_produce_models; ptr_vector m_plugins; diff --git a/src/qe/qe_arith_plugin.cpp b/src/qe/qe_arith_plugin.cpp index 7bf0978f6..83c7b79d8 100644 --- a/src/qe/qe_arith_plugin.cpp +++ b/src/qe/qe_arith_plugin.cpp @@ -31,6 +31,7 @@ Revision History: #include "obj_pair_hashtable.h" #include "nlarith_util.h" #include "model_evaluator.h" +#include "smt_kernel.h" namespace qe { @@ -80,9 +81,9 @@ namespace qe { ast_manager& m; i_solver_context& m_ctx; public: - arith_util m_arith; // initialize before m_zero_i, etc. + arith_util m_arith; // initialize before m_zero_i, etc. + th_rewriter simplify; private: - th_rewriter m_rewriter; arith_eq_solver m_arith_solver; bv_util m_bv; @@ -102,7 +103,7 @@ namespace qe { m(m), m_ctx(ctx), m_arith(m), - m_rewriter(m), + simplify(m), m_arith_solver(m), m_bv(m), m_zero_i(m_arith.mk_numeral(numeral(0), true), m), @@ -434,7 +435,6 @@ namespace qe { expr_ref tmp(e, m); simplify(tmp); m_arith_rewriter.mk_le(tmp, mk_zero(e), result); - TRACE("qe_verbose", tout << "mk_le " << mk_pp(result, m) << "\n";); } void mk_lt(expr* e, expr_ref& result) { @@ -521,7 +521,8 @@ namespace qe { expr_ref result1(m), result2(m); // a*s + b*t <= 0 - expr_ref as_bt_le_0(result, m), tmp2(m), tmp3(m), tmp4(m); + expr_ref as_bt_le_0(result, m), tmp2(m), asz_bt_le_0(m), tmp3(m), tmp4(m); + expr_ref b_divides_sz(m); // a*s + b*t + (a-1)(b-1) <= 0 tmp2 = m_arith.mk_add(as_bt, slack); @@ -560,30 +561,36 @@ namespace qe { sz = m_arith.mk_uminus(sz); } tmp4 = mk_add(mk_mul(a1, sz), bt); - mk_le(tmp4, tmp3); + mk_le(tmp4, asz_bt_le_0); - if (to_app(tmp3)->get_arg(0) == x && - m_arith.is_zero(to_app(tmp3)->get_arg(1))) { + if (to_app(asz_bt_le_0)->get_arg(0) == x && + m_arith.is_zero(to_app(asz_bt_le_0)->get_arg(1))) { // exists z in [0 .. |b|-2] . |b| | (z + s) && z <= 0 // <=> // |b| | s mk_divides(abs_b, s, tmp2); } else { - mk_divides(abs_b, sz, tmp2); - mk_and(tmp2, tmp3, tmp4); - mk_big_or(abs_b - numeral(2), x, tmp4, tmp2); - + mk_divides(abs_b, sz, b_divides_sz); + mk_and(b_divides_sz, asz_bt_le_0, tmp4); + mk_big_or(abs_b - numeral(2), x, tmp4, tmp2); + TRACE("qe", + tout << "b | s + z: " << mk_pp(b_divides_sz, m) << "\n"; + tout << "a(s+z) + bt <= 0: " << mk_pp(asz_bt_le_0, m) << "\n"; + ); } mk_flat_and(as_bt_le_0, tmp2, result2); mk_or(result1, result2, result); simplify(result); + + + // a*s + b*t + (a-1)(b-1) <= 0 + // or exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 } TRACE("qe", { - expr_ref_vector trail(m); - tout << "is_strict: " << (is_strict?"true":"false") << "\n"; + tout << (is_strict?"strict":"non-strict") << "\n"; bound(m, a, t, false).pp(tout, x); tout << "\n"; bound(m, b, s, false).pp(tout, x); @@ -592,10 +599,6 @@ namespace qe { }); } - void simplify(expr_ref& p) { - m_rewriter(p); - } - struct mul_lt { arith_util& u; mul_lt(arith_qe_util& u): u(u.m_arith) {} @@ -1052,7 +1055,6 @@ namespace qe { } bool reduce_equation(expr* p, expr* fml) { - TRACE("qe", tout << mk_pp(p, m) << "\n";); numeral k; if (m_arith.is_numeral(p, k) && k.is_zero()) { @@ -1555,9 +1557,10 @@ public: mk_non_resolve(bounds, true, is_lower, result); mk_non_resolve(bounds, false, is_lower, result); + m_util.simplify(result); add_cache(x, fml, v, result, x_t.get_coeff(), x_t.get_term()); TRACE("qe", - tout << vl << " " << mk_pp(x, m) << "\n"; + tout << vl << " " << mk_pp(x, m) << " infinite case\n"; tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); return; @@ -1591,19 +1594,22 @@ public: SASSERT(index < bounds.size(is_strict, is_lower)); expr_ref t(bounds.exprs(is_strict, is_lower)[index], m); rational a = bounds.coeffs(is_strict, is_lower)[index]; + - t = x_t.mk_term(a, t); - a = x_t.mk_coeff(a); mk_bounds(bounds, x, true, is_eq, is_strict, is_lower, index, a, t, result); mk_bounds(bounds, x, false, is_eq, is_strict, is_lower, index, a, t, result); + + t = x_t.mk_term(a, t); + a = x_t.mk_coeff(a); mk_resolve(bounds, x, x_t, true, is_eq, is_strict, is_lower, index, a, t, result); mk_resolve(bounds, x, x_t, false, is_eq, is_strict, is_lower, index, a, t, result); + m_util.simplify(result); add_cache(x, fml, v, result, x_t.get_coeff(), x_t.get_term()); TRACE("qe", { - tout << vl << " " << mk_pp(x, m) << "\n"; + tout << vl << " " << mk_pp(bounds.atoms(is_strict, is_lower)[index],m) << "\n"; tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n"; } @@ -2225,6 +2231,12 @@ public: } } m_util.simplify(result); + TRACE("qe", + tout << (is_strict?"strict":"non-strict") << "\n"; + tout << (is_lower?"is-lower":"is-upper") << "\n"; + tout << "a: " << a << " " << mk_pp(t, m) << "\n"; + tout << "b: " << b << " " << mk_pp(s, m) << "\n"; + tout << mk_pp(result, m) << "\n";); } // @@ -2245,10 +2257,12 @@ public: void mk_bounds(bounds_proc& bounds, - app* x, bool is_strict, bool is_eq_ctx, bool is_strict_ctx, bool is_lower, unsigned index, + app* x, bool is_strict, bool is_eq_ctx, + bool is_strict_ctx, bool is_lower, unsigned index, rational const& a, expr* t, expr_ref& result) { + TRACE("qe", tout << mk_pp(t, m) << "\n";); SASSERT(!is_eq_ctx || !is_strict_ctx); unsigned sz = bounds.size(is_strict, is_lower); expr_ref tmp(m), eq(m); @@ -2258,13 +2272,14 @@ public: for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, is_lower)[i]; - expr* s = bounds.exprs(is_strict, is_lower)[i]; + expr_ref s(bounds.exprs(is_strict, is_lower)[i], m); rational b = bounds.coeffs(is_strict, is_lower)[i]; if (same_strict && i == index) { if (non_strict_real) { m_util.mk_eq(a, x, t, eq); - TRACE("qe", tout << "a:" << a << " x: " << mk_pp(x, m) << " t: " << mk_pp(t, m) << " eq: " << mk_pp(eq, m) << "\n";); + TRACE("qe", tout << "a:" << a << " x: " << mk_pp(x, m) << "t: " << + mk_pp(t, m) << " eq: " << mk_pp(eq, m) << "\n";); if (is_eq_ctx) { m_ctx.add_constraint(true, eq); } @@ -2292,6 +2307,7 @@ public: (non_strict_real && is_eq_ctx && is_strict) || (same_strict && i < index); + mk_bound(result_is_strict, is_lower, a, t, b, s, tmp); m_util.m_replace.apply_substitution(e, tmp.get(), result); @@ -2330,14 +2346,17 @@ public: s = x_t.mk_term(b, s); b = x_t.mk_coeff(b); m_util.mk_resolve(x, strict_resolve, a, t, b, s, tmp); + expr_ref save_result(result); m_util.m_replace.apply_substitution(e, tmp.get(), result); m_ctx.add_constraint(true, mk_not(e), tmp); TRACE("qe_verbose", tout << mk_pp(atm, m) << " "; - tout << mk_pp(e, m) << " ==> "; + tout << mk_pp(e, m) << " ==>\n"; tout << mk_pp(tmp, m) << "\n"; + tout << "old fml: " << mk_pp(save_result, m) << "\n"; + tout << "new fml: " << mk_pp(result, m) << "\n"; ); } } diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index a338be50a..44715c37a 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -1198,6 +1198,7 @@ namespace smt { void theory_bv::relevant_eh(app * n) { ast_manager & m = get_manager(); context & ctx = get_context(); + TRACE("bv", tout << "relevant: " << mk_pp(n, m) << "\n";); if (m.is_bool(n)) { bool_var v = ctx.get_bool_var(n); atom * a = get_bv2a(v); @@ -1210,11 +1211,11 @@ namespace smt { } } } - else if (m_params.m_bv_enable_int2bv2int && m_util.is_bv2int(n)) { + else if (/*m_params.m_bv_enable_int2bv2int &&*/ m_util.is_bv2int(n)) { ctx.mark_as_relevant(n->get_arg(0)); assert_bv2int_axiom(n); } - else if (m_params.m_bv_enable_int2bv2int && m_util.is_int2bv(n)) { + else if (/*m_params.m_bv_enable_int2bv2int &&*/ m_util.is_int2bv(n)) { ctx.mark_as_relevant(n->get_arg(0)); assert_int2bv_axiom(n); } diff --git a/src/tactic/arith/bv2int_rewriter.cpp b/src/tactic/arith/bv2int_rewriter.cpp index 872981283..00d2f53ad 100644 --- a/src/tactic/arith/bv2int_rewriter.cpp +++ b/src/tactic/arith/bv2int_rewriter.cpp @@ -218,6 +218,7 @@ br_status bv2int_rewriter::mk_mod(expr * s, expr * t, expr_ref & result) { if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_urem(s1, t1)); + TRACE("bv2int_rewriter", tout << mk_pp(result,m()) << "\n";); return BR_DONE; } @@ -232,6 +233,7 @@ br_status bv2int_rewriter::mk_mod(expr * s, expr * t, expr_ref & result) { u1 = mk_bv_add(s1, u1, false); align_sizes(u1, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_urem(u1, t1)); + TRACE("bv2int_rewriter", tout << mk_pp(result,m()) << "\n";); return BR_DONE; } From dd786bb5bf4babb7eef988ad3487d627fee13ce4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 Jul 2014 15:41:03 +0200 Subject: [PATCH 380/509] fix quantifier elimination bugs reported by Berdine and Bornat Signed-off-by: Nikolaj Bjorner --- src/smt/theory_bv.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 44715c37a..8b3021573 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -1211,11 +1211,11 @@ namespace smt { } } } - else if (/*m_params.m_bv_enable_int2bv2int &&*/ m_util.is_bv2int(n)) { + else if (m_params.m_bv_enable_int2bv2int && m_util.is_bv2int(n)) { ctx.mark_as_relevant(n->get_arg(0)); assert_bv2int_axiom(n); } - else if (/*m_params.m_bv_enable_int2bv2int &&*/ m_util.is_int2bv(n)) { + else if (m_params.m_bv_enable_int2bv2int && m_util.is_int2bv(n)) { ctx.mark_as_relevant(n->get_arg(0)); assert_int2bv_axiom(n); } From 752a6b2e33c954eba65a2d88d3b8e6ef82a251e0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 Jul 2014 16:46:27 +0200 Subject: [PATCH 381/509] fix quantifier elimination bugs reported by Berdine and Bornat Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/arith_rewriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index e67300970..b040efc0c 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -704,7 +704,7 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul } // propagate mod inside only if not all arguments are not already mod. - if (false && m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos() && (is_add(arg1) || is_mul(arg1))) { + if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos() && (is_add(arg1) || is_mul(arg1))) { TRACE("mod_bug", tout << "mk_mod:\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";); unsigned num_args = to_app(arg1)->get_num_args(); unsigned i; From 72fe197bda0d89674a1056def7640bd20e237b4f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 14 Jul 2014 17:06:36 +0200 Subject: [PATCH 382/509] fix model generation bug reported by Saga Chaki Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_bit_blast.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 15d3b2f5f..fd1dbb205 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -26,6 +26,7 @@ Revision History: #include "dl_mk_interp_tail_simplifier.h" #include "fixedpoint_params.hpp" #include "scoped_proof.h" +#include "model_v2_pp.h" namespace datalog { @@ -67,10 +68,16 @@ namespace datalog { func_decl* p = m_new_funcs[i].get(); func_decl* q = m_old_funcs[i].get(); func_interp* f = model->get_func_interp(p); + if (!f) continue; expr_ref body(m); unsigned arity_p = p->get_arity(); unsigned arity_q = q->get_arity(); + TRACE("dl", + model_v2_pp(tout, *model); + tout << mk_pp(p, m) << "\n"; + tout << mk_pp(q, m) << "\n";); SASSERT(0 < arity_p); + SASSERT(f); model->register_decl(p, f->copy()); func_interp* g = alloc(func_interp, m, arity_q); From 4957e71408ebfa96545f767b665e37390512092e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 21 Jul 2014 17:12:39 +0200 Subject: [PATCH 383/509] make get_vars populate all indices with sorts even if variable does not occur in rule. This makes the use of get_vars less prone to callers having to double check for null pointers Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_rule.cpp | 5 +++-- src/muz/base/dl_rule.h | 2 +- src/muz/bmc/dl_bmc_engine.cpp | 10 +++++----- src/muz/transforms/dl_mk_array_blast.cpp | 2 +- src/muz/transforms/dl_mk_coalesce.cpp | 6 +++--- src/muz/transforms/dl_mk_quantifier_instantiation.cpp | 5 +---- src/muz/transforms/dl_mk_rule_inliner.cpp | 5 +---- src/muz/transforms/dl_mk_scale.cpp | 2 +- 8 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index 184a5fa02..017bac724 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -978,13 +978,14 @@ namespace datalog { } } - void rule::get_vars(ptr_vector& sorts) const { + void rule::get_vars(ast_manager& m, ptr_vector& sorts) const { sorts.reset(); used_vars used; get_used_vars(used); unsigned sz = used.get_max_found_var_idx_plus_1(); for (unsigned i = 0; i < sz; ++i) { - sorts.push_back(used.get(i)); + sort* s = used.get(i); + sorts.push_back(s?s:m.mk_bool_sort()); } } diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h index 1c31dc6b4..7104bae1f 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -304,7 +304,7 @@ namespace datalog { void norm_vars(rule_manager & rm); - void get_vars(ptr_vector& sorts) const; + void get_vars(ast_manager& m, ptr_vector& sorts) const; void to_formula(expr_ref& result) const; diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 0dcbf4644..51b1a5295 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -148,7 +148,7 @@ namespace datalog { void mk_qrule_vars(datalog::rule const& r, unsigned rule_id, expr_ref_vector& sub) { ptr_vector sorts; - r.get_vars(sorts); + r.get_vars(m, sorts); // populate substitution of bound variables. sub.reset(); sub.resize(sorts.size()); @@ -421,7 +421,7 @@ namespace datalog { ptr_vector rule_vars; expr_ref_vector args(m), conjs(m); - r.get_vars(rule_vars); + r.get_vars(m, rule_vars); obj_hashtable used_vars; unsigned num_vars = 0; for (unsigned i = 0; i < r.get_decl()->get_arity(); ++i) { @@ -514,7 +514,7 @@ namespace datalog { unsigned sz = r->get_uninterpreted_tail_size(); ptr_vector rule_vars; - r->get_vars(rule_vars); + r->get_vars(m, rule_vars); args.append(prop->get_num_args(), prop->get_args()); expr_ref_vector sub = mk_skolem_binding(*r, rule_vars, args); if (sub.empty() && sz == 0) { @@ -803,7 +803,7 @@ namespace datalog { func_decl* p = r.get_decl(); ptr_vector const& succs = *dtu.get_datatype_constructors(m.get_sort(path)); // populate substitution of bound variables. - r.get_vars(sorts); + r.get_vars(m, sorts); sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { @@ -1327,7 +1327,7 @@ namespace datalog { void mk_rule_vars(rule& r, unsigned level, unsigned rule_id, expr_ref_vector& sub) { ptr_vector sorts; - r.get_vars(sorts); + r.get_vars(m, sorts); // populate substitution of bound variables. sub.reset(); sub.resize(sorts.size()); diff --git a/src/muz/transforms/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp index 9303181b6..641d40779 100644 --- a/src/muz/transforms/dl_mk_array_blast.cpp +++ b/src/muz/transforms/dl_mk_array_blast.cpp @@ -87,7 +87,7 @@ namespace datalog { else { if (m_next_var == 0) { ptr_vector vars; - r.get_vars(vars); + r.get_vars(m, vars); m_next_var = vars.size() + 1; } v = m.mk_var(m_next_var, m.get_sort(e)); diff --git a/src/muz/transforms/dl_mk_coalesce.cpp b/src/muz/transforms/dl_mk_coalesce.cpp index 8c7f1d5b4..ac7a58d8d 100644 --- a/src/muz/transforms/dl_mk_coalesce.cpp +++ b/src/muz/transforms/dl_mk_coalesce.cpp @@ -62,7 +62,7 @@ namespace datalog { rule_ref r(const_cast(&rl), rm); ptr_vector sorts; expr_ref_vector revsub(m), conjs(m); - rl.get_vars(sorts); + rl.get_vars(m, sorts); revsub.resize(sorts.size()); svector valid(sorts.size(), true); for (unsigned i = 0; i < sub.size(); ++i) { @@ -117,8 +117,8 @@ namespace datalog { rule_ref res(rm); bool_rewriter bwr(m); svector is_neg; - tgt->get_vars(sorts1); - src.get_vars(sorts2); + tgt->get_vars(m, sorts1); + src.get_vars(m, sorts2); mk_pred(head, src.get_head(), tgt->get_head()); for (unsigned i = 0; i < src.get_uninterpreted_tail_size(); ++i) { diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp index 9c55ca658..fcb8d7b85 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -199,7 +199,7 @@ namespace datalog { expr_ref fml(m), cnst(m); var_ref var(m); ptr_vector sorts; - r.get_vars(sorts); + r.get_vars(m, sorts); m_uf.reset(); m_terms.reset(); m_var2cnst.reset(); @@ -207,9 +207,6 @@ namespace datalog { fml = m.mk_and(conjs.size(), conjs.c_ptr()); for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } var = m.mk_var(i, sorts[i]); cnst = m.mk_fresh_const("C", sorts[i]); m_var2cnst.insert(var, cnst); diff --git a/src/muz/transforms/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp index 712c82feb..522bd2e86 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -143,11 +143,8 @@ namespace datalog { expr_ref_vector result(m); ptr_vector sorts; expr_ref v(m), w(m); - r.get_vars(sorts); + r.get_vars(m, sorts); for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } v = m.mk_var(i, sorts[i]); m_subst.apply(2, m_deltas, expr_offset(v, is_tgt?0:1), w); result.push_back(w); diff --git a/src/muz/transforms/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp index 9ada7be20..271bf5f62 100644 --- a/src/muz/transforms/dl_mk_scale.cpp +++ b/src/muz/transforms/dl_mk_scale.cpp @@ -141,7 +141,7 @@ namespace datalog { m_cache.reset(); m_trail.reset(); m_eqs.reset(); - r.get_vars(vars); + r.get_vars(m, vars); unsigned num_vars = vars.size(); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(mk_pred(num_vars, r.get_tail(j))); From 44751c0ef8212bc78400aac42432e4fc7db3fa15 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Jul 2014 15:27:24 -0700 Subject: [PATCH 384/509] Add missing .NET API functions for parsing rules into fixedpoint object Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/Fixedpoint.cs | 39 +++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/api/dotnet/Fixedpoint.cs b/src/api/dotnet/Fixedpoint.cs index 8c6e6c4c6..3f40b220b 100644 --- a/src/api/dotnet/Fixedpoint.cs +++ b/src/api/dotnet/Fixedpoint.cs @@ -269,6 +269,14 @@ namespace Microsoft.Z3 AST.ArrayLength(queries), AST.ArrayToNative(queries)); } + BoolExpr[] ToBoolExprs(ASTVector v) { + uint n = v.Size; + BoolExpr[] res = new BoolExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = new BoolExpr(Context, v[i].NativeObject); + return res; + } + /// /// Retrieve set of rules added to fixedpoint context. /// @@ -278,12 +286,7 @@ namespace Microsoft.Z3 { Contract.Ensures(Contract.Result() != null); - ASTVector v = new ASTVector(Context, Native.Z3_fixedpoint_get_rules(Context.nCtx, NativeObject)); - uint n = v.Size; - BoolExpr[] res = new BoolExpr[n]; - for (uint i = 0; i < n; i++) - res[i] = new BoolExpr(Context, v[i].NativeObject); - return res; + return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_get_rules(Context.nCtx, NativeObject))); } } @@ -296,15 +299,27 @@ namespace Microsoft.Z3 { Contract.Ensures(Contract.Result() != null); - ASTVector v = new ASTVector(Context, Native.Z3_fixedpoint_get_assertions(Context.nCtx, NativeObject)); - uint n = v.Size; - BoolExpr[] res = new BoolExpr[n]; - for (uint i = 0; i < n; i++) - res[i] = new BoolExpr(Context, v[i].NativeObject); - return res; + return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_get_assertions(Context.nCtx, NativeObject))); } } + /// + /// Parse an SMT-LIB2 file with fixedpoint rules. + /// Add the rules to the current fixedpoint context. + /// Return the set of queries in the file. + /// + public BoolExpr[] ParseFile(string file) { + return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_from_file(Context.nCtx, NativeObject, file))); + } + + /// + /// Similar to ParseFile. Instead it takes as argument a string. + /// + + public BoolExpr[] ParseString(string s) { + return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_from_string(Context.nCtx, NativeObject, s))); + } + #region Internal internal Fixedpoint(Context ctx, IntPtr obj) From 5b1a98a15587ff49ca023f93bba20fb3c1672790 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 24 Jul 2014 13:53:56 +0100 Subject: [PATCH 385/509] Bugfix for Python 3 --- scripts/mk_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 6a9ace762..16393d2b8 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2592,7 +2592,7 @@ def mk_win_dist(build_path, dist_path): for c in get_components(): c.mk_win_dist(build_path, dist_path) # Add Z3Py to bin directory - print "Adding to %s\n" % dist_path + print("Adding to %s\n" % dist_path) for pyc in filter(lambda f: f.endswith('.pyc') or f.endswith('.py'), os.listdir(build_path)): shutil.copy(os.path.join(build_path, pyc), os.path.join(dist_path, 'bin', pyc)) From 1abf3beaba62d5a426ce5e76a9a7b356f1b99506 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 24 Jul 2014 16:52:32 +0100 Subject: [PATCH 386/509] bugfix for Python 3 Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 16393d2b8..b73f7a7a8 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -764,7 +764,7 @@ class Component: out.write('\n') mk_dir(os.path.join(BUILD_DIR, self.build_dir)) if VS_PAR and IS_WINDOWS: - cppfiles = get_cpp_files(self.src_dir) + cppfiles = list(get_cpp_files(self.src_dir)) dependencies = set() for cppfile in cppfiles: dependencies.add(os.path.join(self.to_src_dir, cppfile)) From 24961dc5f16645b345f99c13cb30b5b46764040a Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Fri, 25 Jul 2014 14:42:00 -0700 Subject: [PATCH 387/509] feat(ast/ast_smt_pp): display quantifier QID when printing proofs, feature requested by Dan Rosen Signed-off-by: Leonardo de Moura --- src/ast/ast_smt_pp.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index f684879ae..59e5c04b9 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -569,8 +569,8 @@ class smt_printer { m_out << ")"; } - if (m_is_smt2 && q->get_num_patterns() > 0) { - m_out << "(!"; + if (m_is_smt2 && (q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) { + m_out << "(! "; } { smt_printer p(m_out, m_manager, m_qlists, m_renaming, m_logic, false, m_is_smt2, m_simplify_implies, m_indent, m_num_var_names, m_var_names); @@ -609,7 +609,11 @@ class smt_printer { m_out << "}"; } } - if (m_is_smt2 && q->get_num_patterns() > 0) { + + if (q->get_qid() != symbol::null) + m_out << " :qid " << q->get_qid(); + + if (m_is_smt2 && (q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) { m_out << ")"; } m_out << ")"; From 3ca85913476fc71d515518e178dd2ae8787b386c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 28 Jul 2014 09:59:35 -0700 Subject: [PATCH 388/509] take theory explanation into account Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 02ee06985..4f3c73ce6 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3945,7 +3945,7 @@ namespace smt { m_fingerprints.display(tout); ); failure fl = get_last_search_failure(); - if (fl == TIMEOUT || fl == MEMOUT || fl == CANCELED || fl == NUM_CONFLICTS) { + if (fl == TIMEOUT || fl == MEMOUT || fl == CANCELED || fl == NUM_CONFLICTS || fl == THEORY) { // don't generate model. return; } From 1944283253dbdd7c27f0586eabaa2456e9aef76b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 28 Jul 2014 19:36:11 +0100 Subject: [PATCH 389/509] FPA unified function names --- src/ast/float_decl_plugin.cpp | 2 +- src/ast/float_decl_plugin.h | 11 +++++++---- src/ast/rewriter/float_rewriter.cpp | 21 ++++++++++----------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index 1adedb2d7..df26422c8 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -106,7 +106,7 @@ bool float_decl_plugin::is_value(expr * n, mpf & val) { return false; } -bool float_decl_plugin::is_rm(expr * n, mpf_rounding_mode & val) { +bool float_decl_plugin::is_rm_value(expr * n, mpf_rounding_mode & val) { if (is_app_of(n, m_family_id, OP_RM_NEAREST_TIES_TO_AWAY)) { val = MPF_ROUND_NEAREST_TAWAY; return true; diff --git a/src/ast/float_decl_plugin.h b/src/ast/float_decl_plugin.h index 3786b76ee..58c37080d 100644 --- a/src/ast/float_decl_plugin.h +++ b/src/ast/float_decl_plugin.h @@ -169,7 +169,8 @@ public: app * mk_value(mpf const & v); bool is_value(expr * n) { return is_app_of(n, m_family_id, OP_FLOAT_VALUE); } bool is_value(expr * n, mpf & val); - bool is_rm(expr * n, mpf_rounding_mode & val); + bool is_rm_value(expr * n, mpf_rounding_mode & val); + bool is_rm_value(expr * n) { mpf_rounding_mode t; return is_rm_value(n, t); } mpf const & get_value(unsigned id) const { SASSERT(m_value_table.contains(id)); @@ -198,7 +199,9 @@ public: sort * mk_float_sort(unsigned ebits, unsigned sbits); sort * mk_rm_sort() { return m().mk_sort(m_fid, ROUNDING_MODE_SORT); } bool is_float(sort * s) { return is_sort_of(s, m_fid, FLOAT_SORT); } - bool is_rm(sort * s) { return is_sort_of(s, m_fid, ROUNDING_MODE_SORT); } + bool is_rm(sort * s) { return is_sort_of(s, m_fid, ROUNDING_MODE_SORT); } + bool is_float(expr * e) { return is_float(m_manager.get_sort(e)); } + bool is_rm(expr * e) { return is_rm(m_manager.get_sort(e)); } unsigned get_ebits(sort * s); unsigned get_sbits(sort * s); @@ -217,8 +220,8 @@ public: app * mk_value(mpf const & v) { return m_plugin->mk_value(v); } bool is_value(expr * n) { return m_plugin->is_value(n); } - bool is_value(expr * n, mpf & v) { return m_plugin->is_value(n, v); } - bool is_rm(expr * n, mpf_rounding_mode & v) { return m_plugin->is_rm(n, v); } + bool is_value(expr * n, mpf & v) { return m_plugin->is_value(n, v); } + bool is_rm_value(expr * n, mpf_rounding_mode & v) { return m_plugin->is_rm_value(n, v); } app * mk_pzero(unsigned ebits, unsigned sbits); app * mk_nzero(unsigned ebits, unsigned sbits); diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index ecd06ff38..a4212d579 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -82,7 +82,7 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c if (num_args == 2) { mpf_rounding_mode rm; - if (!m_util.is_rm(args[0], rm)) + if (!m_util.is_rm_value(args[0], rm)) return BR_FAILED; rational q; @@ -109,12 +109,12 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c return BR_FAILED; } else if (num_args == 3 && - m_util.is_rm(m().get_sort(args[0])) && + m_util.is_rm(args[0]) && m_util.au().is_real(args[1]) && m_util.au().is_int(args[2])) { mpf_rounding_mode rm; - if (!m_util.is_rm(args[0], rm)) + if (!m_util.is_rm_value(args[0], rm)) return BR_FAILED; rational q; @@ -129,8 +129,7 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c mpf v; m_util.fm().set(v, ebits, sbits, rm, q.to_mpq(), e.to_mpq().numerator()); result = m_util.mk_value(v); - m_util.fm().del(v); - // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); + m_util.fm().del(v); return BR_DONE; } else { @@ -140,7 +139,7 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c br_status float_rewriter::mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()), v3(m_util.fm()); if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) { scoped_mpf t(m_util.fm()); @@ -161,7 +160,7 @@ br_status float_rewriter::mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref br_status float_rewriter::mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()), v3(m_util.fm()); if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) { scoped_mpf t(m_util.fm()); @@ -176,7 +175,7 @@ br_status float_rewriter::mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref br_status float_rewriter::mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()), v3(m_util.fm()); if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) { scoped_mpf t(m_util.fm()); @@ -287,7 +286,7 @@ br_status float_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) { br_status float_rewriter::mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()), v3(m_util.fm()), v4(m_util.fm()); if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3) && m_util.is_value(arg4, v4)) { scoped_mpf t(m_util.fm()); @@ -302,7 +301,7 @@ br_status float_rewriter::mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, exp br_status float_rewriter::mk_sqrt(expr * arg1, expr * arg2, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()); if (m_util.is_value(arg2, v2)) { scoped_mpf t(m_util.fm()); @@ -317,7 +316,7 @@ br_status float_rewriter::mk_sqrt(expr * arg1, expr * arg2, expr_ref & result) { br_status float_rewriter::mk_round(expr * arg1, expr * arg2, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()); if (m_util.is_value(arg2, v2)) { scoped_mpf t(m_util.fm()); From b423418810e1f8099e2e575464668b9ac974dd9a Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 28 Jul 2014 19:37:58 +0100 Subject: [PATCH 390/509] FPA fixed omissions reported by user xor88 (codeplex discussion 554193) Signed-off-by: Christoph M. Wintersteiger --- src/ast/fpa/fpa2bv_converter.cpp | 96 +++++++++++++++++++++++++------- src/ast/fpa/fpa2bv_converter.h | 8 ++- src/ast/fpa/fpa2bv_rewriter.h | 10 +++- 3 files changed, 90 insertions(+), 24 deletions(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 20925a0f9..d6f362902 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -28,9 +28,10 @@ fpa2bv_converter::fpa2bv_converter(ast_manager & m) : m(m), m_simp(m), m_util(m), - m_mpf_manager(m_util.fm()), - m_mpz_manager(m_mpf_manager.mpz_manager()), m_bv_util(m), + m_arith_util(m), + m_mpf_manager(m_util.fm()), + m_mpz_manager(m_mpf_manager.mpz_manager()), extra_assertions(m) { m_plugin = static_cast(m.get_plugin(m.mk_family_id("float"))); } @@ -268,7 +269,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { result = r; } else { - SASSERT(is_rm_sort(f->get_range())); + SASSERT(is_rm(f->get_range())); result = m.mk_fresh_const( #ifdef Z3DEBUG @@ -281,6 +282,10 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { m_rm_const2bv.insert(f, result); m.inc_ref(f); m.inc_ref(result); + + expr_ref rcc(m); + rcc = bu().mk_ule(result, bu().mk_numeral(4, 3)); + extra_assertions.push_back(rcc); } } @@ -1847,6 +1852,55 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a // Just keep it here, as there will be something else that uses it. mk_triple(args[0], args[1], args[2], result); } + else if (num == 3 && + m_bv_util.is_bv(args[0]) && + m_arith_util.is_numeral(args[1]) && + m_arith_util.is_numeral(args[2])) + { + // Three arguments, some of them are not numerals. + SASSERT(m_util.is_float(f->get_range())); + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + + expr * rm = args[0]; + + rational q; + if (!m_arith_util.is_numeral(args[1], q)) + NOT_IMPLEMENTED_YET(); + + rational e; + if (!m_arith_util.is_numeral(args[2], e)) + NOT_IMPLEMENTED_YET(); + + SASSERT(e.is_int64()); + SASSERT(m_mpz_manager.eq(e.to_mpq().denominator(), 1)); + + mpf nte, nta, tp, tn, tz; + m_mpf_manager.set(nte, ebits, sbits, MPF_ROUND_NEAREST_TEVEN, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(nta, ebits, sbits, MPF_ROUND_NEAREST_TAWAY, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(tp, ebits, sbits, MPF_ROUND_TOWARD_POSITIVE, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, q.to_mpq(), e.to_mpq().numerator()); + + app_ref a_nte(m), a_nta(m), a_tp(m), a_tn(m), a_tz(m); + a_nte = m_plugin->mk_value(nte); + a_nta = m_plugin->mk_value(nta); + a_tp = m_plugin->mk_value(tp); + a_tn = m_plugin->mk_value(tn); + a_tz = m_plugin->mk_value(tz); + + expr_ref bv_nte(m), bv_nta(m), bv_tp(m), bv_tn(m), bv_tz(m); + mk_value(a_nte->get_decl(), 0, 0, bv_nte); + mk_value(a_nta->get_decl(), 0, 0, bv_nta); + mk_value(a_tp->get_decl(), 0, 0, bv_tp); + mk_value(a_tn->get_decl(), 0, 0, bv_tn); + mk_value(a_tz->get_decl(), 0, 0, bv_tz); + + mk_ite(m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)), bv_tn, bv_tz, result); + mk_ite(m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)), bv_tp, result, result); + mk_ite(m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3)), bv_nta, result, result); + mk_ite(m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3)), bv_nte, result, result); + } else if (num == 1 && m_bv_util.is_bv(args[0])) { sort * s = f->get_range(); unsigned to_sbits = m_util.get_sbits(s); @@ -1862,7 +1916,9 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a m_bv_util.mk_extract(sz - 2, sz - to_ebits - 1, bv), result); } - else if (num == 2 && is_app(args[1]) && m_util.is_float(m.get_sort(args[1]))) { + else if (num == 2 && + is_app(args[1]) && + m_util.is_float(m.get_sort(args[1]))) { // We also support float to float conversion sort * s = f->get_range(); expr_ref rm(m), x(m); @@ -2015,23 +2071,24 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); } - } - else { + } + else if (num == 2 && + m_util.is_rm(args[0]), + m_arith_util.is_real(args[1])) { // .. other than that, we only support rationals for asFloat - SASSERT(num == 2); SASSERT(m_util.is_float(f->get_range())); unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); - - SASSERT(m_bv_util.is_numeral(args[0])); - rational tmp_rat; unsigned sz; - m_bv_util.is_numeral(to_expr(args[0]), tmp_rat, sz); + unsigned sbits = m_util.get_sbits(f->get_range()); + + SASSERT(m_bv_util.is_numeral(args[0])); + rational tmp_rat; unsigned sz; + m_bv_util.is_numeral(to_expr(args[0]), tmp_rat, sz); SASSERT(tmp_rat.is_int32()); SASSERT(sz == 3); - BV_RM_VAL bv_rm = (BV_RM_VAL) tmp_rat.get_unsigned(); - + BV_RM_VAL bv_rm = (BV_RM_VAL)tmp_rat.get_unsigned(); + mpf_rounding_mode rm; - switch(bv_rm) + switch (bv_rm) { case BV_RM_TIES_TO_AWAY: rm = MPF_ROUND_NEAREST_TAWAY; break; case BV_RM_TIES_TO_EVEN: rm = MPF_ROUND_NEAREST_TEVEN; break; @@ -2042,22 +2099,23 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a } SASSERT(m_util.au().is_numeral(args[1])); - + rational q; - SASSERT(m_util.au().is_numeral(args[1])); m_util.au().is_numeral(args[1], q); mpf v; m_util.fm().set(v, ebits, sbits, rm, q.to_mpq()); - + expr * sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v)) ? 1 : 0, 1); - expr * s = m_bv_util.mk_numeral(m_util.fm().sig(v), sbits-1); + expr * s = m_bv_util.mk_numeral(m_util.fm().sig(v), sbits - 1); expr * e = m_bv_util.mk_numeral(m_util.fm().exp(v), ebits); mk_triple(sgn, s, e, result); m_util.fm().del(v); } + else + UNREACHABLE(); SASSERT(is_well_sorted(m, result)); } diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index 2ccdac6a9..dcb508ffd 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -45,9 +45,10 @@ class fpa2bv_converter { ast_manager & m; basic_simplifier_plugin m_simp; float_util m_util; + bv_util m_bv_util; + arith_util m_arith_util; mpf_manager & m_mpf_manager; - unsynch_mpz_manager & m_mpz_manager; - bv_util m_bv_util; + unsynch_mpz_manager & m_mpz_manager; float_decl_plugin * m_plugin; obj_map m_const2bv; @@ -64,8 +65,9 @@ public: bool is_float(sort * s) { return m_util.is_float(s); } bool is_float(expr * e) { return is_app(e) && m_util.is_float(to_app(e)->get_decl()->get_range()); } + bool is_rm(expr * e) { return m_util.is_rm(e); } + bool is_rm(sort * s) { return m_util.is_rm(s); } bool is_float_family(func_decl * f) { return f->get_family_id() == m_util.get_family_id(); } - bool is_rm_sort(sort * s) { return m_util.is_rm(s); } void mk_triple(expr * sign, expr * significand, expr * exponent, expr_ref & result) { SASSERT(m_bv_util.is_bv(sign) && m_bv_util.get_bv_size(sign) == 1); diff --git a/src/ast/fpa/fpa2bv_rewriter.h b/src/ast/fpa/fpa2bv_rewriter.h index b2e3da939..225aad668 100644 --- a/src/ast/fpa/fpa2bv_rewriter.h +++ b/src/ast/fpa/fpa2bv_rewriter.h @@ -78,17 +78,23 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { return BR_DONE; } - if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_rm_sort(f->get_range())) { + if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_rm(f->get_range())) { m_conv.mk_rm_const(f, result); return BR_DONE; } if (m().is_eq(f)) { SASSERT(num == 2); - if (m_conv.is_float(args[0])) { + SASSERT(m().get_sort(args[0]) == m().get_sort(args[1])); + sort * ds = f->get_domain()[0]; + if (m_conv.is_float(ds)) { m_conv.mk_eq(args[0], args[1], result); return BR_DONE; } + else if (m_conv.is_rm(ds)) { + result = m().mk_eq(args[0], args[1]); + return BR_DONE; + } return BR_FAILED; } From 06a4a3599da0fd89479f30e30f4aaea46bff47f9 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 29 Jul 2014 18:04:54 +0100 Subject: [PATCH 391/509] Added git hashcode information to (get-info :version) Signed-off-by: Christoph M. Wintersteiger --- src/cmd_context/basic_cmds.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 527230e16..836ade9ce 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -547,6 +547,9 @@ public: } }; +#define STRINGIZE(x) #x +#define STRINGIZE_VALUE_OF(x) STRINGIZE(x) + class get_info_cmd : public cmd { symbol m_error_behavior; symbol m_name; @@ -584,7 +587,11 @@ public: ctx.regular_stream() << "(:authors \"Leonardo de Moura and Nikolaj Bjorner\")" << std::endl; } else if (opt == m_version) { - ctx.regular_stream() << "(:version \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER << "\")" << std::endl; + ctx.regular_stream() << "(:version \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER +#ifdef Z3GITHASH + << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH) +#endif + << "\")" << std::endl; } else if (opt == m_status) { ctx.regular_stream() << "(:status " << ctx.get_status() << ")" << std::endl; From 39646bac3e58d8a28c0c5abc818d28b054b3a4bb Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 31 Jul 2014 16:32:25 +0100 Subject: [PATCH 392/509] added operator[] to obj_map for convenience Signed-off-by: Christoph M. Wintersteiger --- src/util/obj_hashtable.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/util/obj_hashtable.h b/src/util/obj_hashtable.h index 603898af8..8ff5f8e8f 100644 --- a/src/util/obj_hashtable.h +++ b/src/util/obj_hashtable.h @@ -165,6 +165,14 @@ public: SASSERT(e); return e->get_data().m_value; } + + value const & operator[](key * k) const { + return find(k); + } + + value & operator[](key * k) { + return find(k); + } iterator find_iterator(Key * k) const { return m_table.find(key_data(k)); From 7bf87e76eaec6ae7cd59b52f7a3c775062d13562 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 5 Aug 2014 11:11:43 -0700 Subject: [PATCH 393/509] fix for tree interpolation --- src/interp/iz3interp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index bed93a56f..1ae3e3a1b 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -347,8 +347,10 @@ public: // get the interps for the tree positions std::vector _interps = interps; interps.resize(pos_map.size()); - for(unsigned i = 0; i < pos_map.size(); i++) - interps[i] = i < _interps.size() ? _interps[i] : mk_false(); + for(unsigned i = 0; i < pos_map.size(); i++){ + unsigned j = pos_map[i]; + interps[i] = j < _interps.size() ? _interps[j] : mk_false(); + } } bool has_interp(hash_map &memo, const ast &t){ From c007a5e5bde7dae458c1dd8b5a7d131f94edc292 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 6 Aug 2014 11:16:06 -0700 Subject: [PATCH 394/509] merged with unstable --- examples/tptp/tptp5.cpp | 4 +- scripts/mk_util.py | 155 ++++++++++-------- src/api/dotnet/Fixedpoint.cs | 39 +++-- src/api/python/z3.py | 28 +++- src/api/z3_api.h | 3 +- src/ast/ast_smt_pp.cpp | 10 +- src/ast/dl_decl_plugin.cpp | 18 +- src/ast/dl_decl_plugin.h | 2 + src/ast/float_decl_plugin.cpp | 2 +- src/ast/float_decl_plugin.h | 11 +- src/ast/fpa/fpa2bv_converter.cpp | 96 ++++++++--- src/ast/fpa/fpa2bv_converter.h | 8 +- src/ast/fpa/fpa2bv_rewriter.h | 10 +- src/ast/macros/quasi_macros.cpp | 17 +- src/ast/macros/quasi_macros.h | 1 + .../bit_blaster/bit_blaster_rewriter.cpp | 3 + .../bit_blaster/bit_blaster_tpl_def.h | 10 +- src/ast/rewriter/bv_rewriter.cpp | 1 + src/ast/rewriter/float_rewriter.cpp | 21 ++- src/cmd_context/basic_cmds.cpp | 9 +- src/muz/base/dl_rule.cpp | 5 +- src/muz/base/dl_rule.h | 2 +- src/muz/base/dl_util.cpp | 2 +- src/muz/bmc/dl_bmc_engine.cpp | 10 +- src/muz/transforms/dl_mk_array_blast.cpp | 2 +- src/muz/transforms/dl_mk_bit_blast.cpp | 14 +- src/muz/transforms/dl_mk_coalesce.cpp | 6 +- src/muz/transforms/dl_mk_coi_filter.cpp | 1 - .../dl_mk_quantifier_instantiation.cpp | 5 +- src/muz/transforms/dl_mk_rule_inliner.cpp | 10 +- src/muz/transforms/dl_mk_scale.cpp | 2 +- src/qe/qe.cpp | 115 +++++++++---- src/qe/qe_arith_plugin.cpp | 73 ++++++--- src/smt/params/preprocessor_params.cpp | 1 + src/smt/params/smt_params_helper.pyg | 1 + src/smt/params/theory_arith_params.cpp | 1 + src/smt/proto_model/datatype_factory.cpp | 11 +- src/smt/smt_context.cpp | 2 +- src/smt/smt_model_generator.cpp | 2 + src/smt/theory_arith_eq.h | 2 +- src/smt/theory_bv.cpp | 1 + src/smt/theory_dl.cpp | 7 +- src/tactic/arith/bv2int_rewriter.cpp | 2 + src/tactic/core/cofactor_elim_term_ite.cpp | 54 +++--- src/tactic/core/cofactor_elim_term_ite.h | 2 +- src/tactic/core/cofactor_term_ite_tactic.cpp | 3 +- src/tactic/filter_model_converter.cpp | 1 + src/util/obj_hashtable.h | 8 + 48 files changed, 537 insertions(+), 256 deletions(-) diff --git a/examples/tptp/tptp5.cpp b/examples/tptp/tptp5.cpp index d0e174914..00d03a8b0 100644 --- a/examples/tptp/tptp5.cpp +++ b/examples/tptp/tptp5.cpp @@ -1630,10 +1630,8 @@ public: unsigned parent_id = Z3_get_ast_id(ctx, p.arg(0)); std::set const& hyps = m_proof_hypotheses.find(parent_id)->second; print_hypotheses(out, hyps); - out << ").\n"; + out << "))).\n"; break; - display_inference(out, "lemma", "thm", p); - break; } case Z3_OP_PR_UNIT_RESOLUTION: display_inference(out, "unit_resolution", "thm", p); diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 89cabe87e..b73f7a7a8 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1,7 +1,7 @@ ############################################ # Copyright (c) 2012 Microsoft Corporation -# -# Auxiliary scripts for generating Makefiles +# +# Auxiliary scripts for generating Makefiles # and Visual Studio project files. # # Author: Leonardo de Moura (leonardo) @@ -42,7 +42,7 @@ BUILD_DIR='build' REV_BUILD_DIR='..' SRC_DIR='src' EXAMPLE_DIR='examples' -# Required Components +# Required Components Z3_DLL_COMPONENT='api_dll' PATTERN_COMPONENT='pattern' UTIL_COMPONENT='util' @@ -80,7 +80,7 @@ GPROF=False GIT_HASH=False def check_output(cmd): - return subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0].rstrip('\r\n') + return str(subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]).rstrip('\r\n') def git_hash(): try: @@ -222,7 +222,7 @@ def test_openmp(cc): return exec_compiler_cmd([cc, CPPFLAGS, 'tstomp.cpp', LDFLAGS, '-fopenmp']) == 0 def find_jni_h(path): - for root, dirs, files in os.walk(path): + for root, dirs, files in os.walk(path): for f in files: if f == 'jni.h': return root @@ -234,7 +234,7 @@ def check_java(): global JAR JDK_HOME = getenv('JDK_HOME', None) # we only need to check this locally. - + if is_verbose(): print("Finding javac ...") @@ -283,7 +283,7 @@ def check_java(): oo = TempFile('output') eo = TempFile('errout') - try: + try: subprocess.call([JAVAC, 'Hello.java', '-verbose'], stdout=oo.fname, stderr=eo.fname) oo.commit() eo.commit() @@ -298,7 +298,7 @@ def check_java(): if JNI_HOME != None: if not os.path.exists(os.path.join(JNI_HOME, 'jni.h')): raise MKException("Failed to detect jni.h '%s'; the environment variable JNI_HOME is probably set to the wrong path." % os.path.join(JNI_HOME)) - else: + else: # Search for jni.h in the library directories... t = open('errout', 'r') open_pat = re.compile("\[search path for class files: (.*)\]") @@ -314,22 +314,22 @@ def check_java(): # ... plus some heuristic ones. extra_dirs = [] - - # For the libraries, even the JDK usually uses a JRE that comes with it. To find the + + # For the libraries, even the JDK usually uses a JRE that comes with it. To find the # headers we have to go a little bit higher up. for dir in cdirs: extra_dirs.append(os.path.abspath(os.path.join(dir, '..'))) if IS_OSX: # Apparently Apple knows best where to put stuff... extra_dirs.append('/System/Library/Frameworks/JavaVM.framework/Headers/') - + cdirs[len(cdirs):] = extra_dirs for dir in cdirs: q = find_jni_h(dir) if q != False: JNI_HOME = q - + if JNI_HOME == None: raise MKException("Failed to detect jni.h. Possible solution: set JNI_HOME with the path to JDK.") @@ -351,7 +351,7 @@ def find_cxx_compiler(): if test_cxx_compiler(cxx): CXX = cxx return CXX - raise MKException('C++ compiler was not found. Try to set the environment variable CXX with the C++ compiler available in your system.') + raise MKException('C++ compiler was not found. Try to set the environment variable CXX with the C++ compiler available in your system.') def find_c_compiler(): global CC, C_COMPILERS @@ -362,7 +362,7 @@ def find_c_compiler(): if test_c_compiler(c): CC = c return CC - raise MKException('C compiler was not found. Try to set the environment variable CC with the C compiler available in your system.') + raise MKException('C compiler was not found. Try to set the environment variable CC with the C compiler available in your system.') def set_version(major, minor, build, revision): global VER_MAJOR, VER_MINOR, VER_BUILD, VER_REVISION @@ -478,8 +478,8 @@ def parse_options(): global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM global DOTNET_ENABLED, JAVA_ENABLED, STATIC_LIB, PREFIX, GMP, FOCI2, FOCI2LIB, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH try: - options, remainder = getopt.gnu_getopt(sys.argv[1:], - 'b:df:sxhmcvtnp:gj', + options, remainder = getopt.gnu_getopt(sys.argv[1:], + 'b:df:sxhmcvtnp:gj', ['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj', 'trace', 'nodotnet', 'staticlib', 'prefix=', 'gmp', 'foci2=', 'java', 'parallel=', 'gprof', 'githash=']) @@ -534,7 +534,7 @@ def parse_options(): elif opt == '--gprof': GPROF = True elif opt == '--githash': - GIT_HASH=arg + GIT_HASH=arg else: print("ERROR: Invalid command line option '%s'" % opt) display_help(1) @@ -548,7 +548,7 @@ def extract_c_includes(fname): system_inc_pat = re.compile("[ \t]*#include[ \t]*\<.*\>[ \t]*") # We should generate and error for any occurrence of #include that does not match the previous pattern. non_std_inc_pat = re.compile(".*#include.*") - + f = open(fname, 'r') linenum = 1 for line in f: @@ -622,7 +622,7 @@ def is_java_enabled(): return JAVA_ENABLED def is_compiler(given, expected): - """ + """ Return True if the 'given' compiler is the expected one. >>> is_compiler('g++', 'g++') True @@ -741,7 +741,7 @@ class Component: self.add_rule_for_each_include(out, include) include_node = '%s.node' % os.path.join(self.build_dir, include) out.write('%s: ' % include_node) - self.add_cpp_h_deps(out, include) + self.add_cpp_h_deps(out, include) out.write('\n') out.write('\t@echo done > %s\n' % include_node) @@ -764,7 +764,7 @@ class Component: out.write('\n') mk_dir(os.path.join(BUILD_DIR, self.build_dir)) if VS_PAR and IS_WINDOWS: - cppfiles = get_cpp_files(self.src_dir) + cppfiles = list(get_cpp_files(self.src_dir)) dependencies = set() for cppfile in cppfiles: dependencies.add(os.path.join(self.to_src_dir, cppfile)) @@ -801,7 +801,7 @@ class Component: # Return true if the component needs builder to generate an install_tactics.cpp file def require_install_tactics(self): return False - + # Return true if the component needs a def file def require_def_file(self): return False @@ -810,6 +810,9 @@ class Component: def require_mem_initializer(self): return False + def mk_install_deps(self, out): + return + def mk_install(self, out): return @@ -818,7 +821,7 @@ class Component: def is_example(self): return False - + # Invoked when creating a (windows) distribution package using components at build_path, and # storing them at dist_path def mk_win_dist(self, build_path, dist_path): @@ -853,10 +856,13 @@ class LibComponent(Component): out.write('\n') out.write('%s: %s\n\n' % (self.name, libfile)) + def mk_install_dep(self, out): + out.write('%s' % libfile) + def mk_install(self, out): for include in self.includes2install: out.write('\t@cp %s %s\n' % (os.path.join(self.to_src_dir, include), os.path.join('$(PREFIX)', 'include', include))) - + def mk_uninstall(self, out): for include in self.includes2install: out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'include', include)) @@ -865,7 +871,7 @@ class LibComponent(Component): mk_dir(os.path.join(dist_path, 'include')) for include in self.includes2install: shutil.copy(os.path.join(self.src_dir, include), - os.path.join(dist_path, 'include', include)) + os.path.join(dist_path, 'include', include)) def mk_unix_dist(self, build_path, dist_path): self.mk_win_dist(build_path, dist_path) @@ -935,11 +941,14 @@ class ExeComponent(Component): def main_component(self): return self.install + def mk_install_dep(self, out): + out.write('%s' % exefile) + def mk_install(self, out): if self.install: exefile = '%s$(EXE_EXT)' % self.exe_name out.write('\t@cp %s %s\n' % (exefile, os.path.join('$(PREFIX)', 'bin', exefile))) - + def mk_uninstall(self, out): exefile = '%s$(EXE_EXT)' % self.exe_name out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'bin', exefile)) @@ -1063,7 +1072,7 @@ class DLLComponent(Component): out.write(' ') out.write(obj) out.write('\n') - + def main_component(self): return self.install @@ -1076,6 +1085,11 @@ class DLLComponent(Component): def require_def_file(self): return IS_WINDOWS and self.export_files + def mk_install_dep(self, out): + out.write('%s$(SO_EXT)' % self.dll_name) + if self.static: + out.write(' %s$(LIB_EXT)' % self.dll_name) + def mk_install(self, out): if self.install: dllfile = '%s$(SO_EXT)' % self.dll_name @@ -1084,7 +1098,7 @@ class DLLComponent(Component): if self.static: libfile = '%s$(LIB_EXT)' % self.dll_name out.write('\t@cp %s %s\n' % (libfile, os.path.join('$(PREFIX)', 'lib', libfile))) - + def mk_uninstall(self, out): dllfile = '%s$(SO_EXT)' % self.dll_name @@ -1118,7 +1132,7 @@ class DotNetDLLComponent(Component): if assembly_info_dir == None: assembly_info_dir = "." self.dll_name = dll_name - self.assembly_info_dir = assembly_info_dir + self.assembly_info_dir = assembly_info_dir def mk_makefile(self, out): if DOTNET_ENABLED: @@ -1147,7 +1161,7 @@ class DotNetDLLComponent(Component): out.write('\n') out.write('%s: %s\n\n' % (self.name, dllfile)) return - + def main_component(self): return DOTNET_ENABLED @@ -1180,7 +1194,7 @@ class JavaDLLComponent(Component): if is_java_enabled(): mk_dir(os.path.join(BUILD_DIR, 'api', 'java', 'classes')) - dllfile = '%s$(SO_EXT)' % self.dll_name + dllfile = '%s$(SO_EXT)' % self.dll_name out.write('libz3java$(SO_EXT): libz3$(SO_EXT) %s\n' % os.path.join(self.to_src_dir, 'Native.cpp')) t = '\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)api/java/Native$(OBJ_EXT) -I"%s" -I"%s/PLATFORM" -I%s %s/Native.cpp\n' % (JNI_HOME, JNI_HOME, get_component('api').to_src_dir, self.to_src_dir) if IS_OSX: @@ -1211,16 +1225,16 @@ class JavaDLLComponent(Component): JAR = '"%s"' % JAR t = ('\t%s %s.java -d %s\n' % (JAVAC, os.path.join(self.to_src_dir, 'enumerations', '*'), os.path.join('api', 'java', 'classes'))) out.write(t) - t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC, - os.path.join('api', 'java', 'classes'), - os.path.join(self.to_src_dir, '*'), + t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC, + os.path.join('api', 'java', 'classes'), + os.path.join(self.to_src_dir, '*'), os.path.join('api', 'java', 'classes'))) out.write(t) - out.write('\t%s cfm %s.jar %s -C %s .\n' % (JAR, self.package_name, - os.path.join(self.to_src_dir, 'manifest'), + out.write('\t%s cfm %s.jar %s -C %s .\n' % (JAR, self.package_name, + os.path.join(self.to_src_dir, 'manifest'), os.path.join('api', 'java', 'classes'))) out.write('java: %s.jar\n\n' % self.package_name) - + def main_component(self): return is_java_enabled() @@ -1229,7 +1243,7 @@ class JavaDLLComponent(Component): mk_dir(os.path.join(dist_path, 'bin')) shutil.copy('%s.jar' % os.path.join(build_path, self.package_name), '%s.jar' % os.path.join(dist_path, 'bin', self.package_name)) - shutil.copy(os.path.join(build_path, 'libz3java.dll'), + shutil.copy(os.path.join(build_path, 'libz3java.dll'), os.path.join(dist_path, 'bin', 'libz3java.dll')) shutil.copy(os.path.join(build_path, 'libz3java.lib'), os.path.join(dist_path, 'bin', 'libz3java.lib')) @@ -1240,7 +1254,7 @@ class JavaDLLComponent(Component): shutil.copy('%s.jar' % os.path.join(build_path, self.package_name), '%s.jar' % os.path.join(dist_path, 'bin', self.package_name)) so = get_so_ext() - shutil.copy(os.path.join(build_path, 'libz3java.%s' % so), + shutil.copy(os.path.join(build_path, 'libz3java.%s' % so), os.path.join(dist_path, 'bin', 'libz3java.%s' % so)) class ExampleComponent(Component): @@ -1296,7 +1310,7 @@ class CExampleComponent(CppExampleComponent): def src_files(self): return get_c_files(self.ex_dir) - + class DotNetExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) @@ -1577,7 +1591,7 @@ def mk_config(): config.write('CC=%s\n' % CC) config.write('CXX=%s\n' % CXX) config.write('CXXFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS)) - config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG) + config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG) config.write('CXX_OUT_FLAG=-o \n') config.write('OBJ_EXT=.o\n') config.write('LIB_EXT=.a\n') @@ -1611,7 +1625,11 @@ def mk_config(): print('Java Compiler: %s' % JAVAC) def mk_install(out): - out.write('install:\n') + out.write('install: ') + for c in get_components(): + c.mk_install_deps(out) + out.write(' ') + out.write('\n') out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'bin')) out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'include')) out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'lib')) @@ -1632,7 +1650,7 @@ def mk_install(out): out.write('\t@echo Z3 shared libraries were installed at \'%s\', make sure this directory is in your %s environment variable.\n' % (os.path.join(PREFIX, 'lib'), LD_LIBRARY_PATH)) out.write('\t@echo Z3Py was installed at \'%s\', make sure this directory is in your PYTHONPATH environment variable.' % PYTHON_PACKAGE_DIR) - out.write('\n') + out.write('\n') def mk_uninstall(out): out.write('uninstall:\n') @@ -1642,7 +1660,7 @@ def mk_uninstall(out): out.write('\t@rm -f %s*.pyc\n' % os.path.join(PYTHON_PACKAGE_DIR, 'z3')) out.write('\t@rm -f %s*.pyc\n' % os.path.join(PYTHON_PACKAGE_DIR, '__pycache__', 'z3')) out.write('\t@echo Z3 was successfully uninstalled.\n') - out.write('\n') + out.write('\n') # Generate the Z3 makefile def mk_makefile(): @@ -1697,7 +1715,7 @@ def mk_makefile(): print('Remark: to open a Visual Studio Command Prompt, go to: "Start > All Programs > Visual Studio > Visual Studio Tools"') else: print("Type 'cd %s; make' to build Z3" % BUILD_DIR) - + # Generate automatically generated source code def mk_auto_src(): if not ONLY_MAKEFILES: @@ -1783,10 +1801,10 @@ def def_module_params(module_name, export, params, class_name=None, description= # Generated accessors for param in params: if export: - out.write(' %s %s() const { return p.%s("%s", g, %s); }\n' % + out.write(' %s %s() const { return p.%s("%s", g, %s); }\n' % (TYPE2CTYPE[param[1]], to_c_method(param[0]), TYPE2GETTER[param[1]], param[0], pyg_default_as_c_literal(param))) else: - out.write(' %s %s() const { return p.%s("%s", %s); }\n' % + out.write(' %s %s() const { return p.%s("%s", %s); }\n' % (TYPE2CTYPE[param[1]], to_c_method(param[0]), TYPE2GETTER[param[1]], param[0], pyg_default_as_c_literal(param))) out.write('};\n') out.write('#endif\n') @@ -1799,8 +1817,8 @@ def max_memory_param(): def max_steps_param(): return ('max_steps', UINT, UINT_MAX, 'maximum number of steps') -PYG_GLOBALS = { 'UINT' : UINT, 'BOOL' : BOOL, 'DOUBLE' : DOUBLE, 'STRING' : STRING, 'SYMBOL' : SYMBOL, - 'UINT_MAX' : UINT_MAX, +PYG_GLOBALS = { 'UINT' : UINT, 'BOOL' : BOOL, 'DOUBLE' : DOUBLE, 'STRING' : STRING, 'SYMBOL' : SYMBOL, + 'UINT_MAX' : UINT_MAX, 'max_memory_param' : max_memory_param, 'max_steps_param' : max_steps_param, 'def_module_params' : def_module_params } @@ -1815,7 +1833,7 @@ def _execfile(file, globals=globals(), locals=locals()): # Execute python auxiliary scripts that generate extra code for Z3. def exec_pyg_scripts(): global CURR_PYG - for root, dirs, files in os.walk('src'): + for root, dirs, files in os.walk('src'): for f in files: if f.endswith('.pyg'): script = os.path.join(root, f) @@ -1831,7 +1849,7 @@ def mk_pat_db(): fout.write('char const * g_pattern_database =\n') for line in fin: fout.write('"%s\\n"\n' % line.strip('\n')) - fout.write(';\n') + fout.write(';\n') if VERBOSE: print("Generated '%s'" % os.path.join(c.src_dir, 'database.h')) @@ -1847,7 +1865,7 @@ def update_version(): mk_version_dot_h(major, minor, build, revision) mk_all_assembly_infos(major, minor, build, revision) mk_def_files() - + # Update files with the version number def mk_version_dot_h(major, minor, build, revision): c = get_component(UTIL_COMPONENT) @@ -1870,8 +1888,8 @@ def mk_all_assembly_infos(major, minor, build, revision): mk_assembly_info_version(assembly, major, minor, build, revision) else: raise MKException("Failed to find assembly info file 'AssemblyInfo' at '%s'" % os.path.join(c.src_dir, c.assembly_info_dir)) - - + + # Generate version number in the given 'AssemblyInfo.cs' file using 'AssemblyInfo' as a template. def mk_assembly_info_version(assemblyinfo, major, minor, build, revision): ver_pat = re.compile('[assembly: AssemblyVersion\("[\.\d]*"\) *') @@ -1936,7 +1954,7 @@ def mk_install_tactic_cpp(cnames, path): if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) - try: + try: exec(line.strip('\n '), globals()) except: raise MKException("Failed processing ADD_TACTIC command at '%s'\n%s" % (fullname, line)) @@ -1944,7 +1962,7 @@ def mk_install_tactic_cpp(cnames, path): if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) - try: + try: exec(line.strip('\n '), globals()) except: raise MKException("Failed processing ADD_PROBE command at '%s'\n%s" % (fullname, line)) @@ -2128,7 +2146,7 @@ def mk_def_files(): def cp_z3py_to_build(): mk_dir(BUILD_DIR) # Erase existing .pyc files - for root, dirs, files in os.walk(Z3PY_SRC_DIR): + for root, dirs, files in os.walk(Z3PY_SRC_DIR): for f in files: if f.endswith('.pyc'): rmf(os.path.join(root, f)) @@ -2171,7 +2189,7 @@ def mk_bindings(api_files): mk_z3consts_java(api_files) _execfile(os.path.join('scripts', 'update_api.py'), g) # HACK cp_z3py_to_build() - + # Extract enumeration types from API files, and add python definitions. def mk_z3consts_py(api_files): if Z3PY_SRC_DIR == None: @@ -2208,7 +2226,7 @@ def mk_z3consts_py(api_files): m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments - linenum = linenum + 1 + linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: @@ -2249,7 +2267,7 @@ def mk_z3consts_py(api_files): linenum = linenum + 1 if VERBOSE: print("Generated '%s'" % os.path.join(Z3PY_SRC_DIR, 'z3consts.py')) - + # Extract enumeration types from z3_api.h, and add .Net definitions def mk_z3consts_dotnet(api_files): @@ -2290,7 +2308,7 @@ def mk_z3consts_dotnet(api_files): m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments - linenum = linenum + 1 + linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: @@ -2373,7 +2391,7 @@ def mk_z3consts_java(api_files): m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments - linenum = linenum + 1 + linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: @@ -2411,7 +2429,7 @@ def mk_z3consts_java(api_files): for k in decls: i = decls[k] if first: - first = False + first = False else: efile.write(',\n') efile.write(' %s (%s)' % (k, i)) @@ -2422,7 +2440,7 @@ def mk_z3consts_java(api_files): efile.write(' }\n\n') efile.write(' public static final %s fromInt(int v) {\n' % name) efile.write(' for (%s k: values()) \n' % name) - efile.write(' if (k.intValue == v) return k;\n') + efile.write(' if (k.intValue == v) return k;\n') efile.write(' return values()[0];\n') efile.write(' }\n\n') efile.write(' public final int toInt() { return this.intValue; }\n') @@ -2573,16 +2591,17 @@ def mk_vs_proj(name, components): def mk_win_dist(build_path, dist_path): for c in get_components(): c.mk_win_dist(build_path, dist_path) - # Add Z3Py to lib directory - for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(build_path)): + # Add Z3Py to bin directory + print("Adding to %s\n" % dist_path) + for pyc in filter(lambda f: f.endswith('.pyc') or f.endswith('.py'), os.listdir(build_path)): shutil.copy(os.path.join(build_path, pyc), os.path.join(dist_path, 'bin', pyc)) def mk_unix_dist(build_path, dist_path): for c in get_components(): c.mk_unix_dist(build_path, dist_path) - # Add Z3Py to lib directory - for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(build_path)): + # Add Z3Py to bin directory + for pyc in filter(lambda f: f.endswith('.pyc') or f.endswith('.py'), os.listdir(build_path)): shutil.copy(os.path.join(build_path, pyc), os.path.join(dist_path, 'bin', pyc)) diff --git a/src/api/dotnet/Fixedpoint.cs b/src/api/dotnet/Fixedpoint.cs index 8c6e6c4c6..3f40b220b 100644 --- a/src/api/dotnet/Fixedpoint.cs +++ b/src/api/dotnet/Fixedpoint.cs @@ -269,6 +269,14 @@ namespace Microsoft.Z3 AST.ArrayLength(queries), AST.ArrayToNative(queries)); } + BoolExpr[] ToBoolExprs(ASTVector v) { + uint n = v.Size; + BoolExpr[] res = new BoolExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = new BoolExpr(Context, v[i].NativeObject); + return res; + } + /// /// Retrieve set of rules added to fixedpoint context. /// @@ -278,12 +286,7 @@ namespace Microsoft.Z3 { Contract.Ensures(Contract.Result() != null); - ASTVector v = new ASTVector(Context, Native.Z3_fixedpoint_get_rules(Context.nCtx, NativeObject)); - uint n = v.Size; - BoolExpr[] res = new BoolExpr[n]; - for (uint i = 0; i < n; i++) - res[i] = new BoolExpr(Context, v[i].NativeObject); - return res; + return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_get_rules(Context.nCtx, NativeObject))); } } @@ -296,15 +299,27 @@ namespace Microsoft.Z3 { Contract.Ensures(Contract.Result() != null); - ASTVector v = new ASTVector(Context, Native.Z3_fixedpoint_get_assertions(Context.nCtx, NativeObject)); - uint n = v.Size; - BoolExpr[] res = new BoolExpr[n]; - for (uint i = 0; i < n; i++) - res[i] = new BoolExpr(Context, v[i].NativeObject); - return res; + return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_get_assertions(Context.nCtx, NativeObject))); } } + /// + /// Parse an SMT-LIB2 file with fixedpoint rules. + /// Add the rules to the current fixedpoint context. + /// Return the set of queries in the file. + /// + public BoolExpr[] ParseFile(string file) { + return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_from_file(Context.nCtx, NativeObject, file))); + } + + /// + /// Similar to ParseFile. Instead it takes as argument a string. + /// + + public BoolExpr[] ParseString(string s) { + return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_from_string(Context.nCtx, NativeObject, s))); + } + #region Internal internal Fixedpoint(Context ctx, IntPtr obj) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 9233a41dc..ce53a4c1f 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -899,6 +899,7 @@ def _coerce_expr_merge(s, a): return s else: if __debug__: + _z3_assert(s1.ctx == s.ctx, "context mismatch") _z3_assert(False, "sort mismatch") else: return s @@ -1459,9 +1460,18 @@ def And(*args): >>> And(P) And(p__0, p__1, p__2, p__3, p__4) """ - args = _get_args(args) - ctx = _ctx_from_ast_arg_list(args) + last_arg = None + if len(args) > 0: + last_arg = args[len(args)-1] + if isinstance(last_arg, Context): + ctx = args[len(args)-1] + args = args[:len(args)-1] + else: + ctx = main_ctx() + args = _get_args(args) + ctx_args = _ctx_from_ast_arg_list(args, ctx) if __debug__: + _z3_assert(ctx_args == None or ctx_args == ctx, "context mismatch") _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression or probe") if _has_probe(args): return _probe_and(args, ctx) @@ -1480,9 +1490,18 @@ def Or(*args): >>> Or(P) Or(p__0, p__1, p__2, p__3, p__4) """ - args = _get_args(args) - ctx = _ctx_from_ast_arg_list(args) + last_arg = None + if len(args) > 0: + last_arg = args[len(args)-1] + if isinstance(last_arg, Context): + ctx = args[len(args)-1] + args = args[:len(args)-1] + else: + ctx = main_ctx() + args = _get_args(args) + ctx_args = _ctx_from_ast_arg_list(args, ctx) if __debug__: + _z3_assert(ctx_args == None or ctx_args == ctx, "context mismatch") _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression or probe") if _has_probe(args): return _probe_or(args, ctx) @@ -4128,6 +4147,7 @@ class Datatype: """ if __debug__: _z3_assert(isinstance(name, str), "String expected") + _z3_assert(name != "", "Constructor name cannot be empty") return self.declare_core(name, "is_" + name, *args) def __repr__(self): diff --git a/src/api/z3_api.h b/src/api/z3_api.h index b29a9c87d..94e20eceb 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -5668,7 +5668,8 @@ END_MLAPI_EXCLUDE Each conjunct encodes values of the bound variables of the query that are satisfied. In PDR mode, the returned answer is a single conjunction. - The previous call to Z3_fixedpoint_query must have returned Z3_L_TRUE. + When used in Datalog mode the previous call to Z3_fixedpoint_query must have returned Z3_L_TRUE. + When used with the PDR engine, the previous call must have been either Z3_L_TRUE or Z3_L_FALSE. def_API('Z3_fixedpoint_get_answer', AST, (_in(CONTEXT), _in(FIXEDPOINT))) */ diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index f684879ae..59e5c04b9 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -569,8 +569,8 @@ class smt_printer { m_out << ")"; } - if (m_is_smt2 && q->get_num_patterns() > 0) { - m_out << "(!"; + if (m_is_smt2 && (q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) { + m_out << "(! "; } { smt_printer p(m_out, m_manager, m_qlists, m_renaming, m_logic, false, m_is_smt2, m_simplify_implies, m_indent, m_num_var_names, m_var_names); @@ -609,7 +609,11 @@ class smt_printer { m_out << "}"; } } - if (m_is_smt2 && q->get_num_patterns() > 0) { + + if (q->get_qid() != symbol::null) + m_out << " :qid " << q->get_qid(); + + if (m_is_smt2 && (q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) { m_out << ")"; } m_out << ")"; diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index 6d0823a24..badf8a59d 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -599,7 +599,23 @@ namespace datalog { return 0; } result = mk_compare(OP_DL_LT, m_lt_sym, domain); - break; + break; + + case OP_DL_REP: { + if (!check_domain(0, 0, num_parameters) || + !check_domain(1, 1, arity)) return 0; + func_decl_info info(m_family_id, k, 0, 0); + result = m_manager->mk_func_decl(symbol("rep"), 1, domain, range, info); + break; + } + + case OP_DL_ABS: { + if (!check_domain(0, 0, num_parameters) || + !check_domain(1, 1, arity)) return 0; + func_decl_info info(m_family_id, k, 0, 0); + result = m_manager->mk_func_decl(symbol("abs"), 1, domain, range, info); + break; + } default: m_manager->raise_exception("operator not recognized"); diff --git a/src/ast/dl_decl_plugin.h b/src/ast/dl_decl_plugin.h index a662de578..65b00235c 100644 --- a/src/ast/dl_decl_plugin.h +++ b/src/ast/dl_decl_plugin.h @@ -48,6 +48,8 @@ namespace datalog { OP_RA_CLONE, OP_DL_CONSTANT, OP_DL_LT, + OP_DL_REP, + OP_DL_ABS, LAST_RA_OP }; diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index 1adedb2d7..df26422c8 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -106,7 +106,7 @@ bool float_decl_plugin::is_value(expr * n, mpf & val) { return false; } -bool float_decl_plugin::is_rm(expr * n, mpf_rounding_mode & val) { +bool float_decl_plugin::is_rm_value(expr * n, mpf_rounding_mode & val) { if (is_app_of(n, m_family_id, OP_RM_NEAREST_TIES_TO_AWAY)) { val = MPF_ROUND_NEAREST_TAWAY; return true; diff --git a/src/ast/float_decl_plugin.h b/src/ast/float_decl_plugin.h index 3786b76ee..58c37080d 100644 --- a/src/ast/float_decl_plugin.h +++ b/src/ast/float_decl_plugin.h @@ -169,7 +169,8 @@ public: app * mk_value(mpf const & v); bool is_value(expr * n) { return is_app_of(n, m_family_id, OP_FLOAT_VALUE); } bool is_value(expr * n, mpf & val); - bool is_rm(expr * n, mpf_rounding_mode & val); + bool is_rm_value(expr * n, mpf_rounding_mode & val); + bool is_rm_value(expr * n) { mpf_rounding_mode t; return is_rm_value(n, t); } mpf const & get_value(unsigned id) const { SASSERT(m_value_table.contains(id)); @@ -198,7 +199,9 @@ public: sort * mk_float_sort(unsigned ebits, unsigned sbits); sort * mk_rm_sort() { return m().mk_sort(m_fid, ROUNDING_MODE_SORT); } bool is_float(sort * s) { return is_sort_of(s, m_fid, FLOAT_SORT); } - bool is_rm(sort * s) { return is_sort_of(s, m_fid, ROUNDING_MODE_SORT); } + bool is_rm(sort * s) { return is_sort_of(s, m_fid, ROUNDING_MODE_SORT); } + bool is_float(expr * e) { return is_float(m_manager.get_sort(e)); } + bool is_rm(expr * e) { return is_rm(m_manager.get_sort(e)); } unsigned get_ebits(sort * s); unsigned get_sbits(sort * s); @@ -217,8 +220,8 @@ public: app * mk_value(mpf const & v) { return m_plugin->mk_value(v); } bool is_value(expr * n) { return m_plugin->is_value(n); } - bool is_value(expr * n, mpf & v) { return m_plugin->is_value(n, v); } - bool is_rm(expr * n, mpf_rounding_mode & v) { return m_plugin->is_rm(n, v); } + bool is_value(expr * n, mpf & v) { return m_plugin->is_value(n, v); } + bool is_rm_value(expr * n, mpf_rounding_mode & v) { return m_plugin->is_rm_value(n, v); } app * mk_pzero(unsigned ebits, unsigned sbits); app * mk_nzero(unsigned ebits, unsigned sbits); diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 20925a0f9..d6f362902 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -28,9 +28,10 @@ fpa2bv_converter::fpa2bv_converter(ast_manager & m) : m(m), m_simp(m), m_util(m), - m_mpf_manager(m_util.fm()), - m_mpz_manager(m_mpf_manager.mpz_manager()), m_bv_util(m), + m_arith_util(m), + m_mpf_manager(m_util.fm()), + m_mpz_manager(m_mpf_manager.mpz_manager()), extra_assertions(m) { m_plugin = static_cast(m.get_plugin(m.mk_family_id("float"))); } @@ -268,7 +269,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { result = r; } else { - SASSERT(is_rm_sort(f->get_range())); + SASSERT(is_rm(f->get_range())); result = m.mk_fresh_const( #ifdef Z3DEBUG @@ -281,6 +282,10 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { m_rm_const2bv.insert(f, result); m.inc_ref(f); m.inc_ref(result); + + expr_ref rcc(m); + rcc = bu().mk_ule(result, bu().mk_numeral(4, 3)); + extra_assertions.push_back(rcc); } } @@ -1847,6 +1852,55 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a // Just keep it here, as there will be something else that uses it. mk_triple(args[0], args[1], args[2], result); } + else if (num == 3 && + m_bv_util.is_bv(args[0]) && + m_arith_util.is_numeral(args[1]) && + m_arith_util.is_numeral(args[2])) + { + // Three arguments, some of them are not numerals. + SASSERT(m_util.is_float(f->get_range())); + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + + expr * rm = args[0]; + + rational q; + if (!m_arith_util.is_numeral(args[1], q)) + NOT_IMPLEMENTED_YET(); + + rational e; + if (!m_arith_util.is_numeral(args[2], e)) + NOT_IMPLEMENTED_YET(); + + SASSERT(e.is_int64()); + SASSERT(m_mpz_manager.eq(e.to_mpq().denominator(), 1)); + + mpf nte, nta, tp, tn, tz; + m_mpf_manager.set(nte, ebits, sbits, MPF_ROUND_NEAREST_TEVEN, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(nta, ebits, sbits, MPF_ROUND_NEAREST_TAWAY, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(tp, ebits, sbits, MPF_ROUND_TOWARD_POSITIVE, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, q.to_mpq(), e.to_mpq().numerator()); + + app_ref a_nte(m), a_nta(m), a_tp(m), a_tn(m), a_tz(m); + a_nte = m_plugin->mk_value(nte); + a_nta = m_plugin->mk_value(nta); + a_tp = m_plugin->mk_value(tp); + a_tn = m_plugin->mk_value(tn); + a_tz = m_plugin->mk_value(tz); + + expr_ref bv_nte(m), bv_nta(m), bv_tp(m), bv_tn(m), bv_tz(m); + mk_value(a_nte->get_decl(), 0, 0, bv_nte); + mk_value(a_nta->get_decl(), 0, 0, bv_nta); + mk_value(a_tp->get_decl(), 0, 0, bv_tp); + mk_value(a_tn->get_decl(), 0, 0, bv_tn); + mk_value(a_tz->get_decl(), 0, 0, bv_tz); + + mk_ite(m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)), bv_tn, bv_tz, result); + mk_ite(m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)), bv_tp, result, result); + mk_ite(m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3)), bv_nta, result, result); + mk_ite(m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3)), bv_nte, result, result); + } else if (num == 1 && m_bv_util.is_bv(args[0])) { sort * s = f->get_range(); unsigned to_sbits = m_util.get_sbits(s); @@ -1862,7 +1916,9 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a m_bv_util.mk_extract(sz - 2, sz - to_ebits - 1, bv), result); } - else if (num == 2 && is_app(args[1]) && m_util.is_float(m.get_sort(args[1]))) { + else if (num == 2 && + is_app(args[1]) && + m_util.is_float(m.get_sort(args[1]))) { // We also support float to float conversion sort * s = f->get_range(); expr_ref rm(m), x(m); @@ -2015,23 +2071,24 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); } - } - else { + } + else if (num == 2 && + m_util.is_rm(args[0]), + m_arith_util.is_real(args[1])) { // .. other than that, we only support rationals for asFloat - SASSERT(num == 2); SASSERT(m_util.is_float(f->get_range())); unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); - - SASSERT(m_bv_util.is_numeral(args[0])); - rational tmp_rat; unsigned sz; - m_bv_util.is_numeral(to_expr(args[0]), tmp_rat, sz); + unsigned sbits = m_util.get_sbits(f->get_range()); + + SASSERT(m_bv_util.is_numeral(args[0])); + rational tmp_rat; unsigned sz; + m_bv_util.is_numeral(to_expr(args[0]), tmp_rat, sz); SASSERT(tmp_rat.is_int32()); SASSERT(sz == 3); - BV_RM_VAL bv_rm = (BV_RM_VAL) tmp_rat.get_unsigned(); - + BV_RM_VAL bv_rm = (BV_RM_VAL)tmp_rat.get_unsigned(); + mpf_rounding_mode rm; - switch(bv_rm) + switch (bv_rm) { case BV_RM_TIES_TO_AWAY: rm = MPF_ROUND_NEAREST_TAWAY; break; case BV_RM_TIES_TO_EVEN: rm = MPF_ROUND_NEAREST_TEVEN; break; @@ -2042,22 +2099,23 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a } SASSERT(m_util.au().is_numeral(args[1])); - + rational q; - SASSERT(m_util.au().is_numeral(args[1])); m_util.au().is_numeral(args[1], q); mpf v; m_util.fm().set(v, ebits, sbits, rm, q.to_mpq()); - + expr * sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v)) ? 1 : 0, 1); - expr * s = m_bv_util.mk_numeral(m_util.fm().sig(v), sbits-1); + expr * s = m_bv_util.mk_numeral(m_util.fm().sig(v), sbits - 1); expr * e = m_bv_util.mk_numeral(m_util.fm().exp(v), ebits); mk_triple(sgn, s, e, result); m_util.fm().del(v); } + else + UNREACHABLE(); SASSERT(is_well_sorted(m, result)); } diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index 2ccdac6a9..dcb508ffd 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -45,9 +45,10 @@ class fpa2bv_converter { ast_manager & m; basic_simplifier_plugin m_simp; float_util m_util; + bv_util m_bv_util; + arith_util m_arith_util; mpf_manager & m_mpf_manager; - unsynch_mpz_manager & m_mpz_manager; - bv_util m_bv_util; + unsynch_mpz_manager & m_mpz_manager; float_decl_plugin * m_plugin; obj_map m_const2bv; @@ -64,8 +65,9 @@ public: bool is_float(sort * s) { return m_util.is_float(s); } bool is_float(expr * e) { return is_app(e) && m_util.is_float(to_app(e)->get_decl()->get_range()); } + bool is_rm(expr * e) { return m_util.is_rm(e); } + bool is_rm(sort * s) { return m_util.is_rm(s); } bool is_float_family(func_decl * f) { return f->get_family_id() == m_util.get_family_id(); } - bool is_rm_sort(sort * s) { return m_util.is_rm(s); } void mk_triple(expr * sign, expr * significand, expr * exponent, expr_ref & result) { SASSERT(m_bv_util.is_bv(sign) && m_bv_util.get_bv_size(sign) == 1); diff --git a/src/ast/fpa/fpa2bv_rewriter.h b/src/ast/fpa/fpa2bv_rewriter.h index b2e3da939..225aad668 100644 --- a/src/ast/fpa/fpa2bv_rewriter.h +++ b/src/ast/fpa/fpa2bv_rewriter.h @@ -78,17 +78,23 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { return BR_DONE; } - if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_rm_sort(f->get_range())) { + if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_rm(f->get_range())) { m_conv.mk_rm_const(f, result); return BR_DONE; } if (m().is_eq(f)) { SASSERT(num == 2); - if (m_conv.is_float(args[0])) { + SASSERT(m().get_sort(args[0]) == m().get_sort(args[1])); + sort * ds = f->get_domain()[0]; + if (m_conv.is_float(ds)) { m_conv.mk_eq(args[0], args[1], result); return BR_DONE; } + else if (m_conv.is_rm(ds)) { + result = m().mk_eq(args[0], args[1]); + return BR_DONE; + } return BR_FAILED; } diff --git a/src/ast/macros/quasi_macros.cpp b/src/ast/macros/quasi_macros.cpp index 084a33ebf..b26b7faba 100644 --- a/src/ast/macros/quasi_macros.cpp +++ b/src/ast/macros/quasi_macros.cpp @@ -41,7 +41,7 @@ void quasi_macros::find_occurrences(expr * e) { // we remember whether we have seen an expr once, or more than once; // when we see it the second time, we don't have to visit it another time, - // as we are only intersted in finding unique function applications. + // as we are only interested in finding unique function applications. m_visited_once.reset(); m_visited_more.reset(); @@ -61,7 +61,7 @@ void quasi_macros::find_occurrences(expr * e) { case AST_VAR: break; case AST_QUANTIFIER: m_todo.push_back(to_quantifier(cur)->get_expr()); break; case AST_APP: - if (is_uninterp(cur) && !is_ground(cur)) { + if (is_non_ground_uninterp(cur)) { func_decl * f = to_app(cur)->get_decl(); m_occurrences.insert_if_not_there(f, 0); occurrences_map::iterator it = m_occurrences.find_iterator(f); @@ -76,6 +76,10 @@ void quasi_macros::find_occurrences(expr * e) { } }; +bool quasi_macros::is_non_ground_uninterp(expr const * e) const { + return is_non_ground(e) && is_uninterp(e); +} + bool quasi_macros::is_unique(func_decl * f) const { return m_occurrences.find(f) == 1; } @@ -149,6 +153,7 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const { // Our definition of a quasi-macro: // Forall X. f[X] = T[X], where f[X] is a term starting with symbol f, f is uninterpreted, // f[X] contains all universally quantified variables, and f does not occur in T[X]. + TRACE("quasi_macros", tout << "Checking for quasi macro: " << mk_pp(e, m_manager) << std::endl;); if (is_quantifier(e) && to_quantifier(e)->is_forall()) { quantifier * q = to_quantifier(e); @@ -157,23 +162,23 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const { expr * lhs = to_app(qe)->get_arg(0); expr * rhs = to_app(qe)->get_arg(1); - if (is_uninterp(lhs) && is_unique(to_app(lhs)->get_decl()) && + if (is_non_ground_uninterp(lhs) && is_unique(to_app(lhs)->get_decl()) && !depends_on(rhs, to_app(lhs)->get_decl()) && fully_depends_on(to_app(lhs), q)) { a = to_app(lhs); t = rhs; return true; - } else if (is_uninterp(rhs) && is_unique(to_app(rhs)->get_decl()) && + } else if (is_non_ground_uninterp(rhs) && is_unique(to_app(rhs)->get_decl()) && !depends_on(lhs, to_app(rhs)->get_decl()) && fully_depends_on(to_app(rhs), q)) { a = to_app(rhs); t = lhs; return true; } - } else if (m_manager.is_not(qe) && is_uninterp(to_app(qe)->get_arg(0)) && + } else if (m_manager.is_not(qe) && is_non_ground_uninterp(to_app(qe)->get_arg(0)) && is_unique(to_app(to_app(qe)->get_arg(0))->get_decl())) { // this is like f(...) = false a = to_app(to_app(qe)->get_arg(0)); t = m_manager.mk_false(); return true; - } else if (is_uninterp(qe) && is_unique(to_app(qe)->get_decl())) { // this is like f(...) = true + } else if (is_non_ground_uninterp(qe) && is_unique(to_app(qe)->get_decl())) { // this is like f(...) = true a = to_app(qe); t = m_manager.mk_true(); return true; diff --git a/src/ast/macros/quasi_macros.h b/src/ast/macros/quasi_macros.h index c5e6b6d4f..64e72e348 100644 --- a/src/ast/macros/quasi_macros.h +++ b/src/ast/macros/quasi_macros.h @@ -45,6 +45,7 @@ class quasi_macros { expr_mark m_visited_more; bool is_unique(func_decl * f) const; + bool is_non_ground_uninterp(expr const * e) const; bool fully_depends_on(app * a, quantifier * q) const; bool depends_on(expr * e, func_decl * f) const; diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp index 80d319377..63843d31e 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp @@ -323,6 +323,9 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; + TRACE("bit_blaster", tout << f->get_name() << " "; + for (unsigned i = 0; i < num; ++i) tout << mk_pp(args[i], m()) << " "; + tout << "\n";); if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) { mk_const(f, result); return BR_DONE; diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h index 72c0a447e..b41aa2238 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h @@ -828,6 +828,12 @@ void bit_blaster_tpl::mk_eq(unsigned sz, expr * const * a_bits, expr * cons template void bit_blaster_tpl::mk_rotate_left(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { + TRACE("bit_blaster", tout << n << ": " << sz << " "; + for (unsigned i = 0; i < sz; ++i) { + tout << mk_pp(a_bits[i], m()) << " "; + } + tout << "\n"; + ); n = n % sz; for (unsigned i = sz - n; i < sz; i++) out_bits.push_back(a_bits[i]); @@ -974,7 +980,7 @@ void bit_blaster_tpl::mk_ashr(unsigned sz, expr * const * a_bits, expr * co mk_ite(eqs.get(i - j), a_bits[sz - j - 1], out, new_out); out = new_out; } - TRACE("bit_blaster_tpl", tout << (sz - i - 1) << " :\n" << mk_pp(out, m()) << "\n";); + TRACE("bit_blaster", tout << (sz - i - 1) << " :\n" << mk_pp(out, m()) << "\n";); out_bits.set(sz - i - 1, out); } } @@ -1004,7 +1010,7 @@ void bit_blaster_tpl::mk_ext_rotate_left_right(unsigned sz, expr * const * out = a_bits[i]; for (unsigned j = 1; j < sz; j++) { expr_ref new_out(m()); - unsigned src = (Left ? (i - j) : (i + j)) % sz; + unsigned src = (Left ? (sz + i - j) : (i + j)) % sz; mk_ite(eqs.get(j), a_bits[src], out, new_out); out = new_out; } diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 11b09003b..1115663d2 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -21,6 +21,7 @@ Notes: #include"poly_rewriter_def.h" #include"ast_smt2_pp.h" + mk_extract_proc::mk_extract_proc(bv_util & u): m_util(u), m_high(0), diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index ecd06ff38..a4212d579 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -82,7 +82,7 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c if (num_args == 2) { mpf_rounding_mode rm; - if (!m_util.is_rm(args[0], rm)) + if (!m_util.is_rm_value(args[0], rm)) return BR_FAILED; rational q; @@ -109,12 +109,12 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c return BR_FAILED; } else if (num_args == 3 && - m_util.is_rm(m().get_sort(args[0])) && + m_util.is_rm(args[0]) && m_util.au().is_real(args[1]) && m_util.au().is_int(args[2])) { mpf_rounding_mode rm; - if (!m_util.is_rm(args[0], rm)) + if (!m_util.is_rm_value(args[0], rm)) return BR_FAILED; rational q; @@ -129,8 +129,7 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c mpf v; m_util.fm().set(v, ebits, sbits, rm, q.to_mpq(), e.to_mpq().numerator()); result = m_util.mk_value(v); - m_util.fm().del(v); - // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); + m_util.fm().del(v); return BR_DONE; } else { @@ -140,7 +139,7 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c br_status float_rewriter::mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()), v3(m_util.fm()); if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) { scoped_mpf t(m_util.fm()); @@ -161,7 +160,7 @@ br_status float_rewriter::mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref br_status float_rewriter::mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()), v3(m_util.fm()); if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) { scoped_mpf t(m_util.fm()); @@ -176,7 +175,7 @@ br_status float_rewriter::mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref br_status float_rewriter::mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()), v3(m_util.fm()); if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3)) { scoped_mpf t(m_util.fm()); @@ -287,7 +286,7 @@ br_status float_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) { br_status float_rewriter::mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()), v3(m_util.fm()), v4(m_util.fm()); if (m_util.is_value(arg2, v2) && m_util.is_value(arg3, v3) && m_util.is_value(arg4, v4)) { scoped_mpf t(m_util.fm()); @@ -302,7 +301,7 @@ br_status float_rewriter::mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, exp br_status float_rewriter::mk_sqrt(expr * arg1, expr * arg2, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()); if (m_util.is_value(arg2, v2)) { scoped_mpf t(m_util.fm()); @@ -317,7 +316,7 @@ br_status float_rewriter::mk_sqrt(expr * arg1, expr * arg2, expr_ref & result) { br_status float_rewriter::mk_round(expr * arg1, expr * arg2, expr_ref & result) { mpf_rounding_mode rm; - if (m_util.is_rm(arg1, rm)) { + if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()); if (m_util.is_value(arg2, v2)) { scoped_mpf t(m_util.fm()); diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 527230e16..836ade9ce 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -547,6 +547,9 @@ public: } }; +#define STRINGIZE(x) #x +#define STRINGIZE_VALUE_OF(x) STRINGIZE(x) + class get_info_cmd : public cmd { symbol m_error_behavior; symbol m_name; @@ -584,7 +587,11 @@ public: ctx.regular_stream() << "(:authors \"Leonardo de Moura and Nikolaj Bjorner\")" << std::endl; } else if (opt == m_version) { - ctx.regular_stream() << "(:version \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER << "\")" << std::endl; + ctx.regular_stream() << "(:version \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER +#ifdef Z3GITHASH + << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH) +#endif + << "\")" << std::endl; } else if (opt == m_status) { ctx.regular_stream() << "(:status " << ctx.get_status() << ")" << std::endl; diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index 184a5fa02..017bac724 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -978,13 +978,14 @@ namespace datalog { } } - void rule::get_vars(ptr_vector& sorts) const { + void rule::get_vars(ast_manager& m, ptr_vector& sorts) const { sorts.reset(); used_vars used; get_used_vars(used); unsigned sz = used.get_max_found_var_idx_plus_1(); for (unsigned i = 0; i < sz; ++i) { - sorts.push_back(used.get(i)); + sort* s = used.get(i); + sorts.push_back(s?s:m.mk_bool_sort()); } } diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h index 1c31dc6b4..7104bae1f 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -304,7 +304,7 @@ namespace datalog { void norm_vars(rule_manager & rm); - void get_vars(ptr_vector& sorts) const; + void get_vars(ast_manager& m, ptr_vector& sorts) const; void to_formula(expr_ref& result) const; diff --git a/src/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index 218f9906a..a6647a1d2 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -290,7 +290,7 @@ namespace datalog { } } TRACE("dl_dr", - tout << r.get_decl()->get_name() << "\n"; + tout << mk_pp(r.get_head(), m) << " :- \n"; for (unsigned i = 0; i < body.size(); ++i) { tout << mk_pp(body[i].get(), m) << "\n"; }); diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 0dcbf4644..51b1a5295 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -148,7 +148,7 @@ namespace datalog { void mk_qrule_vars(datalog::rule const& r, unsigned rule_id, expr_ref_vector& sub) { ptr_vector sorts; - r.get_vars(sorts); + r.get_vars(m, sorts); // populate substitution of bound variables. sub.reset(); sub.resize(sorts.size()); @@ -421,7 +421,7 @@ namespace datalog { ptr_vector rule_vars; expr_ref_vector args(m), conjs(m); - r.get_vars(rule_vars); + r.get_vars(m, rule_vars); obj_hashtable used_vars; unsigned num_vars = 0; for (unsigned i = 0; i < r.get_decl()->get_arity(); ++i) { @@ -514,7 +514,7 @@ namespace datalog { unsigned sz = r->get_uninterpreted_tail_size(); ptr_vector rule_vars; - r->get_vars(rule_vars); + r->get_vars(m, rule_vars); args.append(prop->get_num_args(), prop->get_args()); expr_ref_vector sub = mk_skolem_binding(*r, rule_vars, args); if (sub.empty() && sz == 0) { @@ -803,7 +803,7 @@ namespace datalog { func_decl* p = r.get_decl(); ptr_vector const& succs = *dtu.get_datatype_constructors(m.get_sort(path)); // populate substitution of bound variables. - r.get_vars(sorts); + r.get_vars(m, sorts); sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { @@ -1327,7 +1327,7 @@ namespace datalog { void mk_rule_vars(rule& r, unsigned level, unsigned rule_id, expr_ref_vector& sub) { ptr_vector sorts; - r.get_vars(sorts); + r.get_vars(m, sorts); // populate substitution of bound variables. sub.reset(); sub.resize(sorts.size()); diff --git a/src/muz/transforms/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp index 9303181b6..641d40779 100644 --- a/src/muz/transforms/dl_mk_array_blast.cpp +++ b/src/muz/transforms/dl_mk_array_blast.cpp @@ -87,7 +87,7 @@ namespace datalog { else { if (m_next_var == 0) { ptr_vector vars; - r.get_vars(vars); + r.get_vars(m, vars); m_next_var = vars.size() + 1; } v = m.mk_var(m_next_var, m.get_sort(e)); diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 112541541..fd1dbb205 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -26,6 +26,7 @@ Revision History: #include "dl_mk_interp_tail_simplifier.h" #include "fixedpoint_params.hpp" #include "scoped_proof.h" +#include "model_v2_pp.h" namespace datalog { @@ -67,11 +68,17 @@ namespace datalog { func_decl* p = m_new_funcs[i].get(); func_decl* q = m_old_funcs[i].get(); func_interp* f = model->get_func_interp(p); + if (!f) continue; expr_ref body(m); unsigned arity_p = p->get_arity(); unsigned arity_q = q->get_arity(); + TRACE("dl", + model_v2_pp(tout, *model); + tout << mk_pp(p, m) << "\n"; + tout << mk_pp(q, m) << "\n";); SASSERT(0 < arity_p); - model->register_decl(p, f); + SASSERT(f); + model->register_decl(p, f->copy()); func_interp* g = alloc(func_interp, m, arity_q); if (f) { @@ -88,11 +95,12 @@ namespace datalog { for (unsigned j = 0; j < arity_q; ++j) { sort* s = q->get_domain(j); arg = m.mk_var(j, s); + expr* t = arg; if (m_bv.is_bv_sort(s)) { - expr* args[1] = { arg }; unsigned sz = m_bv.get_bv_size(s); for (unsigned k = 0; k < sz; ++k) { - proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, args); + parameter p(k); + proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t); sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj); } } diff --git a/src/muz/transforms/dl_mk_coalesce.cpp b/src/muz/transforms/dl_mk_coalesce.cpp index 8c7f1d5b4..ac7a58d8d 100644 --- a/src/muz/transforms/dl_mk_coalesce.cpp +++ b/src/muz/transforms/dl_mk_coalesce.cpp @@ -62,7 +62,7 @@ namespace datalog { rule_ref r(const_cast(&rl), rm); ptr_vector sorts; expr_ref_vector revsub(m), conjs(m); - rl.get_vars(sorts); + rl.get_vars(m, sorts); revsub.resize(sorts.size()); svector valid(sorts.size(), true); for (unsigned i = 0; i < sub.size(); ++i) { @@ -117,8 +117,8 @@ namespace datalog { rule_ref res(rm); bool_rewriter bwr(m); svector is_neg; - tgt->get_vars(sorts1); - src.get_vars(sorts2); + tgt->get_vars(m, sorts1); + src.get_vars(m, sorts2); mk_pred(head, src.get_head(), tgt->get_head()); for (unsigned i = 0; i < src.get_uninterpreted_tail_size(); ++i) { diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index c7a8d5aa0..cdb4a5b9e 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -205,7 +205,6 @@ namespace datalog { for (unsigned i = 0; i < rules.size(); ++i) { app* head = rules[i]->get_head(); expr_ref_vector conj(m); - unsigned n = head->get_num_args()-1; for (unsigned j = 0; j < head->get_num_args(); ++j) { expr* arg = head->get_arg(j); if (!is_var(arg)) { diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp index 9c55ca658..fcb8d7b85 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -199,7 +199,7 @@ namespace datalog { expr_ref fml(m), cnst(m); var_ref var(m); ptr_vector sorts; - r.get_vars(sorts); + r.get_vars(m, sorts); m_uf.reset(); m_terms.reset(); m_var2cnst.reset(); @@ -207,9 +207,6 @@ namespace datalog { fml = m.mk_and(conjs.size(), conjs.c_ptr()); for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } var = m.mk_var(i, sorts[i]); cnst = m.mk_fresh_const("C", sorts[i]); m_var2cnst.insert(var, cnst); diff --git a/src/muz/transforms/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp index d9dad5c56..522bd2e86 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -143,11 +143,8 @@ namespace datalog { expr_ref_vector result(m); ptr_vector sorts; expr_ref v(m), w(m); - r.get_vars(sorts); + r.get_vars(m, sorts); for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } v = m.mk_var(i, sorts[i]); m_subst.apply(2, m_deltas, expr_offset(v, is_tgt?0:1), w); result.push_back(w); @@ -423,6 +420,11 @@ namespace datalog { } TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; ); + + for (unsigned i = 0; i < m_inlined_rules.get_num_rules(); ++i) { + rule* r = m_inlined_rules.get_rule(i); + datalog::del_rule(m_mc, *r); + } } bool mk_rule_inliner::transform_rule(rule_set const& orig, rule * r0, rule_set& tgt) { diff --git a/src/muz/transforms/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp index 9ada7be20..271bf5f62 100644 --- a/src/muz/transforms/dl_mk_scale.cpp +++ b/src/muz/transforms/dl_mk_scale.cpp @@ -141,7 +141,7 @@ namespace datalog { m_cache.reset(); m_trail.reset(); m_eqs.reset(); - r.get_vars(vars); + r.get_vars(m, vars); unsigned num_vars = vars.size(); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(mk_pred(num_vars, r.get_tail(j))); diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index d63f0ae00..770a49c07 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -1120,6 +1120,7 @@ namespace qe { st->init(fml); st->m_vars.append(m_vars.size(), m_vars.c_ptr()); SASSERT(invariant()); + TRACE("qe", tout << mk_pp(m_fml, m) << " child: " << mk_pp(fml, m) << "\n";); return st; } @@ -1133,6 +1134,7 @@ namespace qe { m_branch_index.insert(branch_id, index); st->m_vars.append(m_vars.size(), m_vars.c_ptr()); SASSERT(invariant()); + //TRACE("qe", tout << mk_pp(m_fml, m) << " child: " << mk_pp(st->fml(), m) << "\n";); return st; } @@ -1164,6 +1166,16 @@ namespace qe { } } + expr_ref abstract_variable(app* x, expr* fml) const { + expr_ref result(m); + expr* y = x; + expr_abstract(m, 0, 1, &y, fml, result); + symbol X(x->get_decl()->get_name()); + sort* s = m.get_sort(x); + result = m.mk_exists(1, &s, &X, result); + return result; + } + void display_validate(std::ostream& out) const { if (m_children.empty()) { return; @@ -1171,25 +1183,53 @@ namespace qe { expr_ref q(m); expr* x = m_var; if (x) { - expr_abstract(m, 0, 1, &x, m_fml, q); - ptr_vector fmls; + q = abstract_variable(m_var, m_fml); + + expr_ref_vector fmls(m); + expr_ref fml(m); for (unsigned i = 0; i < m_children.size(); ++i) { - expr* fml = m_children[i]->fml(); + search_tree const& child = *m_children[i]; + fml = child.fml(); if (fml) { + // abstract free variables in children. + ptr_vector child_vars, new_vars; + child_vars.append(child.m_vars.size(), child.m_vars.c_ptr()); + if (child.m_var) { + child_vars.push_back(child.m_var); + } + for (unsigned j = 0; j < child_vars.size(); ++j) { + if (!m_vars.contains(child_vars[j]) && + !new_vars.contains(child_vars[j])) { + fml = abstract_variable(child_vars[j], fml); + new_vars.push_back(child_vars[j]); + } + } fmls.push_back(fml); } } - symbol X(m_var->get_decl()->get_name()); - sort* s = m.get_sort(x); - q = m.mk_exists(1, &s, &X, q); - expr_ref tmp(m); - bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), tmp); - expr_ref f(m.mk_not(m.mk_iff(q, tmp)), m); + bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), fml); + + fml = m.mk_not(m.mk_iff(q, fml)); ast_smt_pp pp(m); - out << "(echo " << m_var->get_decl()->get_name() << ")\n"; + out << "; eliminate " << mk_pp(m_var, m) << "\n"; out << "(push)\n"; - pp.display_smt2(out, f); + pp.display_smt2(out, fml); out << "(pop)\n\n"; + DEBUG_CODE( + smt_params params; + params.m_simplify_bit2int = true; + params.m_arith_enum_const_mod = true; + params.m_bv_enable_int2bv2int = true; + params.m_relevancy_lvl = 0; + smt::kernel ctx(m, params); + ctx.assert_expr(fml); + lbool is_sat = ctx.check(); + if (is_sat == l_true) { + std::cout << "; Validation failed:\n"; + std::cout << mk_pp(fml, m) << "\n"; + } +); + } for (unsigned i = 0; i < m_children.size(); ++i) { if (m_children[i]->fml()) { @@ -1410,13 +1450,9 @@ namespace qe { m_solver.assert_expr(m_fml); if (assumption) m_solver.assert_expr(assumption); bool is_sat = false; - while (l_false != m_solver.check()) { + while (l_true == m_solver.check()) { is_sat = true; - model_ref model; - m_solver.get_model(model); - TRACE("qe", model_v2_pp(tout, *model);); - model_evaluator model_eval(*model); - final_check(model_eval); + final_check(); } if (!is_sat) { @@ -1466,14 +1502,30 @@ namespace qe { private: - void final_check(model_evaluator& model_eval) { - TRACE("qe", tout << "\n";); - while (can_propagate_assignment(model_eval)) { - propagate_assignment(model_eval); - } - VERIFY(CHOOSE_VAR == update_current(model_eval, true)); - SASSERT(m_current->fml()); - pop(model_eval); + void final_check() { + model_ref model; + m_solver.get_model(model); + scoped_ptr model_eval = alloc(model_evaluator, *model); + + while (true) { + TRACE("qe", model_v2_pp(tout, *model);); + while (can_propagate_assignment(*model_eval)) { + propagate_assignment(*model_eval); + } + VERIFY(CHOOSE_VAR == update_current(*model_eval, true)); + SASSERT(m_current->fml()); + if (l_true != m_solver.check()) { + return; + } + m_solver.get_model(model); + model_eval = alloc(model_evaluator, *model); + search_tree* st = m_current; + update_current(*model_eval, false); + if (st == m_current) { + break; + } + } + pop(*model_eval); } ast_manager& get_manager() { return m; } @@ -1633,6 +1685,7 @@ namespace qe { nb = m_current->get_num_branches(); expr_ref fml(m_current->fml(), m); if (!eval(model_eval, b, branch) || branch >= nb) { + TRACE("qe", tout << "evaluation failed: setting branch to 0\n";); branch = rational::zero(); } SASSERT(!branch.is_neg()); @@ -1694,11 +1747,12 @@ namespace qe { } // - // The current state is satisfiable - // and the closed portion of the formula - // can be used as the quantifier-free portion. + // The closed portion of the formula + // can be used as the quantifier-free portion, + // unless the current state is unsatisfiable. // if (m.is_true(fml_mixed)) { + SASSERT(l_true == m_solver.check()); m_current = m_current->add_child(fml_closed); for (unsigned i = 0; m_defs && i < m_current->num_free_vars(); ++i) { expr_ref val(m); @@ -1708,6 +1762,7 @@ namespace qe { if (val == x) { model_ref model; lbool is_sat = m_solver.check(); + if (is_sat == l_undef) return; m_solver.get_model(model); SASSERT(is_sat == l_true); model_evaluator model_eval2(*model); @@ -1890,7 +1945,7 @@ namespace qe { vars.reset(); closed = closed && (r != l_undef); } - TRACE("qe", tout << mk_ismt2_pp(fml, m) << "\n";); + TRACE("qe", tout << mk_pp(fml, m) << "\n";); m_current->add_child(fml)->reset_free_vars(); block_assignment(); } @@ -1959,7 +2014,7 @@ namespace qe { class quant_elim_new : public quant_elim { ast_manager& m; - smt_params& m_fparams; + smt_params& m_fparams; expr_ref m_assumption; bool m_produce_models; ptr_vector m_plugins; diff --git a/src/qe/qe_arith_plugin.cpp b/src/qe/qe_arith_plugin.cpp index 7bf0978f6..83c7b79d8 100644 --- a/src/qe/qe_arith_plugin.cpp +++ b/src/qe/qe_arith_plugin.cpp @@ -31,6 +31,7 @@ Revision History: #include "obj_pair_hashtable.h" #include "nlarith_util.h" #include "model_evaluator.h" +#include "smt_kernel.h" namespace qe { @@ -80,9 +81,9 @@ namespace qe { ast_manager& m; i_solver_context& m_ctx; public: - arith_util m_arith; // initialize before m_zero_i, etc. + arith_util m_arith; // initialize before m_zero_i, etc. + th_rewriter simplify; private: - th_rewriter m_rewriter; arith_eq_solver m_arith_solver; bv_util m_bv; @@ -102,7 +103,7 @@ namespace qe { m(m), m_ctx(ctx), m_arith(m), - m_rewriter(m), + simplify(m), m_arith_solver(m), m_bv(m), m_zero_i(m_arith.mk_numeral(numeral(0), true), m), @@ -434,7 +435,6 @@ namespace qe { expr_ref tmp(e, m); simplify(tmp); m_arith_rewriter.mk_le(tmp, mk_zero(e), result); - TRACE("qe_verbose", tout << "mk_le " << mk_pp(result, m) << "\n";); } void mk_lt(expr* e, expr_ref& result) { @@ -521,7 +521,8 @@ namespace qe { expr_ref result1(m), result2(m); // a*s + b*t <= 0 - expr_ref as_bt_le_0(result, m), tmp2(m), tmp3(m), tmp4(m); + expr_ref as_bt_le_0(result, m), tmp2(m), asz_bt_le_0(m), tmp3(m), tmp4(m); + expr_ref b_divides_sz(m); // a*s + b*t + (a-1)(b-1) <= 0 tmp2 = m_arith.mk_add(as_bt, slack); @@ -560,30 +561,36 @@ namespace qe { sz = m_arith.mk_uminus(sz); } tmp4 = mk_add(mk_mul(a1, sz), bt); - mk_le(tmp4, tmp3); + mk_le(tmp4, asz_bt_le_0); - if (to_app(tmp3)->get_arg(0) == x && - m_arith.is_zero(to_app(tmp3)->get_arg(1))) { + if (to_app(asz_bt_le_0)->get_arg(0) == x && + m_arith.is_zero(to_app(asz_bt_le_0)->get_arg(1))) { // exists z in [0 .. |b|-2] . |b| | (z + s) && z <= 0 // <=> // |b| | s mk_divides(abs_b, s, tmp2); } else { - mk_divides(abs_b, sz, tmp2); - mk_and(tmp2, tmp3, tmp4); - mk_big_or(abs_b - numeral(2), x, tmp4, tmp2); - + mk_divides(abs_b, sz, b_divides_sz); + mk_and(b_divides_sz, asz_bt_le_0, tmp4); + mk_big_or(abs_b - numeral(2), x, tmp4, tmp2); + TRACE("qe", + tout << "b | s + z: " << mk_pp(b_divides_sz, m) << "\n"; + tout << "a(s+z) + bt <= 0: " << mk_pp(asz_bt_le_0, m) << "\n"; + ); } mk_flat_and(as_bt_le_0, tmp2, result2); mk_or(result1, result2, result); simplify(result); + + + // a*s + b*t + (a-1)(b-1) <= 0 + // or exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 } TRACE("qe", { - expr_ref_vector trail(m); - tout << "is_strict: " << (is_strict?"true":"false") << "\n"; + tout << (is_strict?"strict":"non-strict") << "\n"; bound(m, a, t, false).pp(tout, x); tout << "\n"; bound(m, b, s, false).pp(tout, x); @@ -592,10 +599,6 @@ namespace qe { }); } - void simplify(expr_ref& p) { - m_rewriter(p); - } - struct mul_lt { arith_util& u; mul_lt(arith_qe_util& u): u(u.m_arith) {} @@ -1052,7 +1055,6 @@ namespace qe { } bool reduce_equation(expr* p, expr* fml) { - TRACE("qe", tout << mk_pp(p, m) << "\n";); numeral k; if (m_arith.is_numeral(p, k) && k.is_zero()) { @@ -1555,9 +1557,10 @@ public: mk_non_resolve(bounds, true, is_lower, result); mk_non_resolve(bounds, false, is_lower, result); + m_util.simplify(result); add_cache(x, fml, v, result, x_t.get_coeff(), x_t.get_term()); TRACE("qe", - tout << vl << " " << mk_pp(x, m) << "\n"; + tout << vl << " " << mk_pp(x, m) << " infinite case\n"; tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); return; @@ -1591,19 +1594,22 @@ public: SASSERT(index < bounds.size(is_strict, is_lower)); expr_ref t(bounds.exprs(is_strict, is_lower)[index], m); rational a = bounds.coeffs(is_strict, is_lower)[index]; + - t = x_t.mk_term(a, t); - a = x_t.mk_coeff(a); mk_bounds(bounds, x, true, is_eq, is_strict, is_lower, index, a, t, result); mk_bounds(bounds, x, false, is_eq, is_strict, is_lower, index, a, t, result); + + t = x_t.mk_term(a, t); + a = x_t.mk_coeff(a); mk_resolve(bounds, x, x_t, true, is_eq, is_strict, is_lower, index, a, t, result); mk_resolve(bounds, x, x_t, false, is_eq, is_strict, is_lower, index, a, t, result); + m_util.simplify(result); add_cache(x, fml, v, result, x_t.get_coeff(), x_t.get_term()); TRACE("qe", { - tout << vl << " " << mk_pp(x, m) << "\n"; + tout << vl << " " << mk_pp(bounds.atoms(is_strict, is_lower)[index],m) << "\n"; tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n"; } @@ -2225,6 +2231,12 @@ public: } } m_util.simplify(result); + TRACE("qe", + tout << (is_strict?"strict":"non-strict") << "\n"; + tout << (is_lower?"is-lower":"is-upper") << "\n"; + tout << "a: " << a << " " << mk_pp(t, m) << "\n"; + tout << "b: " << b << " " << mk_pp(s, m) << "\n"; + tout << mk_pp(result, m) << "\n";); } // @@ -2245,10 +2257,12 @@ public: void mk_bounds(bounds_proc& bounds, - app* x, bool is_strict, bool is_eq_ctx, bool is_strict_ctx, bool is_lower, unsigned index, + app* x, bool is_strict, bool is_eq_ctx, + bool is_strict_ctx, bool is_lower, unsigned index, rational const& a, expr* t, expr_ref& result) { + TRACE("qe", tout << mk_pp(t, m) << "\n";); SASSERT(!is_eq_ctx || !is_strict_ctx); unsigned sz = bounds.size(is_strict, is_lower); expr_ref tmp(m), eq(m); @@ -2258,13 +2272,14 @@ public: for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, is_lower)[i]; - expr* s = bounds.exprs(is_strict, is_lower)[i]; + expr_ref s(bounds.exprs(is_strict, is_lower)[i], m); rational b = bounds.coeffs(is_strict, is_lower)[i]; if (same_strict && i == index) { if (non_strict_real) { m_util.mk_eq(a, x, t, eq); - TRACE("qe", tout << "a:" << a << " x: " << mk_pp(x, m) << " t: " << mk_pp(t, m) << " eq: " << mk_pp(eq, m) << "\n";); + TRACE("qe", tout << "a:" << a << " x: " << mk_pp(x, m) << "t: " << + mk_pp(t, m) << " eq: " << mk_pp(eq, m) << "\n";); if (is_eq_ctx) { m_ctx.add_constraint(true, eq); } @@ -2292,6 +2307,7 @@ public: (non_strict_real && is_eq_ctx && is_strict) || (same_strict && i < index); + mk_bound(result_is_strict, is_lower, a, t, b, s, tmp); m_util.m_replace.apply_substitution(e, tmp.get(), result); @@ -2330,14 +2346,17 @@ public: s = x_t.mk_term(b, s); b = x_t.mk_coeff(b); m_util.mk_resolve(x, strict_resolve, a, t, b, s, tmp); + expr_ref save_result(result); m_util.m_replace.apply_substitution(e, tmp.get(), result); m_ctx.add_constraint(true, mk_not(e), tmp); TRACE("qe_verbose", tout << mk_pp(atm, m) << " "; - tout << mk_pp(e, m) << " ==> "; + tout << mk_pp(e, m) << " ==>\n"; tout << mk_pp(tmp, m) << "\n"; + tout << "old fml: " << mk_pp(save_result, m) << "\n"; + tout << "new fml: " << mk_pp(result, m) << "\n"; ); } } diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index 4799b8b9f..9ad787c2a 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -23,6 +23,7 @@ void preprocessor_params::updt_local_params(params_ref const & _p) { smt_params_helper p(_p); m_macro_finder = p.macro_finder(); m_pull_nested_quantifiers = p.pull_nested_quantifiers(); + m_refine_inj_axiom = p.refine_inj_axioms(); } void preprocessor_params::updt_params(params_ref const & p) { diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 50bb6422b..869b8cdc4 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -14,6 +14,7 @@ def_module_params(module_name='smt', ('delay_units', BOOL, False, 'if true then z3 will not restart when a unit clause is learned'), ('delay_units_threshold', UINT, 32, 'maximum number of learned unit clauses before restarting, ingored if delay_units is false'), ('pull_nested_quantifiers', BOOL, False, 'pull nested quantifiers'), + ('refine_inj_axioms', BOOL, True, 'refine injectivity axioms'), ('soft_timeout', UINT, 0, 'soft timeout (0 means no timeout)'), ('mbqi', BOOL, True, 'model based quantifier instantiation (MBQI)'), ('mbqi.max_cexs', UINT, 1, 'initial maximal number of counterexamples used in MBQI, each counterexample generates a quantifier instantiation'), diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index 7281a5daa..7287cac2f 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -22,6 +22,7 @@ Revision History: void theory_arith_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_arith_random_initial_value = p.arith_random_initial_value(); + m_arith_random_seed = p.random_seed(); m_arith_mode = static_cast(p.arith_solver()); m_nl_arith = p.arith_nl(); m_nl_arith_gb = p.arith_nl_gb(); diff --git a/src/smt/proto_model/datatype_factory.cpp b/src/smt/proto_model/datatype_factory.cpp index 5e66ab738..3b0ec8e5f 100644 --- a/src/smt/proto_model/datatype_factory.cpp +++ b/src/smt/proto_model/datatype_factory.cpp @@ -191,8 +191,10 @@ expr * datatype_factory::get_fresh_value(sort * s) { // Approach 2) // For recursive datatypes. // search for constructor... + unsigned num_iterations = 0; if (m_util.is_recursive(s)) { while(true) { + ++num_iterations; TRACE("datatype_factory", tout << mk_pp(get_last_fresh_value(s), m_manager) << "\n";); ptr_vector const * constructors = m_util.get_datatype_constructors(s); ptr_vector::const_iterator it = constructors->begin(); @@ -212,7 +214,13 @@ expr * datatype_factory::get_fresh_value(sort * s) { << found_sibling << "\n";); if (!found_sibling && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) { found_sibling = true; - expr * maybe_new_arg = get_almost_fresh_value(s_arg); + expr * maybe_new_arg = 0; + if (num_iterations <= 1) { + maybe_new_arg = get_almost_fresh_value(s_arg); + } + else { + maybe_new_arg = get_fresh_value(s_arg); + } if (!maybe_new_arg) { TRACE("datatype_factory", tout << "no argument found for " << mk_pp(s_arg, m_manager) << "\n";); @@ -231,6 +239,7 @@ expr * datatype_factory::get_fresh_value(sort * s) { if (found_sibling) { expr_ref new_value(m_manager); new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); + TRACE("datatype_factory", tout << "potential new value: " << mk_pp(new_value, m_manager) << "\n";); m_last_fresh_value.insert(s, new_value); if (!set->contains(new_value)) { register_value(new_value); diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 02ee06985..4f3c73ce6 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3945,7 +3945,7 @@ namespace smt { m_fingerprints.display(tout); ); failure fl = get_last_search_failure(); - if (fl == TIMEOUT || fl == MEMOUT || fl == CANCELED || fl == NUM_CONFLICTS) { + if (fl == TIMEOUT || fl == MEMOUT || fl == CANCELED || fl == NUM_CONFLICTS || fl == THEORY) { // don't generate model. return; } diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index cf22c3e3a..6c138fd57 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -102,6 +102,7 @@ namespace smt { if (th && th->build_models()) { if (r->get_th_var(th->get_id()) != null_theory_var) { proc = th->mk_value(r, *this); + SASSERT(proc); } else { TRACE("model_bug", tout << "creating fresh value for #" << r->get_owner_id() << "\n";); @@ -110,6 +111,7 @@ namespace smt { } else { proc = mk_model_value(r); + SASSERT(proc); } } SASSERT(proc); diff --git a/src/smt/theory_arith_eq.h b/src/smt/theory_arith_eq.h index 04e16e778..368ccb87d 100644 --- a/src/smt/theory_arith_eq.h +++ b/src/smt/theory_arith_eq.h @@ -193,7 +193,7 @@ namespace smt { return true; } - if (!r.get_base_var() == x && x > y) { + if (r.get_base_var() != x && x > y) { std::swap(x, y); k.neg(); } diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index a338be50a..8b3021573 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -1198,6 +1198,7 @@ namespace smt { void theory_bv::relevant_eh(app * n) { ast_manager & m = get_manager(); context & ctx = get_context(); + TRACE("bv", tout << "relevant: " << mk_pp(n, m) << "\n";); if (m.is_bool(n)) { bool_var v = ctx.get_bool_var(n); atom * a = get_bv2a(v); diff --git a/src/smt/theory_dl.cpp b/src/smt/theory_dl.cpp index 4ef1bd8e0..fc9138bf6 100644 --- a/src/smt/theory_dl.cpp +++ b/src/smt/theory_dl.cpp @@ -162,7 +162,7 @@ namespace smt { m.register_factory(alloc(dl_factory, m_util, m.get_model())); } - virtual smt::model_value_proc * mk_value(smt::enode * n) { + virtual smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator&) { return alloc(dl_value_proc, *this, n); } @@ -201,9 +201,8 @@ namespace smt { if(!m_reps.find(s, r) || !m_vals.find(s,v)) { SASSERT(!m_reps.contains(s)); sort* bv = b().mk_sort(64); - // TBD: filter these from model. - r = m().mk_fresh_func_decl("rep",1, &s,bv); - v = m().mk_fresh_func_decl("val",1, &bv,s); + r = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_REP, 0, 0, 1, &s, bv); + v = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_ABS, 0, 0, 1, &bv, s); m_reps.insert(s, r); m_vals.insert(s, v); add_trail(r); diff --git a/src/tactic/arith/bv2int_rewriter.cpp b/src/tactic/arith/bv2int_rewriter.cpp index 872981283..00d2f53ad 100644 --- a/src/tactic/arith/bv2int_rewriter.cpp +++ b/src/tactic/arith/bv2int_rewriter.cpp @@ -218,6 +218,7 @@ br_status bv2int_rewriter::mk_mod(expr * s, expr * t, expr_ref & result) { if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_urem(s1, t1)); + TRACE("bv2int_rewriter", tout << mk_pp(result,m()) << "\n";); return BR_DONE; } @@ -232,6 +233,7 @@ br_status bv2int_rewriter::mk_mod(expr * s, expr * t, expr_ref & result) { u1 = mk_bv_add(s1, u1, false); align_sizes(u1, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_urem(u1, t1)); + TRACE("bv2int_rewriter", tout << mk_pp(result,m()) << "\n";); return BR_DONE; } diff --git a/src/tactic/core/cofactor_elim_term_ite.cpp b/src/tactic/core/cofactor_elim_term_ite.cpp index d81b4fa13..1f560ef62 100644 --- a/src/tactic/core/cofactor_elim_term_ite.cpp +++ b/src/tactic/core/cofactor_elim_term_ite.cpp @@ -29,14 +29,15 @@ struct cofactor_elim_term_ite::imp { ast_manager & m; params_ref m_params; unsigned long long m_max_memory; - volatile bool m_cancel; + bool m_cofactor_equalities; + volatile bool m_cancel; void checkpoint() { cooperate("cofactor ite"); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); if (m_cancel) - throw tactic_exception(TACTIC_MAX_MEMORY_MSG); + throw tactic_exception(TACTIC_CANCELED_MSG); } // Collect atoms that contain term if-then-else @@ -111,7 +112,7 @@ struct cofactor_elim_term_ite::imp { frame & fr = m_frame_stack.back(); expr * t = fr.m_t; bool form_ctx = fr.m_form_ctx; - TRACE("cofactor_ite_analyzer", tout << "processing, form_ctx: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); + TRACE("cofactor", tout << "processing, form_ctx: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); m_owner.checkpoint(); @@ -150,7 +151,7 @@ struct cofactor_elim_term_ite::imp { } if (i < num_args) { m_has_term_ite.mark(t); - TRACE("cofactor_ite_analyzer", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); + TRACE("cofactor", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); save_candidate(t, form_ctx); } } @@ -167,6 +168,7 @@ struct cofactor_elim_term_ite::imp { }; expr * get_first(expr * t) { + TRACE("cofactor", tout << mk_ismt2_pp(t, m) << "\n";); typedef std::pair frame; expr_fast_mark1 visited; sbuffer stack; @@ -225,6 +227,7 @@ struct cofactor_elim_term_ite::imp { \brief Fuctor for selecting the term if-then-else condition with the most number of occurrences. */ expr * get_best(expr * t) { + TRACE("cofactor", tout << mk_ismt2_pp(t, m) << "\n";); typedef std::pair frame; obj_map occs; expr_fast_mark1 visited; @@ -299,12 +302,17 @@ struct cofactor_elim_term_ite::imp { } } visited.reset(); - CTRACE("cofactor_ite_get_best", best != 0, tout << "best num-occs: " << best_occs << "\n" << mk_ismt2_pp(best, m) << "\n";); + CTRACE("cofactor", best != 0, tout << "best num-occs: " << best_occs << "\n" << mk_ismt2_pp(best, m) << "\n";); return best; } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_cofactor_equalities = p.get_bool("cofactor_equalities", true); + } + + void collect_param_descrs(param_descrs & r) { + r.insert("cofactor_equalities", CPK_BOOL, "(default: true) use equalities to rewrite bodies of ite-expressions. This is potentially expensive."); } void set_cancel(bool f) { @@ -354,16 +362,16 @@ struct cofactor_elim_term_ite::imp { m_term = 0; expr * lhs; expr * rhs; - if (m.is_eq(t, lhs, rhs)) { + if (m_owner.m_cofactor_equalities && m.is_eq(t, lhs, rhs)) { if (m.is_unique_value(lhs)) { m_term = rhs; m_value = to_app(lhs); - TRACE("set_cofactor_atom", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); + TRACE("cofactor", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); } else if (m.is_unique_value(rhs)) { m_term = lhs; m_value = to_app(rhs); - TRACE("set_cofactor_atom", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); + TRACE("cofactor", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); } } // TODO: bounds @@ -467,7 +475,7 @@ struct cofactor_elim_term_ite::imp { m_cofactor.set_cofactor_atom(neg_c); m_cofactor(curr, neg_cofactor); curr = m.mk_ite(c, pos_cofactor, neg_cofactor); - TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); + TRACE("cofactor", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); } } return false; @@ -522,7 +530,7 @@ struct cofactor_elim_term_ite::imp { void cofactor(expr * t, expr_ref & r) { unsigned step = 0; - TRACE("cofactor_ite", tout << "cofactor target:\n" << mk_ismt2_pp(t, m) << "\n";); + TRACE("cofactor", tout << "cofactor target:\n" << mk_ismt2_pp(t, m) << "\n";); expr_ref curr(m); curr = t; while (true) { @@ -543,21 +551,20 @@ struct cofactor_elim_term_ite::imp { m_cofactor(curr, neg_cofactor); if (pos_cofactor == neg_cofactor) { curr = pos_cofactor; - TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); - continue; } - if (m.is_true(pos_cofactor) && m.is_false(neg_cofactor)) { + else if (m.is_true(pos_cofactor) && m.is_false(neg_cofactor)) { curr = c; - TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); - continue; } - if (m.is_false(pos_cofactor) && m.is_true(neg_cofactor)) { + else if (m.is_false(pos_cofactor) && m.is_true(neg_cofactor)) { curr = neg_c; - TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); - continue; } - curr = m.mk_ite(c, pos_cofactor, neg_cofactor); - TRACE("cofactor_ite", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); + else { + curr = m.mk_ite(c, pos_cofactor, neg_cofactor); + } + TRACE("cofactor", + tout << "cofactor_ite step: " << step << "\n"; + tout << "cofactor: " << mk_ismt2_pp(c, m) << "\n"; + tout << mk_ismt2_pp(curr, m) << "\n";); } } @@ -570,6 +577,7 @@ struct cofactor_elim_term_ite::imp { void operator()(expr * t, expr_ref & r) { ptr_vector new_args; + SASSERT(m_frames.empty()); m_frames.push_back(frame(t, true)); while (!m_frames.empty()) { m_owner.checkpoint(); @@ -649,7 +657,8 @@ struct cofactor_elim_term_ite::imp { imp(ast_manager & _m, params_ref const & p): m(_m), - m_params(p) { + m_params(p), + m_cofactor_equalities(true) { m_cancel = false; updt_params(p); } @@ -686,7 +695,8 @@ void cofactor_elim_term_ite::updt_params(params_ref const & p) { m_imp->updt_params(p); } -void cofactor_elim_term_ite::get_param_descrs(param_descrs & r) { +void cofactor_elim_term_ite::collect_param_descrs(param_descrs & r) { + m_imp->collect_param_descrs(r); } void cofactor_elim_term_ite::operator()(expr * t, expr_ref & r) { diff --git a/src/tactic/core/cofactor_elim_term_ite.h b/src/tactic/core/cofactor_elim_term_ite.h index 9b325b1f0..ce2f31ea0 100644 --- a/src/tactic/core/cofactor_elim_term_ite.h +++ b/src/tactic/core/cofactor_elim_term_ite.h @@ -31,7 +31,7 @@ public: virtual ~cofactor_elim_term_ite(); void updt_params(params_ref const & p); - static void get_param_descrs(param_descrs & r); + void collect_param_descrs(param_descrs & r); void operator()(expr * t, expr_ref & r); diff --git a/src/tactic/core/cofactor_term_ite_tactic.cpp b/src/tactic/core/cofactor_term_ite_tactic.cpp index 16b4d1ad4..bc719a85e 100644 --- a/src/tactic/core/cofactor_term_ite_tactic.cpp +++ b/src/tactic/core/cofactor_term_ite_tactic.cpp @@ -52,8 +52,7 @@ public: virtual ~cofactor_term_ite_tactic() {} virtual void updt_params(params_ref const & p) { m_params = p; m_elim_ite.updt_params(p); } - static void get_param_descrs(param_descrs & r) { cofactor_elim_term_ite::get_param_descrs(r); } - virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } + virtual void collect_param_descrs(param_descrs & r) { m_elim_ite.collect_param_descrs(r); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, diff --git a/src/tactic/filter_model_converter.cpp b/src/tactic/filter_model_converter.cpp index da4c71e54..ba6ee4f0d 100644 --- a/src/tactic/filter_model_converter.cpp +++ b/src/tactic/filter_model_converter.cpp @@ -43,6 +43,7 @@ void filter_model_converter::operator()(model_ref & old_model, unsigned goal_idx if (fs.is_marked(f)) continue; func_interp * fi = old_model->get_func_interp(f); + SASSERT(fi); new_model->register_decl(f, fi->copy()); } new_model->copy_usort_interps(*old_model); diff --git a/src/util/obj_hashtable.h b/src/util/obj_hashtable.h index 603898af8..8ff5f8e8f 100644 --- a/src/util/obj_hashtable.h +++ b/src/util/obj_hashtable.h @@ -165,6 +165,14 @@ public: SASSERT(e); return e->get_data().m_value; } + + value const & operator[](key * k) const { + return find(k); + } + + value & operator[](key * k) { + return find(k); + } iterator find_iterator(Key * k) const { return m_table.find(key_data(k)); From ab13987884de98e21184550b5a2ea95be0544a8f Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 6 Aug 2014 11:16:24 -0700 Subject: [PATCH 395/509] working on python interp --- src/api/api_interp.cpp | 80 +++++++++++++++++++++++++++++++ src/api/python/z3.py | 15 ++++++ src/api/python/z3types.py | 3 ++ src/api/z3_api.h | 99 +++++++++++++++++++++++++++++++++++++++ src/interp/iz3interp.cpp | 3 ++ src/interp/iz3interp.h | 2 + 6 files changed, 202 insertions(+) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 2c88a1978..4ea0f5d77 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -38,6 +38,7 @@ Revision History: #include"iz3hash.h" #include"iz3pp.h" #include"iz3checker.h" +#include"scoped_proof.h" using namespace stl_ext; @@ -290,6 +291,85 @@ extern "C" { opts->map[name] = value; } + Z3_ast_vector Z3_API Z3_get_interpolant(__in Z3_context c, __in Z3_ast pf, __in Z3_ast pat, __in Z3_params p){ + Z3_TRY; + LOG_Z3_get_interpolant(c, pf, pat, p); + RESET_ERROR_CODE(); + + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + mk_c(c)->save_object(v); + + ast *_pf = to_ast(pf); + ast *_pat = to_ast(pat); + + ptr_vector interp; + ptr_vector cnsts; // to throw away + + ast_manager &_m = mk_c(c)->m(); + + iz3interpolate(_m, + _pf, + cnsts, + _pat, + interp, + (interpolation_options_struct *) 0 // ignore params for now + ); + + // copy result back + for(unsigned i = 0; i < interp.size(); i++){ + v->m_ast_vector.push_back(interp[i]); + _m.dec_ref(interp[i]); + } + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(0); + } + + Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, __in Z3_ast pat, __in Z3_params p, __out Z3_ast_vector *out_interp){ + Z3_TRY; + LOG_Z3_compute_interpolant(c, pat, p, out_interp); + RESET_ERROR_CODE(); + + + params_ref &_p = to_params(p)->m_params; + scoped_ptr sf = mk_smt_solver_factory(); + scoped_ptr m_solver((*sf)(mk_c(c)->m(), _p, true, true, true, ::symbol::null)); + m_solver.get()->updt_params(_p); // why do we have to do this? + scoped_proof_mode spm(mk_c(c)->m(),PGM_FINE); + + ast *_pat = to_ast(pat); + + ptr_vector interp; + ptr_vector cnsts; // to throw away + + ast_manager &_m = mk_c(c)->m(); + + model_ref m; + lbool _status = iz3interpolate(_m, + *(m_solver.get()), + _pat, + cnsts, + interp, + m, + 0 // ignore params for now + ); + + Z3_lbool status = of_lbool(_status); + + Z3_ast_vector_ref *v = 0; + if(_status == l_false){ + // copy result back + v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + mk_c(c)->save_object(v); + for(unsigned i = 0; i < interp.size(); i++){ + v->m_ast_vector.push_back(interp[i]); + _m.dec_ref(interp[i]); + } + } + *out_interp = of_ast_vector(v); + + return status; + Z3_CATCH_RETURN(Z3_L_UNDEF); + } }; diff --git a/src/api/python/z3.py b/src/api/python/z3.py index ce53a4c1f..533a71956 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -7253,3 +7253,18 @@ def parse_smt2_file(f, sorts={}, decls={}, ctx=None): dsz, dnames, ddecls = _dict2darray(decls, ctx) return _to_expr_ref(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) +def Interp(a): + ctx = main_ctx() + s = BoolSort(ctx) + a = s.cast(a) + return BoolRef(Z3_mk_interp(ctx.ref(), a.as_ast()), ctx) + +def tree_interpolant(f,p=None,ctx=None): + ctx = _get_ctx(ctx) + ptr = (ctypes.POINTER(AstVectorObj) * 1)() + if p == None: + p = ParamsRef(ctx) + res = Z3_compute_interpolant(ctx.ref(),f.as_ast(),p.params,ptr[0]) + if res == Z3_L_FALSE: + return AstVector(ptr[0],ctx) + raise NoInterpolant() diff --git a/src/api/python/z3types.py b/src/api/python/z3types.py index a26a958c0..56316eb46 100644 --- a/src/api/python/z3types.py +++ b/src/api/python/z3types.py @@ -109,3 +109,6 @@ class FuncEntryObj(ctypes.c_void_p): class RCFNumObj(ctypes.c_void_p): def __init__(self, e): self._as_parameter_ = e def from_param(obj): return obj + +class NoInterpolant(): + def __init__(self): diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 94e20eceb..554c792e3 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7699,6 +7699,105 @@ END_MLAPI_EXCLUDE Z3_context Z3_API Z3_mk_interpolation_context(__in Z3_config cfg); + /** Compute an interpolant from a refutation. This takes a proof of + "false" from a set of formulas C, and an interpolation + pattern. The pattern pat is a formula combining the formulas in C + using logical conjunction and the "interp" operator (see + #Z3_mk_interp). This interp operator is logically the identity + operator. It marks the sub-formulas of the pattern for which interpolants should + be computed. The interpolant is a map sigma from marked subformulas to + formulas, such that, for each marked subformula phi of pat (where phi sigma + is phi with sigma(psi) substituted for each subformula psi of phi such that + psi in dom(sigma)): + + 1) phi sigma implies sigma(phi), and + + 2) sigma(phi) is in the common uninterpreted vocabulary between + the formulas of C occurring in phi and those not occurring in + phi + + and moreover pat sigma implies false. In the simplest case + an interpolant for the pattern "(and (interp A) B)" maps A + to an interpolant for A /\ B. + + The return value is a vector of formulas representing sigma. The + vector contains sigma(phi) for each marked subformula of pat, in + pre-order traversal. This means that subformulas of phi occur before phi + in the vector. Also, subformulas that occur multiply in pat will + occur multiply in the result vector. + + In particular, calling Z3_get_interpolant on a pattern of the + form (interp ... (interp (and (interp A_1) A_2)) ... A_N) will + result in a sequence interpolant for A_1, A_2,... A_N. + + Neglecting interp markers, the pattern must be a conjunction of + formulas in C, the set of premises of the proof. Otherwise an + error is flagged. + + Any premises of the proof not present in the pattern are + treated as "background theory". Predicate and function symbols + occurring in the background theory are treated as interpreted and + thus always allowed in the interpolant. + + Interpolant may not necessarily be computable from all + proofs. To be sure an interpolant can be computed, the proof + must be generated by an SMT solver for which interpoaltion is + supported, and the premises must be expressed using only + theories and operators for which interpolation is supported. + + Currently, the only SMT solver that is supported is the legacy + SMT solver. Such a solver is available as the default solver in + #Z3_context objects produced by #Z3_mk_interpolation_context. + Currently, the theories supported are equality with + uninterpreted functions, linear integer arithmetic, and the + theory of arrays (in SMT-LIB terms, this is AUFLIA). + Quantifiers are allowed. Use of any other operators (including + "labels") may result in failure to compute an interpolant from a + proof. + + Parameters: + + \param c logical context. + \param pf a refutation from premises (assertions) C + \param pat an interpolation pattern over C + \param p parameters + + def_API('Z3_get_interpolant', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(PARAMS))) + */ + + Z3_ast_vector Z3_API Z3_get_interpolant(__in Z3_context c, __in Z3_ast pf, __in Z3_ast pat, __in Z3_params p); + + /* Compute an interpolant for an unsatisfiable conjunction of formulas. + + This takes as an argument an interpolation pattern as in + #Z3_get_interpolant. This is a conjunction, some subformulas of + which are marked with the "interp" operator (see #Z3_mk_interp). + + The conjunction is first checked for unsatisfiability. The result + of this check is returned in the out parameter "status". If the result + is unsat, an interpolant is computed from the refutation as in #Z3_get_interpolant + and returned as a vector of formulas. Otherwise the return value is + an empty formula. + + See #Z3_get_interpolant for a discussion of supported theories. + + The advantage of this function over #Z3_get_interpolant is that + it is not necessary to create a suitable SMT solver and generate + a proof. The disadvantage is that it is not possible to use the + solver incrementally. + + Parameters: + + \param c logical context. + \param pat an interpolation pattern + \param p parameters + \param status returns the status of the sat check + + def_API('Z3_compute_interpolant', INT, (_in(CONTEXT), _in(AST), _in(PARAMS), _out(AST_VECTOR))) + */ + + Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, __in Z3_ast pat, __in Z3_params p, __out Z3_ast_vector *interp); + /** Constant reprepresenting a root of a formula tree for tree interpolation */ #define IZ3_ROOT SHRT_MAX diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 1ae3e3a1b..0d394090f 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -41,6 +41,7 @@ Revision History: #include "iz3hash.h" #include "iz3interp.h" +#include"scoped_proof.h" using namespace stl_ext; @@ -503,6 +504,8 @@ lbool iz3interpolate(ast_manager &_m_manager, return res; } + + void interpolation_options_struct::apply(iz3base &b){ for(stl_ext::hash_map::iterator it = map.begin(), en = map.end(); it != en; diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h index 52aa716c3..1c6f9513e 100644 --- a/src/interp/iz3interp.h +++ b/src/interp/iz3interp.h @@ -76,6 +76,7 @@ void iz3interpolate(ast_manager &_m_manager, ptr_vector &interps, interpolation_options_struct * options); + /* Compute an interpolant from an ast representing an interpolation problem, if unsat, else return a model (if enabled). Uses the given solver to produce the proof/model. Also returns a vector @@ -90,4 +91,5 @@ lbool iz3interpolate(ast_manager &_m_manager, model_ref &m, interpolation_options_struct * options); + #endif From 5a107095c937dd879ae411cfce87dc8806c5eba3 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 6 Aug 2014 11:32:51 -0700 Subject: [PATCH 396/509] removing python changes for interp --- src/api/python/z3.py | 15 --------------- src/api/python/z3types.py | 2 -- 2 files changed, 17 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 533a71956..ce53a4c1f 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -7253,18 +7253,3 @@ def parse_smt2_file(f, sorts={}, decls={}, ctx=None): dsz, dnames, ddecls = _dict2darray(decls, ctx) return _to_expr_ref(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) -def Interp(a): - ctx = main_ctx() - s = BoolSort(ctx) - a = s.cast(a) - return BoolRef(Z3_mk_interp(ctx.ref(), a.as_ast()), ctx) - -def tree_interpolant(f,p=None,ctx=None): - ctx = _get_ctx(ctx) - ptr = (ctypes.POINTER(AstVectorObj) * 1)() - if p == None: - p = ParamsRef(ctx) - res = Z3_compute_interpolant(ctx.ref(),f.as_ast(),p.params,ptr[0]) - if res == Z3_L_FALSE: - return AstVector(ptr[0],ctx) - raise NoInterpolant() diff --git a/src/api/python/z3types.py b/src/api/python/z3types.py index 56316eb46..593312d68 100644 --- a/src/api/python/z3types.py +++ b/src/api/python/z3types.py @@ -110,5 +110,3 @@ class RCFNumObj(ctypes.c_void_p): def __init__(self, e): self._as_parameter_ = e def from_param(obj): return obj -class NoInterpolant(): - def __init__(self): From 6880945435696f9090a99fd241cdc7adcc467cf1 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 6 Aug 2014 15:30:24 -0700 Subject: [PATCH 397/509] added simple interpolation bindings for python --- src/api/api_interp.cpp | 22 ++++++-- src/api/python/z3.py | 125 +++++++++++++++++++++++++++++++++++++++++ src/api/z3_api.h | 9 ++- 3 files changed, 149 insertions(+), 7 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 4ea0f5d77..03d5ff843 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -324,17 +324,20 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, __in Z3_ast pat, __in Z3_params p, __out Z3_ast_vector *out_interp){ + Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, __in Z3_ast pat, __in Z3_params p, __out Z3_ast_vector *out_interp, __out Z3_model *model){ Z3_TRY; - LOG_Z3_compute_interpolant(c, pat, p, out_interp); + LOG_Z3_compute_interpolant(c, pat, p, out_interp, model); RESET_ERROR_CODE(); - params_ref &_p = to_params(p)->m_params; + // params_ref &_p = to_params(p)->m_params; + params_ref _p; + _p.set_bool("proof", true); // this is currently useless + + scoped_proof_mode spm(mk_c(c)->m(),PGM_FINE); scoped_ptr sf = mk_smt_solver_factory(); scoped_ptr m_solver((*sf)(mk_c(c)->m(), _p, true, true, true, ::symbol::null)); m_solver.get()->updt_params(_p); // why do we have to do this? - scoped_proof_mode spm(mk_c(c)->m(),PGM_FINE); ast *_pat = to_ast(pat); @@ -356,6 +359,8 @@ extern "C" { Z3_lbool status = of_lbool(_status); Z3_ast_vector_ref *v = 0; + *model = 0; + if(_status == l_false){ // copy result back v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); @@ -365,6 +370,15 @@ extern "C" { _m.dec_ref(interp[i]); } } + else { + model_ref _m; + m_solver.get()->get_model(_m); + Z3_model_ref *crap = alloc(Z3_model_ref); + crap->m_model = _m.get(); + mk_c(c)->save_object(crap); + *model = of_model(crap); + } + *out_interp = of_ast_vector(v); return status; diff --git a/src/api/python/z3.py b/src/api/python/z3.py index ce53a4c1f..9c5ff8f14 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -7253,3 +7253,128 @@ def parse_smt2_file(f, sorts={}, decls={}, ctx=None): dsz, dnames, ddecls = _dict2darray(decls, ctx) return _to_expr_ref(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) +def Interp(a,ctx=None): + """Create an interpolation operator. + + The argument is an interpolation pattern (see tree_interpolant). + + >>> x = Int('x') + >>> print Interp(x>0) + interp(x > 0) + """ + ctx = _get_ctx(_ctx_from_ast_arg_list([a], ctx)) + s = BoolSort(ctx) + a = s.cast(a) + return BoolRef(Z3_mk_interp(ctx.ref(), a.as_ast()), ctx) + +def tree_interpolant(pat,p=None,ctx=None): + """Compute interpolant for a tree of formulas. + + The input is an interpolation pattern over a set of formulas C. + The pattern pat is a formula combining the formulas in C using + logical conjunction and the "interp" operator (see Interp). This + interp operator is logically the identity operator. It marks the + sub-formulas of the pattern for which interpolants should be + computed. The interpolant is a map sigma from marked subformulas + to formulas, such that, for each marked subformula phi of pat + (where phi sigma is phi with sigma(psi) substituted for each + subformula psi of phi such that psi in dom(sigma)): + + 1) phi sigma implies sigma(phi), and + + 2) sigma(phi) is in the common uninterpreted vocabulary between + the formulas of C occurring in phi and those not occurring in + phi + + and moreover pat sigma implies false. In the simplest case + an interpolant for the pattern "(and (interp A) B)" maps A + to an interpolant for A /\ B. + + The return value is a vector of formulas representing sigma. This + vector contains sigma(phi) for each marked subformula of pat, in + pre-order traversal. This means that subformulas of phi occur before phi + in the vector. Also, subformulas that occur multiply in pat will + occur multiply in the result vector. + + If pat is satisfiable, raises an object of class ModelRef + that represents a model of pat. + + If parameters p are supplied, these are used in creating the + solver that determines satisfiability. + + >>> x = Int('x') + >>> y = Int('y') + >>> print tree_interpolant(And(Interp(x < 0), Interp(y > 2), x == y)) + [Not(x >= 0), Not(y <= 2)] + """ + f = pat + ctx = _get_ctx(_ctx_from_ast_arg_list([f], ctx)) + ptr = (AstVectorObj * 1)() + mptr = (Model * 1)() + if p == None: + p = ParamsRef(ctx) + res = Z3_compute_interpolant(ctx.ref(),f.as_ast(),p.params,ptr,mptr) + if res == Z3_L_FALSE: + return AstVector(ptr[0],ctx) + raise ModelRef(mptr[0], ctx) + +def binary_interpolant(a,b,p=None,ctx=None): + """Compute an interpolant for a binary conjunction. + + If a & b is unsatisfiable, returns an interpolant for a & b. + This is a formula phi such that + + 1) a implies phi + 2) b implies not phi + 3) All the uninterpreted symbols of phi occur in both a and b. + + If a & b is satisfiable, raises an object of class ModelRef + that represents a model of a &b. + + If parameters p are supplied, these are used in creating the + solver that determines satisfiability. + + >>> x = Int('x') + >>> print binary_interpolant(x<0,x>2) + x <= 2 + """ + f = And(Interp(a),b) + return tree_interpolant(f,p,ctx)[0] + +def sequence_interpolant(v,p=None,ctx=None): + """Compute interpolant for a sequence of formulas. + + If len(v) == N, and if the conjunction of the formulas in v is + unsatisfiable, the interpolant is a sequence of formulas w + such that len(w) = N-1 and v[0] implies w[0] and for i in 0..N-1: + + 1) w[i] & v[i+1] implies w[i+1] (or false if i+1 = N) + 2) All uninterpreted symbols in w[i] occur in both v[0]..v[i] + and v[i+1]..v[n] + + Requires len(v) >= 1. + + If a & b is satisfiable, raises an object of class ModelRef + that represents a model of a & b. + + If parameters p are supplied, these are used in creating the + solver that determines satisfiability. + + >>> x = Int('x') + >>> y = Int('y') + >>> print sequence_interpolant([x < 0, y == x , y > 2]) + [Not(x >= 0), Not(y >= 0)] + + >>> g = And(Interp(x<0),x<2) + >>> try: + ... print tree_interpolant(g).sexpr() + ... except ModelRef as m: + ... print m.sexpr() + (define-fun x () Int + (- 1)) + """ + f = v[0] + for i in range(1,len(v)): + f = And(Interp(f),v[i]) + return tree_interpolant(f,p,ctx) + diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 554c792e3..b9f4975e8 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7790,13 +7790,16 @@ END_MLAPI_EXCLUDE \param c logical context. \param pat an interpolation pattern - \param p parameters + \param p parameters for solver creation \param status returns the status of the sat check + \param model returns model if satisfiable + + Return value: status of SAT check - def_API('Z3_compute_interpolant', INT, (_in(CONTEXT), _in(AST), _in(PARAMS), _out(AST_VECTOR))) + def_API('Z3_compute_interpolant', INT, (_in(CONTEXT), _in(AST), _in(PARAMS), _out(AST_VECTOR), _out(MODEL))) */ - Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, __in Z3_ast pat, __in Z3_params p, __out Z3_ast_vector *interp); + Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, __in Z3_ast pat, __in Z3_params p, __out Z3_ast_vector *interp, __out Z3_model *model); /** Constant reprepresenting a root of a formula tree for tree interpolation */ From e17af8a5dec251082c2843223b9ffdaca4d0ccf6 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 6 Aug 2014 15:34:58 -0700 Subject: [PATCH 398/509] doc fix for interpolation bindings for python --- src/api/python/z3.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 9c5ff8f14..50d29a790 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -7306,6 +7306,14 @@ def tree_interpolant(pat,p=None,ctx=None): >>> y = Int('y') >>> print tree_interpolant(And(Interp(x < 0), Interp(y > 2), x == y)) [Not(x >= 0), Not(y <= 2)] + + >>> g = And(Interp(x<0),x<2) + >>> try: + ... print tree_interpolant(g).sexpr() + ... except ModelRef as m: + ... print m.sexpr() + (define-fun x () Int + (- 1)) """ f = pat ctx = _get_ctx(_ctx_from_ast_arg_list([f], ctx)) @@ -7364,14 +7372,6 @@ def sequence_interpolant(v,p=None,ctx=None): >>> y = Int('y') >>> print sequence_interpolant([x < 0, y == x , y > 2]) [Not(x >= 0), Not(y >= 0)] - - >>> g = And(Interp(x<0),x<2) - >>> try: - ... print tree_interpolant(g).sexpr() - ... except ModelRef as m: - ... print m.sexpr() - (define-fun x () Int - (- 1)) """ f = v[0] for i in range(1,len(v)): From 3d995648eea72722cee40bd51694c2b2a2d3f374 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 7 Aug 2014 20:39:20 +0900 Subject: [PATCH 399/509] partial fix to model generation bug for non-linear constraints: avoid epsilon refinment for non-shared variables Signed-off-by: Nikolaj Bjorner --- src/smt/theory_arith.h | 1 + src/smt/theory_arith_core.h | 3 ++- src/smt/theory_arith_nl.h | 12 ++++++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index e7037f31a..998dd72e6 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -85,6 +85,7 @@ namespace smt { typedef typename Ext::numeral numeral; typedef typename Ext::inf_numeral inf_numeral; typedef vector numeral_vector; + typedef map, default_eq > rational2var; static const int dead_row_id = -1; protected: diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 195d78e25..fbc043048 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -2780,7 +2780,6 @@ namespace smt { */ template void theory_arith::refine_epsilon() { - typedef map, default_eq > rational2var; while (true) { rational2var mapping; theory_var num = get_num_vars(); @@ -2788,6 +2787,8 @@ namespace smt { for (theory_var v = 0; v < num; v++) { if (is_int(v)) continue; + if (!get_context().is_shared(get_enode(v))) + continue; inf_numeral const & val = get_value(v); rational value = val.get_rational().to_rational() + m_epsilon.to_rational() * val.get_infinitesimal().to_rational(); theory_var v2; diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index bab921ed2..d02c9e540 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -654,6 +654,7 @@ namespace smt { } return get_value(v, computed_epsilon) == val; } + /** \brief Return true if for every monomial x_1 * ... * x_n, @@ -2309,8 +2310,9 @@ namespace smt { if (m_nl_monomials.empty()) return FC_DONE; - if (check_monomial_assignments()) + if (check_monomial_assignments()) { return FC_DONE; + } if (!m_params.m_nl_arith) return FC_GIVEUP; @@ -2338,9 +2340,10 @@ namespace smt { if (!max_min_nl_vars()) return FC_CONTINUE; - if (check_monomial_assignments()) + if (check_monomial_assignments()) { return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; - + } + svector vars; get_non_linear_cluster(vars); @@ -2391,8 +2394,9 @@ namespace smt { } while (m_nl_strategy_idx != old_idx); - if (check_monomial_assignments()) + if (check_monomial_assignments()) { return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; + } TRACE("non_linear", display(tout);); From 0df0174d6216a9cbaeb1dab0443e9a626ec5dddb Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 8 Aug 2014 15:24:08 +0100 Subject: [PATCH 400/509] .NET API: Enabled .xml documentation generation by default. Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_util.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index b73f7a7a8..035cdd3c4 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1151,7 +1151,11 @@ class DotNetDLLComponent(Component): out.write(' ') out.write(cs_file) out.write('\n') - out.write(' csc /noconfig /unsafe+ /nowarn:1701,1702 /nostdlib+ /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:mscorlib.dll /reference:System.Core.dll /reference:System.dll /reference:System.Numerics.dll /debug+ /debug:full /filealign:512 /optimize- /linkresource:%s.dll /out:%s.dll /target:library' % (get_component(Z3_DLL_COMPONENT).dll_name, self.dll_name)) + out.write(' csc /noconfig /unsafe+ /nowarn:1701,1702 /nostdlib+ /errorreport:prompt /warn:4 /reference:mscorlib.dll /reference:System.Core.dll /reference:System.dll /reference:System.Numerics.dll /filealign:512 /linkresource:%s.dll /out:%s.dll /target:library /doc:%s.xml' % (get_component(Z3_DLL_COMPONENT).dll_name, self.dll_name, self.dll_name)) + if DEBUG_MODE: + out.write(' /define:DEBUG;TRACE /debug+ /debug:full /optimize-') + else: + out.write(' /optimize+') if VS_X64: out.write(' /platform:x64') else: @@ -1174,6 +1178,13 @@ class DotNetDLLComponent(Component): mk_dir(os.path.join(dist_path, 'bin')) shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name), '%s.dll' % os.path.join(dist_path, 'bin', self.dll_name)) + shutil.copy('%s.xml' % os.path.join(build_path, self.dll_name), + '%s.xml' % os.path.join(dist_path, 'bin', self.dll_name)) + if DEBUG_MODE: + shutil.copy('%s.pdb' % os.path.join(build_path, self.dll_name), + '%s.pdb' % os.path.join(dist_path, 'bin', self.dll_name)) + + def mk_unix_dist(self, build_path, dist_path): # Do nothing From 47ac5c06333bdd81b1f8084c4e5416cc5f235d23 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Aug 2014 11:41:04 +0900 Subject: [PATCH 401/509] fix doc bug Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/Context.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 41ac1fa31..16073f3b3 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -3523,7 +3523,7 @@ namespace Microsoft.Z3 /// /// /// The list of all configuration parameters can be obtained using the Z3 executable: - /// z3.exe -ini? + /// z3.exe -p /// Only a few configuration parameters are mutable once the context is created. /// An exception is thrown when trying to modify an immutable parameter. /// From 5a45711f22d92880c771e81e95da0c6525c781a4 Mon Sep 17 00:00:00 2001 From: mattpark Date: Mon, 11 Aug 2014 15:44:10 +0100 Subject: [PATCH 402/509] Dealt with some concurrency issues due to concurrent GC. --- src/api/dotnet/Context.cs | 2 +- src/api/dotnet/Z3Object.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 16073f3b3..5436fe0a0 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -3646,7 +3646,7 @@ namespace Microsoft.Z3 internal Fixedpoint.DecRefQueue Fixedpoint_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Fixedpoint_DRQ; } } - internal uint refCount = 0; + internal int refCount = 0; /// /// Finalizer. diff --git a/src/api/dotnet/Z3Object.cs b/src/api/dotnet/Z3Object.cs index e8654ce21..8e474041a 100644 --- a/src/api/dotnet/Z3Object.cs +++ b/src/api/dotnet/Z3Object.cs @@ -19,6 +19,7 @@ Notes: using System; using System.Diagnostics.Contracts; +using System.Threading; namespace Microsoft.Z3 { @@ -50,8 +51,7 @@ namespace Microsoft.Z3 if (m_ctx != null) { - m_ctx.refCount--; - if (m_ctx.refCount == 0) + if (Interlocked.Decrement(ref m_ctx.refCount) == 0) GC.ReRegisterForFinalize(m_ctx); m_ctx = null; } @@ -77,7 +77,7 @@ namespace Microsoft.Z3 { Contract.Requires(ctx != null); - ctx.refCount++; + Interlocked.Increment(ref ctx.refCount); m_ctx = ctx; } @@ -85,7 +85,7 @@ namespace Microsoft.Z3 { Contract.Requires(ctx != null); - ctx.refCount++; + Interlocked.Increment(ref ctx.refCount); m_ctx = ctx; IncRef(obj); m_n_obj = obj; From 0cf1f9c2106ee7110465809f0fd000946c9d0e9a Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 14 Aug 2014 12:15:58 +0100 Subject: [PATCH 403/509] .NET API context refcounting; changed int to long to be on the safe side on 64-bit platforms. Signed-off-by: Christoph M. Wintersteiger --- src/api/dotnet/Context.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 5436fe0a0..2b88cbab7 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -3646,7 +3646,7 @@ namespace Microsoft.Z3 internal Fixedpoint.DecRefQueue Fixedpoint_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Fixedpoint_DRQ; } } - internal int refCount = 0; + internal long refCount = 0; /// /// Finalizer. From 37ed4b04d078d6d1e35db2799d769e8d4b87f775 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 14 Aug 2014 12:18:00 +0100 Subject: [PATCH 404/509] Bugfix: param_refs didn't make it through to smt::solver (smt_params) in some cases. Thanks to user xor88 for pointing us in the right direction! Signed-off-by: Christoph M. Wintersteiger --- src/api/api_solver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index ac30a0c21..c8b1723f1 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -40,6 +40,7 @@ extern "C" { params_ref p = s->m_params; mk_c(c)->params().get_solver_params(mk_c(c)->m(), p, proofs_enabled, models_enabled, unsat_core_enabled); s->m_solver = (*(s->m_solver_factory))(mk_c(c)->m(), p, proofs_enabled, models_enabled, unsat_core_enabled, s->m_logic); + s->m_solver->updt_params(p); } static void init_solver(Z3_context c, Z3_solver s) { From 60054ce469c3e2bc8c34e7b9285c8450e4232812 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 17 Aug 2014 21:20:56 -0700 Subject: [PATCH 405/509] fix cache bug in PDR reported by Phillip Ruemmer Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_context.cpp | 38 +++++++++++++++++++++++++------------ src/muz/pdr/pdr_context.h | 13 ++++++++----- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index b09280d35..94dd5d0a0 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -736,6 +736,11 @@ namespace pdr { m_closed = true; } + void model_node::reopen() { + SASSERT(m_closed); + m_closed = false; + } + static bool is_ini(datalog::rule const& r) { return r.get_uninterpreted_tail_size() == 0; } @@ -745,6 +750,7 @@ namespace pdr { return const_cast(m_rule); } // only initial states are not set by the PDR search. + SASSERT(m_model.get()); datalog::rule const& rl1 = pt().find_rule(*m_model); if (is_ini(rl1)) { set_rule(&rl1); @@ -864,9 +870,10 @@ namespace pdr { } void model_search::add_leaf(model_node& n) { - unsigned& count = cache(n).insert_if_not_there2(n.state(), 0)->get_data().m_value; - ++count; - if (count == 1 || is_repeated(n)) { + model_nodes ns; + model_nodes& nodes = cache(n).insert_if_not_there2(n.state(), ns)->get_data().m_value; + nodes.push_back(&n); + if (nodes.size() == 1 || is_repeated(n)) { set_leaf(n); } else { @@ -875,7 +882,7 @@ namespace pdr { } void model_search::set_leaf(model_node& n) { - erase_children(n); + erase_children(n, true); SASSERT(n.is_open()); enqueue_leaf(n); } @@ -897,7 +904,7 @@ namespace pdr { set_leaf(*root); } - obj_map& model_search::cache(model_node const& n) { + obj_map >& model_search::cache(model_node const& n) { unsigned l = n.orig_level(); if (l >= m_cache.size()) { m_cache.resize(l + 1); @@ -905,7 +912,7 @@ namespace pdr { return m_cache[l]; } - void model_search::erase_children(model_node& n) { + void model_search::erase_children(model_node& n, bool backtrack) { ptr_vector todo, nodes; todo.append(n.children()); erase_leaf(n); @@ -916,13 +923,20 @@ namespace pdr { nodes.push_back(m); todo.append(m->children()); erase_leaf(*m); - remove_node(*m); + remove_node(*m, backtrack); } std::for_each(nodes.begin(), nodes.end(), delete_proc()); } - void model_search::remove_node(model_node& n) { - if (0 == --cache(n).find(n.state())) { + void model_search::remove_node(model_node& n, bool backtrack) { + model_nodes& nodes = cache(n).find(n.state()); + nodes.erase(&n); + if (nodes.size() > 0 && n.is_open() && backtrack) { + for (unsigned i = 0; i < nodes.size(); ++i) { + nodes[i]->reopen(); + } + } + if (nodes.empty()) { cache(n).remove(n.state()); } } @@ -1203,8 +1217,8 @@ namespace pdr { void model_search::reset() { if (m_root) { - erase_children(*m_root); - remove_node(*m_root); + erase_children(*m_root, false); + remove_node(*m_root, false); dealloc(m_root); m_root = 0; } @@ -1240,7 +1254,7 @@ namespace pdr { m_pm(m_fparams, params.max_num_contexts(), m), m_query_pred(m), m_query(0), - m_search(m_params.bfs_model_search()), + m_search(m_params.bfs_model_search(), m), m_last_result(l_undef), m_inductive_lvl(0), m_expanded_lvl(0), diff --git a/src/muz/pdr/pdr_context.h b/src/muz/pdr/pdr_context.h index 8a4f3e438..a85011600 100644 --- a/src/muz/pdr/pdr_context.h +++ b/src/muz/pdr/pdr_context.h @@ -231,6 +231,7 @@ namespace pdr { } void set_closed(); + void reopen(); void set_pre_closed() { m_closed = true; } void reset() { m_children.reset(); } @@ -243,19 +244,21 @@ namespace pdr { }; class model_search { + typedef ptr_vector model_nodes; + ast_manager& m; bool m_bfs; model_node* m_root; std::deque m_leaves; - vector > m_cache; + vector > m_cache; - obj_map& cache(model_node const& n); - void erase_children(model_node& n); + obj_map& cache(model_node const& n); + void erase_children(model_node& n, bool backtrack); void erase_leaf(model_node& n); - void remove_node(model_node& n); + void remove_node(model_node& n, bool backtrack); void enqueue_leaf(model_node& n); // add leaf to priority queue. void update_models(); public: - model_search(bool bfs): m_bfs(bfs), m_root(0) {} + model_search(bool bfs, ast_manager& m): m(m), m_bfs(bfs), m_root(0) {} ~model_search(); void reset(); From 70a1155d711e38895174ada51d6568ae3af3b7ce Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 18 Aug 2014 17:13:16 -0700 Subject: [PATCH 406/509] fixed duality bug and added some code for returning bounded status (not yet used) --- src/duality/duality.h | 7 +++ src/duality/duality_solver.cpp | 24 +++++++++++ src/muz/base/dl_context.h | 3 ++ src/muz/duality/duality_dl_interface.cpp | 55 +++++++++++++++++++++++- src/muz/fp/dl_cmds.cpp | 5 +++ 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index c315c5431..c1c9797f3 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1152,6 +1152,13 @@ protected: virtual void LearnFrom(Solver *old_solver) = 0; + /** Return true if the solution be incorrect due to recursion bounding. + That is, the returned "solution" might contain all derivable facts up to the + given recursion bound, but not be actually a fixed point. + */ + + virtual bool IsResultRecursionBounded() = 0; + virtual ~Solver(){} static Solver *Create(const std::string &solver_class, RPFP *rpfp); diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 59611c814..681582415 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -768,6 +768,29 @@ namespace Duality { annot.Simplify(); } + bool recursionBounded; + + /** See if the solution might be bounded. */ + void TestRecursionBounded(){ + recursionBounded = false; + if(RecursionBound == -1) + return; + for(unsigned i = 0; i < nodes.size(); i++){ + Node *node = nodes[i]; + std::vector &insts = insts_of_node[node]; + for(unsigned j = 0; j < insts.size(); j++) + if(indset->Contains(insts[j])) + if(NodePastRecursionBound(insts[j])){ + recursionBounded = true; + return; + } + } + } + + bool IsResultRecursionBounded(){ + return recursionBounded; + } + /** Generate a proposed solution of the input RPFP from the unwinding, by unioning the instances of each node. */ void GenSolutionFromIndSet(bool with_markers = false){ @@ -1026,6 +1049,7 @@ namespace Duality { timer_stop("ProduceCandidatesForExtension"); if(candidates.empty()){ GenSolutionFromIndSet(); + TestRecursionBounded(); return true; } Candidate cand = candidates.front(); diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 1952cc42f..7349fa889 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -53,6 +53,7 @@ namespace datalog { MEMOUT, INPUT_ERROR, APPROX, + BOUNDED, CANCELED }; @@ -304,6 +305,8 @@ namespace datalog { \brief Retrieve predicates */ func_decl_set const& get_predicates() const { return m_preds; } + ast_ref_vector const &get_pinned() const {return m_pinned; } + bool is_predicate(func_decl* pred) const { return m_preds.contains(pred); } bool is_predicate(expr * e) const { return is_app(e) && is_predicate(to_app(e)->get_decl()); } diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 0584e6b58..849cf94ea 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -36,6 +36,7 @@ Revision History: #include "model_v2_pp.h" #include "fixedpoint_params.hpp" #include "used_vars.h" +#include "func_decl_dependencies.h" // template class symbol_table; @@ -207,6 +208,46 @@ lbool dl_interface::query(::expr * query) { _d->rpfp->AssertAxiom(e); } + // make sure each predicate is the head of at least one clause + func_decl_set heads; + for(unsigned i = 0; i < clauses.size(); i++){ + expr cl = clauses[i]; + + while(true){ + if(cl.is_app()){ + decl_kind k = cl.decl().get_decl_kind(); + if(k == Implies) + cl = cl.arg(1); + else { + heads.insert(cl.decl()); + break; + } + } + else if(cl.is_quantifier()) + cl = cl.body(); + else break; + } + } + ast_ref_vector const &pinned = m_ctx.get_pinned(); + for(unsigned i = 0; i < pinned.size(); i++){ + ::ast *fa = pinned[i]; + if(is_func_decl(fa)){ + ::func_decl *fd = to_func_decl(fa); + if(m_ctx.is_predicate(fd)) { + func_decl f(_d->ctx,fd); + if(!heads.contains(fd)){ + int arity = f.arity(); + std::vector args; + for(int j = 0; j < arity; j++) + args.push_back(_d->ctx.fresh_func_decl("X",f.domain(j))()); + expr c = implies(_d->ctx.bool_val(false),f(args)); + c = _d->ctx.make_quant(Forall,args,c); + clauses.push_back(c); + } + } + } + } + // creates 1-1 map between clauses and rpfp edges _d->rpfp->FromClauses(clauses); @@ -265,7 +306,19 @@ lbool dl_interface::query(::expr * query) { // 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; + // but we return undef if the UNSAT result is bounded + if(ans){ + if(rs->IsResultRecursionBounded()){ +#if 0 + m_ctx.set_status(datalog::BOUNDED); + return l_undef; +#else + return l_false; +#endif + } + return l_false; + } + return l_true; } expr_ref dl_interface::get_cover_delta(int level, ::func_decl* pred_orig) { diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index 827b90e60..f9916808c 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -252,6 +252,11 @@ public: print_certificate(ctx); break; case l_undef: + if(dlctx.get_status() == datalog::BOUNDED){ + ctx.regular_stream() << "bounded\n"; + print_certificate(ctx); + break; + } ctx.regular_stream() << "unknown\n"; switch(dlctx.get_status()) { case datalog::INPUT_ERROR: From 38ee8cb807a5afd9c5b9961d3e7b6213dbbb679f Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 22 Aug 2014 12:57:33 +0100 Subject: [PATCH 407/509] .NET API: bugfix. Thanks to Konrad Jamrozik for catching this one. Signed-off-by: Christoph M. Wintersteiger --- scripts/update_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index f88e0393f..97dacb44b 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -420,7 +420,7 @@ def mk_dotnet(): NULLWrapped = [ 'Z3_mk_context', 'Z3_mk_context_rc' ] -Unwrapped = [ 'Z3_del_context' ] +Unwrapped = [ 'Z3_del_context', 'Z3_get_error_code' ] def mk_dotnet_wrappers(): global Type2Str From 51aa10821ea54b8c221f6952da146c362edea862 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 26 Aug 2014 13:46:53 -0700 Subject: [PATCH 408/509] fixed pop issue and interpolation proof mode issue --- src/cmd_context/interpolant_cmds.cpp | 3 ++- src/solver/solver_na2as.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index 7fd44c088..cb83b52f6 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -33,6 +33,7 @@ Notes: #include"iz3checker.h" #include"iz3profiling.h" #include"interp_params.hpp" +#include"scoped_proof.h" static void show_interpolant_and_maybe_check(cmd_context & ctx, ptr_vector &cnsts, @@ -153,7 +154,7 @@ static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, par ast_manager &_m = ctx.m(); // TODO: the following is a HACK to enable proofs in the old smt solver // When we stop using that solver, this hack can be removed - _m.toggle_proof_mode(PGM_FINE); + scoped_proof_mode spm(_m,PGM_FINE); ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); p.set_bool("proof", true); scoped_ptr sp = (ctx.get_interpolating_solver_factory())(_m, p, true, models_enabled, false, ctx.get_logic()); diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index 9889b5872..01c45ee44 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -82,7 +82,7 @@ void solver_na2as::pop(unsigned n) { } void solver_na2as::restore_assumptions(unsigned old_sz) { - SASSERT(old_sz == 0); + // SASSERT(old_sz == 0); for (unsigned i = old_sz; i < m_assumptions.size(); i++) { m_manager.dec_ref(m_assumptions[i]); } From 8ea7109f8fcca01f57e42c625b4c63ecac801bb4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 28 Aug 2014 10:18:42 -0700 Subject: [PATCH 409/509] update documentation to clarify reference counting policies Signed-off-by: Nikolaj Bjorner --- src/api/z3_api.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index b9f4975e8..4ffdfa9c2 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1375,6 +1375,16 @@ extern "C" { although some parameters can be changed using #Z3_update_param_value. All main interaction with Z3 happens in the context of a \c Z3_context. + In contrast to #Z3_mk_context_rc, the life time of Z3_ast objects + are determined by the scope level of #Z3_push and #Z3_pop. + In other words, a Z3_ast object remains valid until there is a + call to Z3_pop that takes the current scope below the level where + the object was created. + + Note that all other reference counted objects, including Z3_model, + Z3_solver, Z3_func_interp have to be managed by the caller. + Their reference counts are not handled by the context. + \conly \sa Z3_del_context \conly \deprecated Use #Z3_mk_context_rc From fa24d9db6fc8598b337795b4d2833c57b242f168 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 1 Sep 2014 17:27:07 +0100 Subject: [PATCH 410/509] Added multi processor compilation to VS project. Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_util.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 035cdd3c4..5e100b527 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2522,7 +2522,11 @@ def mk_vs_proj(name, components): f.write(' \n') f.write(' Disabled\n') f.write(' WIN32;_DEBUG;Z3DEBUG;_TRACE;_MP_INTERNAL;_WINDOWS;%(PreprocessorDefinitions)\n') - f.write(' true\n') + if VS_PAR: + f.write(' false\n') + f.write(' true\n') + else: + f.write(' true\n') f.write(' EnableFastChecks\n') f.write(' Level3\n') f.write(' MultiThreadedDebugDLL\n') @@ -2556,7 +2560,11 @@ def mk_vs_proj(name, components): f.write(' \n') f.write(' Disabled\n') f.write(' WIN32;_NDEBUG;_MP_INTERNAL;_WINDOWS;%(PreprocessorDefinitions)\n') - f.write(' true\n') + if VS_PAR: + f.write(' false\n') + f.write(' true\n') + else: + f.write(' true\n') f.write(' EnableFastChecks\n') f.write(' Level3\n') f.write(' MultiThreadedDLL\n') From 23af977d68ee85a3a1b88d1e59c26bf33f5189df Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 3 Sep 2014 17:49:10 +0100 Subject: [PATCH 411/509] Multi-threading bugfix, DLL could be used from other threads before the main thread initializes it. Thanks to user xor88 for reporting this one. Signed-off-by: Christoph M. Wintersteiger --- src/util/memory_manager.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index 3f2e224d9..45088d5d4 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -35,6 +35,7 @@ static long long g_memory_max_used_size = 0; static long long g_memory_watermark = 0; static bool g_exit_when_out_of_memory = false; static char const * g_out_of_memory_msg = "ERROR: out of memory"; +static volatile bool g_memory_fully_initialized = false; void memory::exit_when_out_of_memory(bool flag, char const * msg) { g_exit_when_out_of_memory = flag; @@ -83,10 +84,18 @@ void memory::initialize(size_t max_size) { initialize = true; } } - if (!initialize) - return; - g_memory_out_of_memory = false; - mem_initialize(); + if (initialize) { + g_memory_out_of_memory = false; + mem_initialize(); + g_memory_fully_initialized = true; + } + else { + // Delay the current thread until the DLL is fully initialized + // Without this, multiple threads can start to call API functions + // before memory::initialize(...) finishes. + while (!g_memory_fully_initialized) + /* wait */ ; + } } bool memory::is_out_of_memory() { @@ -98,9 +107,9 @@ bool memory::is_out_of_memory() { return r; } -void memory::set_high_watermark(size_t watermak) { +void memory::set_high_watermark(size_t watermark) { // This method is only safe to invoke at initialization time, that is, before the threads are created. - g_memory_watermark = watermak; + g_memory_watermark = watermark; } bool memory::above_high_watermark() { From 3d9120c74503171c00838f64fe73bb91c3aba821 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 4 Sep 2014 14:49:58 -0700 Subject: [PATCH 412/509] lifetime of expressions from model follow life-time of model, not the push/pop scope making scope based reference counting error prone Signed-off-by: Nikolaj Bjorner --- src/api/api_model.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index f17f7a586..f0c31d800 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -60,6 +60,7 @@ extern "C" { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } + mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } @@ -263,6 +264,7 @@ extern "C" { RESET_ERROR_CODE(); CHECK_NON_NULL(f, 0); expr * e = to_func_interp_ref(f)->get_else(); + mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(0); } @@ -301,6 +303,7 @@ extern "C" { LOG_Z3_func_entry_get_value(c, e); RESET_ERROR_CODE(); expr * v = to_func_entry_ref(e)->get_result(); + mk_c(c)->save_ast_trail(v); RETURN_Z3(of_expr(v)); Z3_CATCH_RETURN(0); } From 904ab4bf9e1cf9f147218b88bbfd8ecc8b1a8a0e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 5 Sep 2014 11:18:34 -0700 Subject: [PATCH 413/509] address race condition in cleanup methods Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 10 +++++++-- src/solver/combined_solver.cpp | 10 +++++++-- src/solver/solver.h | 4 +++- src/solver/tactic2solver.cpp | 8 +++++-- src/tactic/aig/aig.h | 2 -- src/tactic/arith/add_bounds_tactic.cpp | 10 ++------- src/tactic/arith/degree_shift_tactic.cpp | 10 ++------- src/tactic/arith/diff_neq_tactic.cpp | 13 +++-------- src/tactic/arith/factor_tactic.cpp | 10 ++------- src/tactic/arith/fix_dl_var_tactic.cpp | 10 ++------- src/tactic/arith/fm_tactic.cpp | 10 ++------- src/tactic/arith/lia2pb_tactic.cpp | 10 ++------- src/tactic/arith/normalize_bounds_tactic.cpp | 9 ++------ src/tactic/arith/pb2bv_tactic.cpp | 9 ++------ src/tactic/arith/propagate_ineqs_tactic.cpp | 10 ++------- src/tactic/arith/recover_01_tactic.cpp | 10 ++------- src/tactic/bv/bit_blaster_tactic.cpp | 10 ++------- src/tactic/bv/bv1_blaster_tactic.cpp | 10 ++------- src/tactic/bv/bv_size_reduction_tactic.cpp | 10 ++------- src/tactic/bv/max_bv_sharing_tactic.cpp | 10 ++------- src/tactic/core/cofactor_elim_term_ite.cpp | 23 +++++++------------- src/tactic/core/cofactor_elim_term_ite.h | 3 ++- src/tactic/core/ctx_simplify_tactic.cpp | 9 ++------ src/tactic/core/der_tactic.cpp | 9 ++------ src/tactic/core/elim_term_ite_tactic.cpp | 9 ++------ src/tactic/core/elim_uncnstr_tactic.cpp | 11 +++------- src/tactic/core/occf_tactic.cpp | 10 ++------- src/tactic/core/propagate_values_tactic.cpp | 9 ++------ src/tactic/core/reduce_args_tactic.cpp | 11 +++------- src/tactic/core/simplify_tactic.cpp | 9 ++------ src/tactic/core/simplify_tactic.h | 4 +++- src/tactic/core/solve_eqs_tactic.cpp | 12 ++++------ src/tactic/core/tseitin_cnf_tactic.cpp | 12 +++------- src/tactic/fpa/fpa2bv_tactic.cpp | 12 +++------- src/tactic/tactic.h | 8 ++++++- src/tactic/tactical.cpp | 16 +++++--------- src/tactic/ufbv/macro_finder_tactic.cpp | 9 ++------ src/tactic/ufbv/quasi_macros_tactic.cpp | 9 ++------ src/tactic/ufbv/ufbv_rewriter_tactic.cpp | 9 ++------ 39 files changed, 115 insertions(+), 264 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 20539266b..ce2838aba 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -346,8 +346,14 @@ cmd_context::~cmd_context() { } void cmd_context::set_cancel(bool f) { - if (m_solver) - m_solver->set_cancel(f); + if (m_solver) { + if (f) { + m_solver->cancel(); + } + else { + m_solver->reset_cancel(); + } + } if (has_manager()) m().set_cancel(f); } diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index a98e5be49..dfbb62f65 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -218,8 +218,14 @@ public: } virtual void set_cancel(bool f) { - m_solver1->set_cancel(f); - m_solver2->set_cancel(f); + if (f) { + m_solver1->cancel(); + m_solver2->cancel(); + } + else { + m_solver1->reset_cancel(); + m_solver2->reset_cancel(); + } } virtual void set_progress_callback(progress_callback * callback) { diff --git a/src/solver/solver.h b/src/solver/solver.h index e047bace1..a95c649c0 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -99,7 +99,6 @@ public: */ virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) = 0; - virtual void set_cancel(bool f) {} /** \brief Interrupt this solver. */ @@ -130,6 +129,9 @@ public: \brief Display the content of this solver. */ virtual void display(std::ostream & out) const; +protected: + virtual void set_cancel(bool f) = 0; + }; #endif diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index 1268fbcea..fb4898ecd 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -173,8 +173,12 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass } void tactic2solver::set_cancel(bool f) { - if (m_tactic.get()) - m_tactic->set_cancel(f); + if (m_tactic.get()) { + if (f) + m_tactic->cancel(); + else + m_tactic->reset_cancel(); + } } void tactic2solver::collect_statistics(statistics & st) const { diff --git a/src/tactic/aig/aig.h b/src/tactic/aig/aig.h index c8befd9b2..291bfbcf3 100644 --- a/src/tactic/aig/aig.h +++ b/src/tactic/aig/aig.h @@ -73,8 +73,6 @@ public: void display(std::ostream & out, aig_ref const & r) const; void display_smt2(std::ostream & out, aig_ref const & r) const; unsigned get_num_aigs() const; - void cancel() { set_cancel(true); } - void reset_cancel() { set_cancel(false); } void set_cancel(bool f); }; diff --git a/src/tactic/arith/add_bounds_tactic.cpp b/src/tactic/arith/add_bounds_tactic.cpp index d396359a3..9bf967be3 100644 --- a/src/tactic/arith/add_bounds_tactic.cpp +++ b/src/tactic/arith/add_bounds_tactic.cpp @@ -172,18 +172,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { - d = m_imp; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } protected: diff --git a/src/tactic/arith/degree_shift_tactic.cpp b/src/tactic/arith/degree_shift_tactic.cpp index a1aa0bdc8..e8fb72be4 100644 --- a/src/tactic/arith/degree_shift_tactic.cpp +++ b/src/tactic/arith/degree_shift_tactic.cpp @@ -319,18 +319,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } protected: diff --git a/src/tactic/arith/diff_neq_tactic.cpp b/src/tactic/arith/diff_neq_tactic.cpp index b534c8295..1a33bf4f2 100644 --- a/src/tactic/arith/diff_neq_tactic.cpp +++ b/src/tactic/arith/diff_neq_tactic.cpp @@ -398,20 +398,13 @@ public: } virtual void cleanup() { - unsigned num_conflicts = m_imp->m_num_conflicts; - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m, m_params); + d->m_num_conflicts = m_imp->m_num_conflicts; #pragma omp critical (tactic_cancel) { - d = m_imp; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } - m_imp->m_num_conflicts = num_conflicts; } protected: diff --git a/src/tactic/arith/factor_tactic.cpp b/src/tactic/arith/factor_tactic.cpp index 4eec83037..dc17a4f87 100644 --- a/src/tactic/arith/factor_tactic.cpp +++ b/src/tactic/arith/factor_tactic.cpp @@ -333,18 +333,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } virtual void set_cancel(bool f) { diff --git a/src/tactic/arith/fix_dl_var_tactic.cpp b/src/tactic/arith/fix_dl_var_tactic.cpp index d2e3ebf1f..693066ee1 100644 --- a/src/tactic/arith/fix_dl_var_tactic.cpp +++ b/src/tactic/arith/fix_dl_var_tactic.cpp @@ -338,18 +338,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } virtual void set_cancel(bool f) { diff --git a/src/tactic/arith/fm_tactic.cpp b/src/tactic/arith/fm_tactic.cpp index c4b7cbaf3..c180029ae 100644 --- a/src/tactic/arith/fm_tactic.cpp +++ b/src/tactic/arith/fm_tactic.cpp @@ -1682,18 +1682,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } virtual void operator()(goal_ref const & in, diff --git a/src/tactic/arith/lia2pb_tactic.cpp b/src/tactic/arith/lia2pb_tactic.cpp index 9ee8f6728..33d5f138e 100644 --- a/src/tactic/arith/lia2pb_tactic.cpp +++ b/src/tactic/arith/lia2pb_tactic.cpp @@ -345,18 +345,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { - d = m_imp; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } protected: diff --git a/src/tactic/arith/normalize_bounds_tactic.cpp b/src/tactic/arith/normalize_bounds_tactic.cpp index a62b311b5..323903f6c 100644 --- a/src/tactic/arith/normalize_bounds_tactic.cpp +++ b/src/tactic/arith/normalize_bounds_tactic.cpp @@ -191,17 +191,12 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { - d = m_imp; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } protected: diff --git a/src/tactic/arith/pb2bv_tactic.cpp b/src/tactic/arith/pb2bv_tactic.cpp index 89195a9d5..643195b05 100644 --- a/src/tactic/arith/pb2bv_tactic.cpp +++ b/src/tactic/arith/pb2bv_tactic.cpp @@ -1002,17 +1002,12 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { - d = m_imp; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } protected: diff --git a/src/tactic/arith/propagate_ineqs_tactic.cpp b/src/tactic/arith/propagate_ineqs_tactic.cpp index 12954005f..d7e4f30d2 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.cpp +++ b/src/tactic/arith/propagate_ineqs_tactic.cpp @@ -548,16 +548,10 @@ void propagate_ineqs_tactic::set_cancel(bool f) { } void propagate_ineqs_tactic::cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { - d = m_imp; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } diff --git a/src/tactic/arith/recover_01_tactic.cpp b/src/tactic/arith/recover_01_tactic.cpp index 3b1a86345..3d222cf56 100644 --- a/src/tactic/arith/recover_01_tactic.cpp +++ b/src/tactic/arith/recover_01_tactic.cpp @@ -425,18 +425,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { - d = m_imp; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } protected: diff --git a/src/tactic/bv/bit_blaster_tactic.cpp b/src/tactic/bv/bit_blaster_tactic.cpp index 0330141cb..33423c8b1 100644 --- a/src/tactic/bv/bit_blaster_tactic.cpp +++ b/src/tactic/bv/bit_blaster_tactic.cpp @@ -140,18 +140,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m(); - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m(), m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } unsigned get_num_steps() const { diff --git a/src/tactic/bv/bv1_blaster_tactic.cpp b/src/tactic/bv/bv1_blaster_tactic.cpp index 8dcaf2112..5f20015ca 100644 --- a/src/tactic/bv/bv1_blaster_tactic.cpp +++ b/src/tactic/bv/bv1_blaster_tactic.cpp @@ -465,18 +465,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m(); - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m(), m_params); #pragma omp critical (tactic_cancel) { - d = m_imp; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } unsigned get_num_steps() const { diff --git a/src/tactic/bv/bv_size_reduction_tactic.cpp b/src/tactic/bv/bv_size_reduction_tactic.cpp index d3fdb6e56..e149afc75 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -392,17 +392,11 @@ void bv_size_reduction_tactic::set_cancel(bool f) { } void bv_size_reduction_tactic::cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } diff --git a/src/tactic/bv/max_bv_sharing_tactic.cpp b/src/tactic/bv/max_bv_sharing_tactic.cpp index f60487d60..56b18dd8d 100644 --- a/src/tactic/bv/max_bv_sharing_tactic.cpp +++ b/src/tactic/bv/max_bv_sharing_tactic.cpp @@ -311,18 +311,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m(); - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m(), m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } virtual void set_cancel(bool f) { diff --git a/src/tactic/core/cofactor_elim_term_ite.cpp b/src/tactic/core/cofactor_elim_term_ite.cpp index 1f560ef62..aca6d9084 100644 --- a/src/tactic/core/cofactor_elim_term_ite.cpp +++ b/src/tactic/core/cofactor_elim_term_ite.cpp @@ -683,12 +683,7 @@ cofactor_elim_term_ite::cofactor_elim_term_ite(ast_manager & m, params_ref const } cofactor_elim_term_ite::~cofactor_elim_term_ite() { - imp * d = m_imp; - #pragma omp critical (cofactor_elim_term_ite) - { - m_imp = 0; - } - dealloc(d); + dealloc(m_imp); } void cofactor_elim_term_ite::updt_params(params_ref const & p) { @@ -704,19 +699,17 @@ void cofactor_elim_term_ite::operator()(expr * t, expr_ref & r) { } void cofactor_elim_term_ite::set_cancel(bool f) { - #pragma omp critical (cofactor_elim_term_ite) - { - if (m_imp) - m_imp->set_cancel(f); - } + if (m_imp) + m_imp->set_cancel(f); } void cofactor_elim_term_ite::cleanup() { - ast_manager & m = m_imp->m; - #pragma omp critical (cofactor_elim_term_ite) + ast_manager & m = m_imp->m; + imp * d = alloc(imp, m, m_params); + #pragma omp critical (tactic_cancel) { - dealloc(m_imp); - m_imp = alloc(imp, m, m_params); + std::swap(d, m_imp); } + dealloc(d); } diff --git a/src/tactic/core/cofactor_elim_term_ite.h b/src/tactic/core/cofactor_elim_term_ite.h index ce2f31ea0..e734fcad6 100644 --- a/src/tactic/core/cofactor_elim_term_ite.h +++ b/src/tactic/core/cofactor_elim_term_ite.h @@ -37,8 +37,9 @@ public: void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } - void set_cancel(bool f); void cleanup(); + void set_cancel(bool f); + }; #endif diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 71f4771a9..bb38d28ce 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -548,16 +548,11 @@ void ctx_simplify_tactic::set_cancel(bool f) { void ctx_simplify_tactic::cleanup() { ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } diff --git a/src/tactic/core/der_tactic.cpp b/src/tactic/core/der_tactic.cpp index c2245b409..2277c3fa8 100644 --- a/src/tactic/core/der_tactic.cpp +++ b/src/tactic/core/der_tactic.cpp @@ -90,17 +90,12 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m(); - imp * d = m_imp; + imp * d = alloc(imp, m); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } virtual void set_cancel(bool f) { diff --git a/src/tactic/core/elim_term_ite_tactic.cpp b/src/tactic/core/elim_term_ite_tactic.cpp index 456bad5e0..e49884004 100644 --- a/src/tactic/core/elim_term_ite_tactic.cpp +++ b/src/tactic/core/elim_term_ite_tactic.cpp @@ -174,17 +174,12 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } virtual void set_cancel(bool f) { diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 4d64bf061..6d7bcb2f2 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -1036,18 +1036,13 @@ public: virtual void cleanup() { unsigned num_elim_apps = get_num_elim_apps(); - ast_manager & m = m_imp->m_manager; - imp * d = m_imp; + ast_manager & m = m_imp->m_manager; + imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } m_imp->m_num_elim_apps = num_elim_apps; } diff --git a/src/tactic/core/occf_tactic.cpp b/src/tactic/core/occf_tactic.cpp index f8f6174b9..9b974ae19 100644 --- a/src/tactic/core/occf_tactic.cpp +++ b/src/tactic/core/occf_tactic.cpp @@ -225,18 +225,12 @@ public: } virtual void cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m_imp->m); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } protected: diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index 1e358177f..5873efd61 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -255,17 +255,12 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m(); - imp * d = m_imp; + imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } protected: diff --git a/src/tactic/core/reduce_args_tactic.cpp b/src/tactic/core/reduce_args_tactic.cpp index 59ed2dcd3..99375e6e8 100644 --- a/src/tactic/core/reduce_args_tactic.cpp +++ b/src/tactic/core/reduce_args_tactic.cpp @@ -541,17 +541,12 @@ void reduce_args_tactic::set_cancel(bool f) { } void reduce_args_tactic::cleanup() { - ast_manager & m = m_imp->m(); - imp * d = m_imp; + ast_manager & m = m_imp->m(); + imp * d = alloc(imp, m); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } diff --git a/src/tactic/core/simplify_tactic.cpp b/src/tactic/core/simplify_tactic.cpp index 95db962a7..39f10e10c 100644 --- a/src/tactic/core/simplify_tactic.cpp +++ b/src/tactic/core/simplify_tactic.cpp @@ -115,17 +115,12 @@ void simplify_tactic::set_cancel(bool f) { void simplify_tactic::cleanup() { ast_manager & m = m_imp->m(); - imp * d = m_imp; + imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } unsigned simplify_tactic::get_num_steps() const { diff --git a/src/tactic/core/simplify_tactic.h b/src/tactic/core/simplify_tactic.h index fc9cb393c..1ba5a6da8 100644 --- a/src/tactic/core/simplify_tactic.h +++ b/src/tactic/core/simplify_tactic.h @@ -43,9 +43,11 @@ public: virtual void cleanup(); unsigned get_num_steps() const; - virtual void set_cancel(bool f); virtual tactic * translate(ast_manager & m) { return alloc(simplify_tactic, m, m_params); } +protected: + virtual void set_cancel(bool f); + }; tactic * mk_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index 995940406..3b0a57d20 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -749,23 +749,19 @@ public: virtual void cleanup() { unsigned num_elim_vars = m_imp->m_num_eliminated_vars; ast_manager & m = m_imp->m(); - imp * d = m_imp; expr_replacer * r = m_imp->m_r; if (r) r->set_substitution(0); bool owner = m_imp->m_r_owner; m_imp->m_r_owner = false; // stole replacer + + imp * d = alloc(imp, m, m_params, r, owner); + d->m_num_eliminated_vars = num_elim_vars; #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params, r, owner); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } - m_imp->m_num_eliminated_vars = num_elim_vars; } virtual void collect_statistics(statistics & st) const { diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index b6c4b0655..c5be7ab1f 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -898,20 +898,14 @@ public: } virtual void cleanup() { - unsigned num_aux_vars = m_imp->m_num_aux_vars; ast_manager & m = m_imp->m; - imp * d = m_imp; + imp * d = alloc(imp, m, m_params); + d->m_num_aux_vars = m_imp->m_num_aux_vars; #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } - m_imp->m_num_aux_vars = num_aux_vars; } virtual void set_cancel(bool f) { diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index 3a6b7ea45..4a3a01b6f 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -138,19 +138,13 @@ public: (*m_imp)(in, result, mc, pc, core); } - virtual void cleanup() { - ast_manager & m = m_imp->m; - imp * d = m_imp; + virtual void cleanup() { + imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { - d = m_imp; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } protected: diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index 80de1bcd0..8b4f9f576 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -47,7 +47,6 @@ public: void cancel(); void reset_cancel(); - virtual void set_cancel(bool f) {} /** \brief Apply tactic to goal \c in. @@ -96,6 +95,13 @@ public: // translate tactic to the given manager virtual tactic * translate(ast_manager & m) = 0; +private: + friend class nary_tactical; + friend class binary_tactical; + friend class unary_tactical; + + virtual void set_cancel(bool f) {} + }; typedef ref tactic_ref; diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index ca66b7a14..1e0b07e9d 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -102,11 +102,8 @@ protected: \brief Reset cancel flag of t if this was not canceled. */ void parent_reset_cancel(tactic & t) { - #pragma omp critical (tactic_cancel) - { - if (!m_cancel) { - t.set_cancel(false); - } + if (!m_cancel) { + t.reset_cancel(); } } @@ -393,11 +390,8 @@ protected: \brief Reset cancel flag of st if this was not canceled. */ void parent_reset_cancel(tactic & t) { - #pragma omp critical (tactic_cancel) - { - if (!m_cancel) { - t.set_cancel(false); - } + if (!m_cancel) { + t.reset_cancel(); } } @@ -988,7 +982,7 @@ protected: virtual void set_cancel(bool f) { m_cancel = f; if (m_t) - m_t->set_cancel(f); + m_t->set_cancel(f); } template diff --git a/src/tactic/ufbv/macro_finder_tactic.cpp b/src/tactic/ufbv/macro_finder_tactic.cpp index 14a42ec51..2f0262fc8 100644 --- a/src/tactic/ufbv/macro_finder_tactic.cpp +++ b/src/tactic/ufbv/macro_finder_tactic.cpp @@ -144,17 +144,12 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m(); - imp * d = m_imp; + imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } virtual void set_cancel(bool f) { diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp index 393a40603..cea8f2cfc 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.cpp +++ b/src/tactic/ufbv/quasi_macros_tactic.cpp @@ -151,17 +151,12 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m(); - imp * d = m_imp; + imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } virtual void set_cancel(bool f) { diff --git a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp index 0705c627c..efecb38ba 100644 --- a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp +++ b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp @@ -119,17 +119,12 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m(); - imp * d = m_imp; + imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { - m_imp = 0; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } virtual void set_cancel(bool f) { From 36816e3b2fe1c27c97f88a4aaac8f03eb3243a97 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 6 Sep 2014 19:03:37 -0700 Subject: [PATCH 414/509] clear cache for crash Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_context.cpp | 1 + src/tactic/sls/sls_tactic.cpp | 9 ++------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index 94dd5d0a0..d8b96297a 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -1221,6 +1221,7 @@ namespace pdr { remove_node(*m_root, false); dealloc(m_root); m_root = 0; + m_cache.reset(); } } diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index ea5d60668..6783d9621 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -557,17 +557,12 @@ public: } virtual void cleanup() { - imp * d = m_imp; + imp * d = alloc(imp, m, m_params, m_stats); #pragma omp critical (tactic_cancel) { - d = m_imp; + std::swap(d, m_imp); } dealloc(d); - d = alloc(imp, m, m_params, m_stats); - #pragma omp critical (tactic_cancel) - { - m_imp = d; - } } virtual void collect_statistics(statistics & st) const { From dd62ca5eb36c2a62ee44fc5a79fc27c883de21ae Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 6 Sep 2014 20:54:16 -0700 Subject: [PATCH 415/509] simplify models Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_context.cpp | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index d8b96297a..f07964395 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -47,6 +47,7 @@ Notes: #include "dl_boogie_proof.h" #include "qe_util.h" #include "scoped_proof.h" +#include "expr_safe_replace.h" namespace pdr { @@ -1062,10 +1063,7 @@ namespace pdr { predicates.pop_back(); for (unsigned i = rule->get_uninterpreted_tail_size(); i < rule->get_tail_size(); ++i) { subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); - dctx.get_rewriter()(tmp); - if (!m.is_true(tmp)) { - constraints.push_back(tmp); - } + constraints.push_back(tmp); } for (unsigned i = 0; i < constraints.size(); ++i) { max_var = std::max(vc.get_max_var(constraints[i].get()), max_var); @@ -1088,7 +1086,28 @@ namespace pdr { children.append(n->children()); } - return pm.mk_and(constraints); + + expr_safe_replace repl(m); + for (unsigned i = 0; i < constraints.size(); ++i) { + expr* e = constraints[i].get(), *e1, *e2; + if (m.is_eq(e, e1, e2) && is_var(e1) && is_ground(e2)) { + repl.insert(e1, e2); + } + else if (m.is_eq(e, e1, e2) && is_var(e2) && is_ground(e1)) { + repl.insert(e2, e1); + } + } + expr_ref_vector result(m); + for (unsigned i = 0; i < constraints.size(); ++i) { + expr_ref tmp(m); + tmp = constraints[i].get(); + repl(tmp); + dctx.get_rewriter()(tmp); + if (!m.is_true(tmp)) { + result.push_back(tmp); + } + } + return pm.mk_and(result); } proof_ref model_search::get_proof_trace(context const& ctx) { From 13b61d894c0c352b2baa1e3c3cae7a939e97c160 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 9 Sep 2014 14:02:46 -0700 Subject: [PATCH 416/509] adding recursion bounds to duality --- src/duality/duality.h | 5 +++-- src/duality/duality_rpfp.cpp | 5 ++++- src/duality/duality_solver.cpp | 23 ++++++++++++++++------- src/muz/base/dl_context.cpp | 8 ++++++-- src/muz/base/dl_context.h | 5 +++-- src/muz/duality/duality_dl_interface.cpp | 17 ++++++++++++++--- src/muz/fp/dl_cmds.cpp | 20 ++++++++++++++------ 7 files changed, 60 insertions(+), 23 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index c1c9797f3..16576dd65 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -488,9 +488,10 @@ protected: std::vector Incoming; Term dual; Node *map; + unsigned recursion_bound; Node(const FuncDecl &_Name, const Transformer &_Annotation, const Transformer &_Bound, const Transformer &_Underapprox, const Term &_dual, RPFP *_owner, int _number) - : Name(_Name), Annotation(_Annotation), Bound(_Bound), Underapprox(_Underapprox), dual(_dual) {owner = _owner; number = _number; Outgoing = 0;} + : Name(_Name), Annotation(_Annotation), Bound(_Bound), Underapprox(_Underapprox), dual(_dual) {owner = _owner; number = _number; Outgoing = 0; recursion_bound = UINT_MAX;} }; /** Create a node in the graph. The input is a term R(v_1...v_n) @@ -829,7 +830,7 @@ protected: #ifdef _WINDOWS __declspec(dllexport) #endif - void FromClauses(const std::vector &clauses); + void FromClauses(const std::vector &clauses, const std::vector *bounds = 0); void FromFixpointContext(fixedpoint fp, std::vector &queries); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index ef37dd8a2..8c88dda72 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -3570,7 +3570,7 @@ namespace Duality { #define USE_QE_LITE - void RPFP::FromClauses(const std::vector &unskolemized_clauses){ + void RPFP::FromClauses(const std::vector &unskolemized_clauses, const std::vector *bounds){ hash_map pmap; func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); @@ -3663,6 +3663,7 @@ namespace Duality { pmap[R] = node; if (is_query) node->Bound = CreateRelation(std::vector(), ctx.bool_val(false)); + node->recursion_bound = bounds ? 0 : UINT_MAX; } } @@ -3728,6 +3729,8 @@ namespace Duality { Transformer T = CreateTransformer(Relparams,Indparams,body); Edge *edge = CreateEdge(Parent,T,Children); edge->labeled = labeled;; // remember for label extraction + if(bounds) + Parent->recursion_bound = std::max(Parent->recursion_bound,(*bounds)[i]); // edges.push_back(edge); } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 681582415..f54e00693 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -33,6 +33,7 @@ Revision History: #include #include #include +#include // TODO: make these official options or get rid of them @@ -304,7 +305,7 @@ namespace Duality { #ifdef BOUNDED struct Counter { - int val; + unsigned val; Counter(){val = 0;} }; typedef std::map NodeToCounter; @@ -321,6 +322,10 @@ namespace Duality { heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp)) : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); #endif + // determine if we are recursion bounded + for(unsigned i = 0; i < rpfp->nodes.size(); i++) + if(rpfp->nodes[i]->recursion_bound < UINT_MAX) + RecursionBound = 0; cex.clear(); // in case we didn't use it for heuristic if(unwinding) delete unwinding; unwinding = new RPFP(rpfp->ls); @@ -461,7 +466,7 @@ namespace Duality { } return false; } - + /** Create an instance of a node in the unwinding. Set its annotation to true, and mark it unexpanded. */ Node* CreateNodeInstance(Node *node, int number = 0){ @@ -780,10 +785,8 @@ namespace Duality { std::vector &insts = insts_of_node[node]; for(unsigned j = 0; j < insts.size(); j++) if(indset->Contains(insts[j])) - if(NodePastRecursionBound(insts[j])){ + if(NodePastRecursionBound(insts[j],true)) recursionBounded = true; - return; - } } } @@ -801,12 +804,18 @@ namespace Duality { } #ifdef BOUNDED - bool NodePastRecursionBound(Node *node){ + bool NodePastRecursionBound(Node *node, bool report = false){ if(RecursionBound < 0) return false; NodeToCounter &backs = back_edges[node]; for(NodeToCounter::iterator it = backs.begin(), en = backs.end(); it != en; ++it){ - if(it->second.val > RecursionBound) + if(it->second.val > it->first->recursion_bound){ + if(report){ + std::ostringstream os; + os << "cut off " << it->first->Name.name() << " at depth " << it->second.val; + reporter->Message(os.str()); + } return true; + } } return false; } diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index fbef46ae3..80af7eeb0 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -246,6 +246,7 @@ namespace datalog { m_rule_fmls_head = 0; m_rule_fmls.reset(); m_rule_names.reset(); + m_rule_bounds.reset(); m_argument_var_names.reset(); m_preds.reset(); m_preds_by_name.reset(); @@ -474,9 +475,10 @@ namespace datalog { return new_pred; } - void context::add_rule(expr* rl, symbol const& name) { + void context::add_rule(expr* rl, symbol const& name, unsigned bound) { m_rule_fmls.push_back(rl); m_rule_names.push_back(name); + m_rule_bounds.push_back(bound); } void context::flush_add_rules() { @@ -1102,12 +1104,13 @@ namespace datalog { } } - void context::get_raw_rule_formulas(expr_ref_vector& rules, svector& names){ + void context::get_raw_rule_formulas(expr_ref_vector& rules, svector& names, vector &bounds){ for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { expr_ref r = bind_variables(m_rule_fmls[i].get(), true); rules.push_back(r.get()); // rules.push_back(m_rule_fmls[i].get()); names.push_back(m_rule_names[i]); + bounds.push_back(m_rule_bounds[i]); } } @@ -1125,6 +1128,7 @@ namespace datalog { m_rule_names[i] = m_rule_names.back(); m_rule_fmls.pop_back(); m_rule_names.pop_back(); + m_rule_bounds.pop_back(); --i; } } diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 7349fa889..cf2c53913 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -194,6 +194,7 @@ namespace datalog { unsigned m_rule_fmls_head; expr_ref_vector m_rule_fmls; svector m_rule_names; + vector m_rule_bounds; expr_ref_vector m_background; model_converter_ref m_mc; proof_converter_ref m_pc; @@ -366,7 +367,7 @@ namespace datalog { rule_set & get_rules() { flush_add_rules(); return m_rule_set; } void get_rules_as_formulas(expr_ref_vector& fmls, svector& names); - void get_raw_rule_formulas(expr_ref_vector& fmls, svector& names); + void get_raw_rule_formulas(expr_ref_vector& fmls, svector& names, vector &bounds); void add_fact(app * head); void add_fact(func_decl * pred, const relation_fact & fact); @@ -383,7 +384,7 @@ namespace datalog { /** Method exposed from API for adding rules. */ - void add_rule(expr* rl, symbol const& name); + void add_rule(expr* rl, symbol const& name, unsigned bound = UINT_MAX); /** diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 849cf94ea..08c57b05e 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -155,8 +155,9 @@ lbool dl_interface::query(::expr * query) { expr_ref_vector rules(m_ctx.get_manager()); svector< ::symbol> names; + vector bounds; // m_ctx.get_rules_as_formulas(rules, names); - m_ctx.get_raw_rule_formulas(rules, names); + m_ctx.get_raw_rule_formulas(rules, names, bounds); // get all the rules as clauses std::vector &clauses = _d->clauses; @@ -200,6 +201,7 @@ lbool dl_interface::query(::expr * query) { expr qc = implies(q,_d->ctx.bool_val(false)); qc = _d->ctx.make_quant(Forall,b_sorts,b_names,qc); clauses.push_back(qc); + bounds.push_back(UINT_MAX); // get the background axioms unsigned num_asserts = m_ctx.get_num_assertions(); @@ -243,13 +245,21 @@ lbool dl_interface::query(::expr * query) { expr c = implies(_d->ctx.bool_val(false),f(args)); c = _d->ctx.make_quant(Forall,args,c); clauses.push_back(c); + bounds.push_back(UINT_MAX); } } } } + unsigned rb = m_ctx.get_params().recursion_bound(); + std::vector std_bounds; + for(unsigned i = 0; i < bounds.size(); i++){ + unsigned b = bounds[i]; + if (b == UINT_MAX) b = rb; + std_bounds.push_back(b); + } // creates 1-1 map between clauses and rpfp edges - _d->rpfp->FromClauses(clauses); + _d->rpfp->FromClauses(clauses,&std_bounds); // populate the edge-to-clause map for(unsigned i = 0; i < _d->rpfp->edges.size(); ++i) @@ -271,11 +281,12 @@ lbool dl_interface::query(::expr * query) { rs->SetOption("stratified_inlining",m_ctx.get_params().stratified_inlining() ? "1" : "0"); rs->SetOption("batch_expand",m_ctx.get_params().batch_expand() ? "1" : "0"); rs->SetOption("conjecture_file",m_ctx.get_params().conjecture_file()); - unsigned rb = m_ctx.get_params().recursion_bound(); +#if 0 if(rb != UINT_MAX){ std::ostringstream os; os << rb; rs->SetOption("recursion_bound", os.str()); } +#endif // Solve! bool ans; diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index f9916808c..e2e3680cc 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -100,7 +100,7 @@ struct dl_context { dlctx().set_predicate_representation(pred, num_kinds, kinds); } - void add_rule(expr * rule, symbol const& name) { + void add_rule(expr * rule, symbol const& name, unsigned bound) { init(); if (m_collected_cmds) { expr_ref rl = m_context->bind_variables(rule, true); @@ -110,7 +110,7 @@ struct dl_context { m_trail.push(push_back_vector >(m_collected_cmds->m_names)); } else { - m_context->add_rule(rule, name); + m_context->add_rule(rule, name, bound); } } @@ -151,19 +151,22 @@ class dl_rule_cmd : public cmd { mutable unsigned m_arg_idx; expr* m_t; symbol m_name; + unsigned m_bound; public: dl_rule_cmd(dl_context * dl_ctx): cmd("rule"), m_dl_ctx(dl_ctx), m_arg_idx(0), - m_t(0) {} - virtual char const * get_usage() const { return "(forall (q) (=> (and body) head)) :optional-name"; } + m_t(0), + m_bound(UINT_MAX) {} + virtual char const * get_usage() const { return "(forall (q) (=> (and body) head)) :optional-name :optional-recursion-bound"; } virtual char const * get_descr(cmd_context & ctx) const { return "add a Horn rule."; } virtual unsigned get_arity() const { return VAR_ARITY; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { switch(m_arg_idx) { case 0: return CPK_EXPR; case 1: return CPK_SYMBOL; + case 2: return CPK_UINT; default: return CPK_SYMBOL; } } @@ -173,13 +176,18 @@ public: } virtual void set_next_arg(cmd_context & ctx, symbol const & s) { m_name = s; + m_arg_idx++; + } + virtual void set_next_arg(cmd_context & ctx, unsigned bound) { + m_bound = bound; + m_arg_idx++; } virtual void reset(cmd_context & ctx) { m_dl_ctx->reset(); prepare(ctx); } - virtual void prepare(cmd_context& ctx) { m_arg_idx = 0; m_name = symbol::null; } + virtual void prepare(cmd_context& ctx) { m_arg_idx = 0; m_name = symbol::null; m_bound = UINT_MAX; } virtual void finalize(cmd_context & ctx) { } virtual void execute(cmd_context & ctx) { - m_dl_ctx->add_rule(m_t, m_name); + m_dl_ctx->add_rule(m_t, m_name, m_bound); } }; From c917c1c53db30b875a645ba19879a61512d89b17 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 12 Sep 2014 15:54:42 -0700 Subject: [PATCH 417/509] reset ast trail on context deletion Signed-off-by: Nikolaj Bjorner --- src/api/api_context.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 05e1ca675..338c12f61 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -128,6 +128,7 @@ namespace api { for (unsigned i = 0; i < m_replay_stack.size(); ++i) { dealloc(m_replay_stack[i]); } + m_ast_trail.reset(); } reset_parser(); dealloc(m_solver); From 67b802c9d905329d2411615131b426789cb3856d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 12 Sep 2014 17:38:34 -0700 Subject: [PATCH 418/509] fix scope accounting bug and documentation per Konrad's request Signed-off-by: Nikolaj Bjorner --- src/api/api_context.cpp | 1 + src/api/api_solver_old.cpp | 2 +- src/api/z3_api.h | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 338c12f61..3349bed7c 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -361,6 +361,7 @@ namespace api { } } } + SASSERT(num_scopes <= get_smt_kernel().get_scope_level()); get_smt_kernel().pop(num_scopes); } diff --git a/src/api/api_solver_old.cpp b/src/api/api_solver_old.cpp index e0533fbd9..b81fb2f2c 100644 --- a/src/api/api_solver_old.cpp +++ b/src/api/api_solver_old.cpp @@ -40,7 +40,7 @@ extern "C" { LOG_Z3_pop(c, num_scopes); RESET_ERROR_CODE(); CHECK_SEARCHING(c); - if (num_scopes > mk_c(c)->get_smt_kernel().get_scope_level()) { + if (num_scopes > mk_c(c)->get_num_scopes()) { SET_ERROR_CODE(Z3_IOB); return; } diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 4ffdfa9c2..e1c42667a 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7069,7 +7069,7 @@ END_MLAPI_EXCLUDE \mlonly then a valid model is returned. Otherwise, it is unsafe to use the returned model.\endmlonly \conly The caller is responsible for deleting the model using the function #Z3_del_model. - \conly \remark In constrast with the rest of the Z3 API, the reference counter of the + \conly \remark In contrast with the rest of the Z3 API, the reference counter of the \conly model is incremented. This is to guarantee backward compatibility. In previous \conly versions, models did not support reference counting. @@ -7182,6 +7182,11 @@ END_MLAPI_EXCLUDE \brief Delete a model object. \sa Z3_check_and_get_model + + \conly \remark The Z3_check_and_get_model automatically increments a reference count on the model. + \conly The expected usage is that models created by that method are deleted using Z3_del_model. + \conly This is for backwards compatibility and in contrast to the rest of the API where + \conly callers are responsible for managing reference counts. \deprecated Subsumed by Z3_solver API From 919e0a5ea2f2bef4fe8ab9880e91dd52a7fc2bd1 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 16 Sep 2014 14:56:59 +0100 Subject: [PATCH 419/509] Z3Py: fix bug in substitute() with a list of on variable e.g. print substitute(Int('x'), [(Int('x'), Int('y'))]) Signed-off-by: Nuno Lopes --- src/api/python/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 50d29a790..d46d40ab2 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -6960,7 +6960,7 @@ def substitute(t, *m): if isinstance(m, tuple): m1 = _get_args(m) if isinstance(m1, list): - m = _get_args(m1) + m = m1 if __debug__: _z3_assert(is_expr(t), "Z3 expression expected") _z3_assert(all([isinstance(p, tuple) and is_expr(p[0]) and is_expr(p[1]) and p[0].sort().eq(p[1].sort()) for p in m]), "Z3 invalid substitution, expression pairs expected.") From f7c3559c31b5d5c7335706a61a7f9c6e47a55545 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 16 Sep 2014 15:26:01 +0100 Subject: [PATCH 420/509] fix a few compiler warnings Signed-off-by: Nuno Lopes --- src/api/api_interp.cpp | 2 +- src/muz/pdr/pdr_farkas_learner.cpp | 1 - src/smt/smt_model_checker.cpp | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 03d5ff843..e06ad5192 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -411,7 +411,7 @@ static void get_file_params(const char *filename, hash_map= 0 && eqpos < tok.size()){ + if(eqpos != std::string::npos){ std::string left = tok.substr(0,eqpos); std::string right = tok.substr(eqpos+1,tok.size()-eqpos-1); params[left] = right; diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index 7c38bf86f..b440ef519 100644 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ b/src/muz/pdr/pdr_farkas_learner.cpp @@ -905,7 +905,6 @@ namespace pdr { void farkas_learner::get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas) { ast_manager& m = lemmas.get_manager(); ast_mark visited; - proof* p0 = p; ptr_vector todo; todo.push_back(p); diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index 526447f9f..7053d63ec 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -136,9 +136,8 @@ namespace smt { if (cex == 0) return false; // no model available. unsigned num_decls = q->get_num_decls(); - unsigned num_sks = sks.size(); // Remark: sks were created for the flat version of q. - SASSERT(num_sks >= num_decls); + SASSERT(sks.size() >= num_decls); expr_ref_buffer bindings(m_manager); bindings.resize(num_decls); unsigned max_generation = 0; From 79326e24dfb9dff723d1ce64b3f50bcf394bd48c Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 16 Sep 2014 15:29:25 +0100 Subject: [PATCH 421/509] fix debug build.. Signed-off-by: Nuno Lopes --- src/muz/pdr/pdr_farkas_learner.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index b440ef519..7c38bf86f 100644 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ b/src/muz/pdr/pdr_farkas_learner.cpp @@ -905,6 +905,7 @@ namespace pdr { void farkas_learner::get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas) { ast_manager& m = lemmas.get_manager(); ast_mark visited; + proof* p0 = p; ptr_vector todo; todo.push_back(p); From d01ca110012a7f92495bf37827df641a2d6378db Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 16 Sep 2014 17:13:09 -0700 Subject: [PATCH 422/509] reduce asymptotic overhead of asserting bounds Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_context.cpp | 8 +- src/smt/theory_arith.h | 17 +++ src/smt/theory_arith_core.h | 264 ++++++++++++++++++++++++++++++++---- 3 files changed, 259 insertions(+), 30 deletions(-) diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index fbef46ae3..3d0c7c880 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -1104,10 +1104,10 @@ namespace datalog { void context::get_raw_rule_formulas(expr_ref_vector& rules, svector& names){ for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { - expr_ref r = bind_variables(m_rule_fmls[i].get(), true); - rules.push_back(r.get()); - // rules.push_back(m_rule_fmls[i].get()); - names.push_back(m_rule_names[i]); + expr_ref r = bind_variables(m_rule_fmls[i].get(), true); + rules.push_back(r.get()); + // rules.push_back(m_rule_fmls[i].get()); + names.push_back(m_rule_names[i]); } } diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 998dd72e6..d1af762bb 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -410,6 +410,7 @@ namespace smt { atoms m_atoms; // set of theory atoms ptr_vector m_asserted_bounds; // set of asserted bounds unsigned m_asserted_qhead; + ptr_vector m_new_atoms; // new bound atoms that have yet to be internalized. svector m_nl_monomials; // non linear monomials svector m_nl_propagated; // non linear monomials that became linear v_dependency_manager m_dep_manager; // for tracking bounds during non-linear reasoning @@ -570,6 +571,22 @@ namespace smt { void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params); void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params); void mk_bound_axioms(atom * a); + void mk_bound_axiom(atom* a1, atom* a2); + void flush_bound_axioms(); + typename atoms::iterator next_sup(atom* a1, atom_kind kind, + typename atoms::iterator it, + typename atoms::iterator end, + bool& found_compatible); + typename atoms::iterator next_inf(atom* a1, atom_kind kind, + typename atoms::iterator it, + typename atoms::iterator end, + bool& found_compatible); + typename atoms::iterator first(atom_kind kind, + typename atoms::iterator it, + typename atoms::iterator end); + struct compare_atoms { + bool operator()(atom* a1, atom* a2) const { return a1->get_k() < a2->get_k(); } + }; virtual bool default_internalizer() const { return false; } virtual bool internalize_atom(app * n, bool gate_ctx); virtual bool internalize_term(app * term); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index fbc043048..ba4eefc36 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -348,13 +348,24 @@ namespace smt { context & ctx = get_context(); simplifier & s = ctx.get_simplifier(); expr_ref s_ante(m), s_conseq(m); + expr* s_conseq_n, * s_ante_n; + bool negated; proof_ref pr(m); + s(ante, s_ante, pr); + negated = m.is_not(s_ante, s_ante_n); + if (negated) s_ante = s_ante_n; ctx.internalize(s_ante, false); literal l_ante = ctx.get_literal(s_ante); + if (negated) l_ante.neg(); + s(conseq, s_conseq, pr); + negated = m.is_not(s_conseq, s_conseq_n); + if (negated) s_conseq = s_conseq_n; ctx.internalize(s_conseq, false); literal l_conseq = ctx.get_literal(s_conseq); + if (negated) l_conseq.neg(); + literal lits[2] = {l_ante, l_conseq}; ctx.mk_th_axiom(get_id(), 2, lits); if (ctx.relevancy()) { @@ -800,48 +811,244 @@ namespace smt { template void theory_arith::mk_bound_axioms(atom * a1) { theory_var v = a1->get_var(); - literal l1(a1->get_bool_var()); + atoms & occs = m_var_occs[v]; + if (!get_context().is_searching()) { + // + // NB. We make an assumption that user push calls propagation + // before internal scopes are pushed. This flushes all newly + // asserted atoms into the right context. + // + m_new_atoms.push_back(a1); + return; + } numeral const & k1(a1->get_k()); atom_kind kind1 = a1->get_atom_kind(); TRACE("mk_bound_axioms", tout << "making bound axioms for v" << v << " " << kind1 << " " << k1 << "\n";); - atoms & occs = m_var_occs[v]; typename atoms::iterator it = occs.begin(); typename atoms::iterator end = occs.end(); + + typename atoms::iterator lo_inf = end, lo_sup = end; + typename atoms::iterator hi_inf = end, hi_sup = end; for (; it != end; ++it) { - atom * a2 = *it; - literal l2(a2->get_bool_var()); - numeral const & k2 = a2->get_k(); - atom_kind kind2 = a2->get_atom_kind(); + atom * a2 = *it; + numeral const & k2(a2->get_k()); + atom_kind kind2 = a2->get_atom_kind(); SASSERT(k1 != k2 || kind1 != kind2); - SASSERT(a2->get_var() == v); - parameter coeffs[3] = { parameter(symbol("farkas")), parameter(rational(1)), parameter(rational(1)) }; - if (kind1 == A_LOWER) { - if (kind2 == A_LOWER) { - // x >= k1, x >= k2 - if (k1 >= k2) mk_clause(~l1, l2, 3, coeffs); - else mk_clause(~l2, l1, 3, coeffs); + if (kind2 == A_LOWER) { + if (k2 < k1) { + if (lo_inf == end || k2 > (*lo_inf)->get_k()) { + lo_inf = it; + } } - else { - // x >= k1, x <= k2 - if (k1 > k2) mk_clause(~l1, ~l2, 3, coeffs); - else mk_clause(l1, l2, 3, coeffs); + else if (lo_sup == end || k2 < (*lo_sup)->get_k()) { + lo_sup = it; } } - else { - if (kind2 == A_LOWER) { - // x <= k1, x >= k2 - if (k1 < k2) mk_clause(~l1, ~l2, 3, coeffs); - else mk_clause(l1, l2, 3, coeffs); + else if (k2 < k1) { + if (hi_inf == end || k2 > (*hi_inf)->get_k()) { + hi_inf = it; + } + } + else if (hi_sup == end || k2 < (*hi_sup)->get_k()) { + hi_sup = it; + } + } + if (lo_inf != end) mk_bound_axiom(a1, *lo_inf); + if (lo_sup != end) mk_bound_axiom(a1, *lo_sup); + if (hi_inf != end) mk_bound_axiom(a1, *hi_inf); + if (hi_sup != end) mk_bound_axiom(a1, *hi_sup); + } + + template + void theory_arith::mk_bound_axiom(atom* a1, atom* a2) { + theory_var v = a1->get_var(); + literal l1(a1->get_bool_var()); + literal l2(a2->get_bool_var()); + numeral const & k1(a1->get_k()); + numeral const & k2(a2->get_k()); + atom_kind kind1 = a1->get_atom_kind(); + atom_kind kind2 = a2->get_atom_kind(); + bool v_is_int = is_int(v); + SASSERT(v == a2->get_var()); + SASSERT(k1 != k2 || kind1 != kind2); + parameter coeffs[3] = { parameter(symbol("farkas")), + parameter(rational(1)), parameter(rational(1)) }; + + //std::cout << "v" << v << " " << ((kind1==A_LOWER)?"<= ":">= ") << k1 << "\t "; + //std::cout << "v" << v << " " << ((kind2==A_LOWER)?"<= ":">= ") << k2 << "\n"; + + if (kind1 == A_LOWER) { + if (kind2 == A_LOWER) { + if (k2 <= k1) { + mk_clause(~l1, l2, 3, coeffs); } else { - // x <= k1, x <= k2 - if (k1 < k2) mk_clause(~l1, l2, 3, coeffs); - else mk_clause(~l2, l1, 3, coeffs); + mk_clause(l1, ~l2, 3, coeffs); + } + } + else if (k1 <= k2) { + // k1 <= k2, k1 <= x or x <= k2 + mk_clause(l1, l2, 3, coeffs); + } + else { + // k1 > hi_inf, k1 <= x => ~(x <= hi_inf) + mk_clause(~l1, ~l2, 3, coeffs); + if (v_is_int && k1 == k2 + numeral(1)) { + // k1 <= x or x <= k1-1 + mk_clause(l1, l2, 3, coeffs); } } } + else if (kind2 == A_LOWER) { + if (k1 >= k2) { + // k1 >= lo_inf, k1 >= x or lo_inf <= x + mk_clause(l1, l2, 3, coeffs); + } + else { + // k1 < k2, k2 <= x => ~(x <= k1) + mk_clause(~l1, ~l2, 3, coeffs); + if (v_is_int && k1 == k2 - numeral(1)) { + // x <= k1 or k1+l <= x + mk_clause(l1, l2, 3, coeffs); + } + + } + } + else { + // kind1 == A_UPPER, kind2 == A_UPPER + if (k1 >= k2) { + // k1 >= k2, x <= k2 => x <= k1 + mk_clause(l1, ~l2, 3, coeffs); + } + else { + // k1 <= hi_sup , x <= k1 => x <= hi_sup + mk_clause(~l1, l2, 3, coeffs); + } + } } + template + void theory_arith::flush_bound_axioms() { + while (!m_new_atoms.empty()) { + ptr_vector atoms; + atoms.push_back(m_new_atoms.back()); + m_new_atoms.pop_back(); + theory_var v = atoms.back()->get_var(); + for (unsigned i = 0; i < m_new_atoms.size(); ++i) { + if (m_new_atoms[i]->get_var() == v) { + atoms.push_back(m_new_atoms[i]); + m_new_atoms[i] = m_new_atoms.back(); + m_new_atoms.pop_back(); + --i; + } + } + ptr_vector occs(m_var_occs[v]); + + std::sort(atoms.begin(), atoms.end(), compare_atoms()); + std::sort(occs.begin(), occs.end(), compare_atoms()); + + typename atoms::iterator begin1 = occs.begin(); + typename atoms::iterator begin2 = occs.begin(); + typename atoms::iterator end = occs.end(); + begin1 = first(A_LOWER, begin1, end); + begin2 = first(A_UPPER, begin2, end); + + typename atoms::iterator lo_inf = begin1, lo_sup = begin1; + typename atoms::iterator hi_inf = begin2, hi_sup = begin2; + typename atoms::iterator lo_inf1 = begin1, lo_sup1 = begin1; + typename atoms::iterator hi_inf1 = begin2, hi_sup1 = begin2; + bool flo_inf, fhi_inf, flo_sup, fhi_sup; + // std::cout << atoms.size() << "\n"; + ptr_addr_hashtable visited; + for (unsigned i = 0; i < atoms.size(); ++i) { + atom* a1 = atoms[i]; + lo_inf1 = next_inf(a1, A_LOWER, lo_inf, end, flo_inf); + hi_inf1 = next_inf(a1, A_UPPER, hi_inf, end, fhi_inf); + lo_sup1 = next_sup(a1, A_LOWER, lo_sup, end, flo_sup); + hi_sup1 = next_sup(a1, A_UPPER, hi_sup, end, fhi_sup); +// std::cout << "v" << a1->get_var() << ((a1->get_atom_kind()==A_LOWER)?" <= ":" >= ") << a1->get_k() << "\n"; + // std::cout << (lo_inf1 != end) << " " << (lo_sup1 != end) << " " << (hi_inf1 != end) << " " << (hi_sup1 != end) << "\n"; + if (lo_inf1 != end) lo_inf = lo_inf1; + if (lo_sup1 != end) lo_sup = lo_sup1; + if (hi_inf1 != end) hi_inf = hi_inf1; + if (hi_sup1 != end) hi_sup = hi_sup1; + if (!flo_inf) lo_inf = end; + if (!fhi_inf) hi_inf = end; + if (!flo_sup) lo_sup = end; + if (!fhi_sup) hi_sup = end; + visited.insert(a1); + if (lo_inf1 != end && lo_inf != end && !visited.contains(*lo_inf)) mk_bound_axiom(a1, *lo_inf); + if (lo_sup1 != end && lo_sup != end && !visited.contains(*lo_sup)) mk_bound_axiom(a1, *lo_sup); + if (hi_inf1 != end && hi_inf != end && !visited.contains(*hi_inf)) mk_bound_axiom(a1, *hi_inf); + if (hi_sup1 != end && hi_sup != end && !visited.contains(*hi_sup)) mk_bound_axiom(a1, *hi_sup); + } + } + } + + template + typename theory_arith::atoms::iterator + theory_arith::first( + atom_kind kind, + typename atoms::iterator it, + typename atoms::iterator end) { + for (; it != end; ++it) { + atom* a = *it; + if (a->get_atom_kind() == kind) return it; + } + return end; + } + + template + typename theory_arith::atoms::iterator + theory_arith::next_inf( + atom* a1, + atom_kind kind, + typename atoms::iterator it, + typename atoms::iterator end, + bool& found_compatible) { + numeral const & k1(a1->get_k()); + typename atoms::iterator result = end; + found_compatible = false; + for (; it != end; ++it) { + atom * a2 = *it; + if (a1 == a2) continue; + if (a2->get_atom_kind() != kind) continue; + numeral const & k2(a2->get_k()); + found_compatible = true; + if (k2 <= k1) { + result = it; + } + else { + break; + } + } + return result; + } + + template + typename theory_arith::atoms::iterator + theory_arith::next_sup( + atom* a1, + atom_kind kind, + typename atoms::iterator it, + typename atoms::iterator end, + bool& found_compatible) { + numeral const & k1(a1->get_k()); + found_compatible = false; + for (; it != end; ++it) { + atom * a2 = *it; + if (a1 == a2) continue; + if (a2->get_atom_kind() != kind) continue; + numeral const & k2(a2->get_k()); + found_compatible = true; + if (k1 < k2) { + return it; + } + } + return end; + } + + template bool theory_arith::internalize_atom(app * n, bool gate_ctx) { TRACE("arith_internalize", tout << "internalising atom:\n" << mk_pp(n, this->get_manager()) << "\n";); @@ -879,7 +1086,7 @@ namespace smt { bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); rational _k; - m_util.is_numeral(rhs, _k); + VERIFY(m_util.is_numeral(rhs, _k)); numeral k(_k); atom * a = alloc(atom, bv, v, k, kind); mk_bound_axioms(a); @@ -927,6 +1134,7 @@ namespace smt { template void theory_arith::assign_eh(bool_var v, bool is_true) { + TRACE("arith", tout << "v" << v << " " << is_true << "\n";); atom * a = get_bv2a(v); if (!a) return; SASSERT(get_context().get_assignment(a->get_bool_var()) != l_undef); @@ -1142,6 +1350,7 @@ namespace smt { CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); + flush_bound_axioms(); propagate_linear_monomials(); while (m_asserted_qhead < m_asserted_bounds.size()) { bound * b = m_asserted_bounds[m_asserted_qhead]; @@ -2421,6 +2630,8 @@ namespace smt { if (val == l_undef) continue; // TODO: check if the following line is a bottleneck + TRACE("arith", tout << "v" << a->get_bool_var() << " " << (val == l_true) << "\n";); + a->assign_eh(val == l_true, get_epsilon(a->get_var())); if (val != l_undef && a->get_bound_kind() == b->get_bound_kind()) { SASSERT((ctx.get_assignment(bv) == l_true) == a->is_true()); @@ -2916,6 +3127,7 @@ namespace smt { SASSERT(m_to_patch.empty()); m_to_check.reset(); m_in_to_check.reset(); + m_new_atoms.reset(); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); From 1636e35bf99443d4186f9a4b9c7f828b2aa9502d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 16 Sep 2014 20:11:44 -0700 Subject: [PATCH 423/509] fix scope accounting bug in deprecated solver mode Signed-off-by: Nikolaj Bjorner --- src/api/api_context.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 3349bed7c..b10621d43 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -343,22 +343,18 @@ namespace api { void context::push() { get_smt_kernel().push(); - if (!m_user_ref_count) { - m_ast_lim.push_back(m_ast_trail.size()); - m_replay_stack.push_back(0); - } + m_ast_lim.push_back(m_ast_trail.size()); + m_replay_stack.push_back(0); } void context::pop(unsigned num_scopes) { for (unsigned i = 0; i < num_scopes; ++i) { - if (!m_user_ref_count) { - unsigned sz = m_ast_lim.back(); - m_ast_lim.pop_back(); - dealloc(m_replay_stack.back()); - m_replay_stack.pop_back(); - while (m_ast_trail.size() > sz) { - m_ast_trail.pop_back(); - } + unsigned sz = m_ast_lim.back(); + m_ast_lim.pop_back(); + dealloc(m_replay_stack.back()); + m_replay_stack.pop_back(); + while (m_ast_trail.size() > sz) { + m_ast_trail.pop_back(); } } SASSERT(num_scopes <= get_smt_kernel().get_scope_level()); From a85f1784db95c5975c3484215407f985d19f61ae Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 16 Sep 2014 23:25:39 -0700 Subject: [PATCH 424/509] updated answer to binary interpolant Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index d46d40ab2..879b19597 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -7344,7 +7344,7 @@ def binary_interpolant(a,b,p=None,ctx=None): >>> x = Int('x') >>> print binary_interpolant(x<0,x>2) - x <= 2 + Not(x >= 0) """ f = And(Interp(a),b) return tree_interpolant(f,p,ctx)[0] From b95f5b0fea8f239d45ffaf88615c400339ca1101 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 17 Sep 2014 16:33:27 +0100 Subject: [PATCH 425/509] fix bug in the datalog compiler when using negation We now perform negation after filtering with interpreted constraints so that the table reflects relevant columns which were not being added by the negation Signed-off-by: Nuno Lopes --- src/muz/rel/dl_compiler.cpp | 84 ++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/muz/rel/dl_compiler.cpp b/src/muz/rel/dl_compiler.cpp index 276e7b836..2a2507d7f 100644 --- a/src/muz/rel/dl_compiler.cpp +++ b/src/muz/rel/dl_compiler.cpp @@ -589,49 +589,8 @@ namespace datalog { dealloc = true; } - //enforce negative predicates - unsigned ut_len=r->get_uninterpreted_tail_size(); - for(unsigned i=pt_len; iget_tail(i); - func_decl * neg_pred = neg_tail->get_decl(); - variable_intersection neg_intersection(m_context.get_manager()); - neg_intersection.populate(single_res_expr, neg_tail); - unsigned_vector t_cols(neg_intersection.size(), neg_intersection.get_cols1()); - unsigned_vector neg_cols(neg_intersection.size(), neg_intersection.get_cols2()); - - unsigned neg_len = neg_tail->get_num_args(); - for(unsigned i=0; iget_arg(i); - if(is_var(e)) { - continue; - } - SASSERT(is_app(e)); - relation_sort arg_sort; - m_context.get_rel_context()->get_rmanager().from_predicate(neg_pred, i, arg_sort); - reg_idx new_reg; - bool new_dealloc; - make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), new_reg, new_dealloc, acc); - - if (dealloc) - make_dealloc_non_void(filtered_res, acc); - dealloc = new_dealloc; - filtered_res = new_reg; // here filtered_res value gets changed !! - - t_cols.push_back(single_res_expr.size()); - neg_cols.push_back(i); - single_res_expr.push_back(e); - } - SASSERT(t_cols.size()==neg_cols.size()); - - reg_idx neg_reg = m_pred_regs.find(neg_pred); - if (!dealloc) - make_clone(filtered_res, filtered_res, acc); - acc.push_back(instruction::mk_filter_by_negation(filtered_res, neg_reg, t_cols.size(), - t_cols.c_ptr(), neg_cols.c_ptr())); - dealloc = true; - } - // enforce interpreted tail predicates + unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned ft_len = r->get_tail_size(); // full tail ptr_vector tail; for (unsigned tail_index = ut_len; tail_index < ft_len; ++tail_index) { @@ -738,6 +697,47 @@ namespace datalog { dealloc = true; } + //enforce negative predicates + for (unsigned i = pt_len; iget_tail(i); + func_decl * neg_pred = neg_tail->get_decl(); + variable_intersection neg_intersection(m_context.get_manager()); + neg_intersection.populate(single_res_expr, neg_tail); + unsigned_vector t_cols(neg_intersection.size(), neg_intersection.get_cols1()); + unsigned_vector neg_cols(neg_intersection.size(), neg_intersection.get_cols2()); + + unsigned neg_len = neg_tail->get_num_args(); + for (unsigned i = 0; iget_arg(i); + if (is_var(e)) { + continue; + } + SASSERT(is_app(e)); + relation_sort arg_sort; + m_context.get_rel_context()->get_rmanager().from_predicate(neg_pred, i, arg_sort); + reg_idx new_reg; + bool new_dealloc; + make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), new_reg, new_dealloc, acc); + + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; + filtered_res = new_reg; // here filtered_res value gets changed !! + + t_cols.push_back(single_res_expr.size()); + neg_cols.push_back(i); + single_res_expr.push_back(e); + } + SASSERT(t_cols.size() == neg_cols.size()); + + reg_idx neg_reg = m_pred_regs.find(neg_pred); + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); + acc.push_back(instruction::mk_filter_by_negation(filtered_res, neg_reg, t_cols.size(), + t_cols.c_ptr(), neg_cols.c_ptr())); + dealloc = true; + } + #if 0 // this version is potentially better for non-symbolic tables, // since it constraints each unbound column at a time (reducing the From 45bfcda16cd209f565b0dad6518b0179b2cb7057 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 17 Sep 2014 16:37:53 -0700 Subject: [PATCH 426/509] remove typename Signed-off-by: Nikolaj Bjorner --- src/smt/theory_arith_core.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index ba4eefc36..c743562da 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -874,9 +874,6 @@ namespace smt { parameter coeffs[3] = { parameter(symbol("farkas")), parameter(rational(1)), parameter(rational(1)) }; - //std::cout << "v" << v << " " << ((kind1==A_LOWER)?"<= ":">= ") << k1 << "\t "; - //std::cout << "v" << v << " " << ((kind2==A_LOWER)?"<= ":">= ") << k2 << "\n"; - if (kind1 == A_LOWER) { if (kind2 == A_LOWER) { if (k2 <= k1) { @@ -958,16 +955,13 @@ namespace smt { typename atoms::iterator lo_inf1 = begin1, lo_sup1 = begin1; typename atoms::iterator hi_inf1 = begin2, hi_sup1 = begin2; bool flo_inf, fhi_inf, flo_sup, fhi_sup; - // std::cout << atoms.size() << "\n"; - ptr_addr_hashtable visited; + ptr_addr_hashtable visited; for (unsigned i = 0; i < atoms.size(); ++i) { atom* a1 = atoms[i]; lo_inf1 = next_inf(a1, A_LOWER, lo_inf, end, flo_inf); hi_inf1 = next_inf(a1, A_UPPER, hi_inf, end, fhi_inf); lo_sup1 = next_sup(a1, A_LOWER, lo_sup, end, flo_sup); hi_sup1 = next_sup(a1, A_UPPER, hi_sup, end, fhi_sup); -// std::cout << "v" << a1->get_var() << ((a1->get_atom_kind()==A_LOWER)?" <= ":" >= ") << a1->get_k() << "\n"; - // std::cout << (lo_inf1 != end) << " " << (lo_sup1 != end) << " " << (hi_inf1 != end) << " " << (hi_sup1 != end) << "\n"; if (lo_inf1 != end) lo_inf = lo_inf1; if (lo_sup1 != end) lo_sup = lo_sup1; if (hi_inf1 != end) hi_inf = hi_inf1; From 61d67dc2dee3270919caf883128f3a655b18f5c9 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 18 Sep 2014 14:38:40 +0100 Subject: [PATCH 427/509] fix a few compiler warnings Signed-off-by: Nuno Lopes --- src/muz/rel/dl_vector_relation.h | 2 -- src/util/debug.h | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/muz/rel/dl_vector_relation.h b/src/muz/rel/dl_vector_relation.h index 114f4ca43..4d0917359 100644 --- a/src/muz/rel/dl_vector_relation.h +++ b/src/muz/rel/dl_vector_relation.h @@ -62,8 +62,6 @@ namespace datalog { dealloc(m_elems); } - virtual bool can_swap() const { return true; } - virtual void swap(relation_base& other) { vector_relation& o = dynamic_cast(other); if (&o == this) return; diff --git a/src/util/debug.h b/src/util/debug.h index 9e519982f..a36743f73 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -73,7 +73,7 @@ bool is_debug_enabled(const char * tag); UNREACHABLE(); \ } #else -#define VERIFY(_x_) (_x_) +#define VERIFY(_x_) (void)(_x_) #endif #define MAKE_NAME2(LINE) zofty_ ## LINE From 9949c7e31c8dae78dcf7dc44eabc8157981c0310 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 18 Sep 2014 17:09:22 +0100 Subject: [PATCH 428/509] fixed typos Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 5e100b527..411fdd1e1 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -455,7 +455,7 @@ def display_help(exit_code): print(" -v, --vsproj generate Visual Studio Project Files.") if IS_WINDOWS: print(" -n, --nodotnet do not generate Microsoft.Z3.dll make rules.") - print(" -j, --java generate Java bindinds.") + print(" -j, --java generate Java bindings.") print(" --staticlib build Z3 static library.") if not IS_WINDOWS: print(" -g, --gmp use GMP.") @@ -587,7 +587,7 @@ def set_z3py_dir(p): raise MKException("Python bindings directory '%s' does not exist" % full) Z3PY_SRC_DIR = full if VERBOSE: - print("Python bindinds directory was detected.") + print("Python bindings directory was detected.") _UNIQ_ID = 0 From d36cecc2f328cdd742c601cee6bc803dca1b2b14 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 19 Sep 2014 15:51:08 +0100 Subject: [PATCH 429/509] make use of count population intrinsincs on MSVC/gcc/clang Signed-off-by: Nuno Lopes --- src/util/util.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/util/util.h b/src/util/util.h index 0aa8f881d..58120b2f2 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -95,7 +95,12 @@ unsigned uint64_log2(uint64 v); COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); // Return the number of 1 bits in v. -inline unsigned get_num_1bits(unsigned v) { +static inline unsigned get_num_1bits(unsigned v) { +#ifdef _MSC_VER + return __popcnt(v); +#elif defined(__GNUC__) + return __builtin_popcount(v); +#else #ifdef Z3DEBUG unsigned c; unsigned v1 = v; @@ -108,15 +113,16 @@ inline unsigned get_num_1bits(unsigned v) { unsigned r = (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; SASSERT(c == r); return r; +#endif } // Remark: on gcc, the operators << and >> do not produce zero when the second argument >= 64. // So, I'm using the following two definitions to fix the problem -inline uint64 shift_right(uint64 x, uint64 y) { +static inline uint64 shift_right(uint64 x, uint64 y) { return y < 64ull ? (x >> y) : 0ull; } -inline uint64 shift_left(uint64 x, uint64 y) { +static inline uint64 shift_left(uint64 x, uint64 y) { return y < 64ull ? (x << y) : 0ull; } @@ -255,10 +261,6 @@ inline std::ostream & operator<<(std::ostream & out, std::pair const & p return out; } -#ifndef _WINDOWS -#define __forceinline inline -#endif - template bool has_duplicates(const IT & begin, const IT & end) { for (IT it1 = begin; it1 != end; ++it1) { From b243ac945f7e87fe669b9527d6f6c0e705cfe666 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 21 Sep 2014 15:20:43 +0100 Subject: [PATCH 430/509] hoprfully fix the build for MSVC 2010 Signed-off-by: Nuno Lopes --- src/util/util.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/util.h b/src/util/util.h index 58120b2f2..eb24ece13 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -24,6 +24,9 @@ Revision History: #include #include #include +#ifdef _MSC_VER +#include +#endif #ifndef SIZE_MAX #define SIZE_MAX std::numeric_limits::max() From dca3ce6b24f1a8752bf0a7252c2eaf1bb888d5ba Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 22 Sep 2014 01:16:16 -0700 Subject: [PATCH 431/509] update documentation on models associated with solver objects Signed-off-by: Nikolaj Bjorner --- src/api/z3_api.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index e1c42667a..b561b6bf1 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -6620,6 +6620,13 @@ END_MLAPI_EXCLUDE /** \brief Create a new (incremental) solver. + The function #Z3_solver_get_model retrieves a model if the + assertions is satisfiable (i.e., the result is \c + Z3_L_TRUE) and model construction is enabled. + The function #Z3_solver_get_model can also be used even + if the result is \c Z3_L_UNDEF, but the returned model + is not guaranteed to satisfy quantified assertions. + def_API('Z3_mk_simple_solver', SOLVER, (_in(CONTEXT),)) */ Z3_solver Z3_API Z3_mk_simple_solver(__in Z3_context c); @@ -6757,8 +6764,11 @@ END_MLAPI_EXCLUDE \brief Check whether the assertions in a given solver are consistent or not. The function #Z3_solver_get_model retrieves a model if the - assertions are not unsatisfiable (i.e., the result is not \c - Z3_L_FALSE) and model construction is enabled. + assertions is satisfiable (i.e., the result is \c + Z3_L_TRUE) and model construction is enabled. + Note that if the call returns Z3_L_UNDEF, Z3 does not + ensure that calls to #Z3_solver_get_model succeed and any models + produced in this case are not guaranteed to satisfy the assertions. The function #Z3_solver_get_proof retrieves a proof if proof generation was enabled when the context was created, and the From 4995ce1fdee47ffd61d4726c89ff908f468d6450 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 22 Sep 2014 22:22:26 -0700 Subject: [PATCH 432/509] disable unstable interpolation sample Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 879b19597..b6cceeb8e 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -7342,8 +7342,8 @@ def binary_interpolant(a,b,p=None,ctx=None): If parameters p are supplied, these are used in creating the solver that determines satisfiability. - >>> x = Int('x') - >>> print binary_interpolant(x<0,x>2) + x = Int('x') + print binary_interpolant(x<0,x>2) Not(x >= 0) """ f = And(Interp(a),b) From 9412890c6324a2df3c5c769fb0e28dddc0adc475 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 Sep 2014 12:58:55 -0700 Subject: [PATCH 433/509] trace reason for undef in arithmetic, enable model generation on THEORY incompleteness, but retain undef result Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 2 +- src/smt/theory_arith_core.h | 4 +++- src/smt/theory_arith_int.h | 4 +++- src/smt/theory_arith_nl.h | 6 ++++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 4f3c73ce6..02ee06985 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3945,7 +3945,7 @@ namespace smt { m_fingerprints.display(tout); ); failure fl = get_last_search_failure(); - if (fl == TIMEOUT || fl == MEMOUT || fl == CANCELED || fl == NUM_CONFLICTS || fl == THEORY) { + if (fl == TIMEOUT || fl == MEMOUT || fl == CANCELED || fl == NUM_CONFLICTS) { // don't generate model. return; } diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index c743562da..66d232227 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -1274,8 +1274,10 @@ namespace smt { } } while (m_final_check_idx != old_idx); - if (result == FC_DONE && m_found_unsupported_op) + if (result == FC_DONE && m_found_unsupported_op) { + TRACE("arith", tout << "Found unsupported operation\n";); result = FC_GIVEUP; + } return result; } diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index e0ecc087a..0a70e2645 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -1294,8 +1294,10 @@ namespace smt { } tout << "max: " << max << ", min: " << min << "\n";); - if (m_params.m_arith_ignore_int) + if (m_params.m_arith_ignore_int) { + TRACE("arith", tout << "Ignore int: give up\n";); return FC_GIVEUP; + } if (!gcd_test()) return FC_CONTINUE; diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index d02c9e540..6a7017a29 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -2314,13 +2314,15 @@ namespace smt { return FC_DONE; } - if (!m_params.m_nl_arith) + if (!m_params.m_nl_arith) { + TRACE("non_linear", tout << "Non-linear is not enabled\n";); return FC_GIVEUP; + } TRACE("process_non_linear", display(tout);); if (m_nl_rounds > m_params.m_nl_arith_rounds) { - TRACE("non_linear", tout << "GIVE UP non linear problem...\n";); + TRACE("non_linear", tout << "GIVEUP non linear problem...\n";); IF_VERBOSE(3, verbose_stream() << "Max. non linear arithmetic rounds. Increase threshold using NL_ARITH_ROUNDS=\n";); return FC_GIVEUP; } From e57e5328cebcd55065cb6bb9da68601fa5a00e8c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 Sep 2014 16:42:11 -0700 Subject: [PATCH 434/509] configuration update to SAT solver on creation time. Adding random_seed to sat parameters to enable command-line and module mode to work at the level of sat solver Signed-off-by: Nikolaj Bjorner --- src/sat/sat_config.cpp | 5 ++++- src/sat/sat_config.h | 1 + src/sat/sat_params.pyg | 1 + src/sat/sat_solver.cpp | 4 ++-- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index cc0f97ff0..fb81809b1 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -68,7 +68,10 @@ namespace sat { m_restart_factor = p.restart_factor(); m_random_freq = p.random_freq(); - + m_random_seed = p.random_seed(); + if (m_random_seed == 0) + m_random_seed = _p.get_uint("random_seed", 0); + m_burst_search = p.burst_search(); m_max_conflicts = p.max_conflicts(); diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index 90ebe968b..253786e11 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -53,6 +53,7 @@ namespace sat { unsigned m_restart_initial; double m_restart_factor; // for geometric case double m_random_freq; + unsigned m_random_seed; unsigned m_burst_search; unsigned m_max_conflicts; diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 71579b86a..de0283897 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -9,6 +9,7 @@ def_module_params('sat', ('restart.initial', UINT, 100, 'initial restart (number of conflicts)'), ('restart.factor', DOUBLE, 1.5, 'restart increment factor for geometric strategy'), ('random_freq', DOUBLE, 0.01, 'frequency of random case splits'), + ('random_seed', UINT, 0, 'random seed'), ('burst_search', UINT, 100, 'number of conflicts before first global simplification'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts'), ('gc', SYMBOL, 'glue_psm', 'garbage collection strategy: psm, glue, glue_psm, dyn_psm'), diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index f4bf5393c..e7dee83ae 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -46,7 +46,7 @@ namespace sat { m_qhead(0), m_scope_lvl(0), m_params(p) { - m_config.updt_params(p); + updt_params(p); } solver::~solver() { @@ -1972,7 +1972,7 @@ namespace sat { m_asymm_branch.updt_params(p); m_probing.updt_params(p); m_scc.updt_params(p); - m_rand.set_seed(p.get_uint("random_seed", 0)); + m_rand.set_seed(m_config.m_random_seed); } void solver::collect_param_descrs(param_descrs & d) { From 5f59dd1644bfe64a02c24b4fc67dfd8f7c09bba6 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 28 Sep 2014 11:37:11 +0100 Subject: [PATCH 435/509] revert usage of popcnt is MSVC Signed-off-by: Nuno Lopes --- src/util/util.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/util/util.h b/src/util/util.h index eb24ece13..cbc19b759 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -24,9 +24,6 @@ Revision History: #include #include #include -#ifdef _MSC_VER -#include -#endif #ifndef SIZE_MAX #define SIZE_MAX std::numeric_limits::max() @@ -99,9 +96,7 @@ COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); // Return the number of 1 bits in v. static inline unsigned get_num_1bits(unsigned v) { -#ifdef _MSC_VER - return __popcnt(v); -#elif defined(__GNUC__) +#ifdef __GNUC__ return __builtin_popcount(v); #else #ifdef Z3DEBUG From 97a5e6d326cbfad117bd40a0730a40ffcb4c2d21 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 28 Sep 2014 12:21:56 +0100 Subject: [PATCH 436/509] assorted compiler warnings fixes Signed-off-by: Nuno Lopes --- src/math/polynomial/polynomial.cpp | 6 ++---- src/smt/theory_diff_logic_def.h | 7 ++----- src/util/bit_vector.cpp | 3 +-- src/util/mpf.cpp | 16 +++++++++++----- src/util/mpff.cpp | 3 +-- src/util/mpfx.cpp | 3 +-- 6 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index f5d96e740..3f1883749 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -4117,7 +4117,6 @@ namespace polynomial { polynomial_ref H(m_wrapper), C(m_wrapper); polynomial_ref lc_H(m_wrapper); unsigned min_deg_q = UINT_MAX; - var next_x = vars[idx+1]; unsigned counter = 0; for (;; counter++) { @@ -4137,7 +4136,7 @@ namespace polynomial { var q_var = max_var(q); unsigned deg_q = q_var == null_var ? 0 : degree(q, q_var); TRACE("mgcd_detail", tout << "counter: " << counter << "\nidx: " << idx << "\nq: " << q << "\ndeg_q: " << deg_q << "\nmin_deg_q: " << - min_deg_q << "\nnext_x: x" << next_x << "\nmax_var(q): " << q_var << "\n";); + min_deg_q << "\nnext_x: x" << vars[idx+1] << "\nmax_var(q): " << q_var << "\n";); if (deg_q < min_deg_q) { TRACE("mgcd_detail", tout << "reseting...\n";); counter = 0; @@ -5113,10 +5112,9 @@ namespace polynomial { monomial const * m_r = R.m(max_R); numeral const & a_r = R.a(max_R); monomial * m_r_q = 0; - bool q_div_r = div(m_r, m_q, m_r_q); + VERIFY(div(m_r, m_q, m_r_q)); TRACE("polynomial", tout << "m_r: "; m_r->display(tout); tout << "\nm_q: "; m_q->display(tout); tout << "\n"; if (m_r_q) { tout << "m_r_q: "; m_r_q->display(tout); tout << "\n"; }); - SASSERT(q_div_r); m_r_q_ref = m_r_q; m_manager.div(a_r, a_q, a_r_q); C.add(a_r_q, m_r_q); // C <- C + (a_r/a_q)*(m_r/m_q) diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 390803957..a2e1ece40 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -983,11 +983,8 @@ template void theory_diff_logic::get_eq_antecedents( theory_var v1, theory_var v2, unsigned timestamp, conflict_resolution & cr) { imp_functor functor(cr); - bool r; - r = m_graph.find_shortest_zero_edge_path(v1, v2, timestamp, functor); - SASSERT(r); - r = m_graph.find_shortest_zero_edge_path(v2, v1, timestamp, functor); - SASSERT(r); + VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, timestamp, functor)); + VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, timestamp, functor)); } template diff --git a/src/util/bit_vector.cpp b/src/util/bit_vector.cpp index 6885b93e8..1490b9628 100644 --- a/src/util/bit_vector.cpp +++ b/src/util/bit_vector.cpp @@ -138,9 +138,8 @@ bool bit_vector::operator==(bit_vector const & source) const { bit_vector & bit_vector::operator|=(bit_vector const & source) { if (size() < source.size()) resize(source.size(), false); - unsigned n1 = num_words(); unsigned n2 = source.num_words(); - SASSERT(n2 <= n1); + SASSERT(n2 <= num_words()); unsigned bit_rest = source.m_num_bits % 32; if (bit_rest == 0) { unsigned i = 0; diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 8cb6ed4fc..f5785c072 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -120,7 +120,8 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, double value) { // double === mpf(11, 53) COMPILE_TIME_ASSERT(sizeof(double) == 8); - uint64 raw = *reinterpret_cast(&value); + uint64 raw; + memcpy(&raw, &value, sizeof(double)); bool sign = (raw >> 63) != 0; int64 e = ((raw & 0x7FF0000000000000ull) >> 52) - 1023; uint64 s = raw & 0x000FFFFFFFFFFFFFull; @@ -155,7 +156,8 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, float value) { // single === mpf(8, 24) COMPILE_TIME_ASSERT(sizeof(float) == 4); - unsigned int raw = *reinterpret_cast(&value); + unsigned int raw; + memcpy(&raw, &value, sizeof(float)); bool sign = (raw >> 31) != 0; signed int e = ((raw & 0x7F800000) >> 23) - 127; unsigned int s = raw & 0x007FFFFF; @@ -1288,7 +1290,9 @@ double mpf_manager::to_double(mpf const & x) { if (x.sign) raw = raw | 0x8000000000000000ull; - return *reinterpret_cast(&raw); + double ret; + memcpy(&ret, &raw, sizeof(double)); + return ret; } float mpf_manager::to_float(mpf const & x) { @@ -1318,7 +1322,9 @@ float mpf_manager::to_float(mpf const & x) { if (x.sign) raw = raw | 0x80000000; - return *reinterpret_cast(&raw); + float ret; + memcpy(&ret, &raw, sizeof(float)); + return ret; } bool mpf_manager::is_nan(mpf const & x) { @@ -1679,7 +1685,7 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { TRACE("mpf_dbg", tout << "OVF2 = " << OVF2 << std::endl;); TRACE("mpf_dbg", tout << "o_has_max_exp = " << o_has_max_exp << std::endl;); - if (!OVFen && SIGovf && o_has_max_exp) + if (!OVFen && OVF2) mk_round_inf(rm, o); else { const mpz & p = m_powers2(o.sbits-1); diff --git a/src/util/mpff.cpp b/src/util/mpff.cpp index 892a7fe24..7678a051e 100644 --- a/src/util/mpff.cpp +++ b/src/util/mpff.cpp @@ -43,8 +43,7 @@ mpff_manager::mpff_manager(unsigned prec, unsigned initial_capacity) { for (unsigned i = 0; i < MPFF_NUM_BUFFERS; i++) m_buffers[i].resize(2 * prec, 0); // Reserve space for zero - unsigned zero_sig_idx = m_id_gen.mk(); - SASSERT(zero_sig_idx == 0); + VERIFY(m_id_gen.mk() == 0); set(m_one, 1); } diff --git a/src/util/mpfx.cpp b/src/util/mpfx.cpp index cf6f8338f..c0b3a1936 100644 --- a/src/util/mpfx.cpp +++ b/src/util/mpfx.cpp @@ -38,8 +38,7 @@ mpfx_manager::mpfx_manager(unsigned int_sz, unsigned frac_sz, unsigned initial_c m_buffer0.resize(2*m_total_sz, 0); m_buffer1.resize(2*m_total_sz, 0); m_buffer2.resize(2*m_total_sz, 0); - unsigned zero_sig_idx = m_id_gen.mk(); - SASSERT(zero_sig_idx == 0); + VERIFY(m_id_gen.mk() == 0); set(m_one, 1); } From 4c71e9479d00048a9a6f8666499d7e1f4b616c73 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 30 Sep 2014 11:21:34 -0700 Subject: [PATCH 437/509] optimizing array final check --- src/smt/theory_array_base.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index 3f96a1d62..6325a2a99 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -376,8 +376,10 @@ namespace smt { enode_vector::const_iterator end = r->end_parents(); for (; it != end; ++it) { enode * parent = *it; +#if 0 if (!ctx.is_relevant(parent)) continue; +#endif unsigned num_args = parent->get_num_args(); if (is_store(parent)) { SET_ARRAY(parent->get_arg(0)); @@ -399,6 +401,7 @@ namespace smt { return false; } +#if 0 void theory_array_base::collect_shared_vars(sbuffer & result) { TRACE("array_shared", tout << "collecting shared vars...\n";); context & ctx = get_context(); @@ -420,6 +423,29 @@ namespace smt { } unmark_enodes(to_unmark.size(), to_unmark.c_ptr()); } +#else + void theory_array_base::collect_shared_vars(sbuffer & result) { + TRACE("array_shared", tout << "collecting shared vars...\n";); + context & ctx = get_context(); + ptr_buffer to_unmark; + unsigned num_vars = get_num_vars(); + for (unsigned i = 0; i < num_vars; i++) { + enode * n = get_enode(i); + if (ctx.is_relevant(n)) { + enode * r = n->get_root(); + if (!r->is_marked() && is_array_sort(r) && ctx.is_shared(r)) { + TRACE("array_shared", tout << "new shared var: #" << r->get_owner_id() << "\n";); + theory_var r_th_var = r->get_th_var(get_id()); + SASSERT(r_th_var != null_theory_var); + result.push_back(r_th_var); + } + r->set_mark(); + to_unmark.push_back(r); + } + } + unmark_enodes(to_unmark.size(), to_unmark.c_ptr()); + } +#endif /** \brief Create interface variables for shared array variables. From 47635325014f2951d203897eae4d8120787a283d Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 30 Sep 2014 11:25:47 -0700 Subject: [PATCH 438/509] adding compile-time option to replace arrays with maps in smt (define SPARSE_MAP) --- src/smt/smt_context.h | 7 ++++++- src/smt/smt_enode.cpp | 6 +++--- src/smt/smt_enode.h | 29 ++++++++++++++++++++++++++--- src/util/map.h | 2 +- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 7940b17be..5abdfa5f9 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -54,7 +54,11 @@ Revision History: // the case that each context only references a few expressions. // Using a map instead of a vector for the literals can compress space // consumption. +#ifdef SPARSE_MAP +#define USE_BOOL_VAR_VECTOR 0 +#else #define USE_BOOL_VAR_VECTOR 1 +#endif namespace smt { @@ -69,6 +73,7 @@ namespace smt { std::string last_failure_as_string() const; void set_progress_callback(progress_callback *callback); + protected: ast_manager & m_manager; smt_params & m_fparams; @@ -106,7 +111,7 @@ namespace smt { // ----------------------------------- enode * m_true_enode; enode * m_false_enode; - ptr_vector m_app2enode; // app -> enode + app2enode_t m_app2enode; // app -> enode ptr_vector m_enodes; plugin_manager m_theories; // mapping from theory_id -> theory ptr_vector m_theory_set; // set of theories for fast traversal diff --git a/src/smt/smt_enode.cpp b/src/smt/smt_enode.cpp index 98b8eaf8b..451577357 100644 --- a/src/smt/smt_enode.cpp +++ b/src/smt/smt_enode.cpp @@ -24,7 +24,7 @@ namespace smt { /** \brief Initialize an enode in the given memory position. */ - enode * enode::init(ast_manager & m, void * mem, ptr_vector const & app2enode, app * owner, + enode * enode::init(ast_manager & m, void * mem, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent) { SASSERT(m.is_bool(owner) || !merge_tf); @@ -60,7 +60,7 @@ namespace smt { return n; } - enode * enode::mk(ast_manager & m, region & r, ptr_vector const & app2enode, app * owner, + enode * enode::mk(ast_manager & m, region & r, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent) { SASSERT(m.is_bool(owner) || !merge_tf); @@ -69,7 +69,7 @@ namespace smt { return init(m, mem, app2enode, owner, generation, suppress_args, merge_tf, iscope_lvl, cgc_enabled, update_children_parent); } - enode * enode::mk_dummy(ast_manager & m, ptr_vector const & app2enode, app * owner) { + enode * enode::mk_dummy(ast_manager & m, app2enode_t const & app2enode, app * owner) { unsigned sz = get_enode_size(owner->get_num_args()); void * mem = alloc_svect(char, sz); return init(m, mem, app2enode, owner, 0, false, false, 0, true, false); diff --git a/src/smt/smt_enode.h b/src/smt/smt_enode.h index 505dea0ee..ca75028c3 100644 --- a/src/smt/smt_enode.h +++ b/src/smt/smt_enode.h @@ -38,6 +38,29 @@ namespace smt { } }; + /** \ brief Use sparse maps in SMT solver. + + Define this to use hash maps rather than vectors over ast + nodes. This is useful in the case there are many solvers, each + referencing few nodes from a large ast manager. There is some + unknown performance penalty for this. */ + + // #define SPARSE_MAP + +#ifndef SPARSE_MAP + typedef ptr_vector app2enode_t; // app -> enode +#else + class app2enode_t : public u_map { + public: + void setx(unsigned x, enode *val, enode *def){ + if(val == 0) + erase(x); + else + insert(x,val); + } + }; +#endif + class tmp_enode; /** @@ -115,7 +138,7 @@ namespace smt { friend class tmp_enode; - static enode * init(ast_manager & m, void * mem, ptr_vector const & app2enode, app * owner, + static enode * init(ast_manager & m, void * mem, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent); public: @@ -124,11 +147,11 @@ namespace smt { return sizeof(enode) + num_args * sizeof(enode*); } - static enode * mk(ast_manager & m, region & r, ptr_vector const & app2enode, app * owner, + static enode * mk(ast_manager & m, region & r, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent); - static enode * mk_dummy(ast_manager & m, ptr_vector const & app2enode, app * owner); + static enode * mk_dummy(ast_manager & m, app2enode_t const & app2enode, app * owner); static void del_dummy(enode * n) { dealloc_svect(reinterpret_cast(n)); } diff --git a/src/util/map.h b/src/util/map.h index 3e8e10891..d659b89c0 100644 --- a/src/util/map.h +++ b/src/util/map.h @@ -131,7 +131,7 @@ public: value const& get(key const& k, value const& default_value) const { entry* e = find_core(k); if (e) { - return e->m_value; + return e->get_data().m_value; } else { return default_value; From 301cb51bbb81a3fd8db81e9baa8850dc2d6bc117 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 30 Sep 2014 12:42:30 -0700 Subject: [PATCH 439/509] added restarts options to duality (plus some other disabled features) --- src/duality/duality.h | 6 + src/duality/duality_rpfp.cpp | 67 ++++ src/duality/duality_solver.cpp | 391 +++++++++++++++++++++-- src/muz/base/fixedpoint_params.pyg | 1 + src/muz/duality/duality_dl_interface.cpp | 1 + 5 files changed, 446 insertions(+), 20 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 16576dd65..edd89d78f 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -104,6 +104,8 @@ namespace Duality { FuncDecl RenumberPred(const FuncDecl &f, int n); + FuncDecl NumberPred(const FuncDecl &f, int n); + Term ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming); @@ -903,6 +905,10 @@ protected: int CumulativeDecisions(); + void GreedyReduceNodes(std::vector &nodes); + + check_result CheckWithConstrainedNodes(std::vector &posnodes,std::vector &negnodes); + solver &slvr(){ return *ls->slvr; } diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 8c88dda72..f2824c9b0 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2816,6 +2816,62 @@ namespace Duality { } } + void foobar(){ + } + + void RPFP::GreedyReduceNodes(std::vector &nodes){ + std::vector lits; + for(unsigned i = 0; i < nodes.size(); i++){ + Term b; std::vector v; + RedVars(nodes[i], b, v); + lits.push_back(!b); + expr bv = dualModel.eval(b); + if(eq(bv,ctx.bool_val(true))){ + check_result res = slvr_check(lits.size(),&lits[0]); + if(res == unsat) + lits.pop_back(); + else + foobar(); + } + } + } + + check_result RPFP::CheckWithConstrainedNodes(std::vector &posnodes,std::vector &negnodes){ + timer_start("Check"); + std::vector lits; + for(unsigned i = 0; i < posnodes.size(); i++){ + Term b; std::vector v; + RedVars(posnodes[i], b, v); + lits.push_back(b); + } + for(unsigned i = 0; i < negnodes.size(); i++){ + Term b; std::vector v; + RedVars(negnodes[i], b, v); + lits.push_back(!b); + } + check_result res = slvr_check(lits.size(),&lits[0]); + if(res == unsat && posnodes.size()){ + lits.resize(posnodes.size()); + res = slvr_check(lits.size(),&lits[0]); + } + dualModel = slvr().get_model(); +#if 0 + if(!dualModel.null()){ + std::cout << "posnodes called:\n"; + for(unsigned i = 0; i < posnodes.size(); i++) + if(!Empty(posnodes[i])) + std::cout << posnodes[i]->Name.name() << "\n"; + std::cout << "negnodes called:\n"; + for(unsigned i = 0; i < negnodes.size(); i++) + if(!Empty(negnodes[i])) + std::cout << negnodes[i]->Name.name() << "\n"; + } +#endif + timer_stop("Check"); + return res; + } + + void RPFP_caching::FilterCore(std::vector &core, std::vector &full_core){ hash_set core_set; std::copy(full_core.begin(),full_core.end(),std::inserter(core_set,core_set.begin())); @@ -3333,6 +3389,17 @@ namespace Duality { return ctx.function(name.c_str(), arity, &domain[0], f.range()); } + Z3User::FuncDecl Z3User::NumberPred(const FuncDecl &f, int n) + { + std::string name = f.name().str(); + name = name + "_" + string_of_int(n); + int arity = f.arity(); + std::vector domain; + for(int i = 0; i < arity; i++) + domain.push_back(f.domain(i)); + return ctx.function(name.c_str(), arity, &domain[0], f.range()); + } + // Scan the clause body for occurrences of the predicate unknowns RPFP::Term RPFP::ScanBody(hash_map &memo, diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index f54e00693..49e591be2 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -255,6 +255,13 @@ namespace Duality { /** Called when done expanding a tree */ virtual void Done() {} + + /** Ask whether a node should be used/unused in the tree. Returns, + 1 if yes, -1 if no, and 0 if don't care. */ + + virtual int UseNode(Node *node){ + return 0; + } }; /** The Proposer class proposes conjectures eagerly. These can come @@ -302,6 +309,7 @@ namespace Duality { hash_set overapproxes; std::vector proposers; std::string ConjectureFile; + bool stratified_inlining_done; #ifdef BOUNDED struct Counter { @@ -314,6 +322,13 @@ namespace Duality { /** Solve the problem. */ virtual bool Solve(){ + PreSolve(); + bool res = SolveMain(); // does the actual work + PostSolve(); + return res; + } + + void PreSolve(){ reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); conj_reporter = ConjectureFile.empty() ? 0 : CreateConjectureFileReporter(rpfp,ConjectureFile); #ifndef LOCALIZE_CONJECTURES @@ -342,9 +357,10 @@ namespace Duality { return false; #endif StratifiedLeafCount = -1; - timer_start("SolveMain"); - bool res = SolveMain(); // does the actual work - timer_stop("SolveMain"); + stratified_inlining_done = false; + } + + void PostSolve(){ // print_profile(std::cout); delete indset; delete heuristic; @@ -354,7 +370,16 @@ namespace Duality { delete conj_reporter; for(unsigned i = 0; i < proposers.size(); i++) delete proposers[i]; - return res; + } + + bool RecheckBounds(){ + for(unsigned i = 0; i < unwinding->nodes.size(); i++){ + Node *node = unwinding->nodes[i]; + node->Bound = node->map->Bound; + if(!SatisfyUpperBound(node)) + return false; + } + return true; } void CreateInitialUnwinding(){ @@ -417,6 +442,7 @@ namespace Duality { bool StratifiedInlining; // Do stratified inlining as preprocessing step int RecursionBound; // Recursion bound for bounded verification bool BatchExpand; + bool EnableRestarts; bool SetBoolOption(bool &opt, const std::string &value){ if(value == "0") { @@ -464,6 +490,9 @@ namespace Duality { if(option == "conjecture_file"){ ConjectureFile = value; } + if(option == "enable_restarts"){ + return SetBoolOption(EnableRestarts,value); + } return false; } @@ -901,6 +930,9 @@ namespace Duality { */ bool DoStratifiedInlining(){ + if(stratified_inlining_done) + return true; + stratified_inlining_done = true; DoTopoSort(); int depth = 1; // TODO: make this an option std::vector > unfolding_levels(depth+1); @@ -1043,10 +1075,17 @@ namespace Duality { h->SetOldNode(node); } + bool SolveMain(){ + timer_start("SolveMain"); + bool res = SolveMainInt(); // does the actual work + timer_stop("SolveMain"); + return res; + } + /** This does the actual solving work. We try to generate candidates for extension. If we succed, we extend the unwinding. If we fail, we have a solution. */ - bool SolveMain(){ + bool SolveMainInt(){ if(StratifiedInlining && !DoStratifiedInlining()) return false; #ifdef BOUNDED @@ -1776,6 +1815,7 @@ namespace Duality { } timer_stop("Propagate"); } + /** This class represents a derivation tree. */ class DerivationTree { @@ -2127,6 +2167,8 @@ namespace Duality { hash_map updates; + int restart_interval; + DerivationTreeSlow(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) : DerivationTree(_duality, rpfp, _reporter, _heuristic, _full_expand) { stack.push_back(stack_entry()); @@ -2135,6 +2177,7 @@ namespace Duality { struct DoRestart {}; virtual bool Build(){ + restart_interval = 3; while (true) { try { return BuildMain(); @@ -2145,15 +2188,47 @@ namespace Duality { while(stack.size() > 1) PopLevel(); reporter->Message("restarted"); + restart_interval += 1; } } } + + // When we check, try to use the same children that were used in the + // previous counterexample. + check_result Check(){ +#if 0 + std::vector posnodes, negnodes; + std::vector &expansions = stack.back().expansions; + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + std::vector &chs = node->Outgoing->Children; + for(unsigned j = 0; j < chs.size(); j++){ + Node *ch = chs[j]; + int use = heuristic->UseNode(ch); + if(use == 1) + posnodes.push_back(ch); + else if (use == -1) + negnodes.push_back(ch); + } + } + if(!(posnodes.empty() && negnodes.empty())){ + check_result res = tree->CheckWithConstrainedNodes(posnodes,negnodes); + if(res != unsat){ + reporter->Message("matched previous counterexample"); + return res; + } + } +#endif + return tree->Check(top); + } + bool BuildMain(){ stack.back().level = tree->slvr().get_scope_level(); bool was_sat = true; int update_failures = 0; + int total_updates = 0; while (true) { @@ -2165,7 +2240,7 @@ namespace Duality { reporter->Depth(stack.size()); // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop - check_result foo = tree->Check(top); + check_result foo = Check(); res = foo == unsat ? l_false : l_true; if (res == l_false) { @@ -2193,11 +2268,18 @@ namespace Duality { throw DoRestart(); } #endif - if(RecordUpdate(node)) + if(RecordUpdate(node)){ update_count++; + total_updates++; + } else heuristic->Update(node->map); // make it less likely to expand this node in future } +#if 1 + if(duality->EnableRestarts) + if(total_updates >= restart_interval) + throw DoRestart(); +#endif if(update_count == 0){ if(was_sat){ update_failures++; @@ -2243,6 +2325,10 @@ namespace Duality { tree->Push(); std::vector &expansions = stack.back().expansions; #ifndef NO_DECISIONS +#if 0 + if(expansions.size() > 0) + tree->GreedyReduceNodes(expansions[0]->Outgoing->Children); // try to reduce number of children +#endif for(unsigned i = 0; i < expansions.size(); i++){ tree->FixCurrentState(expansions[i]->Outgoing); } @@ -2262,15 +2348,42 @@ namespace Duality { if(ExpandSomeNodes(false,expand_max)) continue; tree->Pop(1); + node_order.clear(); while(stack.size() > 1){ tree->Pop(1); + std::vector &expansions = stack.back().expansions; + for(unsigned i = 0; i < expansions.size(); i++) + node_order.push_back(expansions[i]); stack.pop_back(); } +#if 0 + Reduce(); +#endif return true; } } } + std::vector node_order; + + void Reduce(){ + tree->Push(); + // tree->AssertNode(top); // assert the negation of the top-level spec + for(int i = node_order.size()-1; i >= 0; --i){ + Edge *edge = node_order[i]->Outgoing; + if(edge){ + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *ch = edge->Children[j]; + if(!ch->Outgoing) + ch->Annotation.SetEmpty(); + } + tree->AssertEdge(edge,0,true); + } + } + tree->GreedyReduceNodes(node_order); // try to reduce the counterexample size + tree->Pop(1); + } + void PopLevel(){ std::vector &expansions = stack.back().expansions; tree->Pop(1); @@ -2869,17 +2982,7 @@ namespace Duality { return name; } - virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ - if(!high_priority || !old_cex.get_tree()){ - Heuristic::ChooseExpand(choices,best,false); - return; - } - // first, try to match the derivatino tree nodes to the old cex - std::set matched, unmatched; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it); - if(cex_map.empty()) - cex_map[node] = old_cex.get_root(); // match the root nodes + Node *MatchNode(Node *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! if(cex_map.find(parent) == cex_map.end()) @@ -2902,7 +3005,30 @@ namespace Duality { cex_map[chs[i]] = 0; } matching_done: - Node *old_node = cex_map[node]; + return cex_map[node]; + } + + int UseNode(Node *node){ + if (!old_cex.get_tree()) + return 0; + Node *old_node = MatchNode(node); + if(!old_node) + return 0; + return old_cex.get_tree()->Empty(old_node) ? -1 : 1; + } + + virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ + if(cex_map.empty()) + cex_map[*(choices.begin())] = old_cex.get_root(); // match the root nodes + if(!high_priority || !old_cex.get_tree()){ + Heuristic::ChooseExpand(choices,best,false); + return; + } + // first, try to match the derivatino tree nodes to the old cex + std::set matched, unmatched; + for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ + Node *node = (*it); + Node *old_node = MatchNode(node); if(!old_node) unmatched.insert(node); else if(old_cex.get_tree()->Empty(old_node)) @@ -3177,8 +3303,233 @@ namespace Duality { }; + + class DualityDepthBounded : public Solver { + + Duality *duality; + context &ctx; // Z3 context + solver &slvr; // Z3 solver + + public: + DualityDepthBounded(RPFP *_rpfp) : + ctx(_rpfp->ctx), + slvr(_rpfp->slvr()){ + rpfp = _rpfp; + DepthBoundRPFP(); + duality = alloc(Duality,drpfp); + } + + ~DualityDepthBounded(){ + dealloc(duality); + delete drpfp; + } + + bool Solve(){ + int depth_bound = 10; + bool res; + SetMaxDepthRPFP(depth_bound); + duality->PreSolve(); + while(true){ + res = duality->SolveMain(); + if(!res || GetSolution()) + break; + depth_bound++; + SetMaxDepthRPFP(depth_bound); + res = duality->RecheckBounds(); + if(!res) + break; + } + duality->PostSolve(); + if(!res) + ConvertCex(); + return res; + } + + Counterexample &GetCounterexample(){ + return cex; + } + + bool SetOption(const std::string &option, const std::string &value){ + return duality->SetOption(option,value); + } + + virtual void LearnFrom(Solver *old_solver){ + DualityDepthBounded *old = dynamic_cast(old_solver); + if(old){ + duality->LearnFrom(old->duality); + } + } + + bool IsResultRecursionBounded(){ + return duality->IsResultRecursionBounded(); + } + + void Cancel(){ + duality->Cancel(); + } + + typedef RPFP::Node Node; + typedef RPFP::Edge Edge; + RPFP *rpfp, *drpfp; + hash_map db_map, db_rev_map; + hash_map db_edge_rev_map; + std::vector db_saved_bounds; + Counterexample cex; + + expr AddParamToRels(hash_map &memo, hash_map &rmap, const expr &p, const expr &t) { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + expr &res = bar.first->second; + if(!bar.second) return res; + + if (t.is_app()) + { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(AddParamToRels(memo, rmap, p, t.arg(i))); + hash_map::iterator rit = rmap.find(f); + if(rit != rmap.end()){ + args.push_back(p); + res = (rit->second)(args); + res = ctx.make(And,res,ctx.make(Geq,p,ctx.int_val(0))); + } + else + res = f(args); + } + else if (t.is_quantifier()) + { + expr body = AddParamToRels(memo, rmap, p, t.body()); + res = clone_quantifier(t, body); + } + else res = t; + return res; + } + + + void DepthBoundRPFP(){ + drpfp = new RPFP(rpfp->ls); + expr dvar = ctx.int_const("@depth"); + expr dmax = ctx.int_const("@depth_max"); + for(unsigned i = 0; i < rpfp->nodes.size(); i++){ + Node *node = rpfp->nodes[i]; + std::vector arg_sorts; + const std::vector ¶ms = node->Annotation.IndParams; + for(unsigned j = 0; j < params.size(); j++) + arg_sorts.push_back(params[j].get_sort()); + arg_sorts.push_back(ctx.int_sort()); + std::string new_name = std::string("@db@") + node->Name.name().str(); + func_decl f = ctx.function(new_name.c_str(),arg_sorts.size(), &arg_sorts[0],ctx.bool_sort()); + std::vector args = params; + args.push_back(dvar); + expr pat = f(args); + Node *dnode = drpfp->CreateNode(pat); + db_map[node] = dnode; + db_rev_map[dnode] = node; + expr bound_fmla = node->Bound.Formula; + if(!eq(bound_fmla,ctx.bool_val(true))){ + bound_fmla = implies(dvar == dmax,bound_fmla); + dnode->Bound.Formula = bound_fmla; + } + db_saved_bounds.push_back(bound_fmla); + // dnode->Annotation.Formula = ctx.make(And,node->Annotation.Formula,ctx.make(Geq,dvar,ctx.int_val(0))); + } + for(unsigned i = 0; i < rpfp->edges.size(); i++){ + Edge *edge = rpfp->edges[i]; + std::vector new_children; + std::vector new_relparams; + hash_map rmap; + for(unsigned j = 0; j < edge->Children.size(); j++){ + Node *ch = edge->Children[j]; + Node *nch = db_map[ch]; + func_decl f = nch->Name; + func_decl sf = drpfp->NumberPred(f,j); + new_children.push_back(nch); + new_relparams.push_back(sf); + rmap[edge->F.RelParams[j]] = sf; + } + std::vector new_indparams = edge->F.IndParams; + new_indparams.push_back(dvar); + hash_map memo; + expr new_fmla = AddParamToRels(memo,rmap,ctx.make(Sub,dvar,ctx.int_val(1)),edge->F.Formula); + RPFP::Transformer new_t = drpfp->CreateTransformer(new_relparams,new_indparams,new_fmla); + Node *new_parent = db_map[edge->Parent]; + db_edge_rev_map[drpfp->CreateEdge(new_parent,new_t,new_children)] = edge; + } + } + + void SetMaxDepthRPFP(int depth){ + hash_map subst; + expr dmax = ctx.int_const("@depth_max"); + subst[dmax] = ctx.int_val(depth); + for(unsigned i = 0; i < drpfp->nodes.size(); i++){ + Node *node = drpfp->nodes[i]; + expr fmla = db_saved_bounds[i]; + fmla = drpfp->SubstRec(subst,fmla); + node->Bound.Formula = fmla; + } + } + + void ConvertCex(){ + cex.clear(); + RPFP *tree = new RPFP(rpfp->ls); + Node *root; + Counterexample &dctx = duality->GetCounterexample(); + hash_map ctx_node_map; + for(unsigned i = 0; i < dctx.get_tree()->nodes.size(); i++){ + Node *dnode = dctx.get_tree()->nodes[i]; + Node *onode = db_rev_map[dnode->map->map]; + Node *node = tree->CloneNode(onode); + node->number = dnode->number; // numbers have to match for model to make sense + ctx_node_map[dnode] = node; + if(dnode == dctx.get_root()) + root = node; + } + for(unsigned i = 0; i < dctx.get_tree()->edges.size(); i++){ + Edge *dedge = dctx.get_tree()->edges[i]; + Edge *oedge = db_edge_rev_map[dedge->map]; + Node *parent = ctx_node_map[dedge->Parent]; + std::vector chs; + for(unsigned j = 0; j < dedge->Children.size(); j++) + chs.push_back(ctx_node_map[dedge->Children[j]]); + Edge *edge = tree->CreateEdge(parent,oedge->F,chs); + edge->number = dedge->number; // numbers have to match for model to make sense + edge->map = oedge; + } + tree->dualModel = dctx.get_tree()->dualModel; + cex.set(tree,root); + } + + bool GetSolution(){ + for(unsigned i = 0; i < rpfp->nodes.size(); i++) + if(!drpfp->nodes[i]->Annotation.SubsetEq(rpfp->nodes[i]->Bound)) + return false; + expr dvar = ctx.int_const("@depth"); + hash_map subst; + subst[dvar] = ctx.int_val(INT_MAX); + for(unsigned i = 0; i < rpfp->nodes.size(); i++){ + expr fmla = drpfp->nodes[i]->Annotation.Formula; + fmla = drpfp->SubstRec(subst,fmla); + fmla = fmla.simplify(); + rpfp->nodes[i]->Annotation.Formula = fmla; + } + return true; + } + + void UndoDepthBoundRPFP(){ +#if 0 + if(cex.get_tree()){ + // here, need to map the cex back... + } + // also need to map the proof back, but we don't... +#endif + } + }; + Solver *Solver::Create(const std::string &solver_class, RPFP *rpfp){ - Duality *s = alloc(Duality,rpfp); + // Solver *s = alloc(DualityDepthBounded,rpfp); + Solver *s = alloc(Duality,rpfp); return s; } diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 5bfbba6b2..832e3329e 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -78,6 +78,7 @@ def_module_params('fixedpoint', ('batch_expand', BOOL, False, 'DUALITY: use batch expansion'), ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), ('conjecture_file', STRING, '', 'DUALITY: save conjectures to file'), + ('enable_restarts', BOOL, False, 'DUALITY: enable restarts'), )) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 08c57b05e..55f6c3747 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -281,6 +281,7 @@ lbool dl_interface::query(::expr * query) { rs->SetOption("stratified_inlining",m_ctx.get_params().stratified_inlining() ? "1" : "0"); rs->SetOption("batch_expand",m_ctx.get_params().batch_expand() ? "1" : "0"); rs->SetOption("conjecture_file",m_ctx.get_params().conjecture_file()); + rs->SetOption("enable_restarts",m_ctx.get_params().enable_restarts() ? "1" : "0"); #if 0 if(rb != UINT_MAX){ std::ostringstream os; os << rb; From c5f17df310405b2f7718f1eaa93242cfa25e54de Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 1 Oct 2014 18:15:33 -0700 Subject: [PATCH 440/509] fixing an assert caused by previous change in theory_array_base.cpp --- src/smt/theory_array_base.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index 6325a2a99..b2c17b4e0 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -433,14 +433,16 @@ namespace smt { enode * n = get_enode(i); if (ctx.is_relevant(n)) { enode * r = n->get_root(); - if (!r->is_marked() && is_array_sort(r) && ctx.is_shared(r)) { - TRACE("array_shared", tout << "new shared var: #" << r->get_owner_id() << "\n";); - theory_var r_th_var = r->get_th_var(get_id()); - SASSERT(r_th_var != null_theory_var); - result.push_back(r_th_var); + if (!r->is_marked()){ + if(is_array_sort(r) && ctx.is_shared(r)) { + TRACE("array_shared", tout << "new shared var: #" << r->get_owner_id() << "\n";); + theory_var r_th_var = r->get_th_var(get_id()); + SASSERT(r_th_var != null_theory_var); + result.push_back(r_th_var); + } + r->set_mark(); + to_unmark.push_back(r); } - r->set_mark(); - to_unmark.push_back(r); } } unmark_enodes(to_unmark.size(), to_unmark.c_ptr()); From d54d758f45b745ec129db375364d5a00eef23ce5 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 1 Oct 2014 18:16:21 -0700 Subject: [PATCH 441/509] getting duality to recover from incompleteness-related failures by restarting --- src/duality/duality_profiling.cpp | 10 ++++++++++ src/duality/duality_solver.cpp | 25 ++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/duality/duality_profiling.cpp b/src/duality/duality_profiling.cpp index d5dac0811..5bcda972a 100755 --- a/src/duality/duality_profiling.cpp +++ b/src/duality/duality_profiling.cpp @@ -125,8 +125,18 @@ namespace Duality { void timer_stop(const char *name){ if(current->name != name || !current->parent){ +#if 0 std::cerr << "imbalanced timer_start and timer_stop"; exit(1); +#endif + // in case we lost a timer stop due to an exception + while(current->name != name && current->parent) + current = current->parent; + if(current->parent){ + current->time += (current_time() - current->start_time); + current = current->parent; + } + return; } current->time += (current_time() - current->start_time); current = current->parent; diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 49e591be2..bbd1ebe0a 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -2252,22 +2252,27 @@ namespace Duality { int update_count = 0; for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; - tree->SolveSingleNode(top,node); -#ifdef NO_GENERALIZE - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); -#else try { + tree->SolveSingleNode(top,node); +#ifdef NO_GENERALIZE + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); +#else if(expansions.size() == 1 && NodeTooComplicated(node)) SimplifyNode(node); else node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); Generalize(node); +#endif } catch(const RPFP::Bad &){ // bad interpolants can get us here throw DoRestart(); } -#endif + catch(char const *msg){ + // bad interpolants can get us here + reporter->Message(std::string("interpolation failure:") + msg); + throw DoRestart(); + } if(RecordUpdate(node)){ update_count++; total_updates++; @@ -2283,8 +2288,14 @@ namespace Duality { if(update_count == 0){ if(was_sat){ update_failures++; - if(update_failures > 10) - throw Incompleteness(); + if(update_failures > 10){ + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + node->map->Annotation.SetFull(); + reporter->Message("incompleteness: cleared annotation"); + } + throw DoRestart(); + } } reporter->Message("backtracked without learning"); } From d03a4bc306ae59ef27d6508a3670cb7f7fd8e900 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Oct 2014 14:33:42 -0700 Subject: [PATCH 442/509] check cancel flag after bcp. BCP returns in incomplete state after it check's the cancel flag. Propagate returns 'true' in this case so that the main loop exits Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 02ee06985..f3f2d4948 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1615,6 +1615,8 @@ namespace smt { unsigned qhead = m_qhead; if (!bcp()) return false; + if (m_cancel_flag) + return true; SASSERT(!inconsistent()); propagate_relevancy(qhead); if (inconsistent()) From fbb01f369959cfd35ea16495bf64c96728376353 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 2 Oct 2014 23:58:52 -0700 Subject: [PATCH 443/509] prevent usage that mixes E/e notation with division / for numerals Signed-off-by: Nikolaj Bjorner --- src/util/mpq.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/mpq.cpp b/src/util/mpq.cpp index eda937029..df4d207a6 100644 --- a/src/util/mpq.cpp +++ b/src/util/mpq.cpp @@ -235,6 +235,9 @@ void mpq_manager::set(mpq & a, char const * val) { SASSERT(str[0] - '0' <= 9); exp = (10*exp) + (str[0] - '0'); } + else if ('/' == str[0]) { + throw default_exception("mixing rational/scientific notation"); + } TRACE("mpq_set", tout << "[exp]: " << exp << ", str[0]: " << (str[0] - '0') << std::endl;); ++str; } From 16445569f1a6cfd3fa04c7227d3d724dd16947c7 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sat, 4 Oct 2014 16:31:01 -0700 Subject: [PATCH 444/509] fix for quantifier abstraction --- src/muz/transforms/dl_transforms.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/muz/transforms/dl_transforms.cpp b/src/muz/transforms/dl_transforms.cpp index 2cf48d46b..9a4667f2c 100644 --- a/src/muz/transforms/dl_transforms.cpp +++ b/src/muz/transforms/dl_transforms.cpp @@ -71,7 +71,8 @@ namespace datalog { transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, ctx, 32000)); transf.register_plugin(alloc(datalog::mk_bit_blast, ctx, 35000)); - transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 36000)); + if (!ctx.get_params().quantify_arrays()) + transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 36000)); transf.register_plugin(alloc(datalog::mk_karr_invariants, ctx, 36010)); if (ctx.get_params().magic()) { transf.register_plugin(alloc(datalog::mk_magic_symbolic, ctx, 36020)); From e8985ff33dcb2d9d2705d632468443abb811ee99 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sat, 4 Oct 2014 17:17:33 -0700 Subject: [PATCH 445/509] working on transforms in duality --- src/muz/duality/duality_dl_interface.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 55f6c3747..c7fa0a08f 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -157,7 +157,27 @@ lbool dl_interface::query(::expr * query) { svector< ::symbol> names; vector bounds; // m_ctx.get_rules_as_formulas(rules, names); - m_ctx.get_raw_rule_formulas(rules, names, bounds); + + expr_ref query_ref(m_ctx.get_manager()); + if(****){ + m_ctx.flush_add_rules(); + datalog::rule_manager& rm = m_ctx.get_rule_manager(); + rm.mk_query(query, m_ctx.get_rules()); + apply_default_transformation(m_ctx); + rule_set &rs = m_ctx.get_rules(); + if(m_ctx.get_rules().get_output_predicates().empty()) + query_ref = m_ctx.get_manager().mk_true(); + else { + query_pred = m_ctx.get_rules().get_output_predicate(); + func_decl_ref query_pred(m_ctx.get_manager()); + query_pred = m_ctx.get_rules().get_output_predicate(); + ptr_vector sorts; + unsi + + } + } + else + m_ctx.get_raw_rule_formulas(rules, names, bounds); // get all the rules as clauses std::vector &clauses = _d->clauses; From 6a3f75822d9ee27b35326a83bb1363d648ec6d7d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 4 Oct 2014 18:35:18 -0700 Subject: [PATCH 446/509] fix format bug (issue 126) and smaller nits in sat solver (const annotation, disable elimination of external or already elimianted variables) Signed-off-by: Nikolaj Bjorner --- src/sat/sat_asymm_branch.cpp | 2 +- src/sat/sat_asymm_branch.h | 2 +- src/sat/sat_cleaner.cpp | 2 +- src/sat/sat_cleaner.h | 2 +- src/sat/sat_probing.cpp | 3 ++- src/sat/sat_probing.h | 2 +- src/sat/sat_scc.cpp | 2 +- src/sat/sat_scc.h | 2 +- src/sat/sat_simplifier.cpp | 8 +++++++- src/sat/sat_simplifier.h | 2 +- src/sat/sat_solver.cpp | 4 +--- src/util/cmd_context_types.h | 8 ++++---- 12 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/sat/sat_asymm_branch.cpp b/src/sat/sat_asymm_branch.cpp index b8ac520b2..193bf59f2 100644 --- a/src/sat/sat_asymm_branch.cpp +++ b/src/sat/sat_asymm_branch.cpp @@ -215,7 +215,7 @@ namespace sat { sat_asymm_branch_params::collect_param_descrs(d); } - void asymm_branch::collect_statistics(statistics & st) { + void asymm_branch::collect_statistics(statistics & st) const { st.update("elim literals", m_elim_literals); } diff --git a/src/sat/sat_asymm_branch.h b/src/sat/sat_asymm_branch.h index 6ffd239eb..f10522fab 100644 --- a/src/sat/sat_asymm_branch.h +++ b/src/sat/sat_asymm_branch.h @@ -49,7 +49,7 @@ namespace sat { void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); - void collect_statistics(statistics & st); + void collect_statistics(statistics & st) const; void reset_statistics(); void dec(unsigned c) { m_counter -= c; } diff --git a/src/sat/sat_cleaner.cpp b/src/sat/sat_cleaner.cpp index 4b1ac2c92..959f5e94f 100644 --- a/src/sat/sat_cleaner.cpp +++ b/src/sat/sat_cleaner.cpp @@ -206,7 +206,7 @@ namespace sat { m_elim_literals = 0; } - void cleaner::collect_statistics(statistics & st) { + void cleaner::collect_statistics(statistics & st) const { st.update("elim clauses", m_elim_clauses); st.update("elim literals", m_elim_literals); } diff --git a/src/sat/sat_cleaner.h b/src/sat/sat_cleaner.h index d4306afcd..d22408926 100644 --- a/src/sat/sat_cleaner.h +++ b/src/sat/sat_cleaner.h @@ -42,7 +42,7 @@ namespace sat { bool operator()(bool force = false); - void collect_statistics(statistics & st); + void collect_statistics(statistics & st) const; void reset_statistics(); void dec() { m_cleanup_counter--; } diff --git a/src/sat/sat_probing.cpp b/src/sat/sat_probing.cpp index f9741cd7b..165d39ad8 100644 --- a/src/sat/sat_probing.cpp +++ b/src/sat/sat_probing.cpp @@ -196,6 +196,7 @@ namespace sat { s.propagate(false); // make sure propagation queue is empty if (s.inconsistent()) return true; + SASSERT(s.m_qhead == s.m_trail.size()); CASSERT("probing", s.check_invariant()); if (!force && m_counter > 0) return true; @@ -259,7 +260,7 @@ namespace sat { m_to_assert.finalize(); } - void probing::collect_statistics(statistics & st) { + void probing::collect_statistics(statistics & st) const { st.update("probing assigned", m_num_assigned); } diff --git a/src/sat/sat_probing.h b/src/sat/sat_probing.h index 9f3c1ae87..2061b74bd 100644 --- a/src/sat/sat_probing.h +++ b/src/sat/sat_probing.h @@ -71,7 +71,7 @@ namespace sat { void free_memory(); - void collect_statistics(statistics & st); + void collect_statistics(statistics & st) const; void reset_statistics(); // return the literals implied by l. diff --git a/src/sat/sat_scc.cpp b/src/sat/sat_scc.cpp index 29f3f006f..ffbdb31c6 100644 --- a/src/sat/sat_scc.cpp +++ b/src/sat/sat_scc.cpp @@ -223,7 +223,7 @@ namespace sat { return to_elim.size(); } - void scc::collect_statistics(statistics & st) { + void scc::collect_statistics(statistics & st) const { st.update("elim bool vars", m_num_elim); } diff --git a/src/sat/sat_scc.h b/src/sat/sat_scc.h index c85cc7d42..5f69e11c6 100644 --- a/src/sat/sat_scc.h +++ b/src/sat/sat_scc.h @@ -40,7 +40,7 @@ namespace sat { void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); - void collect_statistics(statistics & st); + void collect_statistics(statistics & st) const; void reset_statistics(); }; }; diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index f20ffa7c7..753075b10 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -918,7 +918,11 @@ namespace sat { void process(literal l) { TRACE("blocked_clause", tout << "processing: " << l << "\n";); model_converter::entry * new_entry = 0; + if (s.is_external(l.var()) || s.was_eliminated(l.var())) + return; + { + m_to_remove.reset(); { clause_use_list & occs = s.m_use_list.get(l); @@ -1339,6 +1343,7 @@ namespace sat { } TRACE("resolution", tout << "found var to eliminate, before: " << before_clauses << " after: " << after_clauses << "\n";); + // eliminate variable model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); save_clauses(mc_entry, m_pos_cls); @@ -1421,6 +1426,7 @@ namespace sat { }; void simplifier::elim_vars() { + IF_VERBOSE(10, s.display(verbose_stream());); elim_var_report rpt(*this); bool_var_vector vars; order_vars_for_elim(vars); @@ -1466,7 +1472,7 @@ namespace sat { sat_simplifier_params::collect_param_descrs(r); } - void simplifier::collect_statistics(statistics & st) { + void simplifier::collect_statistics(statistics & st) const { st.update("subsumed", m_num_subsumed); st.update("subsumption resolution", m_num_sub_res); st.update("elim literals", m_num_elim_lits); diff --git a/src/sat/sat_simplifier.h b/src/sat/sat_simplifier.h index 96d346598..cb6fa9557 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -180,7 +180,7 @@ namespace sat { void free_memory(); - void collect_statistics(statistics & st); + void collect_statistics(statistics & st) const; void reset_statistics(); }; }; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index e7dee83ae..f898130af 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -459,9 +459,6 @@ namespace sat { void solver::set_conflict(justification c, literal not_l) { if (m_inconsistent) return; - TRACE("sat_conflict", tout << "conflict\n";); - // int * p = 0; - // *p = 0; m_inconsistent = true; m_conflict = c; m_not_l = not_l; @@ -863,6 +860,7 @@ namespace sat { m_next_simplify = 0; m_stopwatch.reset(); m_stopwatch.start(); + TRACE("sat", display(tout);); } /** diff --git a/src/util/cmd_context_types.h b/src/util/cmd_context_types.h index b0a0226e8..e334dc0d2 100644 --- a/src/util/cmd_context_types.h +++ b/src/util/cmd_context_types.h @@ -55,12 +55,12 @@ class cmd_exception : public default_exception { } public: cmd_exception(char const * msg):default_exception(msg), m_line(-1), m_pos(-1) {} - cmd_exception(std::string const & msg):default_exception(msg.c_str()), m_line(-1), m_pos(-1) {} - cmd_exception(std::string const & msg, int line, int pos):default_exception(msg.c_str()), m_line(line), m_pos(pos) {} + cmd_exception(std::string const & msg):default_exception(msg), m_line(-1), m_pos(-1) {} + cmd_exception(std::string const & msg, int line, int pos):default_exception(msg), m_line(line), m_pos(pos) {} cmd_exception(char const * msg, symbol const & s): - default_exception(compose(msg,s).c_str()),m_line(-1),m_pos(-1) {} + default_exception(compose(msg,s)),m_line(-1),m_pos(-1) {} cmd_exception(char const * msg, symbol const & s, int line, int pos): - default_exception(compose(msg,s).c_str()),m_line(line),m_pos(pos) {} + default_exception(compose(msg,s)),m_line(line),m_pos(pos) {} bool has_pos() const { return m_line >= 0; } int line() const { SASSERT(has_pos()); return m_line; } From ec48f6d129f856f216fc1885593e8d77ab6c89e8 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sat, 4 Oct 2014 19:07:14 -0700 Subject: [PATCH 447/509] working on transforms for duality --- src/muz/base/dl_context.cpp | 3 +++ src/muz/duality/duality_dl_interface.cpp | 33 +++++++++++++++++------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 80af7eeb0..ca7277d07 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -985,6 +985,9 @@ namespace datalog { flush_add_rules(); break; case DUALITY_ENGINE: + // this lets us use duality with SAS 2013 abstraction + if(quantify_arrays()) + flush_add_rules(); break; default: UNREACHABLE(); diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index c7fa0a08f..7666b39cf 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -37,6 +37,7 @@ Revision History: #include "fixedpoint_params.hpp" #include "used_vars.h" #include "func_decl_dependencies.h" +#include "dl_transforms.h" // template class symbol_table; @@ -158,22 +159,34 @@ lbool dl_interface::query(::expr * query) { vector bounds; // m_ctx.get_rules_as_formulas(rules, names); + + // If using SAS 2013 abstractiion, we need to perform some transforms expr_ref query_ref(m_ctx.get_manager()); - if(****){ - m_ctx.flush_add_rules(); + if(m_ctx.quantify_arrays()){ datalog::rule_manager& rm = m_ctx.get_rule_manager(); rm.mk_query(query, m_ctx.get_rules()); apply_default_transformation(m_ctx); - rule_set &rs = m_ctx.get_rules(); + datalog::rule_set &rs = m_ctx.get_rules(); if(m_ctx.get_rules().get_output_predicates().empty()) - query_ref = m_ctx.get_manager().mk_true(); + query_ref = m_ctx.get_manager().mk_false(); else { - query_pred = m_ctx.get_rules().get_output_predicate(); - func_decl_ref query_pred(m_ctx.get_manager()); - query_pred = m_ctx.get_rules().get_output_predicate(); - ptr_vector sorts; - unsi - + func_decl_ref query_pred(m_ctx.get_manager()); + query_pred = m_ctx.get_rules().get_output_predicate(); + ptr_vector sorts; + unsigned nargs = query_pred.get()->get_arity(); + expr_ref_vector vars(m_ctx.get_manager()); + for(unsigned i = 0; i < nargs; i++){ + ::sort *s = query_pred.get()->get_domain(i); + vars.push_back(m_ctx.get_manager().mk_var(nargs-1-i,s)); + } + query_ref = m_ctx.get_manager().mk_app(query_pred.get(),nargs,vars.c_ptr()); + query = query_ref.get(); + } + unsigned nrules = rs.get_num_rules(); + for(unsigned i = 0; i < nrules; i++){ + expr_ref f(m_ctx.get_manager()); + rs.get_rule(i)->to_formula(f); + rules.push_back(f); } } else From 4e55f0494212272be80a7242505b1821811b7b13 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 5 Oct 2014 10:41:37 -0700 Subject: [PATCH 448/509] use more efficient encoding of shift operations Signed-off-by: Nikolaj Bjorner --- .../bit_blaster/bit_blaster_tpl_def.h | 101 ++++++++++++------ 1 file changed, 71 insertions(+), 30 deletions(-) diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h index b41aa2238..b579d698e 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h @@ -911,18 +911,34 @@ void bit_blaster_tpl::mk_shl(unsigned sz, expr * const * a_bits, expr * con out_bits.push_back(a_bits[i]); } else { - expr_ref_vector eqs(m()); - mk_eqs(sz, b_bits, eqs); - for (unsigned i = 0; i < sz; i++) { + out_bits.append(sz, a_bits); + + unsigned i = 0; + expr_ref_vector new_out_bits(m()); + for (; i < sz; ++i) { checkpoint(); - expr_ref out(m()); - mk_ite(eqs.get(i), a_bits[0], m().mk_false(), out); - for (unsigned j = 1; j <= i; j++) { + unsigned shift_i = 1 << i; + if (shift_i >= sz) break; + for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); - mk_ite(eqs.get(i - j), a_bits[j], out, new_out); - out = new_out; + expr* a_j = m().mk_false(); + if (shift_i <= j) a_j = out_bits[j-shift_i].get(); + mk_ite(b_bits[i], a_j, out_bits[j].get(), new_out); + new_out_bits.push_back(new_out); } - out_bits.push_back(out); + out_bits.reset(); + out_bits.append(new_out_bits); + new_out_bits.reset(); + } + expr_ref is_large(m()); + is_large = m().mk_false(); + for (; i < sz; ++i) { + mk_or(is_large, b_bits[i], is_large); + } + for (unsigned j = 0; j < sz; ++j) { + expr_ref new_out(m()); + mk_ite(is_large, m().mk_false(), out_bits[j].get(), new_out); + out_bits[j] = new_out; } } } @@ -939,19 +955,32 @@ void bit_blaster_tpl::mk_lshr(unsigned sz, expr * const * a_bits, expr * co out_bits.push_back(m().mk_false()); } else { - expr_ref_vector eqs(m()); - mk_eqs(sz, b_bits, eqs); - out_bits.resize(sz); - for (unsigned i = 0; i < sz; i++) { + out_bits.append(sz, a_bits); + unsigned i = 0; + for (; i < sz; ++i) { checkpoint(); - expr_ref out(m()); - mk_ite(eqs.get(i), a_bits[sz-1], m().mk_false(), out); - for (unsigned j = 1; j <= i; j++) { + expr_ref_vector new_out_bits(m()); + unsigned shift_i = 1 << i; + if (shift_i >= sz) break; + for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); - mk_ite(eqs.get(i - j), a_bits[sz - j - 1], out, new_out); - out = new_out; + expr* a_j = m().mk_false(); + if (shift_i + j < sz) a_j = out_bits[j+shift_i].get(); + mk_ite(b_bits[i], a_j, out_bits[j].get(), new_out); + new_out_bits.push_back(new_out); } - out_bits.set(sz - i - 1, out); + out_bits.reset(); + out_bits.append(new_out_bits); + } + expr_ref is_large(m()); + is_large = m().mk_false(); + for (; i < sz; ++i) { + mk_or(is_large, b_bits[i], is_large); + } + for (unsigned j = 0; j < sz; ++j) { + expr_ref new_out(m()); + mk_ite(is_large, m().mk_false(), out_bits[j].get(), new_out); + out_bits[j] = new_out; } } } @@ -968,20 +997,32 @@ void bit_blaster_tpl::mk_ashr(unsigned sz, expr * const * a_bits, expr * co out_bits.push_back(a_bits[sz-1]); } else { - expr_ref_vector eqs(m()); - mk_eqs(sz, b_bits, eqs); - out_bits.resize(sz); - for (unsigned i = 0; i < sz; i++) { + out_bits.append(sz, a_bits); + unsigned i = 0; + for (; i < sz; ++i) { checkpoint(); - expr_ref out(m()); - out = a_bits[sz-1]; - for (unsigned j = 1; j <= i; j++) { + expr_ref_vector new_out_bits(m()); + unsigned shift_i = 1 << i; + if (shift_i >= sz) break; + for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); - mk_ite(eqs.get(i - j), a_bits[sz - j - 1], out, new_out); - out = new_out; + expr* a_j = a_bits[sz-1]; + if (shift_i + j < sz) a_j = out_bits[j+shift_i].get(); + mk_ite(b_bits[i], a_j, out_bits[j].get(), new_out); + new_out_bits.push_back(new_out); } - TRACE("bit_blaster", tout << (sz - i - 1) << " :\n" << mk_pp(out, m()) << "\n";); - out_bits.set(sz - i - 1, out); + out_bits.reset(); + out_bits.append(new_out_bits); + } + expr_ref is_large(m()); + is_large = m().mk_false(); + for (; i < sz; ++i) { + mk_or(is_large, b_bits[i], is_large); + } + for (unsigned j = 0; j < sz; ++j) { + expr_ref new_out(m()); + mk_ite(is_large, a_bits[sz-1], out_bits[j].get(), new_out); + out_bits[j] = new_out; } } } From cbf470422e501c2596aa7a824cbef009dc61e158 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 5 Oct 2014 12:10:23 -0700 Subject: [PATCH 449/509] remove extra verbose output Signed-off-by: Nikolaj Bjorner --- src/sat/sat_simplifier.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 753075b10..219b9f278 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -1426,7 +1426,6 @@ namespace sat { }; void simplifier::elim_vars() { - IF_VERBOSE(10, s.display(verbose_stream());); elim_var_report rpt(*this); bool_var_vector vars; order_vars_for_elim(vars); From c6683fd6fa51e48e29cad58f90225f5b5df053fc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 5 Oct 2014 12:27:57 -0700 Subject: [PATCH 450/509] to fix that timeout of 0 has different interpretations across platforms Signed-off-by: Nikolaj Bjorner --- src/util/scoped_timer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index c4a640009..15891168c 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -129,7 +129,7 @@ struct scoped_timer::imp { WT_EXECUTEINTIMERTHREAD); #elif defined(__APPLE__) && defined(__MACH__) // Mac OS X - m_interval = ms; + m_interval = ms?ms:0xFFFFFFFF; if (pthread_attr_init(&m_attributes) != 0) throw default_exception("failed to initialize timer thread attributes"); if (pthread_cond_init(&m_condition_var, NULL) != 0) From 18e77bd53960b4cbc0fda837c210255981aabd4d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 5 Oct 2014 18:36:15 -0700 Subject: [PATCH 451/509] fix qe for undef scenarios, codeplex issue 130 Signed-off-by: Nikolaj Bjorner --- src/qe/qe.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index 770a49c07..e573508bb 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -1449,11 +1449,19 @@ namespace qe { m_solver.assert_expr(m_fml); if (assumption) m_solver.assert_expr(assumption); - bool is_sat = false; - while (l_true == m_solver.check()) { - is_sat = true; + bool is_sat = false; + lbool res = l_true; + while (res == l_true) { + res = m_solver.check(); + if (res == l_true) is_sat = true; final_check(); } + if (res == l_undef) { + free_vars.append(num_vars, vars); + reset(); + m_solver.pop(1); + return; + } if (!is_sat) { fml = m.mk_false(); @@ -1484,12 +1492,13 @@ namespace qe { ); free_vars.append(m_free_vars); - SASSERT(!m_free_vars.empty() || m_solver.inconsistent()); + if (!m_free_vars.empty() || m_solver.inconsistent()) { - if (m_fml.get() != m_subfml.get()) { - scoped_ptr rp = mk_default_expr_replacer(m); - rp->apply_substitution(to_app(m_subfml.get()), fml, m_fml); - fml = m_fml; + if (m_fml.get() != m_subfml.get()) { + scoped_ptr rp = mk_default_expr_replacer(m); + rp->apply_substitution(to_app(m_subfml.get()), fml, m_fml); + fml = m_fml; + } } reset(); m_solver.pop(1); From 7ef1e8a3de0ee0ed6ff146710e75041d7542d463 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 5 Oct 2014 19:04:15 -0700 Subject: [PATCH 452/509] turn friends into inliers to respect namespace for non-operator friends. Operaor friends will stil be in file scope so do not take name-space qualifier Signed-off-by: Nikolaj Bjorner --- examples/c++/example.cpp | 2 + src/api/c++/z3++.h | 120 +++++++++++++++++++++++++-------------- 2 files changed, 79 insertions(+), 43 deletions(-) diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index 77f7702f2..b348e7d36 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -1,5 +1,7 @@ #include #include"z3++.h" + + using namespace z3; /** diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 8f228cdba..20100d124 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -85,6 +85,8 @@ namespace z3 { friend std::ostream & operator<<(std::ostream & out, exception const & e) { out << e.msg(); return out; } }; + + /** \brief Z3 global configuration object. */ @@ -269,8 +271,9 @@ namespace z3 { object(object const & s):m_ctx(s.m_ctx) {} context & ctx() const { return *m_ctx; } void check_error() const { m_ctx->check_error(); } - friend void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } + friend void check_context(object const & a, object const & b); }; + inline void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } class symbol : public object { Z3_symbol m_sym; @@ -282,7 +285,7 @@ namespace z3 { Z3_symbol_kind kind() const { return Z3_get_symbol_kind(ctx(), m_sym); } std::string str() const { assert(kind() == Z3_STRING_SYMBOL); return Z3_get_symbol_string(ctx(), m_sym); } int to_int() const { assert(kind() == Z3_INT_SYMBOL); return Z3_get_symbol_int(ctx(), m_sym); } - friend std::ostream & operator<<(std::ostream & out, symbol const & s) { + friend std::ostream & operator<<(std::ostream & out, symbol const & s) { if (s.kind() == Z3_INT_SYMBOL) out << "k!" << s.to_int(); else @@ -291,6 +294,7 @@ namespace z3 { } }; + class params : public object { Z3_params m_params; public: @@ -309,7 +313,9 @@ namespace z3 { void set(char const * k, unsigned n) { Z3_params_set_uint(ctx(), m_params, ctx().str_symbol(k), n); } void set(char const * k, double n) { Z3_params_set_double(ctx(), m_params, ctx().str_symbol(k), n); } void set(char const * k, symbol const & s) { Z3_params_set_symbol(ctx(), m_params, ctx().str_symbol(k), s); } - friend std::ostream & operator<<(std::ostream & out, params const & p) { out << Z3_params_to_string(p.ctx(), p); return out; } + friend std::ostream & operator<<(std::ostream & out, params const & p) { + out << Z3_params_to_string(p.ctx(), p); return out; + } }; class ast : public object { @@ -325,14 +331,19 @@ namespace z3 { ast & operator=(ast const & s) { Z3_inc_ref(s.ctx(), s.m_ast); if (m_ast) Z3_dec_ref(ctx(), m_ast); m_ctx = s.m_ctx; m_ast = s.m_ast; return *this; } Z3_ast_kind kind() const { Z3_ast_kind r = Z3_get_ast_kind(ctx(), m_ast); check_error(); return r; } unsigned hash() const { unsigned r = Z3_get_ast_hash(ctx(), m_ast); check_error(); return r; } - friend std::ostream & operator<<(std::ostream & out, ast const & n) { out << Z3_ast_to_string(n.ctx(), n.m_ast); return out; } + friend std::ostream & operator<<(std::ostream & out, ast const & n) { + out << Z3_ast_to_string(n.ctx(), n.m_ast); return out; + } /** \brief Return true if the ASTs are structurally identical. */ - friend bool eq(ast const & a, ast const & b) { return Z3_is_eq_ast(a.ctx(), a, b) != 0; } + friend bool eq(ast const & a, ast const & b); }; + inline bool eq(ast const & a, ast const & b) { return Z3_is_eq_ast(a.ctx(), a, b) != 0; } + + /** \brief A Z3 sort (aka type). Every expression (i.e., formula or term) in Z3 has a sort. */ @@ -570,6 +581,7 @@ namespace z3 { return expr(a.ctx(), r); } + /** \brief Return an expression representing a and b. @@ -585,6 +597,7 @@ namespace z3 { return expr(a.ctx(), r); } + /** \brief Return an expression representing a and b. The C++ Boolean value \c b is automatically converted into a Z3 Boolean constant. @@ -636,21 +649,10 @@ namespace z3 { a.check_error(); return expr(a.ctx(), r); } - friend expr implies(expr const & a, bool b) { return implies(a, a.ctx().bool_val(b)); } - friend expr implies(bool a, expr const & b) { return implies(b.ctx().bool_val(a), b); } + friend expr implies(expr const & a, bool b); + friend expr implies(bool a, expr const & b); - /** - \brief Create the if-then-else expression ite(c, t, e) - - \pre c.is_bool() - */ - friend expr ite(expr const & c, expr const & t, expr const & e) { - check_context(c, t); check_context(c, e); - assert(c.is_bool()); - Z3_ast r = Z3_mk_ite(c.ctx(), c, t, e); - c.check_error(); - return expr(c.ctx(), r); - } + friend expr ite(expr const & c, expr const & t, expr const & e); friend expr distinct(expr_vector const& args); @@ -716,15 +718,9 @@ namespace z3 { /** \brief Power operator */ - friend expr pw(expr const & a, expr const & b) { - assert(a.is_arith() && b.is_arith()); - check_context(a, b); - Z3_ast r = Z3_mk_power(a.ctx(), a, b); - a.check_error(); - return expr(a.ctx(), r); - } - friend expr pw(expr const & a, int b) { return pw(a, a.ctx().num_val(b, a.get_sort())); } - friend expr pw(int a, expr const & b) { return pw(b.ctx().num_val(a, b.get_sort()), b); } + friend expr pw(expr const & a, expr const & b); + friend expr pw(expr const & a, int b); + friend expr pw(int a, expr const & b); friend expr operator/(expr const & a, expr const & b) { check_context(a, b); @@ -891,6 +887,38 @@ namespace z3 { expr substitute(expr_vector const& dst); }; + + inline expr implies(expr const & a, bool b) { return implies(a, a.ctx().bool_val(b)); } + inline expr implies(bool a, expr const & b) { return implies(b.ctx().bool_val(a), b); } + + inline expr pw(expr const & a, expr const & b) { + assert(a.is_arith() && b.is_arith()); + check_context(a, b); + Z3_ast r = Z3_mk_power(a.ctx(), a, b); + a.check_error(); + return expr(a.ctx(), r); + } + inline expr pw(expr const & a, int b) { return pw(a, a.ctx().num_val(b, a.get_sort())); } + inline expr pw(int a, expr const & b) { return pw(b.ctx().num_val(a, b.get_sort()), b); } + + + + + + /** + \brief Create the if-then-else expression ite(c, t, e) + + \pre c.is_bool() + */ + + inline expr ite(expr const & c, expr const & t, expr const & e) { + check_context(c, t); check_context(c, e); + assert(c.is_bool()); + Z3_ast r = Z3_mk_ite(c.ctx(), c, t, e); + c.check_error(); + return expr(c.ctx(), r); + } + /** \brief Wraps a Z3_ast as an expr object. It also checks for errors. @@ -1404,22 +1432,28 @@ namespace z3 { t1.check_error(); return tactic(t1.ctx(), r); } - friend tactic repeat(tactic const & t, unsigned max=UINT_MAX) { - Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); - t.check_error(); - return tactic(t.ctx(), r); - } - friend tactic with(tactic const & t, params const & p) { - Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); - t.check_error(); - return tactic(t.ctx(), r); - } - friend tactic try_for(tactic const & t, unsigned ms) { - Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); - t.check_error(); - return tactic(t.ctx(), r); - } + friend tactic repeat(tactic const & t, unsigned max=UINT_MAX); + friend tactic with(tactic const & t, params const & p); + friend tactic try_for(tactic const & t, unsigned ms); }; + + inline tactic repeat(tactic const & t, unsigned max) { + Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); + t.check_error(); + return tactic(t.ctx(), r); + } + + inline tactic with(tactic const & t, params const & p) { + Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); + t.check_error(); + return tactic(t.ctx(), r); + } + inline tactic try_for(tactic const & t, unsigned ms) { + Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); + t.check_error(); + return tactic(t.ctx(), r); + } + class probe : public object { Z3_probe m_probe; From 6d8daacdecc84bafbb7c493aeeccfe5660d19d43 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Oct 2014 08:35:05 -0700 Subject: [PATCH 453/509] fix check for satisfiability before calling final_check Signed-off-by: Nikolaj Bjorner --- src/qe/qe.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index e573508bb..192d97c3b 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -1451,10 +1451,15 @@ namespace qe { if (assumption) m_solver.assert_expr(assumption); bool is_sat = false; lbool res = l_true; - while (res == l_true) { + while (true) { res = m_solver.check(); - if (res == l_true) is_sat = true; - final_check(); + if (res == l_true) { + is_sat = true; + final_check(); + } + else { + break; + } } if (res == l_undef) { free_vars.append(num_vars, vars); From 929880e4fd1f5f40955ba8caed7ed84eb5bd53a2 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 6 Oct 2014 18:06:36 +0100 Subject: [PATCH 454/509] Fix for bogus runtime reports on Linux. Thanks to Vladimir Klebanov for reporting this one. --- src/util/stopwatch.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/util/stopwatch.h b/src/util/stopwatch.h index 661d3762b..b2a397b6e 100644 --- a/src/util/stopwatch.h +++ b/src/util/stopwatch.h @@ -105,7 +105,7 @@ public: mach_timespec_t _stop; clock_get_time(m_host_clock, &_stop); m_time += (_stop.tv_sec - m_start.tv_sec) * 1000000000ull; - m_time += (_stop.tv_nsec - m_start.tv_nsec); + m_time += (_stop.tv_nsec - m_start.tv_nsec); m_running = false; } } @@ -146,7 +146,7 @@ public: void start() { if (!m_running) { - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &m_start); + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &m_start); m_running = true; } } @@ -154,9 +154,10 @@ public: void stop() { if (m_running) { struct timespec _stop; - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &_stop); + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &_stop); m_time += (_stop.tv_sec - m_start.tv_sec) * 1000000000ull; - m_time += (_stop.tv_nsec - m_start.tv_nsec); + if (m_time != 0 || _stop.tv_nsec >= m_start.tv_nsec) + m_time += (_stop.tv_nsec - m_start.tv_nsec); m_running = false; } } From 3222ecd992ab24e0dc33ef48d5cc105420e9b79a Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 6 Oct 2014 18:09:40 +0100 Subject: [PATCH 455/509] tabs Signed-off-by: Christoph M. Wintersteiger --- src/util/stopwatch.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/util/stopwatch.h b/src/util/stopwatch.h index 661d3762b..b9cf312b2 100644 --- a/src/util/stopwatch.h +++ b/src/util/stopwatch.h @@ -90,7 +90,7 @@ public: ~stopwatch() {} void reset() { - m_time = 0ull; + m_time = 0ull; } void start() { @@ -101,7 +101,7 @@ public: } void stop() { - if (m_running) { + if (m_running) { mach_timespec_t _stop; clock_get_time(m_host_clock, &_stop); m_time += (_stop.tv_sec - m_start.tv_sec) * 1000000000ull; @@ -120,7 +120,7 @@ public: } double get_current_seconds() const { - return get_seconds(); + return get_seconds(); } }; @@ -141,7 +141,7 @@ public: ~stopwatch() {} void reset() { - m_time = 0ull; + m_time = 0ull; } void start() { @@ -152,7 +152,7 @@ public: } void stop() { - if (m_running) { + if (m_running) { struct timespec _stop; clock_gettime(CLOCK_THREAD_CPUTIME_ID, &_stop); m_time += (_stop.tv_sec - m_start.tv_sec) * 1000000000ull; @@ -171,7 +171,7 @@ public: } double get_current_seconds() const { - return get_seconds(); + return get_seconds(); } }; From 8438ac6e219b6ff5d88514a90032644d6b1e4068 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 Oct 2014 15:43:24 -0700 Subject: [PATCH 456/509] fix internalization bug when bit2bool is applied to numeral Signed-off-by: Nikolaj Bjorner --- src/smt/theory_bv.cpp | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 8b3021573..6bcae6f60 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -53,6 +53,7 @@ namespace smt { unsigned bv_size = get_bv_size(n); context & ctx = get_context(); literal_vector & bits = m_bits[v]; + bits.reset(); for (unsigned i = 0; i < bv_size; i++) { app * bit = mk_bit2bool(owner, i); ctx.internalize(bit, true); @@ -75,12 +76,14 @@ namespace smt { void theory_bv::mk_bit2bool(app * n) { context & ctx = get_context(); SASSERT(!ctx.b_internalized(n)); - if (!ctx.e_internalized(n->get_arg(0))) { + + expr* first_arg = n->get_arg(0); + + if (!ctx.e_internalized(first_arg)) { // This may happen if bit2bool(x) is in a conflict // clause that is being reinitialized, and x was not reinitialized // yet. - // So, we internalize x (i.e., n->get_arg(0)) - expr * first_arg = n->get_arg(0); + // So, we internalize x (i.e., arg) ctx.internalize(first_arg, false); SASSERT(ctx.e_internalized(first_arg)); // In most cases, when x is internalized, its bits are created. @@ -91,10 +94,27 @@ namespace smt { // This will also force the creation of all bits for x. enode * first_arg_enode = ctx.get_enode(first_arg); get_var(first_arg_enode); - SASSERT(ctx.b_internalized(n)); + // numerals are not blasted into bit2bool, so we do this directly. + if (!ctx.b_internalized(n)) { + rational val; + unsigned sz; + VERIFY(m_util.is_numeral(first_arg, val, sz)); + theory_var v = first_arg_enode->get_th_var(get_id()); + app* owner = first_arg_enode->get_owner(); + for (unsigned i = 0; i < sz; ++i) { + ctx.internalize(mk_bit2bool(owner, i), true); + } + m_bits[v].reset(); + rational bit; + for (unsigned i = 0; i < sz; ++i) { + div(val, rational::power_of_two(i), bit); + mod(bit, rational(2), bit); + m_bits[v].push_back(bit.is_zero()?false_literal:true_literal); + } + } } else { - enode * arg = ctx.get_enode(n->get_arg(0)); + enode * arg = ctx.get_enode(first_arg); // The argument was already internalized, but it may not have a theory variable associated with it. // For example, for ite-terms the method apply_sort_cnstr is not invoked. // See comment in the then-branch. @@ -1041,6 +1061,7 @@ namespace smt { void theory_bv::new_diseq_eh(theory_var v1, theory_var v2) { if (is_bv(v1)) { + SASSERT(m_bits[v1].size() == m_bits[v2].size()); expand_diseq(v1, v2); } } @@ -1381,6 +1402,7 @@ namespace smt { if (v1 != null_theory_var) { // conflict was detected ... v1 and v2 have complementary bits SASSERT(m_bits[v1][it->m_idx] == ~(m_bits[v2][it->m_idx])); + SASSERT(m_bits[v1].size() == m_bits[v2].size()); mk_new_diseq_axiom(v1, v2, it->m_idx); RESET_MERGET_AUX(); return false; From 7fc95aff3cb70e95376bb3ce38b37c028df69c29 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 7 Oct 2014 14:24:28 +0100 Subject: [PATCH 457/509] Minor cleanliness fix. Signed-off-by: Christoph M. Wintersteiger --- src/ast/rewriter/th_rewriter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 5d19c53e3..0e2c8e781 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -73,6 +73,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { m_a_rw.updt_params(p); m_bv_rw.updt_params(p); m_ar_rw.updt_params(p); + m_f_rw.updt_params(p); updt_local_params(p); } From 4ea3ed7e273a0c9170541b43902d2d8723f881f6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Oct 2014 11:00:45 -0700 Subject: [PATCH 458/509] ensure parameters are updated and ensure that global use of auto-config is not obscured by smt.auto-config scoping Signed-off-by: Nikolaj Bjorner --- src/qe/qe.cpp | 25 ++++++++++++++----------- src/qe/qe.h | 7 +++++-- src/qe/qe_sat_tactic.cpp | 9 ++++++--- src/qe/qe_tactic.cpp | 1 + src/smt/params/smt_params.cpp | 3 ++- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index 192d97c3b..d229d8735 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -2238,8 +2238,7 @@ namespace qe { m_params(p), m_trail(m), m_qe(0), - m_assumption(m.mk_true()), - m_use_new_qe(true) + m_assumption(m.mk_true()) { } @@ -2261,12 +2260,6 @@ namespace qe { } void expr_quant_elim::updt_params(params_ref const& p) { - bool r = p.get_bool("use_neq_qe", m_use_new_qe); - if (r != m_use_new_qe) { - dealloc(m_qe); - m_qe = 0; - m_use_new_qe = r; - } init_qe(); m_qe->updt_params(p); } @@ -2274,7 +2267,6 @@ namespace qe { void expr_quant_elim::collect_param_descrs(param_descrs& r) { r.insert("eliminate_variables_as_block", CPK_BOOL, "(default: true) eliminate variables as a block (true) or one at a time (false)"); - // r.insert("use_new_qe", CPK_BOOL, "(default: true) invoke quantifier engine based on abstracted solver"); } void expr_quant_elim::init_qe() { @@ -2504,7 +2496,7 @@ namespace qe { class simplify_solver_context : public i_solver_context { ast_manager& m; - smt_params m_fparams; + smt_params m_fparams; app_ref_vector* m_vars; expr_ref* m_fml; ptr_vector m_contains; @@ -2520,6 +2512,10 @@ namespace qe { add_plugin(mk_arith_plugin(*this, false, m_fparams)); } + void updt_params(params_ref const& p) { + m_fparams.updt_params(p); + } + virtual ~simplify_solver_context() { reset(); } void solve(expr_ref& fml, app_ref_vector& vars) { @@ -2610,6 +2606,10 @@ namespace qe { public: impl(ast_manager& m) : m(m), m_ctx(m) {} + void updt_params(params_ref const& p) { + m_ctx.updt_params(p); + } + bool reduce_quantifier( quantifier * old_q, expr * new_body, @@ -2673,6 +2673,10 @@ namespace qe { return imp->reduce_quantifier(old_q, new_body, new_patterns, new_no_patterns, result, result_pr); } + void simplify_rewriter_cfg::updt_params(params_ref const& p) { + imp->updt_params(p); + } + bool simplify_rewriter_cfg::pre_visit(expr* e) { if (!is_quantifier(e)) return true; quantifier * q = to_quantifier(e); @@ -2680,7 +2684,6 @@ namespace qe { } void simplify_exists(app_ref_vector& vars, expr_ref& fml) { - smt_params params; ast_manager& m = fml.get_manager(); simplify_solver_context ctx(m); ctx.solve(fml, vars); diff --git a/src/qe/qe.h b/src/qe/qe.h index 1697a5cbd..0fd2ff60c 100644 --- a/src/qe/qe.h +++ b/src/qe/qe.h @@ -275,13 +275,12 @@ namespace qe { class expr_quant_elim { ast_manager& m; - smt_params const& m_fparams; + smt_params const& m_fparams; params_ref m_params; expr_ref_vector m_trail; obj_map m_visited; quant_elim* m_qe; expr* m_assumption; - bool m_use_new_qe; public: expr_quant_elim(ast_manager& m, smt_params const& fp, params_ref const& p = params_ref()); ~expr_quant_elim(); @@ -372,6 +371,8 @@ namespace qe { bool pre_visit(expr* e); + void updt_params(params_ref const& p); + }; class simplify_rewriter_star : public rewriter_tpl { @@ -380,6 +381,8 @@ namespace qe { simplify_rewriter_star(ast_manager& m): rewriter_tpl(m, false, m_cfg), m_cfg(m) {} + + void updt_params(params_ref const& p) { m_cfg.updt_params(p); } }; }; diff --git a/src/qe/qe_sat_tactic.cpp b/src/qe/qe_sat_tactic.cpp index b4a1a6a8b..2be32c02d 100644 --- a/src/qe/qe_sat_tactic.cpp +++ b/src/qe/qe_sat_tactic.cpp @@ -74,6 +74,7 @@ namespace qe { is_relevant_default m_is_relevant; mk_atom_default m_mk_atom; th_rewriter m_rewriter; + simplify_rewriter_star m_qe_rw; expr_strong_context_simplifier m_ctx_rewriter; class solver_context : public i_solver_context { @@ -218,6 +219,7 @@ namespace qe { m_Ms(m), m_assignments(m), m_rewriter(m), + m_qe_rw(m), m_ctx_rewriter(m_fparams, m) { m_fparams.m_model = true; } @@ -256,10 +258,9 @@ namespace qe { ptr_vector fmls; goal->get_formulas(fmls); m_fml = m.mk_and(fmls.size(), fmls.c_ptr()); - TRACE("qe", tout << "input: " << mk_pp(m_fml,m) << "\n";); - simplify_rewriter_star rw(m); + TRACE("qe", tout << "input: " << mk_pp(m_fml,m) << "\n";); expr_ref tmp(m); - rw(m_fml, tmp); + m_qe_rw(m_fml, tmp); m_fml = tmp; TRACE("qe", tout << "reduced: " << mk_pp(m_fml,m) << "\n";); skolemize_existential_prefix(); @@ -305,6 +306,8 @@ namespace qe { m_projection_mode_param = p.get_bool("projection_mode", m_projection_mode_param); m_strong_context_simplify_param = p.get_bool("strong_context_simplify", m_strong_context_simplify_param); m_ctx_simplify_local_param = p.get_bool("strong_context_simplify_local", m_ctx_simplify_local_param); + m_fparams.updt_params(p); + m_qe_rw.updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { diff --git a/src/qe/qe_tactic.cpp b/src/qe/qe_tactic.cpp index 5b522e041..8819d704b 100644 --- a/src/qe/qe_tactic.cpp +++ b/src/qe/qe_tactic.cpp @@ -36,6 +36,7 @@ class qe_tactic : public tactic { } void updt_params(params_ref const & p) { + m_fparams.updt_params(p); m_fparams.m_nlquant_elim = p.get_bool("qe_nonlinear", false); m_qe.updt_params(p); } diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index f1c407dc7..18619c8ec 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -19,10 +19,11 @@ Revision History: #include"smt_params.h" #include"smt_params_helper.hpp" #include"model_params.hpp" +#include"gparams.h" void smt_params::updt_local_params(params_ref const & _p) { smt_params_helper p(_p); - m_auto_config = p.auto_config(); + m_auto_config = p.auto_config() && gparams::get_value("auto_config") == "true"; // auto-config is not scoped by smt in gparams. m_random_seed = p.random_seed(); m_relevancy_lvl = p.relevancy(); m_ematching = p.ematching(); From d6964226c7f5d85f443becbf7e9c2d7144d1059f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 7 Oct 2014 15:38:44 -0700 Subject: [PATCH 459/509] indentation Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.h | 2 +- src/smt/smt_quantifier.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 7940b17be..a9fc5c091 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -71,7 +71,7 @@ namespace smt { protected: ast_manager & m_manager; - smt_params & m_fparams; + smt_params & m_fparams; params_ref m_params; setup m_setup; volatile bool m_cancel_flag; diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index e9b0e069a..b76fb6c74 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -453,9 +453,9 @@ namespace smt { instantiated. */ virtual void add(quantifier * q) { - if (m_fparams->m_mbqi && mbqi_enabled(q)) { - m_model_finder->register_quantifier(q); - } + if (m_fparams->m_mbqi && mbqi_enabled(q)) { + m_model_finder->register_quantifier(q); + } } virtual void del(quantifier * q) { From 335f9a9be1b1bbd19b229459d77920cb0f6a6729 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 8 Oct 2014 10:55:24 -0700 Subject: [PATCH 460/509] add parameter validation to tactic parameters Signed-off-by: Nikolaj Bjorner --- src/tactic/tactical.cpp | 5 ++- src/util/params.cpp | 67 ++++++++++++++++++++++++++++++++++++----- src/util/params.h | 1 + 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 1e0b07e9d..87974ea60 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -1229,6 +1229,9 @@ class using_params_tactical : public unary_tactical { params_ref m_params; public: using_params_tactical(tactic * t, params_ref const & p):unary_tactical(t), m_params(p) { + param_descrs r; + collect_param_descrs(r); + p.validate(r); t->updt_params(p); } @@ -1276,7 +1279,7 @@ public: model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { - if (m_p->operator()(*(in.get())).is_true()) + if (m_p->operator()(*(in.get())).is_true()) m_t1->operator()(in, result, mc, pc, core); else m_t2->operator()(in, result, mc, pc, core); diff --git a/src/util/params.cpp b/src/util/params.cpp index 4aff0de92..130b18cc9 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -302,10 +302,19 @@ public: svector::const_iterator end = m_entries.end(); for (; it != end; ++it) { param_kind expected = p.get_kind(it->first); - if (expected == CPK_INVALID) - throw default_exception("unknown parameter '%s'", it->first.str().c_str()); - if (it->second.m_kind != expected) - throw default_exception("parameter kind mismatch '%s'", it->first.str().c_str()); + if (expected == CPK_INVALID) { + std::stringstream strm; + strm << "unknown parameter '" << it->first.str() << "'\n"; + strm << "Legal parameters are:\n"; + p.display(strm, 2, false, false); + throw default_exception(strm.str()); + } + if (it->second.m_kind != expected) { + std::stringstream strm; + strm << "Parameter " << it->first.str() << " was given argument of type "; + strm << it->second.m_kind << ", expected " << expected; + throw default_exception(strm.str()); + } } } @@ -347,11 +356,11 @@ public: out << "(params"; svector::const_iterator it = m_entries.begin(); svector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - out << " " << it->first; + for (; it != end; ++it) { + out << " " << it->first; switch (it->second.m_kind) { case CPK_BOOL: - out << " " << it->second.m_bool_value; + out << " " << (it->second.m_bool_value?"true":"false"); break; case CPK_UINT: out << " " <second.m_uint_value; @@ -376,6 +385,41 @@ public: out << ")"; } + void display_smt2(std::ostream & out, char const* module, param_descrs& descrs) const { + svector::const_iterator it = m_entries.begin(); + svector::const_iterator end = m_entries.end(); + for (; it != end; ++it) { + if (!descrs.contains(it->first)) continue; + out << "(set-option :"; + out << module << "."; + out << it->first; + switch (it->second.m_kind) { + case CPK_BOOL: + out << " " << (it->second.m_bool_value?"true":"false"); + break; + case CPK_UINT: + out << " " <second.m_uint_value; + break; + case CPK_DOUBLE: + out << " " << it->second.m_double_value; + break; + case CPK_NUMERAL: + out << " " << *(it->second.m_rat_value); + break; + case CPK_SYMBOL: + out << " " << symbol::mk_symbol_from_c_ptr(it->second.m_sym_value); + break; + case CPK_STRING: + out << " " << it->second.m_str_value; + break; + default: + UNREACHABLE(); + break; + } + out << ")\n"; + } + } + void display(std::ostream & out, symbol const & k) const { svector::const_iterator it = m_entries.begin(); svector::const_iterator end = m_entries.end(); @@ -423,10 +467,17 @@ params_ref::params_ref(params_ref const & p): void params_ref::display(std::ostream & out) const { if (m_params) m_params->display(out); - else + else out << "(params)"; } +void params_ref::display_smt2(std::ostream& out, char const* module, param_descrs& descrs) const { + if (m_params) + m_params->display_smt2(out, module, descrs); + +} + + void params_ref::display(std::ostream & out, char const * k) const { display(out, symbol(k)); } diff --git a/src/util/params.h b/src/util/params.h index 15be825b0..06be486bb 100644 --- a/src/util/params.h +++ b/src/util/params.h @@ -90,6 +90,7 @@ public: void set_sym(char const * k, symbol const & v); void display(std::ostream & out) const; + void display_smt2(std::ostream& out, char const* module, param_descrs& module_desc) const; void validate(param_descrs const & p) const; From b03a9d3f0a58ac962d04792423aa1d2db56fec84 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 8 Oct 2014 21:01:27 +0100 Subject: [PATCH 461/509] Interpolation API: infrastructure fixes and .NET API Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_project.py | 2 +- scripts/update_api.py | 38 +- src/api/api_ast.cpp | 2 +- src/api/api_interp.cpp | 1200 ++++++++++++------------ src/api/dotnet/Constructor.cs | 6 +- src/api/dotnet/Context.cs | 2 +- src/api/dotnet/EnumSort.cs | 2 +- src/api/dotnet/Expr.cs | 10 +- src/api/dotnet/InterpolationContext.cs | 206 ++++ src/api/dotnet/Microsoft.Z3.csproj | 9 +- src/api/dotnet/TupleSort.cs | 3 +- src/api/python/z3.py | 14 +- src/api/z3.h | 1 + src/api/z3_api.h | 318 ------- src/api/z3_interp.h | 359 +++++++ 15 files changed, 1223 insertions(+), 949 deletions(-) create mode 100644 src/api/dotnet/InterpolationContext.cs create mode 100644 src/api/z3_interp.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 6f8fe1f7f..170124bd8 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -75,7 +75,7 @@ def init_project_def(): # dll_name='foci2', # export_files=['foci2stub.cpp']) # add_lib('interp', ['solver','foci2']) - API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h'] + API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_interp.h'] add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure', 'interp'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) add_exe('shell', ['api', 'sat', 'extra_cmds'], exe_name='z3') diff --git a/scripts/update_api.py b/scripts/update_api.py index 97dacb44b..791c23ddd 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -123,6 +123,7 @@ SYMBOL = 9 PRINT_MODE = 10 ERROR_CODE = 11 DOUBLE = 12 +UINT_PTR = 13 FIRST_OBJ_ID = 100 @@ -131,28 +132,28 @@ def is_obj(ty): Type2Str = { VOID : 'void', VOID_PTR : 'void*', INT : 'int', UINT : 'unsigned', INT64 : '__int64', UINT64 : '__uint64', DOUBLE : 'double', STRING : 'Z3_string', STRING_PTR : 'Z3_string_ptr', BOOL : 'Z3_bool', SYMBOL : 'Z3_symbol', - PRINT_MODE : 'Z3_ast_print_mode', ERROR_CODE : 'Z3_error_code', + PRINT_MODE : 'Z3_ast_print_mode', ERROR_CODE : 'Z3_error_code', UINT_PTR : 'unsigned*' } Type2PyStr = { VOID_PTR : 'ctypes.c_void_p', INT : 'ctypes.c_int', UINT : 'ctypes.c_uint', INT64 : 'ctypes.c_longlong', UINT64 : 'ctypes.c_ulonglong', DOUBLE : 'ctypes.c_double', STRING : 'ctypes.c_char_p', STRING_PTR : 'ctypes.POINTER(ctypes.c_char_p)', BOOL : 'ctypes.c_bool', SYMBOL : 'Symbol', - PRINT_MODE : 'ctypes.c_uint', ERROR_CODE : 'ctypes.c_uint', + PRINT_MODE : 'ctypes.c_uint', ERROR_CODE : 'ctypes.c_uint', UINT_PTR : 'ctypes.POINTER(ctypes.c_uint)' } # Mapping to .NET types Type2Dotnet = { VOID : 'void', VOID_PTR : 'IntPtr', INT : 'int', UINT : 'uint', INT64 : 'Int64', UINT64 : 'UInt64', DOUBLE : 'double', STRING : 'string', STRING_PTR : 'byte**', BOOL : 'int', SYMBOL : 'IntPtr', - PRINT_MODE : 'uint', ERROR_CODE : 'uint' } + PRINT_MODE : 'uint', ERROR_CODE : 'uint', UINT_PTR : 'uint[]'} # Mapping to Java types Type2Java = { VOID : 'void', VOID_PTR : 'long', INT : 'int', UINT : 'int', INT64 : 'long', UINT64 : 'long', DOUBLE : 'double', STRING : 'String', STRING_PTR : 'StringPtr', - BOOL : 'boolean', SYMBOL : 'long', PRINT_MODE : 'int', ERROR_CODE : 'int' } + BOOL : 'boolean', SYMBOL : 'long', PRINT_MODE : 'int', ERROR_CODE : 'int', UINT_PTR : 'int[]'} Type2JavaW = { VOID : 'void', VOID_PTR : 'jlong', INT : 'jint', UINT : 'jint', INT64 : 'jlong', UINT64 : 'jlong', DOUBLE : 'jdouble', STRING : 'jstring', STRING_PTR : 'jobject', - BOOL : 'jboolean', SYMBOL : 'jlong', PRINT_MODE : 'jint', ERROR_CODE : 'jint' } + BOOL : 'jboolean', SYMBOL : 'jlong', PRINT_MODE : 'jint', ERROR_CODE : 'jint', UINT_PTR : 'jlong'} next_type_id = FIRST_OBJ_ID @@ -259,7 +260,7 @@ def param2dotnet(p): if k == INOUT_ARRAY: return "[In, Out] %s[]" % type2dotnet(param_type(p)) if k == OUT_ARRAY: - return "[Out] %s[]" % type2dotnet(param_type(p)) + return "[Out] out %s[]" % type2dotnet(param_type(p)) else: return type2dotnet(param_type(p)) @@ -466,6 +467,8 @@ def mk_dotnet_wrappers(): dotnet.write('out '); else: dotnet.write('ref ') + elif param_kind(param) == OUT_ARRAY: + dotnet.write('out '); dotnet.write('a%d' % i) i = i + 1 dotnet.write(');\n'); @@ -953,20 +956,33 @@ def def_API(name, result, params): log_c.write(" Au(a%s);\n" % sz) exe_c.write("in.get_uint_array(%s)" % i) else: - error ("unsupported parameter for %s, %s" % (name, p)) + error ("unsupported parameter for %s, %s" % (ty, name, p)) elif kind == OUT_ARRAY: sz = param_array_capacity_pos(p) - log_c.write(" for (unsigned i = 0; i < a%s; i++) { " % sz) + sz_p = params[sz] + sz_p_k = param_kind(sz_p) + tstr = type2str(ty) + if sz_p_k == OUT or sz_p_k == INOUT: + sz_e = ("(*a%s)" % sz) + tstr = tstr + '*' + else: + sz_e = ("a%s" % sz) + log_c.write(" for (unsigned i = 0; i < %s; i++) { " % sz_e) if is_obj(ty): log_c.write("P(0);") log_c.write(" }\n") - log_c.write(" Ap(a%s);\n" % sz) - exe_c.write("reinterpret_cast<%s*>(in.get_obj_array(%s))" % (type2str(ty), i)) + log_c.write(" Ap(%s);\n" % sz_e) + exe_c.write("reinterpret_cast<%s*>(in.get_obj_array(%s))" % (tstr, i)) elif ty == UINT: log_c.write("U(0);") log_c.write(" }\n") - log_c.write(" Au(a%s);\n" % sz) + log_c.write(" Au(%s);\n" % sz_e) exe_c.write("in.get_uint_array(%s)" % i) + elif ty == UINT_PTR: + log_c.write("P(0);") + log_c.write(" }\n") + log_c.write(" Ap(%s);\n" % sz_e) + exe_c.write("reinterpret_cast<%s>(in.get_obj_array(%s))" % (tstr, i)) else: error ("unsupported parameter for %s, %s" % (name, p)) else: diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 715732be6..c2864ca2d 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -208,7 +208,7 @@ extern "C" { MK_BINARY(Z3_mk_xor, mk_c(c)->get_basic_fid(), OP_XOR, SKIP); MK_NARY(Z3_mk_and, mk_c(c)->get_basic_fid(), OP_AND, SKIP); MK_NARY(Z3_mk_or, mk_c(c)->get_basic_fid(), OP_OR, SKIP); - MK_UNARY(Z3_mk_interp, mk_c(c)->get_basic_fid(), OP_INTERP, SKIP); + MK_UNARY(Z3_mk_interpolant, mk_c(c)->get_basic_fid(), OP_INTERP, SKIP); Z3_ast mk_ite_core(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3) { expr * result = mk_c(c)->m().mk_ite(to_expr(t1), to_expr(t2), to_expr(t3)); diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index e06ad5192..8740e99a5 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -3,14 +3,14 @@ Copyright (c) 2011 Microsoft Corporation Module Name: - api_interp.cpp +api_interp.cpp Abstract: - API for interpolation +API for interpolation Author: - Ken McMillan +Ken McMillan Revision History: @@ -45,669 +45,669 @@ using namespace stl_ext; // WARNING: don't make a hash_map with this if the range type // has a destructor: you'll get an address dependency!!! namespace stl_ext { - template <> - class hash { - public: - size_t operator()(const Z3_ast p) const { - return (size_t) p; - } - }; + template <> + class hash < Z3_ast > { + public: + size_t operator()(const Z3_ast p) const { + return (size_t)p; + } + }; } typedef interpolation_options_struct *Z3_interpolation_options; extern "C" { - Z3_context Z3_mk_interpolation_context(Z3_config cfg){ - if(!cfg) cfg = Z3_mk_config(); - Z3_set_param_value(cfg, "PROOF", "true"); - Z3_set_param_value(cfg, "MODEL", "true"); - // Z3_set_param_value(cfg, "PRE_SIMPLIFIER","false"); - // Z3_set_param_value(cfg, "SIMPLIFY_CLAUSES","false"); - - Z3_context ctx = Z3_mk_context(cfg); - Z3_del_config(cfg); - return ctx; - } - - void Z3_interpolate_proof(Z3_context ctx, - Z3_ast proof, - int num, - Z3_ast *cnsts, - unsigned *parents, - Z3_params options, - Z3_ast *interps, - int num_theory, - Z3_ast *theory - ){ + Z3_context Z3_mk_interpolation_context(Z3_config cfg){ + if (!cfg) cfg = Z3_mk_config(); + Z3_set_param_value(cfg, "PROOF", "true"); + Z3_set_param_value(cfg, "MODEL", "true"); + // Z3_set_param_value(cfg, "PRE_SIMPLIFIER","false"); + // Z3_set_param_value(cfg, "SIMPLIFY_CLAUSES","false"); - if(num > 1){ // if we have interpolants to compute - - ptr_vector pre_cnsts_vec(num); // get constraints in a vector - for(int i = 0; i < num; i++){ - ast *a = to_ast(cnsts[i]); - pre_cnsts_vec[i] = a; - } - - ::vector pre_parents_vec; // get parents in a vector - if(parents){ - pre_parents_vec.resize(num); - for(int i = 0; i < num; i++) - pre_parents_vec[i] = parents[i]; + Z3_context ctx = Z3_mk_context(cfg); + Z3_del_config(cfg); + return ctx; + } + + void Z3_interpolate_proof(Z3_context ctx, + Z3_ast proof, + int num, + Z3_ast *cnsts, + unsigned *parents, + Z3_params options, + Z3_ast *interps, + int num_theory, + Z3_ast *theory) + { + + if (num > 1){ // if we have interpolants to compute + + ptr_vector pre_cnsts_vec(num); // get constraints in a vector + for (int i = 0; i < num; i++){ + ast *a = to_ast(cnsts[i]); + pre_cnsts_vec[i] = a; + } + + ::vector pre_parents_vec; // get parents in a vector + if (parents){ + pre_parents_vec.resize(num); + for (int i = 0; i < num; i++) + pre_parents_vec[i] = parents[i]; + } + + ptr_vector theory_vec; // get background theory in a vector + if (theory){ + theory_vec.resize(num_theory); + for (int i = 0; i < num_theory; i++) + theory_vec[i] = to_ast(theory[i]); + } + + ptr_vector interpolants(num - 1); // make space for result + + ast_manager &_m = mk_c(ctx)->m(); + iz3interpolate(_m, + to_ast(proof), + pre_cnsts_vec, + pre_parents_vec, + interpolants, + theory_vec, + 0); // ignore params for now FIXME + + // copy result back + for (unsigned i = 0; i < interpolants.size(); i++){ + mk_c(ctx)->save_ast_trail(interpolants[i]); + interps[i] = of_ast(interpolants[i]); + _m.dec_ref(interpolants[i]); + } } - - ptr_vector theory_vec; // get background theory in a vector - if(theory){ - theory_vec.resize(num_theory); - for(int i = 0; i < num_theory; i++) - theory_vec[i] = to_ast(theory[i]); - } - - ptr_vector interpolants(num-1); // make space for result - - ast_manager &_m = mk_c(ctx)->m(); - iz3interpolate(_m, - to_ast(proof), - pre_cnsts_vec, - pre_parents_vec, - interpolants, - theory_vec, - 0); // ignore params for now FIXME + } - // copy result back - for(unsigned i = 0; i < interpolants.size(); i++){ - mk_c(ctx)->save_ast_trail(interpolants[i]); - interps[i] = of_ast(interpolants[i]); - _m.dec_ref(interpolants[i]); - } - } - } + Z3_lbool Z3_interpolate(Z3_context ctx, + unsigned num, + Z3_ast *cnsts, + unsigned *parents, + Z3_params options, + Z3_ast *interps, + Z3_model *model, + Z3_literals *labels, + unsigned incremental, + unsigned num_theory, + Z3_ast *theory + ){ - Z3_lbool Z3_interpolate(Z3_context ctx, - int num, - Z3_ast *cnsts, - unsigned *parents, - Z3_params options, - Z3_ast *interps, - Z3_model *model, - Z3_literals *labels, - int incremental, - int num_theory, - Z3_ast *theory - ){ - - profiling::timer_start("Solve"); + profiling::timer_start("Solve"); - if(!incremental){ + if (!incremental){ - profiling::timer_start("Z3 assert"); + profiling::timer_start("Z3 assert"); - Z3_push(ctx); // so we can rewind later - - for(int i = 0; i < num; i++) - Z3_assert_cnstr(ctx,cnsts[i]); // assert all the constraints + Z3_push(ctx); // so we can rewind later - if(theory){ - for(int i = 0; i < num_theory; i++) - Z3_assert_cnstr(ctx,theory[i]); - } + for (int i = 0; i < num; i++) + Z3_assert_cnstr(ctx, cnsts[i]); // assert all the constraints - profiling::timer_stop("Z3 assert"); + if (theory){ + for (int i = 0; i < num_theory; i++) + Z3_assert_cnstr(ctx, theory[i]); + } + + profiling::timer_stop("Z3 assert"); + } + + + // Get a proof of unsat + + Z3_ast proof; + Z3_lbool result; + + profiling::timer_start("Z3 solving"); + result = Z3_check_assumptions(ctx, 0, 0, model, &proof, 0, 0); + profiling::timer_stop("Z3 solving"); + + switch (result) { + case Z3_L_FALSE: + + Z3_interpolate_proof(ctx, + proof, + num, + cnsts, + parents, + options, + interps, + num_theory, + theory); + + if (!incremental) + for (int i = 0; i < num - 1; i++) + Z3_persist_ast(ctx, interps[i], 1); + break; + + case Z3_L_UNDEF: + if (labels) + *labels = Z3_get_relevant_labels(ctx); + break; + + case Z3_L_TRUE: + if (labels) + *labels = Z3_get_relevant_labels(ctx); + break; + } + + profiling::timer_start("Z3 pop"); + if (!incremental) + Z3_pop(ctx, 1); + profiling::timer_stop("Z3 pop"); + + profiling::timer_stop("Solve"); + + return result; + + } + + static std::ostringstream itp_err; + + int Z3_check_interpolant(Z3_context ctx, + unsigned num, + Z3_ast *cnsts, + unsigned *parents, + Z3_ast *itp, + Z3_string *error, + unsigned num_theory, + Z3_ast *theory){ + + ast_manager &_m = mk_c(ctx)->m(); + itp_err.clear(); + + // need a solver -- make one here, but how? + params_ref p = params_ref::get_empty(); //FIXME + scoped_ptr sf(mk_smt_solver_factory()); + scoped_ptr sp((*(sf))(_m, p, false, true, false, symbol("AUFLIA"))); + + ptr_vector cnsts_vec(num); // get constraints in a vector + for (int i = 0; i < num; i++){ + ast *a = to_ast(cnsts[i]); + cnsts_vec[i] = a; + } + + ptr_vector itp_vec(num); // get interpolants in a vector + for (int i = 0; i < num - 1; i++){ + ast *a = to_ast(itp[i]); + itp_vec[i] = a; + } + + ::vector parents_vec; // get parents in a vector + if (parents){ + parents_vec.resize(num); + for (int i = 0; i < num; i++) + parents_vec[i] = parents[i]; + } + + ptr_vector theory_vec; // get background theory in a vector + if (theory){ + theory_vec.resize(num_theory); + for (int i = 0; i < num_theory; i++) + theory_vec[i] = to_ast(theory[i]); + } + + bool res = iz3check(_m, + sp.get(), + itp_err, + cnsts_vec, + parents_vec, + itp_vec, + theory_vec); + + *error = res ? 0 : itp_err.str().c_str(); + return res; } - // Get a proof of unsat - - Z3_ast proof; - Z3_lbool result; - - profiling::timer_start("Z3 solving"); - result = Z3_check_assumptions(ctx, 0, 0, model, &proof, 0, 0); - profiling::timer_stop("Z3 solving"); - - switch (result) { - case Z3_L_FALSE: - - Z3_interpolate_proof(ctx, - proof, - num, - cnsts, - parents, - options, - interps, - num_theory, - theory); + static std::string Z3_profile_string; - if(!incremental) - for(int i = 0; i < num-1; i++) - Z3_persist_ast(ctx,interps[i],1); - break; - - case Z3_L_UNDEF: - if(labels) - *labels = Z3_get_relevant_labels(ctx); - break; - - case Z3_L_TRUE: - if(labels) - *labels = Z3_get_relevant_labels(ctx); - break; + Z3_string Z3_interpolation_profile(Z3_context ctx){ + std::ostringstream f; + profiling::print(f); + Z3_profile_string = f.str(); + return Z3_profile_string.c_str(); } - profiling::timer_start("Z3 pop"); - if(!incremental) - Z3_pop(ctx,1); - profiling::timer_stop("Z3 pop"); - profiling::timer_stop("Solve"); - - return result; - - } - - static std::ostringstream itp_err; - - int Z3_check_interpolant(Z3_context ctx, - int num, - Z3_ast *cnsts, - int *parents, - Z3_ast *itp, - const char **error, - int num_theory, - Z3_ast *theory){ - - ast_manager &_m = mk_c(ctx)->m(); - itp_err.clear(); - - // need a solver -- make one here, but how? - params_ref p = params_ref::get_empty(); //FIXME - scoped_ptr sf(mk_smt_solver_factory()); - scoped_ptr sp((*(sf))(_m, p, false, true, false, symbol("AUFLIA"))); - - ptr_vector cnsts_vec(num); // get constraints in a vector - for(int i = 0; i < num; i++){ - ast *a = to_ast(cnsts[i]); - cnsts_vec[i] = a; - } - - ptr_vector itp_vec(num); // get interpolants in a vector - for(int i = 0; i < num-1; i++){ - ast *a = to_ast(itp[i]); - itp_vec[i] = a; + Z3_interpolation_options + Z3_mk_interpolation_options(){ + return (Z3_interpolation_options) new interpolation_options_struct; } - ::vector parents_vec; // get parents in a vector - if(parents){ - parents_vec.resize(num); - for(int i = 0; i < num; i++) - parents_vec[i] = parents[i]; - } - - ptr_vector theory_vec; // get background theory in a vector - if(theory){ - theory_vec.resize(num_theory); - for(int i = 0; i < num_theory; i++) - theory_vec[i] = to_ast(theory[i]); - } - - bool res = iz3check(_m, - sp.get(), - itp_err, - cnsts_vec, - parents_vec, - itp_vec, - theory_vec); - - *error = res ? 0 : itp_err.str().c_str(); - return res; - } - - - static std::string Z3_profile_string; - - Z3_string Z3_interpolation_profile(Z3_context ctx){ - std::ostringstream f; - profiling::print(f); - Z3_profile_string = f.str(); - return Z3_profile_string.c_str(); - } - - - Z3_interpolation_options - Z3_mk_interpolation_options(){ - return (Z3_interpolation_options) new interpolation_options_struct; - } - - void - Z3_del_interpolation_options(Z3_interpolation_options opts){ - delete opts; - } - - void - Z3_set_interpolation_option(Z3_interpolation_options opts, - Z3_string name, - Z3_string value){ - opts->map[name] = value; - } - - Z3_ast_vector Z3_API Z3_get_interpolant(__in Z3_context c, __in Z3_ast pf, __in Z3_ast pat, __in Z3_params p){ - Z3_TRY; - LOG_Z3_get_interpolant(c, pf, pat, p); - RESET_ERROR_CODE(); - - Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); - mk_c(c)->save_object(v); - - ast *_pf = to_ast(pf); - ast *_pat = to_ast(pat); - - ptr_vector interp; - ptr_vector cnsts; // to throw away - - ast_manager &_m = mk_c(c)->m(); - - iz3interpolate(_m, - _pf, - cnsts, - _pat, - interp, - (interpolation_options_struct *) 0 // ignore params for now - ); - - // copy result back - for(unsigned i = 0; i < interp.size(); i++){ - v->m_ast_vector.push_back(interp[i]); - _m.dec_ref(interp[i]); - } - RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); - } - - Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, __in Z3_ast pat, __in Z3_params p, __out Z3_ast_vector *out_interp, __out Z3_model *model){ - Z3_TRY; - LOG_Z3_compute_interpolant(c, pat, p, out_interp, model); - RESET_ERROR_CODE(); - - - // params_ref &_p = to_params(p)->m_params; - params_ref _p; - _p.set_bool("proof", true); // this is currently useless - - scoped_proof_mode spm(mk_c(c)->m(),PGM_FINE); - scoped_ptr sf = mk_smt_solver_factory(); - scoped_ptr m_solver((*sf)(mk_c(c)->m(), _p, true, true, true, ::symbol::null)); - m_solver.get()->updt_params(_p); // why do we have to do this? - - ast *_pat = to_ast(pat); - - ptr_vector interp; - ptr_vector cnsts; // to throw away - - ast_manager &_m = mk_c(c)->m(); - - model_ref m; - lbool _status = iz3interpolate(_m, - *(m_solver.get()), - _pat, - cnsts, - interp, - m, - 0 // ignore params for now - ); - - Z3_lbool status = of_lbool(_status); - - Z3_ast_vector_ref *v = 0; - *model = 0; - - if(_status == l_false){ - // copy result back - v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); - mk_c(c)->save_object(v); - for(unsigned i = 0; i < interp.size(); i++){ - v->m_ast_vector.push_back(interp[i]); - _m.dec_ref(interp[i]); - } - } - else { - model_ref _m; - m_solver.get()->get_model(_m); - Z3_model_ref *crap = alloc(Z3_model_ref); - crap->m_model = _m.get(); - mk_c(c)->save_object(crap); - *model = of_model(crap); + void + Z3_del_interpolation_options(Z3_interpolation_options opts){ + delete opts; } - *out_interp = of_ast_vector(v); - - return status; - Z3_CATCH_RETURN(Z3_L_UNDEF); - } + void + Z3_set_interpolation_option(Z3_interpolation_options opts, + Z3_string name, + Z3_string value){ + opts->map[name] = value; + } + + Z3_ast_vector Z3_API Z3_get_interpolant(__in Z3_context c, __in Z3_ast pf, __in Z3_ast pat, __in Z3_params p){ + Z3_TRY; + LOG_Z3_get_interpolant(c, pf, pat, p); + RESET_ERROR_CODE(); + + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + mk_c(c)->save_object(v); + + ast *_pf = to_ast(pf); + ast *_pat = to_ast(pat); + + ptr_vector interp; + ptr_vector cnsts; // to throw away + + ast_manager &_m = mk_c(c)->m(); + + iz3interpolate(_m, + _pf, + cnsts, + _pat, + interp, + (interpolation_options_struct *)0 // ignore params for now + ); + + // copy result back + for (unsigned i = 0; i < interp.size(); i++){ + v->m_ast_vector.push_back(interp[i]); + _m.dec_ref(interp[i]); + } + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(0); + } + + Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, __in Z3_ast pat, __in Z3_params p, __out Z3_ast_vector *out_interp, __out Z3_model *model){ + Z3_TRY; + LOG_Z3_compute_interpolant(c, pat, p, out_interp, model); + RESET_ERROR_CODE(); + + + // params_ref &_p = to_params(p)->m_params; + params_ref _p; + _p.set_bool("proof", true); // this is currently useless + + scoped_proof_mode spm(mk_c(c)->m(), PGM_FINE); + scoped_ptr sf = mk_smt_solver_factory(); + scoped_ptr m_solver((*sf)(mk_c(c)->m(), _p, true, true, true, ::symbol::null)); + m_solver.get()->updt_params(_p); // why do we have to do this? + + ast *_pat = to_ast(pat); + + ptr_vector interp; + ptr_vector cnsts; // to throw away + + ast_manager &_m = mk_c(c)->m(); + + model_ref m; + lbool _status = iz3interpolate(_m, + *(m_solver.get()), + _pat, + cnsts, + interp, + m, + 0 // ignore params for now + ); + + Z3_lbool status = of_lbool(_status); + + Z3_ast_vector_ref *v = 0; + *model = 0; + + if (_status == l_false){ + // copy result back + v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + mk_c(c)->save_object(v); + for (unsigned i = 0; i < interp.size(); i++){ + v->m_ast_vector.push_back(interp[i]); + _m.dec_ref(interp[i]); + } + } + else { + model_ref _m; + m_solver.get()->get_model(_m); + Z3_model_ref *crap = alloc(Z3_model_ref); + crap->m_model = _m.get(); + mk_c(c)->save_object(crap); + *model = of_model(crap); + } + + *out_interp = of_ast_vector(v); + + return status; + Z3_CATCH_RETURN(Z3_L_UNDEF); + } }; static void tokenize(const std::string &str, std::vector &tokens){ - for(unsigned i = 0; i < str.size();){ - if(str[i] == ' '){i++; continue;} - unsigned beg = i; - while(i < str.size() && str[i] != ' ')i++; - if(i > beg) - tokens.push_back(str.substr(beg,i-beg)); - } + for (unsigned i = 0; i < str.size();){ + if (str[i] == ' '){ i++; continue; } + unsigned beg = i; + while (i < str.size() && str[i] != ' ')i++; + if (i > beg) + tokens.push_back(str.substr(beg, i - beg)); + } } -static void get_file_params(const char *filename, hash_map ¶ms){ - std::ifstream f(filename); - if(f){ - std::string first_line; - std::getline(f,first_line); - // std::cout << "first line: '" << first_line << "'" << std::endl; - if(first_line.size() >= 2 && first_line[0] == ';' && first_line[1] == '!'){ - std::vector tokens; - tokenize(first_line.substr(2,first_line.size()-2),tokens); - for(unsigned i = 0; i < tokens.size(); i++){ - std::string &tok = tokens[i]; - size_t eqpos = tok.find('='); - if(eqpos != std::string::npos){ - std::string left = tok.substr(0,eqpos); - std::string right = tok.substr(eqpos+1,tok.size()-eqpos-1); - params[left] = right; +static void get_file_params(const char *filename, hash_map ¶ms){ + std::ifstream f(filename); + if (f){ + std::string first_line; + std::getline(f, first_line); + // std::cout << "first line: '" << first_line << "'" << std::endl; + if (first_line.size() >= 2 && first_line[0] == ';' && first_line[1] == '!'){ + std::vector tokens; + tokenize(first_line.substr(2, first_line.size() - 2), tokens); + for (unsigned i = 0; i < tokens.size(); i++){ + std::string &tok = tokens[i]; + size_t eqpos = tok.find('='); + if (eqpos != std::string::npos){ + std::string left = tok.substr(0, eqpos); + std::string right = tok.substr(eqpos + 1, tok.size() - eqpos - 1); + params[left] = right; + } + } } - } + f.close(); } - f.close(); - } } extern "C" { #if 0 - static void iZ3_write_seq(Z3_context ctx, int num, Z3_ast *cnsts, const char *filename, int num_theory, Z3_ast *theory){ - int num_fmlas = num+num_theory; - std::vector fmlas(num_fmlas); - if(num_theory) - std::copy(theory,theory+num_theory,fmlas.begin()); - for(int i = 0; i < num_theory; i++) - fmlas[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),fmlas[i]); - std::copy(cnsts,cnsts+num,fmlas.begin()+num_theory); - Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"none","AUFLIA","unknown","",num_fmlas-1,&fmlas[0],fmlas[num_fmlas-1]); - std::ofstream f(filename); - if(num_theory) - f << ";! THEORY=" << num_theory << "\n"; - f << smt; - f.close(); - } + static void iZ3_write_seq(Z3_context ctx, int num, Z3_ast *cnsts, const char *filename, int num_theory, Z3_ast *theory){ + int num_fmlas = num+num_theory; + std::vector fmlas(num_fmlas); + if(num_theory) + std::copy(theory,theory+num_theory,fmlas.begin()); + for(int i = 0; i < num_theory; i++) + fmlas[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),fmlas[i]); + std::copy(cnsts,cnsts+num,fmlas.begin()+num_theory); + Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"none","AUFLIA","unknown","",num_fmlas-1,&fmlas[0],fmlas[num_fmlas-1]); + std::ofstream f(filename); + if(num_theory) + f << ";! THEORY=" << num_theory << "\n"; + f << smt; + f.close(); + } - void Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, const char *filename, int num_theory, Z3_ast *theory){ - if(!parents){ - iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); - return; + void Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, unsigned *parents, const char *filename, int num_theory, Z3_ast *theory){ + if(!parents){ + iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); + return; + } + std::vector tcnsts(num); + hash_map syms; + for(int j = 0; j < num - 1; j++){ + std::ostringstream oss; + oss << "$P" << j; + std::string name = oss.str(); + Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); + Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); + syms[j] = symbol; + tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); + } + tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); + for(int j = num-2; j >= 0; j--){ + int parent = parents[j]; + // assert(parent >= 0 && parent < num); + tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); + } + iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); } - std::vector tcnsts(num); - hash_map syms; - for(int j = 0; j < num - 1; j++){ - std::ostringstream oss; - oss << "$P" << j; - std::string name = oss.str(); - Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); - Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); - syms[j] = symbol; - tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); - } - tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); - for(int j = num-2; j >= 0; j--){ - int parent = parents[j]; - // assert(parent >= 0 && parent < num); - tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); - } - iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); - } #else - static Z3_ast and_vec(Z3_context ctx,svector &c){ - return (c.size() > 1) ? Z3_mk_and(ctx,c.size(),&c[0]) : c[0]; - } - - static Z3_ast parents_vector_to_tree(Z3_context ctx, int num, Z3_ast *cnsts, int *parents){ - Z3_ast res; - if(!parents){ - res = Z3_mk_interp(ctx,cnsts[0]); - for(int i = 1; i < num-1; i++){ - Z3_ast bar[2] = {res,cnsts[i]}; - res = Z3_mk_interp(ctx,Z3_mk_and(ctx,2,bar)); - } - if(num > 1){ - Z3_ast bar[2] = {res,cnsts[num-1]}; - res = Z3_mk_and(ctx,2,bar); - } + static Z3_ast and_vec(Z3_context ctx, svector &c){ + return (c.size() > 1) ? Z3_mk_and(ctx, c.size(), &c[0]) : c[0]; } - else { - std::vector > chs(num); - for(int i = 0; i < num-1; i++){ - svector &c = chs[i]; - c.push_back(cnsts[i]); - Z3_ast foo = Z3_mk_interp(ctx,and_vec(ctx,c)); - chs[parents[i]].push_back(foo); - } - { - svector &c = chs[num-1]; - c.push_back(cnsts[num-1]); - res = and_vec(ctx,c); - } - } - Z3_inc_ref(ctx,res); - return res; - } - void Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, const char *filename, int num_theory, Z3_ast *theory){ - std::ofstream f(filename); - if(num > 0){ - ptr_vector cnsts_vec(num); // get constraints in a vector - for(int i = 0; i < num; i++){ - expr *a = to_expr(cnsts[i]); - cnsts_vec[i] = a; + static Z3_ast parents_vector_to_tree(Z3_context ctx, int num, Z3_ast *cnsts, unsigned *parents){ + Z3_ast res; + if (!parents){ + res = Z3_mk_interpolant(ctx, cnsts[0]); + for (int i = 1; i < num - 1; i++){ + Z3_ast bar[2] = { res, cnsts[i] }; + res = Z3_mk_interpolant(ctx, Z3_mk_and(ctx, 2, bar)); + } + if (num > 1){ + Z3_ast bar[2] = { res, cnsts[num - 1] }; + res = Z3_mk_and(ctx, 2, bar); + } + } + else { + std::vector > chs(num); + for (int i = 0; i < num - 1; i++){ + svector &c = chs[i]; + c.push_back(cnsts[i]); + Z3_ast foo = Z3_mk_interpolant(ctx, and_vec(ctx, c)); + chs[parents[i]].push_back(foo); + } + { + svector &c = chs[num - 1]; + c.push_back(cnsts[num - 1]); + res = and_vec(ctx, c); } - Z3_ast tree = parents_vector_to_tree(ctx,num,cnsts,parents); - for(int i = 0; i < num_theory; i++){ - expr *a = to_expr(theory[i]); - cnsts_vec.push_back(a); - } - iz3pp(mk_c(ctx)->m(),cnsts_vec,to_expr(tree),f); - Z3_dec_ref(ctx,tree); + } + Z3_inc_ref(ctx, res); + return res; } - f.close(); + + void Z3_write_interpolation_problem(Z3_context ctx, unsigned num, Z3_ast *cnsts, unsigned *parents, const char *filename, unsigned num_theory, Z3_ast *theory){ + std::ofstream f(filename); + if (num > 0){ + ptr_vector cnsts_vec(num); // get constraints in a vector + for (int i = 0; i < num; i++){ + expr *a = to_expr(cnsts[i]); + cnsts_vec[i] = a; + } + Z3_ast tree = parents_vector_to_tree(ctx, num, cnsts, parents); + for (int i = 0; i < num_theory; i++){ + expr *a = to_expr(theory[i]); + cnsts_vec.push_back(a); + } + iz3pp(mk_c(ctx)->m(), cnsts_vec, to_expr(tree), f); + Z3_dec_ref(ctx, tree); + } + f.close(); #if 0 - - if(!parents){ - iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); - return; - } - std::vector tcnsts(num); - hash_map syms; - for(int j = 0; j < num - 1; j++){ - std::ostringstream oss; - oss << "$P" << j; - std::string name = oss.str(); - Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); - Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); - syms[j] = symbol; - tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); - } - tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); - for(int j = num-2; j >= 0; j--){ - int parent = parents[j]; - // assert(parent >= 0 && parent < num); - tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); - } - iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); + + if(!parents){ + iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); + return; + } + std::vector tcnsts(num); + hash_map syms; + for(int j = 0; j < num - 1; j++){ + std::ostringstream oss; + oss << "$P" << j; + std::string name = oss.str(); + Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); + Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); + syms[j] = symbol; + tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); + } + tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); + for(int j = num-2; j >= 0; j--){ + int parent = parents[j]; + // assert(parent >= 0 && parent < num); + tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); + } + iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); #endif - } + } #endif - static std::vector read_cnsts; - static std::vector read_parents; - static std::ostringstream read_error; - static std::string read_msg; - static std::vector read_theory; + static std::vector read_cnsts; + static std::vector read_parents; + static std::ostringstream read_error; + static std::string read_msg; + static std::vector read_theory; - static bool iZ3_parse(Z3_context ctx, const char *filename, const char **error, svector &assertions){ - read_error.clear(); - try { - std::string foo(filename); - if(foo.size() >= 5 && foo.substr(foo.size()-5) == ".smt2"){ - Z3_ast ass = Z3_parse_smtlib2_file(ctx, filename, 0, 0, 0, 0, 0, 0); - Z3_app app = Z3_to_app(ctx,ass); - int nconjs = Z3_get_app_num_args(ctx,app); - assertions.resize(nconjs); - for(int k = 0; k < nconjs; k++) - assertions[k] = Z3_get_app_arg(ctx,app,k); - } - else { - Z3_parse_smtlib_file(ctx, filename, 0, 0, 0, 0, 0, 0); - int numa = Z3_get_smtlib_num_assumptions(ctx); - int numf = Z3_get_smtlib_num_formulas(ctx); - int num = numa + numf; - - assertions.resize(num); - for(int j = 0; j < num; j++){ - if(j < numa) - assertions[j] = Z3_get_smtlib_assumption(ctx,j); - else - assertions[j] = Z3_get_smtlib_formula(ctx,j-numa); - } - } - } - catch(...) { - read_error << "SMTLIB parse error: " << Z3_get_smtlib_error(ctx); - read_msg = read_error.str(); - *error = read_msg.c_str(); - return false; - } - Z3_set_error_handler(ctx, 0); - return true; - } - + static bool iZ3_parse(Z3_context ctx, const char *filename, const char **error, svector &assertions){ + read_error.clear(); + try { + std::string foo(filename); + if (foo.size() >= 5 && foo.substr(foo.size() - 5) == ".smt2"){ + Z3_ast ass = Z3_parse_smtlib2_file(ctx, filename, 0, 0, 0, 0, 0, 0); + Z3_app app = Z3_to_app(ctx, ass); + int nconjs = Z3_get_app_num_args(ctx, app); + assertions.resize(nconjs); + for (int k = 0; k < nconjs; k++) + assertions[k] = Z3_get_app_arg(ctx, app, k); + } + else { + Z3_parse_smtlib_file(ctx, filename, 0, 0, 0, 0, 0, 0); + int numa = Z3_get_smtlib_num_assumptions(ctx); + int numf = Z3_get_smtlib_num_formulas(ctx); + int num = numa + numf; - int Z3_read_interpolation_problem(Z3_context ctx, int *_num, Z3_ast **cnsts, int **parents, const char *filename, const char **error, int *ret_num_theory, Z3_ast **theory ){ - - hash_map file_params; - get_file_params(filename,file_params); - - unsigned num_theory = 0; - if(file_params.find("THEORY") != file_params.end()) - num_theory = atoi(file_params["THEORY"].c_str()); - - svector assertions; - if(!iZ3_parse(ctx,filename,error,assertions)) - return false; - - if(num_theory > assertions.size()) - num_theory = assertions.size(); - unsigned num = assertions.size() - num_theory; - - read_cnsts.resize(num); - read_parents.resize(num); - read_theory.resize(num_theory); - - for(unsigned j = 0; j < num_theory; j++) - read_theory[j] = assertions[j]; - for(unsigned j = 0; j < num; j++) - read_cnsts[j] = assertions[j+num_theory]; - - if(ret_num_theory) - *ret_num_theory = num_theory; - if(theory) - *theory = &read_theory[0]; - - if(!parents){ - *_num = num; - *cnsts = &read_cnsts[0]; - return true; + assertions.resize(num); + for (int j = 0; j < num; j++){ + if (j < numa) + assertions[j] = Z3_get_smtlib_assumption(ctx, j); + else + assertions[j] = Z3_get_smtlib_formula(ctx, j - numa); + } + } + } + catch (...) { + read_error << "SMTLIB parse error: " << Z3_get_smtlib_error(ctx); + read_msg = read_error.str(); + *error = read_msg.c_str(); + return false; + } + Z3_set_error_handler(ctx, 0); + return true; } - for(unsigned j = 0; j < num; j++) - read_parents[j] = SHRT_MAX; - - hash_map pred_map; - for(unsigned j = 0; j < num; j++){ - Z3_ast lhs = 0, rhs = read_cnsts[j]; + int Z3_read_interpolation_problem(Z3_context ctx, unsigned *_num, Z3_ast **cnsts, unsigned **parents, const char *filename, const char **error, unsigned *ret_num_theory, Z3_ast **theory){ - if(Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx,Z3_to_app(ctx,rhs))) == Z3_OP_IMPLIES){ - Z3_app app1 = Z3_to_app(ctx,rhs); - Z3_ast lhs1 = Z3_get_app_arg(ctx,app1,0); - Z3_ast rhs1 = Z3_get_app_arg(ctx,app1,1); - if(Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx,Z3_to_app(ctx,lhs1))) == Z3_OP_AND){ - Z3_app app2 = Z3_to_app(ctx,lhs1); - int nconjs = Z3_get_app_num_args(ctx,app2); - for(int k = nconjs - 1; k >= 0; --k) - rhs1 = Z3_mk_implies(ctx,Z3_get_app_arg(ctx,app2,k),rhs1); - rhs = rhs1; - } - } + hash_map file_params; + get_file_params(filename, file_params); + + unsigned num_theory = 0; + if (file_params.find("THEORY") != file_params.end()) + num_theory = atoi(file_params["THEORY"].c_str()); + + svector assertions; + if (!iZ3_parse(ctx, filename, error, assertions)) + return false; + + if (num_theory > assertions.size()) + num_theory = assertions.size(); + unsigned num = assertions.size() - num_theory; + + read_cnsts.resize(num); + read_parents.resize(num); + read_theory.resize(num_theory); + + for (unsigned j = 0; j < num_theory; j++) + read_theory[j] = assertions[j]; + for (unsigned j = 0; j < num; j++) + read_cnsts[j] = assertions[j + num_theory]; + + if (ret_num_theory) + *ret_num_theory = num_theory; + if (theory) + *theory = &read_theory[0]; + + if (!parents){ + *_num = num; + *cnsts = &read_cnsts[0]; + return true; + } + + for (unsigned j = 0; j < num; j++) + read_parents[j] = SHRT_MAX; + + hash_map pred_map; + + for (unsigned j = 0; j < num; j++){ + Z3_ast lhs = 0, rhs = read_cnsts[j]; + + if (Z3_get_decl_kind(ctx, Z3_get_app_decl(ctx, Z3_to_app(ctx, rhs))) == Z3_OP_IMPLIES){ + Z3_app app1 = Z3_to_app(ctx, rhs); + Z3_ast lhs1 = Z3_get_app_arg(ctx, app1, 0); + Z3_ast rhs1 = Z3_get_app_arg(ctx, app1, 1); + if (Z3_get_decl_kind(ctx, Z3_get_app_decl(ctx, Z3_to_app(ctx, lhs1))) == Z3_OP_AND){ + Z3_app app2 = Z3_to_app(ctx, lhs1); + int nconjs = Z3_get_app_num_args(ctx, app2); + for (int k = nconjs - 1; k >= 0; --k) + rhs1 = Z3_mk_implies(ctx, Z3_get_app_arg(ctx, app2, k), rhs1); + rhs = rhs1; + } + } + + while (1){ + Z3_app app = Z3_to_app(ctx, rhs); + Z3_func_decl func = Z3_get_app_decl(ctx, app); + Z3_decl_kind dk = Z3_get_decl_kind(ctx, func); + if (dk == Z3_OP_IMPLIES){ + if (lhs){ + Z3_ast child = lhs; + if (pred_map.find(child) == pred_map.end()){ + read_error << "formula " << j + 1 << ": unknown: " << Z3_ast_to_string(ctx, child); + goto fail; + } + int child_num = pred_map[child]; + if (read_parents[child_num] != SHRT_MAX){ + read_error << "formula " << j + 1 << ": multiple reference: " << Z3_ast_to_string(ctx, child); + goto fail; + } + read_parents[child_num] = j; + } + lhs = Z3_get_app_arg(ctx, app, 0); + rhs = Z3_get_app_arg(ctx, app, 1); + } + else { + if (!lhs){ + read_error << "formula " << j + 1 << ": should be (implies {children} fmla parent)"; + goto fail; + } + read_cnsts[j] = lhs; + Z3_ast name = rhs; + if (pred_map.find(name) != pred_map.end()){ + read_error << "formula " << j + 1 << ": duplicate symbol"; + goto fail; + } + pred_map[name] = j; + break; + } + } + } + + for (unsigned j = 0; j < num - 1; j++) + if (read_parents[j] == SHRT_MIN){ + read_error << "formula " << j + 1 << ": unreferenced"; + goto fail; + } + + *_num = num; + *cnsts = &read_cnsts[0]; + *parents = &read_parents[0]; + return true; + + fail: + read_msg = read_error.str(); + *error = read_msg.c_str(); + return false; - while(1){ - Z3_app app = Z3_to_app(ctx,rhs); - Z3_func_decl func = Z3_get_app_decl(ctx,app); - Z3_decl_kind dk = Z3_get_decl_kind(ctx,func); - if(dk == Z3_OP_IMPLIES){ - if(lhs){ - Z3_ast child = lhs; - if(pred_map.find(child) == pred_map.end()){ - read_error << "formula " << j+1 << ": unknown: " << Z3_ast_to_string(ctx,child); - goto fail; - } - int child_num = pred_map[child]; - if(read_parents[child_num] != SHRT_MAX){ - read_error << "formula " << j+1 << ": multiple reference: " << Z3_ast_to_string(ctx,child); - goto fail; - } - read_parents[child_num] = j; - } - lhs = Z3_get_app_arg(ctx,app,0); - rhs = Z3_get_app_arg(ctx,app,1); - } - else { - if(!lhs){ - read_error << "formula " << j+1 << ": should be (implies {children} fmla parent)"; - goto fail; - } - read_cnsts[j] = lhs; - Z3_ast name = rhs; - if(pred_map.find(name) != pred_map.end()){ - read_error << "formula " << j+1 << ": duplicate symbol"; - goto fail; - } - pred_map[name] = j; - break; - } - } } - - for(unsigned j = 0; j < num-1; j++) - if(read_parents[j] == SHRT_MIN){ - read_error << "formula " << j+1 << ": unreferenced"; - goto fail; - } - - *_num = num; - *cnsts = &read_cnsts[0]; - *parents = &read_parents[0]; - return true; - - fail: - read_msg = read_error.str(); - *error = read_msg.c_str(); - return false; - - } } diff --git a/src/api/dotnet/Constructor.cs b/src/api/dotnet/Constructor.cs index 527b8bc13..8d478dd85 100644 --- a/src/api/dotnet/Constructor.cs +++ b/src/api/dotnet/Constructor.cs @@ -50,7 +50,7 @@ namespace Microsoft.Z3 IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; - Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); + Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, out accessors); return new FuncDecl(Context, constructor); } } @@ -66,7 +66,7 @@ namespace Microsoft.Z3 IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; - Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); + Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, out accessors); return new FuncDecl(Context, tester); } } @@ -82,7 +82,7 @@ namespace Microsoft.Z3 IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; - Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); + Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, out accessors); FuncDecl[] t = new FuncDecl[n]; for (uint i = 0; i < n; i++) t[i] = new FuncDecl(Context, accessors[i]); diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 2b88cbab7..a9e25de4f 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -424,7 +424,7 @@ namespace Microsoft.Z3 n_constr[i] = cla[i].NativeObject; } IntPtr[] n_res = new IntPtr[n]; - Native.Z3_mk_datatypes(nCtx, n, Symbol.ArrayToNative(names), n_res, n_constr); + Native.Z3_mk_datatypes(nCtx, n, Symbol.ArrayToNative(names), out n_res, n_constr); DatatypeSort[] res = new DatatypeSort[n]; for (uint i = 0; i < n; i++) res[i] = new DatatypeSort(this, n_res[i]); diff --git a/src/api/dotnet/EnumSort.cs b/src/api/dotnet/EnumSort.cs index e62043078..db3d5123f 100644 --- a/src/api/dotnet/EnumSort.cs +++ b/src/api/dotnet/EnumSort.cs @@ -88,7 +88,7 @@ namespace Microsoft.Z3 IntPtr[] n_constdecls = new IntPtr[n]; IntPtr[] n_testers = new IntPtr[n]; NativeObject = Native.Z3_mk_enumeration_sort(ctx.nCtx, name.NativeObject, (uint)n, - Symbol.ArrayToNative(enumNames), n_constdecls, n_testers); + Symbol.ArrayToNative(enumNames), out n_constdecls, out n_testers); } #endregion }; diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index c8fdfb51f..f4a63a61b 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -323,6 +323,14 @@ namespace Microsoft.Z3 #endregion + #region Interpolation + /// + /// Indicates whether the term is marked for interpolation. + /// + /// + public bool IsInterpolant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_INTERP; } } + #endregion + #region Arithmetic Terms /// /// Indicates whether the term is of integer sort. @@ -791,7 +799,7 @@ namespace Microsoft.Z3 /// /// A label literal has a set of string parameters. It takes no arguments. public bool IsLabelLit { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL_LIT; } } - #endregion + #endregion #region Proof Terms /// diff --git a/src/api/dotnet/InterpolationContext.cs b/src/api/dotnet/InterpolationContext.cs new file mode 100644 index 000000000..559a1bfc7 --- /dev/null +++ b/src/api/dotnet/InterpolationContext.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; + +namespace Microsoft.Z3 +{ + /// + /// The InterpolationContext is suitable for generation of interpolants. + /// + /// For more information on interpolation please refer + /// too the C/C++ API, which is well documented. + [ContractVerification(true)] + class InterpolationContext : Context + { + + /// + /// Constructor. + /// + public InterpolationContext() : base() { } + + /// + /// Constructor. + /// + /// + public InterpolationContext(Dictionary settings) : base(settings) { } + + #region Terms + /// + /// Create an expression that marks a formula position for interpolation. + /// + public BoolExpr MkInterpolant(BoolExpr a) + { + Contract.Requires(a != null); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(a); + return new BoolExpr(this, Native.Z3_mk_interpolant(nCtx, a.NativeObject)); + } + #endregion + + /// + /// Computes an interpolant. + /// + /// For more information on interpolation please refer + /// too the function Z3_get_interpolant in the C/C++ API, which is + /// well documented. + Expr[] GetInterpolant(Expr pf, Expr pat, Params p) + { + Contract.Requires(pf != null); + Contract.Requires(pat != null); + Contract.Requires(p != null); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(pf); + CheckContextMatch(pat); + CheckContextMatch(p); + + ASTVector seq = new ASTVector(this, Native.Z3_get_interpolant(nCtx, pf.NativeObject, pat.NativeObject, p.NativeObject)); + uint n = seq.Size; + Expr[] res = new Expr[n]; + for (uint i = 0; i < n; i++) + res[i] = Expr.Create(this, seq[i].NativeObject); + return res; + } + + /// + /// Computes an interpolant. + /// + /// For more information on interpolation please refer + /// too the function Z3_compute_interpolant in the C/C++ API, which is + /// well documented. + Z3_lbool ComputeInterpolant(Expr pat, Params p, out ASTVector interp, out Model model) + { + Contract.Requires(pat != null); + Contract.Requires(p != null); + Contract.Ensures(Contract.ValueAtReturn(out interp) != null); + Contract.Ensures(Contract.ValueAtReturn(out model) != null); + + CheckContextMatch(pat); + CheckContextMatch(p); + + IntPtr i = IntPtr.Zero, m = IntPtr.Zero; + int r = Native.Z3_compute_interpolant(nCtx, pat.NativeObject, p.NativeObject, ref i, ref m); + interp = new ASTVector(this, i); + model = new Model(this, m); + return (Z3_lbool)r; + } + + /// + /// Computes an interpolant. + /// + /// For more information on interpolation please refer + /// too the function Z3_compute_interpolant in the C/C++ API, which is + /// well documented. + Z3_lbool Interpolate(Expr[] cnsts, uint[] parents, Params options, bool incremental, Expr[] theory, out Expr[] interps, out Model model) + { + Contract.Requires(cnsts != null); + Contract.Requires(parents != null); + Contract.Requires(cnsts.Length == parents.Length); + Contract.Ensures(Contract.ValueAtReturn(out interps) != null); + Contract.Ensures(Contract.ValueAtReturn(out model) != null); + + CheckContextMatch(cnsts); + CheckContextMatch(theory); + + uint sz = (uint)cnsts.Length; + + IntPtr[] ni = new IntPtr[sz - 1]; + IntPtr nm = IntPtr.Zero; + IntPtr z = IntPtr.Zero; + + int r = Native.Z3_interpolate(nCtx, + sz, Expr.ArrayToNative(cnsts), parents, + options.NativeObject, + out ni, + ref nm, + ref z, // Z3_lterals are deprecated. + (uint)(incremental ? 1 : 0), + (uint)theory.Length, Expr.ArrayToNative(theory)); + + interps = new Expr[sz - 1]; + for (uint i = 0; i < sz - 1; i++) + interps[i] = Expr.Create(this, ni[i]); + + model = new Model(this, nm); + + return (Z3_lbool)r; + } + + /// + /// Return a string summarizing cumulative time used for interpolation. + /// + /// For more information on interpolation please refer + /// too the function Z3_interpolation_profile in the C/C++ API, which is + /// well documented. + public string InterpolationProfile() + { + return Native.Z3_interpolation_profile(nCtx); + } + + /// + /// Checks the correctness of an interpolant. + /// + /// For more information on interpolation please refer + /// too the function Z3_check_interpolant in the C/C++ API, which is + /// well documented. + public int CheckInterpolant(Expr[] cnsts, uint[] parents, Expr[] interps, out string error, Expr[] theory) + { + Contract.Requires(cnsts.Length == parents.Length); + Contract.Requires(cnsts.Length == interps.Length+1); + IntPtr n_err_str; + int r = Native.Z3_check_interpolant(nCtx, + (uint)cnsts.Length, + Expr.ArrayToNative(cnsts), + parents, + Expr.ArrayToNative(interps), + out n_err_str, + (uint)theory.Length, + Expr.ArrayToNative(theory)); + error = Marshal.PtrToStringAnsi(n_err_str); + return r; + } + + /// + /// Reads an interpolation problem from a file. + /// + /// For more information on interpolation please refer + /// too the function Z3_read_interpolation_problem in the C/C++ API, which is + /// well documented. + public int ReadInterpolationProblem(string filename, out Expr[] cnsts, out uint[] parents, out string error, out Expr[] theory) + { + uint num = 0, num_theory = 0; + IntPtr[] n_cnsts; + IntPtr[] n_theory; + IntPtr n_err_str; + uint[][] n_parents; + int r = Native.Z3_read_interpolation_problem(nCtx, ref num, out n_cnsts, out n_parents, filename, out n_err_str, ref num_theory, out n_theory); + error = Marshal.PtrToStringAnsi(n_err_str); + cnsts = new Expr[num]; + parents = new uint[num]; + theory = new Expr[num_theory]; + for (int i = 0; i < num; i++) + { + cnsts[i] = Expr.Create(this, n_cnsts[i]); + parents[i] = n_parents[0][i]; + } + for (int i = 0; i < num_theory; i++) + theory[i] = Expr.Create(this, n_theory[i]); + return r; + } + + /// + /// Writes an interpolation problem to a file. + /// + /// For more information on interpolation please refer + /// too the function Z3_write_interpolation_problem in the C/C++ API, which is + /// well documented. + public void WriteInterpolationProblem(string filename, Expr[] cnsts, int[] parents, string error, Expr[] theory) + { + Contract.Requires(cnsts.Length == parents.Length); + } + } +} diff --git a/src/api/dotnet/Microsoft.Z3.csproj b/src/api/dotnet/Microsoft.Z3.csproj index 0a062054d..cc7d3c0c5 100644 --- a/src/api/dotnet/Microsoft.Z3.csproj +++ b/src/api/dotnet/Microsoft.Z3.csproj @@ -19,12 +19,12 @@ true full false - ..\Debug\ + ..\..\..\..\..\cwinter\bugs\z3bugs\Debug\ DEBUG;TRACE prompt 4 true - ..\Debug\Microsoft.Z3.XML + C:\cwinter\bugs\z3bugs\Debug\Microsoft.Z3.XML False False True @@ -254,7 +254,7 @@ true - bin\x86\Debug\ + ..\..\..\..\..\cwinter\bugs\z3bugs\Debug\ DEBUG;TRACE true full @@ -266,7 +266,7 @@ MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - bin\x86\Debug\Microsoft.Z3.XML + C:\cwinter\bugs\z3bugs\Debug\Microsoft.Z3.XML bin\x86\Release\ @@ -352,6 +352,7 @@ + diff --git a/src/api/dotnet/TupleSort.cs b/src/api/dotnet/TupleSort.cs index 1e4af5cd7..7d0b6a853 100644 --- a/src/api/dotnet/TupleSort.cs +++ b/src/api/dotnet/TupleSort.cs @@ -74,9 +74,10 @@ namespace Microsoft.Z3 Contract.Requires(name != null); IntPtr t = IntPtr.Zero; + IntPtr[] f; NativeObject = Native.Z3_mk_tuple_sort(ctx.nCtx, name.NativeObject, numFields, Symbol.ArrayToNative(fieldNames), AST.ArrayToNative(fieldSorts), - ref t, new IntPtr[numFields]); + ref t, out f); } #endregion }; diff --git a/src/api/python/z3.py b/src/api/python/z3.py index b6cceeb8e..1a5565ab5 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -7253,19 +7253,19 @@ def parse_smt2_file(f, sorts={}, decls={}, ctx=None): dsz, dnames, ddecls = _dict2darray(decls, ctx) return _to_expr_ref(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) -def Interp(a,ctx=None): +def Interpolant(a,ctx=None): """Create an interpolation operator. The argument is an interpolation pattern (see tree_interpolant). >>> x = Int('x') - >>> print Interp(x>0) + >>> print Interpolant(x>0) interp(x > 0) """ ctx = _get_ctx(_ctx_from_ast_arg_list([a], ctx)) s = BoolSort(ctx) a = s.cast(a) - return BoolRef(Z3_mk_interp(ctx.ref(), a.as_ast()), ctx) + return BoolRef(Z3_mk_interpolant(ctx.ref(), a.as_ast()), ctx) def tree_interpolant(pat,p=None,ctx=None): """Compute interpolant for a tree of formulas. @@ -7304,10 +7304,10 @@ def tree_interpolant(pat,p=None,ctx=None): >>> x = Int('x') >>> y = Int('y') - >>> print tree_interpolant(And(Interp(x < 0), Interp(y > 2), x == y)) + >>> print tree_interpolant(And(Interpolant(x < 0), Interpolant(y > 2), x == y)) [Not(x >= 0), Not(y <= 2)] - >>> g = And(Interp(x<0),x<2) + >>> g = And(Interpolant(x<0),x<2) >>> try: ... print tree_interpolant(g).sexpr() ... except ModelRef as m: @@ -7346,7 +7346,7 @@ def binary_interpolant(a,b,p=None,ctx=None): print binary_interpolant(x<0,x>2) Not(x >= 0) """ - f = And(Interp(a),b) + f = And(Interpolant(a),b) return tree_interpolant(f,p,ctx)[0] def sequence_interpolant(v,p=None,ctx=None): @@ -7375,6 +7375,6 @@ def sequence_interpolant(v,p=None,ctx=None): """ f = v[0] for i in range(1,len(v)): - f = And(Interp(f),v[i]) + f = And(Interpolant(f),v[i]) return tree_interpolant(f,p,ctx) diff --git a/src/api/z3.h b/src/api/z3.h index db8becafd..2ebc20977 100644 --- a/src/api/z3.h +++ b/src/api/z3.h @@ -27,6 +27,7 @@ Notes: #include"z3_algebraic.h" #include"z3_polynomial.h" #include"z3_rcf.h" +#include"z3_interp.h" #undef __in #undef __out diff --git a/src/api/z3_api.h b/src/api/z3_api.h index b561b6bf1..41ccf532e 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -2116,17 +2116,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_not', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_not(__in Z3_context c, __in Z3_ast a); - - /** - \brief \mlh mk_interp c a \endmlh - Create an AST node marking a formula position for interpolation. - The node \c a must have Boolean sort. - - def_API('Z3_mk_interp', AST, (_in(CONTEXT), _in(AST))) - */ - Z3_ast Z3_API Z3_mk_interp(__in Z3_context c, __in Z3_ast a); - /** \brief \mlh mk_ite c t1 t2 t2 \endmlh Create an AST node representing an if-then-else: ite(t1, t2, @@ -7700,314 +7690,6 @@ END_MLAPI_EXCLUDE Z3_ast Z3_API Z3_get_context_assignment(__in Z3_context c); /*@}*/ - - /** - @name Interpolation - */ - /*@{*/ - - /** \brief This function generates a Z3 context suitable for generation of - interpolants. Formulas can be generated as abstract syntx trees in - this context using the Z3 C API. - - Interpolants are also generated as AST's in this context. - - If cfg is non-null, it will be used as the base configuration - for the Z3 context. This makes it possible to set Z3 options - to be used during interpolation. This feature should be used - with some caution however, as it may be that certain Z3 options - are incompatible with interpolation. - - def_API('Z3_mk_interpolation_context', CONTEXT, (_in(CONFIG),)) - - */ - - Z3_context Z3_API Z3_mk_interpolation_context(__in Z3_config cfg); - - /** Compute an interpolant from a refutation. This takes a proof of - "false" from a set of formulas C, and an interpolation - pattern. The pattern pat is a formula combining the formulas in C - using logical conjunction and the "interp" operator (see - #Z3_mk_interp). This interp operator is logically the identity - operator. It marks the sub-formulas of the pattern for which interpolants should - be computed. The interpolant is a map sigma from marked subformulas to - formulas, such that, for each marked subformula phi of pat (where phi sigma - is phi with sigma(psi) substituted for each subformula psi of phi such that - psi in dom(sigma)): - - 1) phi sigma implies sigma(phi), and - - 2) sigma(phi) is in the common uninterpreted vocabulary between - the formulas of C occurring in phi and those not occurring in - phi - - and moreover pat sigma implies false. In the simplest case - an interpolant for the pattern "(and (interp A) B)" maps A - to an interpolant for A /\ B. - - The return value is a vector of formulas representing sigma. The - vector contains sigma(phi) for each marked subformula of pat, in - pre-order traversal. This means that subformulas of phi occur before phi - in the vector. Also, subformulas that occur multiply in pat will - occur multiply in the result vector. - - In particular, calling Z3_get_interpolant on a pattern of the - form (interp ... (interp (and (interp A_1) A_2)) ... A_N) will - result in a sequence interpolant for A_1, A_2,... A_N. - - Neglecting interp markers, the pattern must be a conjunction of - formulas in C, the set of premises of the proof. Otherwise an - error is flagged. - - Any premises of the proof not present in the pattern are - treated as "background theory". Predicate and function symbols - occurring in the background theory are treated as interpreted and - thus always allowed in the interpolant. - - Interpolant may not necessarily be computable from all - proofs. To be sure an interpolant can be computed, the proof - must be generated by an SMT solver for which interpoaltion is - supported, and the premises must be expressed using only - theories and operators for which interpolation is supported. - - Currently, the only SMT solver that is supported is the legacy - SMT solver. Such a solver is available as the default solver in - #Z3_context objects produced by #Z3_mk_interpolation_context. - Currently, the theories supported are equality with - uninterpreted functions, linear integer arithmetic, and the - theory of arrays (in SMT-LIB terms, this is AUFLIA). - Quantifiers are allowed. Use of any other operators (including - "labels") may result in failure to compute an interpolant from a - proof. - - Parameters: - - \param c logical context. - \param pf a refutation from premises (assertions) C - \param pat an interpolation pattern over C - \param p parameters - - def_API('Z3_get_interpolant', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(PARAMS))) - */ - - Z3_ast_vector Z3_API Z3_get_interpolant(__in Z3_context c, __in Z3_ast pf, __in Z3_ast pat, __in Z3_params p); - - /* Compute an interpolant for an unsatisfiable conjunction of formulas. - - This takes as an argument an interpolation pattern as in - #Z3_get_interpolant. This is a conjunction, some subformulas of - which are marked with the "interp" operator (see #Z3_mk_interp). - - The conjunction is first checked for unsatisfiability. The result - of this check is returned in the out parameter "status". If the result - is unsat, an interpolant is computed from the refutation as in #Z3_get_interpolant - and returned as a vector of formulas. Otherwise the return value is - an empty formula. - - See #Z3_get_interpolant for a discussion of supported theories. - - The advantage of this function over #Z3_get_interpolant is that - it is not necessary to create a suitable SMT solver and generate - a proof. The disadvantage is that it is not possible to use the - solver incrementally. - - Parameters: - - \param c logical context. - \param pat an interpolation pattern - \param p parameters for solver creation - \param status returns the status of the sat check - \param model returns model if satisfiable - - Return value: status of SAT check - - def_API('Z3_compute_interpolant', INT, (_in(CONTEXT), _in(AST), _in(PARAMS), _out(AST_VECTOR), _out(MODEL))) - */ - - Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, __in Z3_ast pat, __in Z3_params p, __out Z3_ast_vector *interp, __out Z3_model *model); - - -/** Constant reprepresenting a root of a formula tree for tree interpolation */ -#define IZ3_ROOT SHRT_MAX - -/** This function uses Z3 to determine satisfiability of a set of - constraints. If UNSAT, an interpolant is returned, based on the - refutation generated by Z3. If SAT, a model is returned. - - If "parents" is non-null, computes a tree interpolant. The tree is - defined by the array "parents". This array maps each formula in - the tree to its parent, where formulas are indicated by their - integer index in "cnsts". The parent of formula n must have index - greater than n. The last formula is the root of the tree. Its - parent entry should be the constant IZ3_ROOT. - - If "parents" is null, computes a sequence interpolant. - - \param ctx The Z3 context. Must be generated by iz3_mk_context - \param num The number of constraints in the sequence - \param cnsts Array of constraints (AST's in context ctx) - \param parents The parents vector defining the tree structure - \param options Interpolation options (may be NULL) - \param interps Array to return interpolants (size at least num-1, may be NULL) - \param model Returns a Z3 model if constraints SAT (may be NULL) - \param labels Returns relevant labels if SAT (may be NULL) - \param incremental - - VERY IMPORTANT: All the Z3 formulas in cnsts must be in Z3 - context ctx. The model and interpolants returned are also - in this context. - - The return code is as in Z3_check_assumptions, that is, - - Z3_L_FALSE = constraints UNSAT (interpolants returned) - Z3_L_TRUE = constraints SAT (model returned) - Z3_L_UNDEF = Z3 produced no result, or interpolation not possible - - Currently, this function supports integer and boolean variables, - as well as arrays over these types, with linear arithmetic, - uninterpreted functions and quantifiers over integers (that is - AUFLIA). Interpolants are produced in AUFLIA. However, some - uses of array operations may cause quantifiers to appear in the - interpolants even when there are no quantifiers in the input formulas. - Although quantifiers may appear in the input formulas, Z3 may give up in - this case, returning Z3_L_UNDEF. - - If "incremental" is true, cnsts must contain exactly the set of - formulas that are currently asserted in the context. If false, - there must be no formulas currently asserted in the context. - Setting "incremental" to true makes it posisble to incrementally - add and remove constraints from the context until the context - becomes UNSAT, at which point an interpolant is computed. Caution - must be used, however. Before popping the context, if you wish to - keep the interolant formulas, you *must* preserve them by using - Z3_persist_ast. Also, if you want to simplify the interpolant - formulas using Z3_simplify, you must first pop all of the - assertions in the context (or use a different context). Otherwise, - the formulas will be simplified *relative* to these constraints, - which is almost certainly not what you want. - - - Current limitations on tree interpolants. In a tree interpolation - problem, each constant (0-ary function symbol) must occur only - along one path from root to leaf. Function symbols (of arity > 0) - are considered to have global scope (i.e., may appear in any - interpolant formula). - - - */ - - - Z3_lbool Z3_API Z3_interpolate(__in Z3_context ctx, - __in int num, - __in_ecount(num) Z3_ast *cnsts, - __in_ecount(num) unsigned *parents, - __in Z3_params options, - __out_ecount(num-1) Z3_ast *interps, - __out Z3_model *model, - __out Z3_literals *labels, - __in int incremental, - __in int num_theory, - __in_ecount(num_theory) Z3_ast *theory); - - /** Return a string summarizing cumulative time used for - interpolation. This string is purely for entertainment purposes - and has no semantics. - - \param ctx The context (currently ignored) - - def_API('Z3_interpolation_profile', STRING, (_in(CONTEXT),)) - */ - - Z3_string Z3_API Z3_interpolation_profile(__in Z3_context ctx); - - /** - \brief Read an interpolation problem from file. - - \param ctx The Z3 context. This resets the error handler of ctx. - \param filename The file name to read. - \param num Returns length of sequence. - \param cnsts Returns sequence of formulas (do not free) - \param parents Returns the parents vector (or NULL for sequence) - \param error Returns an error message in case of failure (do not free the string) - - Returns true on success. - - File formats: Currently two formats are supported, based on - SMT-LIB2. For sequence interpolants, the sequence of constraints is - represented by the sequence of "assert" commands in the file. - - For tree interpolants, one symbol of type bool is associated to - each vertex of the tree. For each vertex v there is an "assert" - of the form: - - (implies (and c1 ... cn f) v) - - where c1 .. cn are the children of v (which must precede v in the file) - and f is the formula assiciated to node v. The last formula in the - file is the root vertex, and is represented by the predicate "false". - - A solution to a tree interpolation problem can be thought of as a - valuation of the vertices that makes all the implications true - where each value is represented using the common symbols between - the formulas in the subtree and the remainder of the formulas. - - */ - - - int Z3_API Z3_read_interpolation_problem(__in Z3_context ctx, - __out int *num, - __out_ecount(*num) Z3_ast **cnsts, - __out_ecount(*num) int **parents, - __in const char *filename, - __out const char **error, - __out int *num_theory, - __out_ecount(*num_theory) Z3_ast **theory); - - - - /** Check the correctness of an interpolant. The Z3 context must - have no constraints asserted when this call is made. That means - that after interpolating, you must first fully pop the Z3 - context before calling this. See Z3_interpolate for meaning of parameters. - - \param ctx The Z3 context. Must be generated by Z3_mk_interpolation_context - \param num The number of constraints in the sequence - \param cnsts Array of constraints (AST's in context ctx) - \param parents The parents vector (or NULL for sequence) - \param interps The interpolant to check - \param error Returns an error message if interpolant incorrect (do not free the string) - - Return value is Z3_L_TRUE if interpolant is verified, Z3_L_FALSE if - incorrect, and Z3_L_UNDEF if unknown. - - */ - - int Z3_API Z3_check_interpolant(Z3_context ctx, int num, Z3_ast *cnsts, int *parents, Z3_ast *interps, const char **error, - int num_theory, Z3_ast *theory); - - /** Write an interpolation problem to file suitable for reading with - Z3_read_interpolation_problem. The output file is a sequence - of SMT-LIB2 format commands, suitable for reading with command-line Z3 - or other interpolating solvers. - - \param ctx The Z3 context. Must be generated by z3_mk_interpolation_context - \param num The number of constraints in the sequence - \param cnsts Array of constraints - \param parents The parents vector (or NULL for sequence) - \param filename The file name to write - - */ - - void Z3_API Z3_write_interpolation_problem(Z3_context ctx, - int num, - Z3_ast *cnsts, - int *parents, - const char *filename, - int num_theory, - Z3_ast *theory); - - - #endif diff --git a/src/api/z3_interp.h b/src/api/z3_interp.h new file mode 100644 index 000000000..1aac13b6e --- /dev/null +++ b/src/api/z3_interp.h @@ -0,0 +1,359 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + z3_interp.h + +Abstract: + + API for interpolation + +Author: + + Kenneth McMillan (kenmcmil) + +Notes: + +--*/ +#ifndef _Z3_INTERPOLATION_H_ +#define _Z3_INTERPOLATION_H_ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + /** + @name Interpolation + */ + /*@{*/ + + /** + \brief \mlh mk_interp c a \endmlh + Create an AST node marking a formula position for interpolation. + + The node \c a must have Boolean sort. + + def_API('Z3_mk_interpolant', AST, (_in(CONTEXT), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_interpolant(__in Z3_context c, __in Z3_ast a); + + + /** \brief This function generates a Z3 context suitable for generation of + interpolants. Formulas can be generated as abstract syntax trees in + this context using the Z3 C API. + + Interpolants are also generated as AST's in this context. + + If cfg is non-null, it will be used as the base configuration + for the Z3 context. This makes it possible to set Z3 options + to be used during interpolation. This feature should be used + with some caution however, as it may be that certain Z3 options + are incompatible with interpolation. + + def_API('Z3_mk_interpolation_context', CONTEXT, (_in(CONFIG),)) + + */ + + Z3_context Z3_API Z3_mk_interpolation_context(__in Z3_config cfg); + + /** Compute an interpolant from a refutation. This takes a proof of + "false" from a set of formulas C, and an interpolation + pattern. The pattern pat is a formula combining the formulas in C + using logical conjunction and the "interp" operator (see + #Z3_mk_interpolant). This interp operator is logically the identity + operator. It marks the sub-formulas of the pattern for which interpolants should + be computed. The interpolant is a map sigma from marked subformulas to + formulas, such that, for each marked subformula phi of pat (where phi sigma + is phi with sigma(psi) substituted for each subformula psi of phi such that + psi in dom(sigma)): + + 1) phi sigma implies sigma(phi), and + + 2) sigma(phi) is in the common uninterpreted vocabulary between + the formulas of C occurring in phi and those not occurring in + phi + + and moreover pat sigma implies false. In the simplest case + an interpolant for the pattern "(and (interp A) B)" maps A + to an interpolant for A /\ B. + + The return value is a vector of formulas representing sigma. The + vector contains sigma(phi) for each marked subformula of pat, in + pre-order traversal. This means that subformulas of phi occur before phi + in the vector. Also, subformulas that occur multiply in pat will + occur multiply in the result vector. + + In particular, calling Z3_get_interpolant on a pattern of the + form (interp ... (interp (and (interp A_1) A_2)) ... A_N) will + result in a sequence interpolant for A_1, A_2,... A_N. + + Neglecting interp markers, the pattern must be a conjunction of + formulas in C, the set of premises of the proof. Otherwise an + error is flagged. + + Any premises of the proof not present in the pattern are + treated as "background theory". Predicate and function symbols + occurring in the background theory are treated as interpreted and + thus always allowed in the interpolant. + + Interpolant may not necessarily be computable from all + proofs. To be sure an interpolant can be computed, the proof + must be generated by an SMT solver for which interpoaltion is + supported, and the premises must be expressed using only + theories and operators for which interpolation is supported. + + Currently, the only SMT solver that is supported is the legacy + SMT solver. Such a solver is available as the default solver in + #Z3_context objects produced by #Z3_mk_interpolation_context. + Currently, the theories supported are equality with + uninterpreted functions, linear integer arithmetic, and the + theory of arrays (in SMT-LIB terms, this is AUFLIA). + Quantifiers are allowed. Use of any other operators (including + "labels") may result in failure to compute an interpolant from a + proof. + + Parameters: + + \param c logical context. + \param pf a refutation from premises (assertions) C + \param pat an interpolation pattern over C + \param p parameters + + def_API('Z3_get_interpolant', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(PARAMS))) + */ + + Z3_ast_vector Z3_API Z3_get_interpolant(__in Z3_context c, __in Z3_ast pf, __in Z3_ast pat, __in Z3_params p); + + /* Compute an interpolant for an unsatisfiable conjunction of formulas. + + This takes as an argument an interpolation pattern as in + #Z3_get_interpolant. This is a conjunction, some subformulas of + which are marked with the "interp" operator (see #Z3_mk_interpolant). + + The conjunction is first checked for unsatisfiability. The result + of this check is returned in the out parameter "status". If the result + is unsat, an interpolant is computed from the refutation as in #Z3_get_interpolant + and returned as a vector of formulas. Otherwise the return value is + an empty formula. + + See #Z3_get_interpolant for a discussion of supported theories. + + The advantage of this function over #Z3_get_interpolant is that + it is not necessary to create a suitable SMT solver and generate + a proof. The disadvantage is that it is not possible to use the + solver incrementally. + + Parameters: + + \param c logical context. + \param pat an interpolation pattern + \param p parameters for solver creation + \param status returns the status of the sat check + \param model returns model if satisfiable + + Return value: status of SAT check + + def_API('Z3_compute_interpolant', INT, (_in(CONTEXT), _in(AST), _in(PARAMS), _out(AST_VECTOR), _out(MODEL))) + */ + + Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, + __in Z3_ast pat, + __in Z3_params p, + __out Z3_ast_vector *interp, + __out Z3_model *model); + + + /** Constant reprepresenting a root of a formula tree for tree interpolation */ +#define IZ3_ROOT SHRT_MAX + + /** This function uses Z3 to determine satisfiability of a set of + constraints. If UNSAT, an interpolant is returned, based on the + refutation generated by Z3. If SAT, a model is returned. + + If "parents" is non-null, computes a tree interpolant. The tree is + defined by the array "parents". This array maps each formula in + the tree to its parent, where formulas are indicated by their + integer index in "cnsts". The parent of formula n must have index + greater than n. The last formula is the root of the tree. Its + parent entry should be the constant IZ3_ROOT. + + If "parents" is null, computes a sequence interpolant. + + \param ctx The Z3 context. Must be generated by iz3_mk_context + \param num The number of constraints in the sequence + \param cnsts Array of constraints (AST's in context ctx) + \param parents The parents vector defining the tree structure + \param options Interpolation options (may be NULL) + \param interps Array to return interpolants (size at least num-1, may be NULL) + \param model Returns a Z3 model if constraints SAT (may be NULL) + \param labels Returns relevant labels if SAT (may be NULL) + \param incremental + + VERY IMPORTANT: All the Z3 formulas in cnsts must be in Z3 + context ctx. The model and interpolants returned are also + in this context. + + The return code is as in Z3_check_assumptions, that is, + + Z3_L_FALSE = constraints UNSAT (interpolants returned) + Z3_L_TRUE = constraints SAT (model returned) + Z3_L_UNDEF = Z3 produced no result, or interpolation not possible + + Currently, this function supports integer and boolean variables, + as well as arrays over these types, with linear arithmetic, + uninterpreted functions and quantifiers over integers (that is + AUFLIA). Interpolants are produced in AULIA. However, some + uses of array operations may cause quantifiers to appear in the + interpolants even when there are no quantifiers in the input formulas. + Although quantifiers may appear in the input formulas, Z3 may give up in + this case, returning Z3_L_UNDEF. + + If "incremental" is true, cnsts must contain exactly the set of + formulas that are currently asserted in the context. If false, + there must be no formulas currently asserted in the context. + Setting "incremental" to true makes it posisble to incrementally + add and remove constraints from the context until the context + becomes UNSAT, at which point an interpolant is computed. Caution + must be used, however. Before popping the context, if you wish to + keep the interolant formulas, you *must* preserve them by using + Z3_persist_ast. Also, if you want to simplify the interpolant + formulas using Z3_simplify, you must first pop all of the + assertions in the context (or use a different context). Otherwise, + the formulas will be simplified *relative* to these constraints, + which is almost certainly not what you want. + + + Current limitations on tree interpolants. In a tree interpolation + problem, each constant (0-ary function symbol) must occur only + along one path from root to leaf. Function symbols (of arity > 0) + are considered to have global scope (i.e., may appear in any + interpolant formula). + + def_API('Z3_interpolate', BOOL, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(PARAMS), _out_array(1, AST), _out(MODEL), _out(LITERALS), _in(UINT), _in(UINT), _in_array(9, AST))) + */ + + Z3_lbool Z3_API Z3_interpolate(__in Z3_context ctx, + __in unsigned num, + __in_ecount(num) Z3_ast *cnsts, + __in_ecount(num) unsigned *parents, + __in Z3_params options, + __out_ecount(num - 1) Z3_ast *interps, + __out Z3_model *model, + __out Z3_literals *labels, + __in unsigned incremental, + __in unsigned num_theory, + __in_ecount(num_theory) Z3_ast *theory); + + /** Return a string summarizing cumulative time used for + interpolation. This string is purely for entertainment purposes + and has no semantics. + + \param ctx The context (currently ignored) + + + def_API('Z3_interpolation_profile', STRING, (_in(CONTEXT),)) + */ + + Z3_string Z3_API Z3_interpolation_profile(__in Z3_context ctx); + + /** + \brief Read an interpolation problem from file. + + \param ctx The Z3 context. This resets the error handler of ctx. + \param filename The file name to read. + \param num Returns length of sequence. + \param cnsts Returns sequence of formulas (do not free) + \param parents Returns the parents vector (or NULL for sequence) + \param error Returns an error message in case of failure (do not free the string) + + Returns true on success. + + File formats: Currently two formats are supported, based on + SMT-LIB2. For sequence interpolants, the sequence of constraints is + represented by the sequence of "assert" commands in the file. + + For tree interpolants, one symbol of type bool is associated to + each vertex of the tree. For each vertex v there is an "assert" + of the form: + + (implies (and c1 ... cn f) v) + + where c1 .. cn are the children of v (which must precede v in the file) + and f is the formula assiciated to node v. The last formula in the + file is the root vertex, and is represented by the predicate "false". + + A solution to a tree interpolation problem can be thought of as a + valuation of the vertices that makes all the implications true + where each value is represented using the common symbols between + the formulas in the subtree and the remainder of the formulas. + + def_API('Z3_read_interpolation_problem', INT, (_in(CONTEXT), _out(UINT), _out_array(1, AST), _out_array(1, UINT_PTR), _in(STRING), _out(STRING), _out(UINT), _out_array(6, AST))) + + */ + + int Z3_API Z3_read_interpolation_problem(__in Z3_context ctx, + __out unsigned *num, + __out_ecount(*num) Z3_ast **cnsts, + __out_ecount(*num) unsigned **parents, + __in Z3_string filename, + __out Z3_string *error, + __out unsigned *num_theory, + __out_ecount(*num_theory) Z3_ast **theory); + + + + /** Check the correctness of an interpolant. The Z3 context must + have no constraints asserted when this call is made. That means + that after interpolating, you must first fully pop the Z3 + context before calling this. See Z3_interpolate for meaning of parameters. + + \param ctx The Z3 context. Must be generated by Z3_mk_interpolation_context + \param num The number of constraints in the sequence + \param cnsts Array of constraints (AST's in context ctx) + \param parents The parents vector (or NULL for sequence) + \param interps The interpolant to check + \param error Returns an error message if interpolant incorrect (do not free the string) + + Return value is Z3_L_TRUE if interpolant is verified, Z3_L_FALSE if + incorrect, and Z3_L_UNDEF if unknown. + + def_API('Z3_check_interpolant', INT, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in_array(1, AST), _out(STRING), _in(UINT), _in_array(6, AST))) + */ + + int Z3_API Z3_check_interpolant(__in Z3_context ctx, + __in unsigned num, + __in_ecount(num) Z3_ast *cnsts, + __in_ecount(num) unsigned *parents, + __in_ecount(num - 1) Z3_ast *interps, + __out Z3_string *error, + __in unsigned num_theory, + __in_ecount(num_theory) Z3_ast *theory); + + /** Write an interpolation problem to file suitable for reading with + Z3_read_interpolation_problem. The output file is a sequence + of SMT-LIB2 format commands, suitable for reading with command-line Z3 + or other interpolating solvers. + + \param ctx The Z3 context. Must be generated by z3_mk_interpolation_context + \param num The number of constraints in the sequence + \param cnsts Array of constraints + \param parents The parents vector (or NULL for sequence) + \param filename The file name to write + + def_API('Z3_write_interpolation_problem', VOID, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(STRING), _in(UINT), _in_array(5, AST))) + */ + + void Z3_API Z3_write_interpolation_problem(__in Z3_context ctx, + __in unsigned num, + __in_ecount(num) Z3_ast *cnsts, + __in_ecount(num) unsigned *parents, + __in Z3_string filename, + __in unsigned num_theory, + __in_ecount(num_theory) Z3_ast *theory); + +#ifdef __cplusplus +}; +#endif // __cplusplus + +#endif From b8b5c4d5b4576621a65d98ad09f60ca26de8dac8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 8 Oct 2014 13:21:34 -0700 Subject: [PATCH 462/509] disable blanket validation Signed-off-by: Nikolaj Bjorner --- src/tactic/tactical.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 87974ea60..168965c17 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -1229,9 +1229,9 @@ class using_params_tactical : public unary_tactical { params_ref m_params; public: using_params_tactical(tactic * t, params_ref const & p):unary_tactical(t), m_params(p) { - param_descrs r; - collect_param_descrs(r); - p.validate(r); + //param_descrs r; + //collect_param_descrs(r); + //p.validate(r); t->updt_params(p); } From 8cf21dc2426b8be12915b8dfd59784fff87a5e14 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 8 Oct 2014 13:47:55 -0700 Subject: [PATCH 463/509] fix tactic parameter checking to API, deal with compiler warnings in api_interp Signed-off-by: Nikolaj Bjorner --- src/api/api_interp.cpp | 26 ++++++++++++++++---------- src/api/api_tactic.cpp | 3 +++ src/api/python/z3.py | 2 +- src/tactic/tactical.cpp | 3 --- src/util/params.cpp | 3 ++- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 8740e99a5..0bfb2d077 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -145,11 +145,11 @@ extern "C" { Z3_push(ctx); // so we can rewind later - for (int i = 0; i < num; i++) + for (unsigned i = 0; i < num; i++) Z3_assert_cnstr(ctx, cnsts[i]); // assert all the constraints if (theory){ - for (int i = 0; i < num_theory; i++) + for (unsigned i = 0; i < num_theory; i++) Z3_assert_cnstr(ctx, theory[i]); } @@ -180,7 +180,7 @@ extern "C" { theory); if (!incremental) - for (int i = 0; i < num - 1; i++) + for (unsigned i = 0; i < num - 1; i++) Z3_persist_ast(ctx, interps[i], 1); break; @@ -226,13 +226,13 @@ extern "C" { scoped_ptr sp((*(sf))(_m, p, false, true, false, symbol("AUFLIA"))); ptr_vector cnsts_vec(num); // get constraints in a vector - for (int i = 0; i < num; i++){ + for (unsigned i = 0; i < num; i++){ ast *a = to_ast(cnsts[i]); cnsts_vec[i] = a; } ptr_vector itp_vec(num); // get interpolants in a vector - for (int i = 0; i < num - 1; i++){ + for (unsigned i = 0; i < num - 1; i++){ ast *a = to_ast(itp[i]); itp_vec[i] = a; } @@ -240,14 +240,14 @@ extern "C" { ::vector parents_vec; // get parents in a vector if (parents){ parents_vec.resize(num); - for (int i = 0; i < num; i++) + for (unsigned i = 0; i < num; i++) parents_vec[i] = parents[i]; } ptr_vector theory_vec; // get background theory in a vector if (theory){ theory_vec.resize(num_theory); - for (int i = 0; i < num_theory; i++) + for (unsigned i = 0; i < num_theory; i++) theory_vec[i] = to_ast(theory[i]); } @@ -506,16 +506,22 @@ extern "C" { void Z3_write_interpolation_problem(Z3_context ctx, unsigned num, Z3_ast *cnsts, unsigned *parents, const char *filename, unsigned num_theory, Z3_ast *theory){ std::ofstream f(filename); if (num > 0){ +#if 0 + // Suggested shorthand: + ptr_vector cnsts_vec; + cnsts_vec.append(num, to_exprs(cnsts)); + cnsts_vec.append(num_theory, to_exprs(theory)); +#endif ptr_vector cnsts_vec(num); // get constraints in a vector - for (int i = 0; i < num; i++){ + for (unsigned i = 0; i < num; i++){ expr *a = to_expr(cnsts[i]); cnsts_vec[i] = a; } - Z3_ast tree = parents_vector_to_tree(ctx, num, cnsts, parents); - for (int i = 0; i < num_theory; i++){ + for (unsigned i = 0; i < num_theory; i++){ expr *a = to_expr(theory[i]); cnsts_vec.push_back(a); } + Z3_ast tree = parents_vector_to_tree(ctx, num, cnsts, parents); iz3pp(mk_c(ctx)->m(), cnsts_vec, to_expr(tree), f); Z3_dec_ref(ctx, tree); } diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index 911360047..7dce33971 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -222,6 +222,9 @@ extern "C" { Z3_TRY; LOG_Z3_tactic_using_params(c, t, p); RESET_ERROR_CODE(); + param_descrs r; + to_tactic_ref(t)->collect_param_descrs(r); + to_param_ref(p).validate(r); tactic * new_t = using_params(to_tactic_ref(t), to_param_ref(p)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 1a5565ab5..82ba85472 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -4448,7 +4448,7 @@ def args2params(arguments, keywords, ctx=None): A ':' is added to the keywords, and '_' is replaced with '-' >>> args2params(['model', True, 'relevancy', 2], {'elim_and' : True}) - (params model 1 relevancy 2 elim_and 1) + (params model true relevancy 2 elim_and true) """ if __debug__: _z3_assert(len(arguments) % 2 == 0, "Argument list must have an even number of elements.") diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 168965c17..cfb4ec194 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -1229,9 +1229,6 @@ class using_params_tactical : public unary_tactical { params_ref m_params; public: using_params_tactical(tactic * t, params_ref const & p):unary_tactical(t), m_params(p) { - //param_descrs r; - //collect_param_descrs(r); - //p.validate(r); t->updt_params(p); } diff --git a/src/util/params.cpp b/src/util/params.cpp index 130b18cc9..a2609f840 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -309,7 +309,8 @@ public: p.display(strm, 2, false, false); throw default_exception(strm.str()); } - if (it->second.m_kind != expected) { + if (it->second.m_kind != expected && + !(it->second.m_kind == CPK_UINT && expected == CPK_NUMERAL)) { std::stringstream strm; strm << "Parameter " << it->first.str() << " was given argument of type "; strm << it->second.m_kind << ", expected " << expected; From bbdc8b33e0ab4153b2f3782f01025cf355a080df Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 8 Oct 2014 13:56:46 -0700 Subject: [PATCH 464/509] prevent creating some useless solvers in duality --- src/duality/duality_solver.cpp | 29 ++++++++++++++++-------- src/muz/duality/duality_dl_interface.cpp | 21 +---------------- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index bbd1ebe0a..69759f9bb 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -802,6 +802,15 @@ namespace Duality { annot.Simplify(); } + bool NodeSolutionFromIndSetFull(Node *node){ + std::vector &insts = insts_of_node[node]; + for(unsigned j = 0; j < insts.size(); j++) + if(indset->Contains(insts[j])) + if(insts[j]->Annotation.IsFull()) + return true; + return false; + } + bool recursionBounded; /** See if the solution might be bounded. */ @@ -1453,16 +1462,18 @@ namespace Duality { slvr.pop(1); delete checker; #else - RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); - gen_cands_rpfp->Push(); - Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); - if(gen_cands_rpfp->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,gen_cands_rpfp,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); + if(!NodeSolutionFromIndSetFull(edge->Parent)){ + RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); + gen_cands_rpfp->Push(); + Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); + if(gen_cands_rpfp->Check(root) != unsat){ + Candidate candidate; + ExtractCandidateFromCex(edge,gen_cands_rpfp,root,candidate); + reporter->InductionFailure(edge,candidate.Children); + candidates.push_back(candidate); + } + gen_cands_rpfp->Pop(1); } - gen_cands_rpfp->Pop(1); #endif } updated_nodes.clear(); diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index c7fa0a08f..9a6d0c633 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -158,26 +158,7 @@ lbool dl_interface::query(::expr * query) { vector bounds; // m_ctx.get_rules_as_formulas(rules, names); - expr_ref query_ref(m_ctx.get_manager()); - if(****){ - m_ctx.flush_add_rules(); - datalog::rule_manager& rm = m_ctx.get_rule_manager(); - rm.mk_query(query, m_ctx.get_rules()); - apply_default_transformation(m_ctx); - rule_set &rs = m_ctx.get_rules(); - if(m_ctx.get_rules().get_output_predicates().empty()) - query_ref = m_ctx.get_manager().mk_true(); - else { - query_pred = m_ctx.get_rules().get_output_predicate(); - func_decl_ref query_pred(m_ctx.get_manager()); - query_pred = m_ctx.get_rules().get_output_predicate(); - ptr_vector sorts; - unsi - - } - } - else - m_ctx.get_raw_rule_formulas(rules, names, bounds); + m_ctx.get_raw_rule_formulas(rules, names, bounds); // get all the rules as clauses std::vector &clauses = _d->clauses; From f0c63e56f3389cfa05d76509b3d77702eeab878e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 8 Oct 2014 16:27:40 -0700 Subject: [PATCH 465/509] make module parameter validation and adjustment more flexible: you can use both module qualifiers and unqualified parameters from the API at local scope Signed-off-by: Nikolaj Bjorner --- scripts/mk_util.py | 2 +- src/util/params.cpp | 66 +++++++++++++++++++++++++++++++++++---------- src/util/params.h | 7 ++--- 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 411fdd1e1..4641a7b1b 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1801,7 +1801,7 @@ def def_module_params(module_name, export, params, class_name=None, description= out.write(' {}\n') out.write(' static void collect_param_descrs(param_descrs & d) {\n') for param in params: - out.write(' d.insert("%s", %s, "%s", "%s");\n' % (param[0], TYPE2CPK[param[1]], param[3], pyg_default(param))) + out.write(' d.insert("%s", %s, "%s", "%s","%s");\n' % (param[0], TYPE2CPK[param[1]], param[3], pyg_default(param), module_name)) out.write(' }\n') if export: out.write(' /*\n') diff --git a/src/util/params.cpp b/src/util/params.cpp index a2609f840..cbb2b2acc 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -51,31 +51,34 @@ struct param_descrs::imp { param_kind m_kind; char const * m_descr; char const * m_default; + char const * m_module; - info(param_kind k, char const * descr, char const * def): + info(param_kind k, char const * descr, char const * def, char const* module): m_kind(k), m_descr(descr), - m_default(def) { + m_default(def), + m_module(module) { } info(): m_kind(CPK_INVALID), m_descr(0), - m_default(0) { + m_default(0), + m_module(0) { } }; dictionary m_info; svector m_names; - void insert(symbol const & name, param_kind k, char const * descr, char const * def) { + void insert(symbol const & name, param_kind k, char const * descr, char const * def, char const* module) { SASSERT(!name.is_numerical()); info i; if (m_info.find(name, i)) { SASSERT(i.m_kind == k); return; } - m_info.insert(name, info(k, descr, def)); + m_info.insert(name, info(k, descr, def, module)); m_names.push_back(name); } @@ -94,6 +97,13 @@ struct param_descrs::imp { return CPK_INVALID; } + char const* get_module(symbol const& name) const { + info i; + if (m_info.find(name, i)) + return i.m_module; + return 0; + } + char const * get_descr(symbol const & name) const { info i; if (m_info.find(name, i)) @@ -162,7 +172,7 @@ struct param_descrs::imp { dictionary::iterator it = other.m_imp->m_info.begin(); dictionary::iterator end = other.m_imp->m_info.end(); for (; it != end; ++it) { - insert(it->m_key, it->m_value.m_kind, it->m_value.m_descr, it->m_value.m_default); + insert(it->m_key, it->m_value.m_kind, it->m_value.m_descr, it->m_value.m_default, it->m_value.m_module); } } @@ -180,12 +190,12 @@ void param_descrs::copy(param_descrs & other) { m_imp->copy(other); } -void param_descrs::insert(symbol const & name, param_kind k, char const * descr, char const * def) { - m_imp->insert(name, k, descr, def); +void param_descrs::insert(symbol const & name, param_kind k, char const * descr, char const * def, char const* module) { + m_imp->insert(name, k, descr, def, module); } -void param_descrs::insert(char const * name, param_kind k, char const * descr, char const * def) { - insert(symbol(name), k, descr, def); +void param_descrs::insert(char const * name, param_kind k, char const * descr, char const * def, char const* module) { + insert(symbol(name), k, descr, def, module); } bool param_descrs::contains(char const * name) const { @@ -236,6 +246,10 @@ symbol param_descrs::get_param_name(unsigned i) const { return m_imp->get_param_name(i); } +char const* param_descrs::get_module(symbol const& name) const { + return m_imp->get_module(name); +} + void param_descrs::display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) const { return m_imp->display(out, indent, smt2_style, include_descr); } @@ -297,11 +311,35 @@ public: void reset(symbol const & k); void reset(char const * k); - void validate(param_descrs const & p) const { - svector::const_iterator it = m_entries.begin(); - svector::const_iterator end = m_entries.end(); + bool split_name(symbol const& name, symbol & prefix, symbol & suffix) { + if (name.is_numerical()) return false; + char const* str = name.bare_str(); + char const* period = strchr(str,'.'); + if (!period) return false; + svector prefix_((unsigned)(period-str), str); + prefix_.push_back(0); + prefix = symbol(prefix_.c_ptr()); + suffix = symbol(period + 1); + return true; + } + + void validate(param_descrs const & p) { + svector::iterator it = m_entries.begin(); + svector::iterator end = m_entries.end(); + symbol suffix, prefix; for (; it != end; ++it) { param_kind expected = p.get_kind(it->first); + if (expected == CPK_INVALID && split_name(it->first, prefix, suffix)) { + expected = p.get_kind(suffix); + if (expected != CPK_INVALID) { + if (symbol(p.get_module(suffix)) == prefix) { + it->first = suffix; + } + else { + expected = CPK_INVALID; + } + } + } if (expected == CPK_INVALID) { std::stringstream strm; strm << "unknown parameter '" << it->first.str() << "'\n"; @@ -490,7 +528,7 @@ void params_ref::display(std::ostream & out, symbol const & k) const { out << "default"; } -void params_ref::validate(param_descrs const & p) const { +void params_ref::validate(param_descrs const & p) { if (m_params) m_params->validate(p); } diff --git a/src/util/params.h b/src/util/params.h index 06be486bb..f11374775 100644 --- a/src/util/params.h +++ b/src/util/params.h @@ -92,7 +92,7 @@ public: void display(std::ostream & out) const; void display_smt2(std::ostream& out, char const* module, param_descrs& module_desc) const; - void validate(param_descrs const & p) const; + void validate(param_descrs const & p); /* \brief Display the value of the given parameter. @@ -115,8 +115,8 @@ public: param_descrs(); ~param_descrs(); void copy(param_descrs & other); - void insert(char const * name, param_kind k, char const * descr, char const * def = 0); - void insert(symbol const & name, param_kind k, char const * descr, char const * def = 0); + void insert(char const * name, param_kind k, char const * descr, char const * def = 0, char const* module = 0); + void insert(symbol const & name, param_kind k, char const * descr, char const * def = 0, char const* module = 0); bool contains(char const * name) const; bool contains(symbol const & name) const; void erase(char const * name); @@ -130,6 +130,7 @@ public: void display(std::ostream & out, unsigned indent = 0, bool smt2_style=false, bool include_descr=true) const; unsigned size() const; symbol get_param_name(unsigned idx) const; + char const * get_module(symbol const& name) const; }; void insert_max_memory(param_descrs & r); From 503ad78bf38ce770c9dbd9e95ee657aaa6f509df Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 9 Oct 2014 18:08:07 +0100 Subject: [PATCH 466/509] Interpolation API bugfixes Signed-off-by: Christoph M. Wintersteiger --- scripts/update_api.py | 17 +-- src/api/api_interp.cpp | 177 +++++++++++++------------ src/api/dotnet/InterpolationContext.cs | 63 ++------- src/api/z3_api.h | 18 ++- src/api/z3_interp.h | 106 ++------------- 5 files changed, 126 insertions(+), 255 deletions(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index 791c23ddd..09c4fbe1c 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -123,7 +123,6 @@ SYMBOL = 9 PRINT_MODE = 10 ERROR_CODE = 11 DOUBLE = 12 -UINT_PTR = 13 FIRST_OBJ_ID = 100 @@ -132,28 +131,28 @@ def is_obj(ty): Type2Str = { VOID : 'void', VOID_PTR : 'void*', INT : 'int', UINT : 'unsigned', INT64 : '__int64', UINT64 : '__uint64', DOUBLE : 'double', STRING : 'Z3_string', STRING_PTR : 'Z3_string_ptr', BOOL : 'Z3_bool', SYMBOL : 'Z3_symbol', - PRINT_MODE : 'Z3_ast_print_mode', ERROR_CODE : 'Z3_error_code', UINT_PTR : 'unsigned*' + PRINT_MODE : 'Z3_ast_print_mode', ERROR_CODE : 'Z3_error_code' } Type2PyStr = { VOID_PTR : 'ctypes.c_void_p', INT : 'ctypes.c_int', UINT : 'ctypes.c_uint', INT64 : 'ctypes.c_longlong', UINT64 : 'ctypes.c_ulonglong', DOUBLE : 'ctypes.c_double', STRING : 'ctypes.c_char_p', STRING_PTR : 'ctypes.POINTER(ctypes.c_char_p)', BOOL : 'ctypes.c_bool', SYMBOL : 'Symbol', - PRINT_MODE : 'ctypes.c_uint', ERROR_CODE : 'ctypes.c_uint', UINT_PTR : 'ctypes.POINTER(ctypes.c_uint)' + PRINT_MODE : 'ctypes.c_uint', ERROR_CODE : 'ctypes.c_uint' } # Mapping to .NET types Type2Dotnet = { VOID : 'void', VOID_PTR : 'IntPtr', INT : 'int', UINT : 'uint', INT64 : 'Int64', UINT64 : 'UInt64', DOUBLE : 'double', STRING : 'string', STRING_PTR : 'byte**', BOOL : 'int', SYMBOL : 'IntPtr', - PRINT_MODE : 'uint', ERROR_CODE : 'uint', UINT_PTR : 'uint[]'} + PRINT_MODE : 'uint', ERROR_CODE : 'uint' } # Mapping to Java types Type2Java = { VOID : 'void', VOID_PTR : 'long', INT : 'int', UINT : 'int', INT64 : 'long', UINT64 : 'long', DOUBLE : 'double', STRING : 'String', STRING_PTR : 'StringPtr', - BOOL : 'boolean', SYMBOL : 'long', PRINT_MODE : 'int', ERROR_CODE : 'int', UINT_PTR : 'int[]'} + BOOL : 'boolean', SYMBOL : 'long', PRINT_MODE : 'int', ERROR_CODE : 'int'} Type2JavaW = { VOID : 'void', VOID_PTR : 'jlong', INT : 'jint', UINT : 'jint', INT64 : 'jlong', UINT64 : 'jlong', DOUBLE : 'jdouble', STRING : 'jstring', STRING_PTR : 'jobject', - BOOL : 'jboolean', SYMBOL : 'jlong', PRINT_MODE : 'jint', ERROR_CODE : 'jint', UINT_PTR : 'jlong'} + BOOL : 'jboolean', SYMBOL : 'jlong', PRINT_MODE : 'jint', ERROR_CODE : 'jint'} next_type_id = FIRST_OBJ_ID @@ -964,7 +963,6 @@ def def_API(name, result, params): tstr = type2str(ty) if sz_p_k == OUT or sz_p_k == INOUT: sz_e = ("(*a%s)" % sz) - tstr = tstr + '*' else: sz_e = ("a%s" % sz) log_c.write(" for (unsigned i = 0; i < %s; i++) { " % sz_e) @@ -978,11 +976,6 @@ def def_API(name, result, params): log_c.write(" }\n") log_c.write(" Au(%s);\n" % sz_e) exe_c.write("in.get_uint_array(%s)" % i) - elif ty == UINT_PTR: - log_c.write("P(0);") - log_c.write(" }\n") - log_c.write(" Ap(%s);\n" % sz_e) - exe_c.write("reinterpret_cast<%s>(in.get_obj_array(%s))" % (tstr, i)) else: error ("unsupported parameter for %s, %s" % (name, p)) else: diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 0bfb2d077..3e9ddc8a3 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -123,89 +123,6 @@ extern "C" { } } - Z3_lbool Z3_interpolate(Z3_context ctx, - unsigned num, - Z3_ast *cnsts, - unsigned *parents, - Z3_params options, - Z3_ast *interps, - Z3_model *model, - Z3_literals *labels, - unsigned incremental, - unsigned num_theory, - Z3_ast *theory - ){ - - - profiling::timer_start("Solve"); - - if (!incremental){ - - profiling::timer_start("Z3 assert"); - - Z3_push(ctx); // so we can rewind later - - for (unsigned i = 0; i < num; i++) - Z3_assert_cnstr(ctx, cnsts[i]); // assert all the constraints - - if (theory){ - for (unsigned i = 0; i < num_theory; i++) - Z3_assert_cnstr(ctx, theory[i]); - } - - profiling::timer_stop("Z3 assert"); - } - - - // Get a proof of unsat - - Z3_ast proof; - Z3_lbool result; - - profiling::timer_start("Z3 solving"); - result = Z3_check_assumptions(ctx, 0, 0, model, &proof, 0, 0); - profiling::timer_stop("Z3 solving"); - - switch (result) { - case Z3_L_FALSE: - - Z3_interpolate_proof(ctx, - proof, - num, - cnsts, - parents, - options, - interps, - num_theory, - theory); - - if (!incremental) - for (unsigned i = 0; i < num - 1; i++) - Z3_persist_ast(ctx, interps[i], 1); - break; - - case Z3_L_UNDEF: - if (labels) - *labels = Z3_get_relevant_labels(ctx); - break; - - case Z3_L_TRUE: - if (labels) - *labels = Z3_get_relevant_labels(ctx); - break; - } - - profiling::timer_start("Z3 pop"); - if (!incremental) - Z3_pop(ctx, 1); - profiling::timer_stop("Z3 pop"); - - profiling::timer_stop("Solve"); - - return result; - - } - static std::ostringstream itp_err; int Z3_check_interpolant(Z3_context ctx, @@ -603,7 +520,7 @@ extern "C" { } - int Z3_read_interpolation_problem(Z3_context ctx, unsigned *_num, Z3_ast **cnsts, unsigned **parents, const char *filename, const char **error, unsigned *ret_num_theory, Z3_ast **theory){ + int Z3_read_interpolation_problem(Z3_context ctx, unsigned *_num, Z3_ast cnsts[], unsigned parents[], const char *filename, Z3_string_ptr error, unsigned *ret_num_theory, Z3_ast theory[]){ hash_map file_params; get_file_params(filename, file_params); @@ -632,11 +549,11 @@ extern "C" { if (ret_num_theory) *ret_num_theory = num_theory; if (theory) - *theory = &read_theory[0]; + theory = &read_theory[0]; if (!parents){ *_num = num; - *cnsts = &read_cnsts[0]; + cnsts = &read_cnsts[0]; return true; } @@ -706,8 +623,8 @@ extern "C" { } *_num = num; - *cnsts = &read_cnsts[0]; - *parents = &read_parents[0]; + cnsts = &read_cnsts[0]; + parents = &read_parents[0]; return true; fail: @@ -717,3 +634,87 @@ extern "C" { } } + + +#if 0 +/** Constant reprepresenting a root of a formula tree for tree interpolation */ +#define IZ3_ROOT SHRT_MAX + +/** This function uses Z3 to determine satisfiability of a set of +constraints. If UNSAT, an interpolant is returned, based on the +refutation generated by Z3. If SAT, a model is returned. + +If "parents" is non-null, computes a tree interpolant. The tree is +defined by the array "parents". This array maps each formula in +the tree to its parent, where formulas are indicated by their +integer index in "cnsts". The parent of formula n must have index +greater than n. The last formula is the root of the tree. Its +parent entry should be the constant IZ3_ROOT. + +If "parents" is null, computes a sequence interpolant. + +\param ctx The Z3 context. Must be generated by iz3_mk_context +\param num The number of constraints in the sequence +\param cnsts Array of constraints (AST's in context ctx) +\param parents The parents vector defining the tree structure +\param options Interpolation options (may be NULL) +\param interps Array to return interpolants (size at least num-1, may be NULL) +\param model Returns a Z3 model if constraints SAT (may be NULL) +\param labels Returns relevant labels if SAT (may be NULL) +\param incremental + +VERY IMPORTANT: All the Z3 formulas in cnsts must be in Z3 +context ctx. The model and interpolants returned are also +in this context. + +The return code is as in Z3_check_assumptions, that is, + +Z3_L_FALSE = constraints UNSAT (interpolants returned) +Z3_L_TRUE = constraints SAT (model returned) +Z3_L_UNDEF = Z3 produced no result, or interpolation not possible + +Currently, this function supports integer and boolean variables, +as well as arrays over these types, with linear arithmetic, +uninterpreted functions and quantifiers over integers (that is +AUFLIA). Interpolants are produced in AULIA. However, some +uses of array operations may cause quantifiers to appear in the +interpolants even when there are no quantifiers in the input formulas. +Although quantifiers may appear in the input formulas, Z3 may give up in +this case, returning Z3_L_UNDEF. + +If "incremental" is true, cnsts must contain exactly the set of +formulas that are currently asserted in the context. If false, +there must be no formulas currently asserted in the context. +Setting "incremental" to true makes it posisble to incrementally +add and remove constraints from the context until the context +becomes UNSAT, at which point an interpolant is computed. Caution +must be used, however. Before popping the context, if you wish to +keep the interolant formulas, you *must* preserve them by using +Z3_persist_ast. Also, if you want to simplify the interpolant +formulas using Z3_simplify, you must first pop all of the +assertions in the context (or use a different context). Otherwise, +the formulas will be simplified *relative* to these constraints, +which is almost certainly not what you want. + + +Current limitations on tree interpolants. In a tree interpolation +problem, each constant (0-ary function symbol) must occur only +along one path from root to leaf. Function symbols (of arity > 0) +are considered to have global scope (i.e., may appear in any +interpolant formula). + +def_API('Z3_interpolate', BOOL, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(PARAMS), _out_array(1, AST), _out(MODEL), _out(LITERALS), _in(UINT), _in(UINT), _in_array(9, AST))) +*/ + +Z3_lbool Z3_API Z3_interpolate(__in Z3_context ctx, + __in unsigned num, + __in_ecount(num) Z3_ast *cnsts, + __in_ecount(num) unsigned *parents, + __in Z3_params options, + __out_ecount(num - 1) Z3_ast *interps, + __out Z3_model *model, + __out Z3_literals *labels, + __in unsigned incremental, + __in unsigned num_theory, + __in_ecount(num_theory) Z3_ast *theory); +#endif \ No newline at end of file diff --git a/src/api/dotnet/InterpolationContext.cs b/src/api/dotnet/InterpolationContext.cs index 559a1bfc7..075ea29e8 100644 --- a/src/api/dotnet/InterpolationContext.cs +++ b/src/api/dotnet/InterpolationContext.cs @@ -89,47 +89,6 @@ namespace Microsoft.Z3 return (Z3_lbool)r; } - /// - /// Computes an interpolant. - /// - /// For more information on interpolation please refer - /// too the function Z3_compute_interpolant in the C/C++ API, which is - /// well documented. - Z3_lbool Interpolate(Expr[] cnsts, uint[] parents, Params options, bool incremental, Expr[] theory, out Expr[] interps, out Model model) - { - Contract.Requires(cnsts != null); - Contract.Requires(parents != null); - Contract.Requires(cnsts.Length == parents.Length); - Contract.Ensures(Contract.ValueAtReturn(out interps) != null); - Contract.Ensures(Contract.ValueAtReturn(out model) != null); - - CheckContextMatch(cnsts); - CheckContextMatch(theory); - - uint sz = (uint)cnsts.Length; - - IntPtr[] ni = new IntPtr[sz - 1]; - IntPtr nm = IntPtr.Zero; - IntPtr z = IntPtr.Zero; - - int r = Native.Z3_interpolate(nCtx, - sz, Expr.ArrayToNative(cnsts), parents, - options.NativeObject, - out ni, - ref nm, - ref z, // Z3_lterals are deprecated. - (uint)(incremental ? 1 : 0), - (uint)theory.Length, Expr.ArrayToNative(theory)); - - interps = new Expr[sz - 1]; - for (uint i = 0; i < sz - 1; i++) - interps[i] = Expr.Create(this, ni[i]); - - model = new Model(this, nm); - - return (Z3_lbool)r; - } - /// /// Return a string summarizing cumulative time used for interpolation. /// @@ -150,7 +109,7 @@ namespace Microsoft.Z3 public int CheckInterpolant(Expr[] cnsts, uint[] parents, Expr[] interps, out string error, Expr[] theory) { Contract.Requires(cnsts.Length == parents.Length); - Contract.Requires(cnsts.Length == interps.Length+1); + Contract.Requires(cnsts.Length == interps.Length + 1); IntPtr n_err_str; int r = Native.Z3_check_interpolant(nCtx, (uint)cnsts.Length, @@ -170,25 +129,27 @@ namespace Microsoft.Z3 /// For more information on interpolation please refer /// too the function Z3_read_interpolation_problem in the C/C++ API, which is /// well documented. - public int ReadInterpolationProblem(string filename, out Expr[] cnsts, out uint[] parents, out string error, out Expr[] theory) + public int ReadInterpolationProblem(string filename, out Expr[] cnsts, ref uint[] parents, out string error, out Expr[] theory) { uint num = 0, num_theory = 0; - IntPtr[] n_cnsts; - IntPtr[] n_theory; + IntPtr n_cnsts = new IntPtr(); + IntPtr n_theory = new IntPtr(); IntPtr n_err_str; - uint[][] n_parents; - int r = Native.Z3_read_interpolation_problem(nCtx, ref num, out n_cnsts, out n_parents, filename, out n_err_str, ref num_theory, out n_theory); + int r = Native.Z3_read_interpolation_problem(nCtx, ref num, ref n_cnsts, out parents, filename, out n_err_str, ref num_theory, ref n_theory); error = Marshal.PtrToStringAnsi(n_err_str); cnsts = new Expr[num]; parents = new uint[num]; - theory = new Expr[num_theory]; + theory = new Expr[num_theory]; for (int i = 0; i < num; i++) { - cnsts[i] = Expr.Create(this, n_cnsts[i]); - parents[i] = n_parents[0][i]; + IntPtr ce = new IntPtr(n_cnsts.ToInt64() + (IntPtr.Size * i)); + cnsts[i] = Expr.Create(this, ce); } for (int i = 0; i < num_theory; i++) - theory[i] = Expr.Create(this, n_theory[i]); + { + IntPtr te = new IntPtr(n_theory.ToInt64() + (IntPtr.Size * i)); + theory[i] = Expr.Create(this, te); + } return r; } diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 41ccf532e..04e2d38af 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1779,7 +1779,7 @@ extern "C" { Z3_sort Z3_API Z3_mk_tuple_sort(__in Z3_context c, __in Z3_symbol mk_tuple_name, __in unsigned num_fields, - __in_ecount(num_fields) Z3_symbol const field_names[], + __in_ecount(num_fields) Z3_symbol const field_names[], __in_ecount(num_fields) Z3_sort const field_sorts[], __out Z3_func_decl * mk_tuple_decl, __out_ecount(num_fields) Z3_func_decl proj_decl[]); @@ -4907,8 +4907,7 @@ END_MLAPI_EXCLUDE __in_ecount(num_sorts) Z3_sort const sorts[], __in unsigned num_decls, __in_ecount(num_decls) Z3_symbol const decl_names[], - __in_ecount(num_decls) Z3_func_decl const decls[] - ); + __in_ecount(num_decls) Z3_func_decl const decls[]); /** \brief Similar to #Z3_parse_smtlib2_string, but reads the benchmark from a file. @@ -4917,13 +4916,12 @@ END_MLAPI_EXCLUDE */ Z3_ast Z3_API Z3_parse_smtlib2_file(__in Z3_context c, __in Z3_string file_name, - __in unsigned num_sorts, - __in_ecount(num_sorts) Z3_symbol const sort_names[], - __in_ecount(num_sorts) Z3_sort const sorts[], - __in unsigned num_decls, - __in_ecount(num_decls) Z3_symbol const decl_names[], - __in_ecount(num_decls) Z3_func_decl const decls[] - ); + __in unsigned num_sorts, + __in_ecount(num_sorts) Z3_symbol const sort_names[], + __in_ecount(num_sorts) Z3_sort const sorts[], + __in unsigned num_decls, + __in_ecount(num_decls) Z3_symbol const decl_names[], + __in_ecount(num_decls) Z3_func_decl const decls[]); #ifdef ML4only #include diff --git a/src/api/z3_interp.h b/src/api/z3_interp.h index 1aac13b6e..88ba83e2d 100644 --- a/src/api/z3_interp.h +++ b/src/api/z3_interp.h @@ -163,88 +163,6 @@ extern "C" { __out Z3_ast_vector *interp, __out Z3_model *model); - - /** Constant reprepresenting a root of a formula tree for tree interpolation */ -#define IZ3_ROOT SHRT_MAX - - /** This function uses Z3 to determine satisfiability of a set of - constraints. If UNSAT, an interpolant is returned, based on the - refutation generated by Z3. If SAT, a model is returned. - - If "parents" is non-null, computes a tree interpolant. The tree is - defined by the array "parents". This array maps each formula in - the tree to its parent, where formulas are indicated by their - integer index in "cnsts". The parent of formula n must have index - greater than n. The last formula is the root of the tree. Its - parent entry should be the constant IZ3_ROOT. - - If "parents" is null, computes a sequence interpolant. - - \param ctx The Z3 context. Must be generated by iz3_mk_context - \param num The number of constraints in the sequence - \param cnsts Array of constraints (AST's in context ctx) - \param parents The parents vector defining the tree structure - \param options Interpolation options (may be NULL) - \param interps Array to return interpolants (size at least num-1, may be NULL) - \param model Returns a Z3 model if constraints SAT (may be NULL) - \param labels Returns relevant labels if SAT (may be NULL) - \param incremental - - VERY IMPORTANT: All the Z3 formulas in cnsts must be in Z3 - context ctx. The model and interpolants returned are also - in this context. - - The return code is as in Z3_check_assumptions, that is, - - Z3_L_FALSE = constraints UNSAT (interpolants returned) - Z3_L_TRUE = constraints SAT (model returned) - Z3_L_UNDEF = Z3 produced no result, or interpolation not possible - - Currently, this function supports integer and boolean variables, - as well as arrays over these types, with linear arithmetic, - uninterpreted functions and quantifiers over integers (that is - AUFLIA). Interpolants are produced in AULIA. However, some - uses of array operations may cause quantifiers to appear in the - interpolants even when there are no quantifiers in the input formulas. - Although quantifiers may appear in the input formulas, Z3 may give up in - this case, returning Z3_L_UNDEF. - - If "incremental" is true, cnsts must contain exactly the set of - formulas that are currently asserted in the context. If false, - there must be no formulas currently asserted in the context. - Setting "incremental" to true makes it posisble to incrementally - add and remove constraints from the context until the context - becomes UNSAT, at which point an interpolant is computed. Caution - must be used, however. Before popping the context, if you wish to - keep the interolant formulas, you *must* preserve them by using - Z3_persist_ast. Also, if you want to simplify the interpolant - formulas using Z3_simplify, you must first pop all of the - assertions in the context (or use a different context). Otherwise, - the formulas will be simplified *relative* to these constraints, - which is almost certainly not what you want. - - - Current limitations on tree interpolants. In a tree interpolation - problem, each constant (0-ary function symbol) must occur only - along one path from root to leaf. Function symbols (of arity > 0) - are considered to have global scope (i.e., may appear in any - interpolant formula). - - def_API('Z3_interpolate', BOOL, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(PARAMS), _out_array(1, AST), _out(MODEL), _out(LITERALS), _in(UINT), _in(UINT), _in_array(9, AST))) - */ - - Z3_lbool Z3_API Z3_interpolate(__in Z3_context ctx, - __in unsigned num, - __in_ecount(num) Z3_ast *cnsts, - __in_ecount(num) unsigned *parents, - __in Z3_params options, - __out_ecount(num - 1) Z3_ast *interps, - __out Z3_model *model, - __out Z3_literals *labels, - __in unsigned incremental, - __in unsigned num_theory, - __in_ecount(num_theory) Z3_ast *theory); - /** Return a string summarizing cumulative time used for interpolation. This string is purely for entertainment purposes and has no semantics. @@ -288,18 +206,18 @@ extern "C" { where each value is represented using the common symbols between the formulas in the subtree and the remainder of the formulas. - def_API('Z3_read_interpolation_problem', INT, (_in(CONTEXT), _out(UINT), _out_array(1, AST), _out_array(1, UINT_PTR), _in(STRING), _out(STRING), _out(UINT), _out_array(6, AST))) + def_API('Z3_read_interpolation_problem', INT, (_in(CONTEXT), _out(UINT), _out(AST), _out_array(1, UINT), _in(STRING), _out(STRING), _out(UINT), _out(AST))) */ int Z3_API Z3_read_interpolation_problem(__in Z3_context ctx, __out unsigned *num, - __out_ecount(*num) Z3_ast **cnsts, - __out_ecount(*num) unsigned **parents, + __out Z3_ast *cnsts, + __out unsigned parents[], __in Z3_string filename, - __out Z3_string *error, + __out_opt Z3_string_ptr error, __out unsigned *num_theory, - __out_ecount(*num_theory) Z3_ast **theory); + __out Z3_ast theory[]); @@ -323,12 +241,12 @@ extern "C" { int Z3_API Z3_check_interpolant(__in Z3_context ctx, __in unsigned num, - __in_ecount(num) Z3_ast *cnsts, - __in_ecount(num) unsigned *parents, + __in_ecount(num) Z3_ast cnsts[], + __in_ecount(num) unsigned parents[], __in_ecount(num - 1) Z3_ast *interps, - __out Z3_string *error, + __out_opt Z3_string_ptr error, __in unsigned num_theory, - __in_ecount(num_theory) Z3_ast *theory); + __in_ecount(num_theory) Z3_ast theory[]); /** Write an interpolation problem to file suitable for reading with Z3_read_interpolation_problem. The output file is a sequence @@ -346,11 +264,11 @@ extern "C" { void Z3_API Z3_write_interpolation_problem(__in Z3_context ctx, __in unsigned num, - __in_ecount(num) Z3_ast *cnsts, - __in_ecount(num) unsigned *parents, + __in_ecount(num) Z3_ast cnsts[], + __in_ecount(num) unsigned parents[], __in Z3_string filename, __in unsigned num_theory, - __in_ecount(num_theory) Z3_ast *theory); + __in_ecount(num_theory) Z3_ast theory[]); #ifdef __cplusplus }; From 1c1351a0644cfa567becd8447568617b05d02600 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 9 Oct 2014 18:11:42 +0100 Subject: [PATCH 467/509] Interpolation .NET API bugfixes Signed-off-by: Christoph M. Wintersteiger --- src/api/dotnet/InterpolationContext.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api/dotnet/InterpolationContext.cs b/src/api/dotnet/InterpolationContext.cs index 075ea29e8..fbedadffe 100644 --- a/src/api/dotnet/InterpolationContext.cs +++ b/src/api/dotnet/InterpolationContext.cs @@ -159,9 +159,10 @@ namespace Microsoft.Z3 /// For more information on interpolation please refer /// too the function Z3_write_interpolation_problem in the C/C++ API, which is /// well documented. - public void WriteInterpolationProblem(string filename, Expr[] cnsts, int[] parents, string error, Expr[] theory) + public void WriteInterpolationProblem(string filename, Expr[] cnsts, uint[] parents, string error, Expr[] theory) { Contract.Requires(cnsts.Length == parents.Length); + Native.Z3_write_interpolation_problem(nCtx, (uint)cnsts.Length, Expr.ArrayToNative(cnsts), parents, error, (uint)theory.Length, Expr.ArrayToNative(theory)); } } } From bcd2d935a99526b561033aff09bcd90b666318aa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 9 Oct 2014 10:18:46 -0700 Subject: [PATCH 468/509] enable modular parameters from the parser Signed-off-by: Nikolaj Bjorner --- src/cmd_context/tactic_cmds.cpp | 2 +- src/util/params.cpp | 57 ++++++++++++++++++++------------- src/util/params.h | 1 + 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index 27014bca9..75d56928e 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -500,7 +500,7 @@ static tactic * mk_using_params(cmd_context & ctx, sexpr * n) { symbol param_name = symbol(norm_param_name(c->get_symbol()).c_str()); c = n->get_child(i); i++; - switch (descrs.get_kind(param_name)) { + switch (descrs.get_kind_in_module(param_name)) { case CPK_INVALID: throw cmd_exception("invalid using-params combinator, unknown parameter ", param_name, c->get_line(), c->get_pos()); case CPK_BOOL: diff --git a/src/util/params.cpp b/src/util/params.cpp index cbb2b2acc..b869f8e3d 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -97,6 +97,35 @@ struct param_descrs::imp { return CPK_INVALID; } + bool split_name(symbol const& name, symbol & prefix, symbol & suffix) const { + if (name.is_numerical()) return false; + char const* str = name.bare_str(); + char const* period = strchr(str,'.'); + if (!period) return false; + svector prefix_((unsigned)(period-str), str); + prefix_.push_back(0); + prefix = symbol(prefix_.c_ptr()); + suffix = symbol(period + 1); + return true; + } + + param_kind get_kind_in_module(symbol & name) const { + param_kind k = get_kind(name); + symbol prefix, suffix; + if (k == CPK_INVALID && split_name(name, prefix, suffix)) { + k = get_kind(suffix); + if (k != CPK_INVALID) { + if (symbol(get_module(suffix)) == prefix) { + name = suffix; + } + else { + k = CPK_INVALID; + } + } + } + return k; + } + char const* get_module(symbol const& name) const { info i; if (m_info.find(name, i)) @@ -230,6 +259,10 @@ void param_descrs::erase(char const * name) { erase(symbol(name)); } +param_kind param_descrs::get_kind_in_module(symbol & name) const { + return m_imp->get_kind_in_module(name); +} + param_kind param_descrs::get_kind(symbol const & name) const { return m_imp->get_kind(name); } @@ -311,35 +344,13 @@ public: void reset(symbol const & k); void reset(char const * k); - bool split_name(symbol const& name, symbol & prefix, symbol & suffix) { - if (name.is_numerical()) return false; - char const* str = name.bare_str(); - char const* period = strchr(str,'.'); - if (!period) return false; - svector prefix_((unsigned)(period-str), str); - prefix_.push_back(0); - prefix = symbol(prefix_.c_ptr()); - suffix = symbol(period + 1); - return true; - } void validate(param_descrs const & p) { svector::iterator it = m_entries.begin(); svector::iterator end = m_entries.end(); symbol suffix, prefix; for (; it != end; ++it) { - param_kind expected = p.get_kind(it->first); - if (expected == CPK_INVALID && split_name(it->first, prefix, suffix)) { - expected = p.get_kind(suffix); - if (expected != CPK_INVALID) { - if (symbol(p.get_module(suffix)) == prefix) { - it->first = suffix; - } - else { - expected = CPK_INVALID; - } - } - } + param_kind expected = p.get_kind_in_module(it->first); if (expected == CPK_INVALID) { std::stringstream strm; strm << "unknown parameter '" << it->first.str() << "'\n"; diff --git a/src/util/params.h b/src/util/params.h index f11374775..e4a2b3693 100644 --- a/src/util/params.h +++ b/src/util/params.h @@ -123,6 +123,7 @@ public: void erase(symbol const & name); param_kind get_kind(char const * name) const; param_kind get_kind(symbol const & name) const; + param_kind get_kind_in_module(symbol & name) const; char const * get_descr(char const * name) const; char const * get_descr(symbol const & name) const; char const * get_default(char const * name) const; From 9b8406c7171b0687a2cb048020374d272965e16b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 10 Oct 2014 11:41:21 +0100 Subject: [PATCH 469/509] Resolved interpolation API issues. Signed-off-by: Christoph M. Wintersteiger --- scripts/update_api.py | 40 ++++++++++++++++++++++---- src/api/api_interp.cpp | 10 +++---- src/api/dotnet/InterpolationContext.cs | 18 ++++-------- src/api/z3_interp.h | 8 +++--- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index 09c4fbe1c..2ffd95e3d 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -108,6 +108,7 @@ INOUT = 2 IN_ARRAY = 3 OUT_ARRAY = 4 INOUT_ARRAY = 5 +OUT_MANAGED_ARRAY = 6 # Primitive Types VOID = 0 @@ -224,6 +225,10 @@ def _out_array2(cap, sz, ty): def _inout_array(sz, ty): return (INOUT_ARRAY, ty, sz, sz); +def _out_managed_array(sz,ty): + return (OUT_MANAGED_ARRAY, ty, 0, sz) + + def param_kind(p): return p[0] @@ -254,11 +259,11 @@ def param2dotnet(p): return "out IntPtr" else: return "[In, Out] ref %s" % type2dotnet(param_type(p)) - if k == IN_ARRAY: + elif k == IN_ARRAY: return "[In] %s[]" % type2dotnet(param_type(p)) - if k == INOUT_ARRAY: + elif k == INOUT_ARRAY: return "[In, Out] %s[]" % type2dotnet(param_type(p)) - if k == OUT_ARRAY: + elif k == OUT_ARRAY or k == OUT_MANAGED_ARRAY: return "[Out] out %s[]" % type2dotnet(param_type(p)) else: return type2dotnet(param_type(p)) @@ -268,7 +273,7 @@ def param2java(p): if k == OUT: if param_type(p) == INT or param_type(p) == UINT: return "IntPtr" - elif param_type(p) == INT64 or param_type(p) == UINT64 or param_type(p) >= FIRST_OBJ_ID: + elif param_type(p) == INT64 or param_type(p) == UINT64 or param_type(p) == VOID_PTR or param_type(p) >= FIRST_OBJ_ID: return "LongPtr" elif param_type(p) == STRING: return "StringPtr" @@ -466,7 +471,7 @@ def mk_dotnet_wrappers(): dotnet.write('out '); else: dotnet.write('ref ') - elif param_kind(param) == OUT_ARRAY: + elif param_kind(param) == OUT_ARRAY or param_kind(param) == OUT_MANAGED_ARRAY: dotnet.write('out '); dotnet.write('a%d' % i) i = i + 1 @@ -678,9 +683,11 @@ def mk_java(): if param_type(param) == INT or param_type(param) == UINT: java_wrapper.write(' jenv->GetIntArrayRegion(a%s, 0, (jsize)a%s, (jint*)_a%s);\n' % (i, param_array_capacity_pos(param), i)) else: - java_wrapper.write(' GETLONGAREGION(%s, a%s, 0, a%s, _a%s);\n' % (type2str(param_type(param)), i, param_array_capacity_pos(param), i)) + java_wrapper.write(' GETLONGAREGION(%s, a%s, 0, a%s, _a%s);\n' % (type2str(param_type(param)), i, param_array_capacity_pos(param), i)) elif k == IN and param_type(param) == STRING: java_wrapper.write(' Z3_string _a%s = (Z3_string) jenv->GetStringUTFChars(a%s, NULL);\n' % (i, i)) + elif k == OUT_MANAGED_ARRAY: + java_wrapper.write(' %s * _a%s = 0;\n' % (type2str(param_type(param)), i)) i = i + 1 # invoke procedure java_wrapper.write(' ') @@ -699,6 +706,8 @@ def mk_java(): java_wrapper.write('&_a%s' % i) elif k == OUT_ARRAY or k == IN_ARRAY or k == INOUT_ARRAY: java_wrapper.write('_a%s' % i) + elif k == OUT_MANAGED_ARRAY: + java_wrapper.write('&_a%s' % i) elif k == IN and param_type(param) == STRING: java_wrapper.write('_a%s' % i) else: @@ -734,6 +743,8 @@ def mk_java(): java_wrapper.write(' jfieldID fid = jenv->GetFieldID(mc, "value", "J");\n') java_wrapper.write(' jenv->SetLongField(a%s, fid, (jlong) _a%s);\n' % (i, i)) java_wrapper.write(' }\n') + elif k == OUT_MANAGED_ARRAY: + java_wrapper.write(' *(jlong**)a%s = (jlong*)_a%s;\n' % (i, i)) i = i + 1 # return if result == STRING: @@ -934,6 +945,9 @@ def def_API(name, result, params): elif ty == INT64: log_c.write(" I(0);\n") exe_c.write("in.get_int64_addr(%s)" % i) + elif ty == VOID_PTR: + log_c.write(" P(0);\n") + exe_c.write("in.get_obj_addr(%s)" % i) else: error("unsupported parameter for %s, %s" % (name, p)) elif kind == IN_ARRAY or kind == INOUT_ARRAY: @@ -978,6 +992,20 @@ def def_API(name, result, params): exe_c.write("in.get_uint_array(%s)" % i) else: error ("unsupported parameter for %s, %s" % (name, p)) + elif kind == OUT_MANAGED_ARRAY: + sz = param_array_size_pos(p) + sz_p = params[sz] + sz_p_k = param_kind(sz_p) + tstr = type2str(ty) + if sz_p_k == OUT or sz_p_k == INOUT: + sz_e = ("(*a%s)" % sz) + else: + sz_e = ("a%s" % sz) + log_c.write(" for (unsigned i = 0; i < %s; i++) { " % sz_e) + log_c.write("P(0);") + log_c.write(" }\n") + log_c.write(" Ap(%s);\n" % sz_e) + exe_c.write("reinterpret_cast<%s**>(in.get_obj_array(%s))" % (tstr, i)) else: error ("unsupported parameter for %s, %s" % (name, p)) i = i + 1 diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 3e9ddc8a3..dbf68da38 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -520,7 +520,7 @@ extern "C" { } - int Z3_read_interpolation_problem(Z3_context ctx, unsigned *_num, Z3_ast cnsts[], unsigned parents[], const char *filename, Z3_string_ptr error, unsigned *ret_num_theory, Z3_ast theory[]){ + int Z3_read_interpolation_problem(Z3_context ctx, unsigned *_num, Z3_ast *cnsts[], unsigned *parents[], const char *filename, Z3_string_ptr error, unsigned *ret_num_theory, Z3_ast *theory[]){ hash_map file_params; get_file_params(filename, file_params); @@ -549,11 +549,11 @@ extern "C" { if (ret_num_theory) *ret_num_theory = num_theory; if (theory) - theory = &read_theory[0]; + *theory = &read_theory[0]; if (!parents){ *_num = num; - cnsts = &read_cnsts[0]; + *cnsts = &read_cnsts[0]; return true; } @@ -623,8 +623,8 @@ extern "C" { } *_num = num; - cnsts = &read_cnsts[0]; - parents = &read_parents[0]; + *cnsts = &read_cnsts[0]; + *parents = &read_parents[0]; return true; fail: diff --git a/src/api/dotnet/InterpolationContext.cs b/src/api/dotnet/InterpolationContext.cs index fbedadffe..8ba5fe773 100644 --- a/src/api/dotnet/InterpolationContext.cs +++ b/src/api/dotnet/InterpolationContext.cs @@ -129,27 +129,21 @@ namespace Microsoft.Z3 /// For more information on interpolation please refer /// too the function Z3_read_interpolation_problem in the C/C++ API, which is /// well documented. - public int ReadInterpolationProblem(string filename, out Expr[] cnsts, ref uint[] parents, out string error, out Expr[] theory) + public int ReadInterpolationProblem(string filename, out Expr[] cnsts, out uint[] parents, out string error, out Expr[] theory) { uint num = 0, num_theory = 0; - IntPtr n_cnsts = new IntPtr(); - IntPtr n_theory = new IntPtr(); + IntPtr[] n_cnsts; + IntPtr[] n_theory; IntPtr n_err_str; - int r = Native.Z3_read_interpolation_problem(nCtx, ref num, ref n_cnsts, out parents, filename, out n_err_str, ref num_theory, ref n_theory); + int r = Native.Z3_read_interpolation_problem(nCtx, ref num, out n_cnsts, out parents, filename, out n_err_str, ref num_theory, out n_theory); error = Marshal.PtrToStringAnsi(n_err_str); cnsts = new Expr[num]; parents = new uint[num]; theory = new Expr[num_theory]; for (int i = 0; i < num; i++) - { - IntPtr ce = new IntPtr(n_cnsts.ToInt64() + (IntPtr.Size * i)); - cnsts[i] = Expr.Create(this, ce); - } + cnsts[i] = Expr.Create(this, n_cnsts[i]); for (int i = 0; i < num_theory; i++) - { - IntPtr te = new IntPtr(n_theory.ToInt64() + (IntPtr.Size * i)); - theory[i] = Expr.Create(this, te); - } + theory[i] = Expr.Create(this, n_theory[i]); return r; } diff --git a/src/api/z3_interp.h b/src/api/z3_interp.h index 88ba83e2d..729851988 100644 --- a/src/api/z3_interp.h +++ b/src/api/z3_interp.h @@ -206,18 +206,18 @@ extern "C" { where each value is represented using the common symbols between the formulas in the subtree and the remainder of the formulas. - def_API('Z3_read_interpolation_problem', INT, (_in(CONTEXT), _out(UINT), _out(AST), _out_array(1, UINT), _in(STRING), _out(STRING), _out(UINT), _out(AST))) + def_API('Z3_read_interpolation_problem', INT, (_in(CONTEXT), _out(UINT), _out_managed_array(1, AST), _out_managed_array(1, UINT), _in(STRING), _out(STRING), _out(UINT), _out_managed_array(6, AST))) */ int Z3_API Z3_read_interpolation_problem(__in Z3_context ctx, __out unsigned *num, - __out Z3_ast *cnsts, - __out unsigned parents[], + __out Z3_ast *cnsts[], + __out unsigned *parents[], __in Z3_string filename, __out_opt Z3_string_ptr error, __out unsigned *num_theory, - __out Z3_ast theory[]); + __out Z3_ast *theory[]); From 3e7c95db6b57aab3e657c928f6a07e24c1f16a62 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 10 Oct 2014 12:34:17 +0100 Subject: [PATCH 470/509] Interpolation API bugfixes Added Interpolation to the Java API Signed-off-by: Christoph M. Wintersteiger --- scripts/update_api.py | 9 +- src/api/dotnet/InterpolationContext.cs | 4 +- src/api/java/InterpolationContext.java | 169 +++++++++++++++++++++++++ 3 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 src/api/java/InterpolationContext.java diff --git a/scripts/update_api.py b/scripts/update_api.py index 2ffd95e3d..837c2e1f4 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -281,8 +281,13 @@ def param2java(p): print("ERROR: unreachable code") assert(False) exit(1) - if k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY: + elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY: return "%s[]" % type2java(param_type(p)) + elif k == OUT_MANAGED_ARRAY: + if param_type(p) == UINT: + return "UIntArrayPtr" + else: + return "ObjArrayPtr" else: return type2java(param_type(p)) @@ -529,6 +534,8 @@ def mk_java(): java_native.write(' public static class IntPtr { public int value; }\n') java_native.write(' public static class LongPtr { public long value; }\n') java_native.write(' public static class StringPtr { public String value; }\n') + java_native.write(' public static class ObjArrayPtr { public long[] value; }\n') + java_native.write(' public static class UIntArrayPtr { public int[] value; }\n') java_native.write(' public static native void setInternalErrorHandler(long ctx);\n\n') if IS_WINDOWS or os.uname()[0]=="CYGWIN": java_native.write(' static { System.loadLibrary("%s"); }\n' % get_component('java').dll_name) diff --git a/src/api/dotnet/InterpolationContext.cs b/src/api/dotnet/InterpolationContext.cs index 8ba5fe773..b5ada1bbd 100644 --- a/src/api/dotnet/InterpolationContext.cs +++ b/src/api/dotnet/InterpolationContext.cs @@ -153,10 +153,10 @@ namespace Microsoft.Z3 /// For more information on interpolation please refer /// too the function Z3_write_interpolation_problem in the C/C++ API, which is /// well documented. - public void WriteInterpolationProblem(string filename, Expr[] cnsts, uint[] parents, string error, Expr[] theory) + public void WriteInterpolationProblem(string filename, Expr[] cnsts, uint[] parents, Expr[] theory) { Contract.Requires(cnsts.Length == parents.Length); - Native.Z3_write_interpolation_problem(nCtx, (uint)cnsts.Length, Expr.ArrayToNative(cnsts), parents, error, (uint)theory.Length, Expr.ArrayToNative(theory)); + Native.Z3_write_interpolation_problem(nCtx, (uint)cnsts.Length, Expr.ArrayToNative(cnsts), parents, filename, (uint)theory.Length, Expr.ArrayToNative(theory)); } } } diff --git a/src/api/java/InterpolationContext.java b/src/api/java/InterpolationContext.java new file mode 100644 index 000000000..067871324 --- /dev/null +++ b/src/api/java/InterpolationContext.java @@ -0,0 +1,169 @@ +/** + * + */ +package com.microsoft.z3; + +import java.util.Map; +import java.lang.String; + +import com.microsoft.z3.Native.IntPtr; +import com.microsoft.z3.Native.UIntArrayPtr; +import com.microsoft.z3.enumerations.Z3_lbool; + +/** + * The InterpolationContext is suitable for generation of interpolants. + * + * For more information on interpolation please refer + * too the C/C++ API, which is well documented. + **/ +public class InterpolationContext extends Context +{ + /** + * Constructor. + **/ + public InterpolationContext() throws Z3Exception + { + m_ctx = Native.mkInterpolationContext(0); + initContext(); + } + + /** + * Constructor. + * + * + **/ + public InterpolationContext(Map settings) throws Z3Exception + { + long cfg = Native.mkConfig(); + for (Map.Entry kv : settings.entrySet()) + Native.setParamValue(cfg, kv.getKey(), kv.getValue()); + m_ctx = Native.mkInterpolationContext(cfg); + Native.delConfig(cfg); + initContext(); + } + + /** + * Create an expression that marks a formula position for interpolation. + * @throws Z3Exception + **/ + public BoolExpr MkInterpolant(BoolExpr a) throws Z3Exception + { + checkContextMatch(a); + return new BoolExpr(this, Native.mkInterpolant(nCtx(), a.getNativeObject())); + } + + /** + * Computes an interpolant. + * For more information on interpolation please refer + * too the function Z3_get_interpolant in the C/C++ API, which is + * well documented. + * @throws Z3Exception + **/ + Expr[] GetInterpolant(Expr pf, Expr pat, Params p) throws Z3Exception + { + checkContextMatch(pf); + checkContextMatch(pat); + checkContextMatch(p); + + ASTVector seq = new ASTVector(this, Native.getInterpolant(nCtx(), pf.getNativeObject(), pat.getNativeObject(), p.getNativeObject())); + int n = seq.size(); + Expr[] res = new Expr[n]; + for (int i = 0; i < n; i++) + res[i] = Expr.create(this, seq.get(i).getNativeObject()); + return res; + } + + /** + * Computes an interpolant. + * For more information on interpolation please refer + * too the function Z3_compute_interpolant in the C/C++ API, which is + * well documented. + * @throws Z3Exception + **/ + Z3_lbool ComputeInterpolant(Expr pat, Params p, ASTVector interp, Model model) throws Z3Exception + { + checkContextMatch(pat); + checkContextMatch(p); + + Native.LongPtr n_i = new Native.LongPtr(); + Native.LongPtr n_m = new Native.LongPtr(); + int r = Native.computeInterpolant(nCtx(), pat.getNativeObject(), p.getNativeObject(), n_i, n_m); + interp = new ASTVector(this, n_i.value); + model = new Model(this, n_m.value); + return Z3_lbool.fromInt(r); + } + + /// + /// Return a string summarizing cumulative time used for interpolation. + /// + /// For more information on interpolation please refer + /// too the function Z3_interpolation_profile in the C/C++ API, which is + /// well documented. + public String InterpolationProfile() throws Z3Exception + { + return Native.interpolationProfile(nCtx()); + } + + /// + /// Checks the correctness of an interpolant. + /// + /// For more information on interpolation please refer + /// too the function Z3_check_interpolant in the C/C++ API, which is + /// well documented. + public int CheckInterpolant(Expr[] cnsts, int[] parents, Expr[] interps, String error, Expr[] theory) throws Z3Exception + { + Native.StringPtr n_err_str = new Native.StringPtr(); + int r = Native.checkInterpolant(nCtx(), + cnsts.length, + Expr.arrayToNative(cnsts), + parents, + Expr.arrayToNative(interps), + n_err_str, + theory.length, + Expr.arrayToNative(theory)); + error = n_err_str.value; + return r; + } + + /// + /// Reads an interpolation problem from a file. + /// + /// For more information on interpolation please refer + /// too the function Z3_read_interpolation_problem in the C/C++ API, which is + /// well documented. + public int ReadInterpolationProblem(String filename, Expr[] cnsts, int[] parents, String error, Expr[] theory) throws Z3Exception + { + Native.IntPtr n_num = new Native.IntPtr(); + Native.IntPtr n_num_theory = new Native.IntPtr(); + Native.ObjArrayPtr n_cnsts = new Native.ObjArrayPtr(); + Native.UIntArrayPtr n_parents = new Native.UIntArrayPtr(); + Native.ObjArrayPtr n_theory = new Native.ObjArrayPtr(); + Native.StringPtr n_err_str = new Native.StringPtr(); + int r = Native.readInterpolationProblem(nCtx(), n_num, n_cnsts, n_parents, filename, n_err_str, n_num_theory, n_theory); + int num = n_num.value; + int num_theory = n_num_theory.value; + error = n_err_str.value; + cnsts = new Expr[num]; + parents = new int[num]; + theory = new Expr[num_theory]; + for (int i = 0; i < num; i++) + { + cnsts[i] = Expr.create(this, n_cnsts.value[i]); + parents[i] = n_parents.value[i]; + } + for (int i = 0; i < num_theory; i++) + theory[i] = Expr.create(this, n_theory.value[i]); + return r; + } + + /// + /// Writes an interpolation problem to a file. + /// + /// For more information on interpolation please refer + /// too the function Z3_write_interpolation_problem in the C/C++ API, which is + /// well documented. + public void WriteInterpolationProblem(String filename, Expr[] cnsts, int[] parents, String error, Expr[] theory) throws Z3Exception + { + Native.writeInterpolationProblem(nCtx(), cnsts.length, Expr.arrayToNative(cnsts), parents, filename, theory.length, Expr.arrayToNative(theory)); + } +} From 342a23cfcbff0dceedaecd6a1ca00e2ca88c8482 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 10 Oct 2014 13:00:41 +0100 Subject: [PATCH 471/509] C++ API bugfix Signed-off-by: Christoph M. Wintersteiger --- src/api/c++/z3++.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 20100d124..c35208d86 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1432,12 +1432,12 @@ namespace z3 { t1.check_error(); return tactic(t1.ctx(), r); } - friend tactic repeat(tactic const & t, unsigned max=UINT_MAX); + friend tactic repeat(tactic const & t, unsigned max); friend tactic with(tactic const & t, params const & p); friend tactic try_for(tactic const & t, unsigned ms); }; - inline tactic repeat(tactic const & t, unsigned max) { + inline tactic repeat(tactic const & t, unsigned max=UINT_MAX) { Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); t.check_error(); return tactic(t.ctx(), r); From 0451a605f43065edccdfd1f091c6414cd1b297f8 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 10 Oct 2014 13:05:11 +0100 Subject: [PATCH 472/509] Interpolation example bugfixes Signed-off-by: Christoph M. Wintersteiger --- examples/interp/iz3.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/interp/iz3.cpp b/examples/interp/iz3.cpp index fa7d8b896..26f62976f 100755 --- a/examples/interp/iz3.cpp +++ b/examples/interp/iz3.cpp @@ -90,12 +90,12 @@ int main(int argc, const char **argv) { /* Read an interpolation problem */ - int num; + unsigned num; Z3_ast *constraints; - int *parents = 0; + unsigned *parents = 0; const char *error; bool ok; - int num_theory; + unsigned num_theory; Z3_ast *theory; ok = Z3_read_interpolation_problem(ctx, &num, &constraints, tree_mode ? &parents : 0, filename, &error, &num_theory, &theory); @@ -144,7 +144,7 @@ int main(int argc, const char **argv) { if(!incremental_mode){ /* In non-incremental mode, we just pass the constraints. */ - result = Z3_interpolate(ctx, num, constraints, (unsigned int *)parents, options, interpolants, &model, 0, false, num_theory, theory); + result = Z3_interpolate(ctx, num, constraints, parents, options, interpolants, &model, 0, false, num_theory, theory); } else { @@ -165,7 +165,7 @@ int main(int argc, const char **argv) { for(int i = 0; i < num; i++){ asserted[i] = constraints[i]; Z3_assert_cnstr(ctx,constraints[i]); // assert one constraint - result = Z3_interpolate(ctx, num, &asserted[0], (unsigned int *)parents, options, interpolants, &model, 0, true, 0, 0); + result = Z3_interpolate(ctx, num, &asserted[0], parents, options, interpolants, &model, 0, true, 0, 0); if(result == Z3_L_FALSE){ for(unsigned j = 0; j < num-1; j++) /* Since we want the interpolant formulas to survive a "pop", we From e8b04790cf20688b5ef829912fcf8afa6da3f8dc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Oct 2014 09:43:55 -0700 Subject: [PATCH 473/509] fix build by disabling removed API call from interpolation sample Signed-off-by: Nikolaj Bjorner --- examples/interp/iz3.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/interp/iz3.cpp b/examples/interp/iz3.cpp index 26f62976f..6d631ebc9 100755 --- a/examples/interp/iz3.cpp +++ b/examples/interp/iz3.cpp @@ -144,7 +144,7 @@ int main(int argc, const char **argv) { if(!incremental_mode){ /* In non-incremental mode, we just pass the constraints. */ - result = Z3_interpolate(ctx, num, constraints, parents, options, interpolants, &model, 0, false, num_theory, theory); + result = Z3_L_UNDEF; // FIXME: Z3_interpolate(ctx, num, constraints, parents, options, interpolants, &model, 0, false, num_theory, theory); } else { @@ -165,7 +165,7 @@ int main(int argc, const char **argv) { for(int i = 0; i < num; i++){ asserted[i] = constraints[i]; Z3_assert_cnstr(ctx,constraints[i]); // assert one constraint - result = Z3_interpolate(ctx, num, &asserted[0], parents, options, interpolants, &model, 0, true, 0, 0); + result = Z3_L_UNDEF; // FIXME: Z3_interpolate(ctx, num, &asserted[0], parents, options, interpolants, &model, 0, true, 0, 0); if(result == Z3_L_FALSE){ for(unsigned j = 0; j < num-1; j++) /* Since we want the interpolant formulas to survive a "pop", we From 0e4e72b1bc70adcd1f05268bfcbce87a33e010af Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 14 Oct 2014 13:22:12 +0100 Subject: [PATCH 474/509] Added new params.Add functions to the .NET and Java APIs. Signed-off-by: Christoph M. Wintersteiger --- src/api/dotnet/Params.cs | 20 ++++++++++++++++++++ src/api/java/Params.java | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/api/dotnet/Params.cs b/src/api/dotnet/Params.cs index 56a7a891f..c33728491 100644 --- a/src/api/dotnet/Params.cs +++ b/src/api/dotnet/Params.cs @@ -58,6 +58,16 @@ namespace Microsoft.Z3 Native.Z3_params_set_double(Context.nCtx, NativeObject, name.NativeObject, value); } + /// + /// Adds a parameter setting. + /// + public void Add(Symbol name, string value) + { + Contract.Requires(value != null); + + Native.Z3_params_set_symbol(Context.nCtx, NativeObject, name.NativeObject, Context.MkSymbol(value).NativeObject); + } + /// /// Adds a parameter setting. /// @@ -103,6 +113,16 @@ namespace Microsoft.Z3 Native.Z3_params_set_symbol(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, value.NativeObject); } + /// + /// Adds a parameter setting. + /// + public void Add(string name, string value) + { + Contract.Requires(value != null); + + Native.Z3_params_set_symbol(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, Context.MkSymbol(value).NativeObject); + } + /// /// A string representation of the parameter set. /// diff --git a/src/api/java/Params.java b/src/api/java/Params.java index 68af4386b..cf3d8c759 100644 --- a/src/api/java/Params.java +++ b/src/api/java/Params.java @@ -28,6 +28,17 @@ public class Params extends Z3Object Native.paramsSetDouble(getContext().nCtx(), getNativeObject(), name.getNativeObject(), value); } + + /** + * Adds a parameter setting. + **/ + public void add(Symbol name, String value) throws Z3Exception + { + + Native.paramsSetSymbol(getContext().nCtx(), getNativeObject(), + name.getNativeObject(), + getContext().mkSymbol(value).getNativeObject()); + } /** * Adds a parameter setting. @@ -75,6 +86,17 @@ public class Params extends Z3Object .mkSymbol(name).getNativeObject(), value.getNativeObject()); } + /** + * Adds a parameter setting. + **/ + public void add(String name, String value) throws Z3Exception + { + + Native.paramsSetSymbol(getContext().nCtx(), getNativeObject(), + getContext().mkSymbol(name).getNativeObject(), + getContext().mkSymbol(value).getNativeObject()); + } + /** * A string representation of the parameter set. **/ From 10c5ed634470c635326f9c8a13fdbf690134ac04 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Oct 2014 11:29:05 -0700 Subject: [PATCH 475/509] add parameter validation in two remaining local cases Signed-off-by: Nikolaj Bjorner --- src/api/api_solver.cpp | 3 +++ src/api/api_tactic.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index c8b1723f1..6fc0c1c72 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -130,6 +130,9 @@ extern "C" { Z3_TRY; LOG_Z3_solver_set_params(c, s, p); RESET_ERROR_CODE(); + param_descrs r; + to_solver_ref(s)->collect_param_descrs(r); + to_param_ref(p).validate(r); if (to_solver(s)->m_solver) { bool old_model = to_solver(s)->m_params.get_bool("model", true); bool new_model = to_param_ref(p).get_bool("model", true); diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index 7dce33971..adfd0fb71 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -450,6 +450,9 @@ extern "C" { Z3_TRY; LOG_Z3_tactic_apply_ex(c, t, g, p); RESET_ERROR_CODE(); + param_descrs pd; + to_tactic_ref(t)->collect_param_descrs(pd); + to_param_ref(p).validate(pd); Z3_apply_result r = _tactic_apply(c, t, g, to_param_ref(p)); RETURN_Z3(r); Z3_CATCH_RETURN(0); From 92166eb5cb6efe0f08ac6824e7c75c6074a24988 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Oct 2014 13:12:40 -0700 Subject: [PATCH 476/509] deal with warning for unused parameter Signed-off-by: Nikolaj Bjorner --- src/muz/pdr/pdr_context.cpp | 2 +- src/muz/pdr/pdr_context.h | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index f07964395..a1f3af401 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -1274,7 +1274,7 @@ namespace pdr { m_pm(m_fparams, params.max_num_contexts(), m), m_query_pred(m), m_query(0), - m_search(m_params.bfs_model_search(), m), + m_search(m_params.bfs_model_search()), m_last_result(l_undef), m_inductive_lvl(0), m_expanded_lvl(0), diff --git a/src/muz/pdr/pdr_context.h b/src/muz/pdr/pdr_context.h index a85011600..e21f46412 100644 --- a/src/muz/pdr/pdr_context.h +++ b/src/muz/pdr/pdr_context.h @@ -245,7 +245,6 @@ namespace pdr { class model_search { typedef ptr_vector model_nodes; - ast_manager& m; bool m_bfs; model_node* m_root; std::deque m_leaves; @@ -258,7 +257,7 @@ namespace pdr { void enqueue_leaf(model_node& n); // add leaf to priority queue. void update_models(); public: - model_search(bool bfs, ast_manager& m): m(m), m_bfs(bfs), m_root(0) {} + model_search(bool bfs): m_bfs(bfs), m_root(0) {} ~model_search(); void reset(); From 136b172b5a3ef2dd943b1af1534416d9abbe956f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Oct 2014 09:58:54 -0700 Subject: [PATCH 477/509] move parameter validation for when solver object is actually crated Signed-off-by: Nikolaj Bjorner --- src/api/api_solver.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 6fc0c1c72..65b2a9047 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -40,6 +40,10 @@ extern "C" { params_ref p = s->m_params; mk_c(c)->params().get_solver_params(mk_c(c)->m(), p, proofs_enabled, models_enabled, unsat_core_enabled); s->m_solver = (*(s->m_solver_factory))(mk_c(c)->m(), p, proofs_enabled, models_enabled, unsat_core_enabled, s->m_logic); + + param_descrs r; + s->m_solver->collect_param_descrs(r); + p.validate(r); s->m_solver->updt_params(p); } @@ -130,14 +134,15 @@ extern "C" { Z3_TRY; LOG_Z3_solver_set_params(c, s, p); RESET_ERROR_CODE(); - param_descrs r; - to_solver_ref(s)->collect_param_descrs(r); - to_param_ref(p).validate(r); + if (to_solver(s)->m_solver) { bool old_model = to_solver(s)->m_params.get_bool("model", true); bool new_model = to_param_ref(p).get_bool("model", true); if (old_model != new_model) to_solver_ref(s)->set_produce_models(new_model); + param_descrs r; + to_solver_ref(s)->collect_param_descrs(r); + to_param_ref(p).validate(r); to_solver_ref(s)->updt_params(to_param_ref(p)); } to_solver(s)->m_params = to_param_ref(p); From c739d803ab0ea7543234d6300841b3809823ce2d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Oct 2014 13:42:56 -0700 Subject: [PATCH 478/509] include model/proof/unsat_core as part of model parameters Signed-off-by: Nikolaj Bjorner --- src/api/api_solver.cpp | 4 ++++ src/cmd_context/context_params.cpp | 10 +++++++--- src/cmd_context/context_params.h | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 65b2a9047..b9b3d8166 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -43,6 +43,7 @@ extern "C" { param_descrs r; s->m_solver->collect_param_descrs(r); + context_params::collect_solver_param_descrs(r); p.validate(r); s->m_solver->updt_params(p); } @@ -106,6 +107,7 @@ extern "C" { if (!initialized) init_solver(c, s); to_solver_ref(s)->collect_param_descrs(descrs); + context_params::collect_solver_param_descrs(descrs); if (!initialized) to_solver(s)->m_solver = 0; descrs.display(buffer); @@ -123,6 +125,7 @@ extern "C" { if (!initialized) init_solver(c, s); to_solver_ref(s)->collect_param_descrs(d->m_descrs); + context_params::collect_solver_param_descrs(d->m_descrs); if (!initialized) to_solver(s)->m_solver = 0; Z3_param_descrs r = of_param_descrs(d); @@ -142,6 +145,7 @@ extern "C" { to_solver_ref(s)->set_produce_models(new_model); param_descrs r; to_solver_ref(s)->collect_param_descrs(r); + context_params::collect_solver_param_descrs(r); to_param_ref(p).validate(r); to_solver_ref(s)->updt_params(to_param_ref(p)); } diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index 0ec44e0cf..d796dc9b1 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -114,15 +114,19 @@ void context_params::collect_param_descrs(param_descrs & d) { d.insert("well_sorted_check", CPK_BOOL, "type checker", "true"); d.insert("type_check", CPK_BOOL, "type checker (alias for well_sorted_check)", "true"); d.insert("auto_config", CPK_BOOL, "use heuristics to automatically select solver and configure it", "true"); - d.insert("proof", CPK_BOOL, "proof generation, it must be enabled when the Z3 context is created", "false"); d.insert("check_interpolants", CPK_BOOL, "check correctness of interpolants", "false"); - d.insert("model", CPK_BOOL, "model generation for solvers, this parameter can be overwritten when creating a solver", "true"); d.insert("model_validate", CPK_BOOL, "validate models produced by solvers", "false"); d.insert("trace", CPK_BOOL, "trace generation for VCC", "false"); d.insert("trace_file_name", CPK_STRING, "trace out file name (see option 'trace')", "z3.log"); - d.insert("unsat_core", CPK_BOOL, "unsat-core generation for solvers, this parameter can be overwritten when creating a solver, not every solver in Z3 supports unsat core generation", "false"); d.insert("debug_ref_count", CPK_BOOL, "debug support for AST reference counting", "false"); d.insert("smtlib2_compliant", CPK_BOOL, "enable/disable SMT-LIB 2.0 compliance", "false"); + collect_solver_param_descrs(d); +} + +void context_params::collect_solver_param_descrs(param_descrs & d) { + d.insert("proof", CPK_BOOL, "proof generation, it must be enabled when the Z3 context is created", "false"); + d.insert("model", CPK_BOOL, "model generation for solvers, this parameter can be overwritten when creating a solver", "true"); + d.insert("unsat_core", CPK_BOOL, "unsat-core generation for solvers, this parameter can be overwritten when creating a solver, not every solver in Z3 supports unsat core generation", "false"); } params_ref context_params::merge_default_params(params_ref const & p) { diff --git a/src/cmd_context/context_params.h b/src/cmd_context/context_params.h index 7271bb84f..e26fd3fe5 100644 --- a/src/cmd_context/context_params.h +++ b/src/cmd_context/context_params.h @@ -55,6 +55,8 @@ public: */ void get_solver_params(ast_manager const & m, params_ref & p, bool & proofs_enabled, bool & models_enabled, bool & unsat_core_enabled); + static void collect_solver_param_descrs(param_descrs & d); + /** \brief Include in p parameters derived from this context_params. These are parameters that are meaningful for tactics and solvers. From 7767a2362685e407a38b8efd5b534992a319a7c2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Oct 2014 21:37:37 -0700 Subject: [PATCH 479/509] improve error messages on incorrect parameter passing Signed-off-by: Nikolaj Bjorner --- src/cmd_context/context_params.cpp | 8 +++++++- src/smt/arith_eq_solver.cpp | 4 +++- src/util/gparams.cpp | 21 +++++++++++++++------ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index d796dc9b1..d1184fbc8 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -86,7 +86,13 @@ void context_params::set(char const * param, char const * value) { set_bool(m_smtlib2_compliant, param, value); } else { - throw default_exception("unknown parameter '%s'", p.c_str()); + param_descrs d; + collect_param_descrs(d); + std::stringstream strm; + strm << "unknown parameter '" << p << "'\n"; + strm << "Legal parameters are:\n"; + d.display(strm, 2, false, false); + throw default_exception(strm.str()); } } diff --git a/src/smt/arith_eq_solver.cpp b/src/smt/arith_eq_solver.cpp index 41a61bf73..9a6868ff1 100644 --- a/src/smt/arith_eq_solver.cpp +++ b/src/smt/arith_eq_solver.cpp @@ -604,7 +604,9 @@ bool arith_eq_solver::solve_integer_equations_gcd( } SASSERT(g == r0[i]); } - SASSERT(abs(r0[i]).is_one()); + if (!abs(r0[i]).is_one()) { + return false; + } live.erase(live.begin()+live_pos); for (j = 0; j < live.size(); ++j) { row& r = rows[live[j]]; diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 1d9426390..04acf1ad9 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -201,7 +201,7 @@ public: } } - void throw_unknown_parameter(symbol const & param_name, symbol const & mod_name) { + void throw_unknown_parameter(symbol const & param_name, param_descrs const& d, symbol const & mod_name) { if (mod_name == symbol::null) { char const * new_name = get_new_param_name(param_name); if (new_name) { @@ -213,11 +213,20 @@ public: param_name.bare_str()); } else { - throw exception("unknown parameter '%s'", param_name.bare_str()); + std::stringstream strm; + strm << "unknown parameter '" << param_name << "'\n"; + strm << "Legal parameters are:\n"; + d.display(strm, 2, false, false); + throw default_exception(strm.str()); } } else { - throw exception("unknown parameter '%s' at module '%s'", param_name.bare_str(), mod_name.bare_str()); + std::stringstream strm; + strm << "unknown parameter '" << param_name << "' "; + strm << "at module '" << mod_name << "'\n"; + strm << "Legal parameters are:\n"; + d.display(strm, 2, false, false); + throw default_exception(strm.str()); } } @@ -225,7 +234,7 @@ public: param_kind k = d.get_kind(param_name); params_ref & ps = get_params(mod_name); if (k == CPK_INVALID) { - throw_unknown_parameter(param_name, mod_name); + throw_unknown_parameter(param_name, d, mod_name); } else if (k == CPK_UINT) { long val = strtol(value, 0, 10); @@ -312,7 +321,7 @@ public: std::string get_default(param_descrs const & d, symbol const & p, symbol const & m) { if (!d.contains(p)) { - throw_unknown_parameter(p, m); + throw_unknown_parameter(p, d, m); } char const * r = d.get_default(p); if (r == 0) @@ -478,7 +487,7 @@ public: throw exception("unknown module '%s'", m.bare_str()); } if (!d->contains(p)) - throw_unknown_parameter(p, m); + throw_unknown_parameter(p, *d, m); out << " name: " << p << "\n"; if (m != symbol::null) { out << " module: " << m << "\n"; From fe4a8b44a51e3364f6006b3171973d43a2ba418d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 Oct 2014 22:40:52 -0700 Subject: [PATCH 480/509] revert some changes to how 'out' parameters are annotated on API calls. Retain the 'out' annotation for so-called managed out parameters. The data-type examples in managed API fail with the out parameter annotation as no memory is allocated on instances of out parameters, other than the interpolation APIs that are new Signed-off-by: Nikolaj Bjorner --- scripts/update_api.py | 8 +++++--- src/api/dotnet/Constructor.cs | 6 +++--- src/api/dotnet/Context.cs | 2 +- src/api/dotnet/EnumSort.cs | 2 +- src/api/dotnet/TupleSort.cs | 4 ++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index 837c2e1f4..e08adf111 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -263,8 +263,10 @@ def param2dotnet(p): return "[In] %s[]" % type2dotnet(param_type(p)) elif k == INOUT_ARRAY: return "[In, Out] %s[]" % type2dotnet(param_type(p)) - elif k == OUT_ARRAY or k == OUT_MANAGED_ARRAY: - return "[Out] out %s[]" % type2dotnet(param_type(p)) + elif k == OUT_ARRAY: + return "[Out] %s[]" % type2dotnet(param_type(p)) + elif k == OUT_MANAGED_ARRAY: + return "[Out] out %s[]" % type2dotnet(param_type(p)) else: return type2dotnet(param_type(p)) @@ -476,7 +478,7 @@ def mk_dotnet_wrappers(): dotnet.write('out '); else: dotnet.write('ref ') - elif param_kind(param) == OUT_ARRAY or param_kind(param) == OUT_MANAGED_ARRAY: + elif param_kind(param) == OUT_MANAGED_ARRAY: dotnet.write('out '); dotnet.write('a%d' % i) i = i + 1 diff --git a/src/api/dotnet/Constructor.cs b/src/api/dotnet/Constructor.cs index 8d478dd85..527b8bc13 100644 --- a/src/api/dotnet/Constructor.cs +++ b/src/api/dotnet/Constructor.cs @@ -50,7 +50,7 @@ namespace Microsoft.Z3 IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; - Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, out accessors); + Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); return new FuncDecl(Context, constructor); } } @@ -66,7 +66,7 @@ namespace Microsoft.Z3 IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; - Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, out accessors); + Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); return new FuncDecl(Context, tester); } } @@ -82,7 +82,7 @@ namespace Microsoft.Z3 IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; - Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, out accessors); + Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); FuncDecl[] t = new FuncDecl[n]; for (uint i = 0; i < n; i++) t[i] = new FuncDecl(Context, accessors[i]); diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index a9e25de4f..2b88cbab7 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -424,7 +424,7 @@ namespace Microsoft.Z3 n_constr[i] = cla[i].NativeObject; } IntPtr[] n_res = new IntPtr[n]; - Native.Z3_mk_datatypes(nCtx, n, Symbol.ArrayToNative(names), out n_res, n_constr); + Native.Z3_mk_datatypes(nCtx, n, Symbol.ArrayToNative(names), n_res, n_constr); DatatypeSort[] res = new DatatypeSort[n]; for (uint i = 0; i < n; i++) res[i] = new DatatypeSort(this, n_res[i]); diff --git a/src/api/dotnet/EnumSort.cs b/src/api/dotnet/EnumSort.cs index db3d5123f..e62043078 100644 --- a/src/api/dotnet/EnumSort.cs +++ b/src/api/dotnet/EnumSort.cs @@ -88,7 +88,7 @@ namespace Microsoft.Z3 IntPtr[] n_constdecls = new IntPtr[n]; IntPtr[] n_testers = new IntPtr[n]; NativeObject = Native.Z3_mk_enumeration_sort(ctx.nCtx, name.NativeObject, (uint)n, - Symbol.ArrayToNative(enumNames), out n_constdecls, out n_testers); + Symbol.ArrayToNative(enumNames), n_constdecls, n_testers); } #endregion }; diff --git a/src/api/dotnet/TupleSort.cs b/src/api/dotnet/TupleSort.cs index 7d0b6a853..81a0eaf60 100644 --- a/src/api/dotnet/TupleSort.cs +++ b/src/api/dotnet/TupleSort.cs @@ -74,10 +74,10 @@ namespace Microsoft.Z3 Contract.Requires(name != null); IntPtr t = IntPtr.Zero; - IntPtr[] f; + IntPtr[] f = new IntPtr[numFields]; NativeObject = Native.Z3_mk_tuple_sort(ctx.nCtx, name.NativeObject, numFields, Symbol.ArrayToNative(fieldNames), AST.ArrayToNative(fieldSorts), - ref t, out f); + ref t, f); } #endregion }; From f4a015602ca0b204ba72373301240eaa89e6fc08 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sat, 18 Oct 2014 13:43:13 +0100 Subject: [PATCH 481/509] Disable FPA-min/max because of name clashes with user-defined functions. Signed-off-by: Christoph M. Wintersteiger --- src/ast/float_decl_plugin.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index df26422c8..c6706b7b1 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -581,8 +581,9 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("isSubnormal", OP_FLOAT_IS_SUBNORMAL)); op_names.push_back(builtin_name("isSignMinus", OP_FLOAT_IS_SIGN_MINUS)); - op_names.push_back(builtin_name("min", OP_FLOAT_MIN)); - op_names.push_back(builtin_name("max", OP_FLOAT_MAX)); + // Disabled min/max, clashes with user-defined min/max functions + // op_names.push_back(builtin_name("min", OP_FLOAT_MIN)); + // op_names.push_back(builtin_name("max", OP_FLOAT_MAX)); op_names.push_back(builtin_name("asFloat", OP_TO_FLOAT)); From de9f6d3e11d536e6dabf4247747214da96af3546 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 21 Oct 2014 16:52:16 +0100 Subject: [PATCH 482/509] FPA name clash fix Signed-off-by: Christoph M. Wintersteiger --- src/ast/float_decl_plugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index c6706b7b1..5466df069 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -314,8 +314,8 @@ func_decl * float_decl_plugin::mk_binary_decl(decl_kind k, unsigned num_paramete symbol name; switch (k) { case OP_FLOAT_REM: name = "remainder"; break; - case OP_FLOAT_MIN: name = "min"; break; - case OP_FLOAT_MAX: name = "max"; break; + case OP_FLOAT_MIN: name = "fp.min"; break; + case OP_FLOAT_MAX: name = "fp.max"; break; default: UNREACHABLE(); break; From 7f045290804ff9564f6db59cb1c6145bf0d6062c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 21 Oct 2014 09:11:38 -0700 Subject: [PATCH 483/509] validate types of parameter values that get set globally Signed-off-by: Nikolaj Bjorner --- src/util/gparams.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 04acf1ad9..6397ad377 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -230,6 +230,43 @@ public: } } + void validate_type(symbol const& name, char const* value, param_descrs const& d) { + param_kind k = d.get_kind(name); + std::stringstream strm; + + switch (k) { + case CPK_UINT: + for (; *value; ++value) { + if (!('0' <= *value && *value <= '9')) { + strm << "Expected values for parameter " << name + << " is an unsigned integer. It was given argument '" << value << "'"; + throw default_exception(strm.str()); + } + } + break; + case CPK_DOUBLE: + for (; *value; ++value) { + if (!('0' <= *value && *value <= '9') && *value != '.' && *value != '-') { + strm << "Expected values for parameter " << name + << " is a double. It was given argument '" << value << "'"; + throw default_exception(strm.str()); + } + } + break; + + case CPK_BOOL: + if (strcmp(value, "true") != 0 && strcmp(value, "false") != 0) { + strm << "Expected values for parameter " << name + << " are 'true' or 'false'. It was given argument '" << value << "'"; + throw default_exception(strm.str()); + } + break; + case CPK_SYMBOL: + case CPK_STRING: + break; + } + } + void set(param_descrs const & d, symbol const & param_name, char const * value, symbol const & mod_name) { param_kind k = d.get_kind(param_name); params_ref & ps = get_params(mod_name); @@ -291,11 +328,13 @@ public: symbol m, p; normalize(name, m, p); if (m == symbol::null) { + validate_type(p, value, get_param_descrs()); set(get_param_descrs(), p, value, m); } else { param_descrs * d; if (get_module_param_descrs().find(m, d)) { + validate_type(p, value, *d); set(*d, p, value, m); } else { From 340f7659839ce492b7eb8483fd2bd91eb1ca9645 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 21 Oct 2014 09:35:32 -0700 Subject: [PATCH 484/509] make sure that parameters are appended such that multiple paramters are not ignored on the solver Signed-off-by: Nikolaj Bjorner --- src/api/api_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index b9b3d8166..e57050e82 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -149,7 +149,7 @@ extern "C" { to_param_ref(p).validate(r); to_solver_ref(s)->updt_params(to_param_ref(p)); } - to_solver(s)->m_params = to_param_ref(p); + to_solver(s)->m_params.append(to_param_ref(p)); Z3_CATCH; } From 3ecffaa1e5c7706c196c2cd11db4f8c865c6846d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 21 Oct 2014 11:12:50 -0700 Subject: [PATCH 485/509] remove unused and always failing get_param_value function Signed-off-by: Nikolaj Bjorner --- src/api/api_config_params.cpp | 6 ------ src/api/dotnet/Context.cs | 17 ----------------- src/api/z3_api.h | 9 --------- src/cmd_context/context_params.cpp | 14 +++++++------- 4 files changed, 7 insertions(+), 39 deletions(-) diff --git a/src/api/api_config_params.cpp b/src/api/api_config_params.cpp index 48c2df4a9..c46e7363d 100644 --- a/src/api/api_config_params.cpp +++ b/src/api/api_config_params.cpp @@ -106,10 +106,4 @@ extern "C" { Z3_CATCH; } - Z3_bool Z3_API Z3_get_param_value(Z3_context c, Z3_string param_id, Z3_string* param_value) { - LOG_Z3_get_param_value(c, param_id, param_value); - // TODO - return Z3_FALSE; - } - }; diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 2b88cbab7..1c03d76b6 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -3527,28 +3527,11 @@ namespace Microsoft.Z3 /// Only a few configuration parameters are mutable once the context is created. /// An exception is thrown when trying to modify an immutable parameter. /// - /// public void UpdateParamValue(string id, string value) { Native.Z3_update_param_value(nCtx, id, value); } - /// - /// Get a configuration parameter. - /// - /// - /// Returns null if the parameter value does not exist. - /// - /// - public string GetParamValue(string id) - { - IntPtr res = IntPtr.Zero; - if (Native.Z3_get_param_value(nCtx, id, out res) == 0) - return null; - else - return Marshal.PtrToStringAnsi(res); - } - #endregion #region Internal diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 04e2d38af..2e3ddf4b4 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1462,15 +1462,6 @@ extern "C" { */ void Z3_API Z3_update_param_value(__in Z3_context c, __in Z3_string param_id, __in Z3_string param_value); - /** - \brief Return the value of a context parameter. - - \sa Z3_global_param_get - - def_API('Z3_get_param_value', BOOL, (_in(CONTEXT), _in(STRING), _out(STRING))) - */ - Z3_bool_opt Z3_API Z3_get_param_value(__in Z3_context c, __in Z3_string param_id, __out_opt Z3_string_ptr param_value); - #ifdef CorML4 /** \brief Interrupt the execution of a Z3 procedure. diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index d1184fbc8..254a1d931 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -86,13 +86,13 @@ void context_params::set(char const * param, char const * value) { set_bool(m_smtlib2_compliant, param, value); } else { - param_descrs d; - collect_param_descrs(d); - std::stringstream strm; - strm << "unknown parameter '" << p << "'\n"; - strm << "Legal parameters are:\n"; - d.display(strm, 2, false, false); - throw default_exception(strm.str()); + param_descrs d; + collect_param_descrs(d); + std::stringstream strm; + strm << "unknown parameter '" << p << "'\n"; + strm << "Legal parameters are:\n"; + d.display(strm, 2, false, false); + throw default_exception(strm.str()); } } From f3a04734d9cd685f5921c368f648899246b094fc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 21 Oct 2014 12:48:49 -0700 Subject: [PATCH 486/509] add pretty printing to SMT2 from solver, add get_id to Ast objects Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 82ba85472..470280630 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -297,6 +297,11 @@ class AstRef(Z3PPObject): """Return a pointer to the corresponding C Z3_ast object.""" return self.ast + def get_id(self): + """Return unique identifier for object. It can be used for hash-tables and maps.""" + return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) + + def ctx_ref(self): """Return a reference to the C context where this AST node is stored.""" return self.ctx.ref() @@ -447,6 +452,10 @@ class SortRef(AstRef): def as_ast(self): return Z3_sort_to_ast(self.ctx_ref(), self.ast) + def get_id(self): + return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) + + def kind(self): """Return the Z3 internal kind of a sort. This method can be used to test if `self` is one of the Z3 builtin sorts. @@ -585,6 +594,9 @@ class FuncDeclRef(AstRef): def as_ast(self): return Z3_func_decl_to_ast(self.ctx_ref(), self.ast) + def get_id(self): + return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) + def as_func_decl(self): return self.ast @@ -730,6 +742,9 @@ class ExprRef(AstRef): def as_ast(self): return self.ast + def get_id(self): + return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) + def sort(self): """Return the sort of expression `self`. @@ -1524,6 +1539,9 @@ class PatternRef(ExprRef): def as_ast(self): return Z3_pattern_to_ast(self.ctx_ref(), self.ast) + def get_id(self): + return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) + def is_pattern(a): """Return `True` if `a` is a Z3 pattern (hint for quantifier instantiation. @@ -1586,6 +1604,9 @@ class QuantifierRef(BoolRef): def as_ast(self): return self.ast + def get_id(self): + return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) + def sort(self): """Return the Boolean sort.""" return BoolSort(self.ctx) @@ -6011,6 +6032,24 @@ class Solver(Z3PPObject): """ return Z3_solver_to_string(self.ctx.ref(), self.solver) + def to_smt2(self): + """return SMTLIB2 formatted benchmark for solver's assertions""" + es = self.assertions() + sz = len(es) + sz1 = sz + if sz1 > 0: + sz1 -= 1 + v = (Ast * sz1)() + for i in range(sz1): + v[i] = es[i].as_ast() + if sz > 0: + e = es[sz1].as_ast() + else: + e = BoolVal(True, self.ctx).as_ast() + return Z3_benchmark_to_smtlib_string(self.ctx.ref(), "benchmark generated from python API", "", "unknown", "", sz1, v, e) + + + def SolverFor(logic, ctx=None): """Create a solver customized for the given logic. From d77d6c6648a1aaf8cafefd1458500c71e2c271ec Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 21 Oct 2014 13:24:31 -0700 Subject: [PATCH 487/509] update parameter checking for doubles, and fix error reporting Signed-off-by: Nikolaj Bjorner --- src/util/gparams.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 6397ad377..83157a0b0 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -233,22 +233,22 @@ public: void validate_type(symbol const& name, char const* value, param_descrs const& d) { param_kind k = d.get_kind(name); std::stringstream strm; - + char const* _value = value; switch (k) { case CPK_UINT: for (; *value; ++value) { if (!('0' <= *value && *value <= '9')) { strm << "Expected values for parameter " << name - << " is an unsigned integer. It was given argument '" << value << "'"; + << " is an unsigned integer. It was given argument '" << _value << "'"; throw default_exception(strm.str()); } } break; case CPK_DOUBLE: for (; *value; ++value) { - if (!('0' <= *value && *value <= '9') && *value != '.' && *value != '-') { + if (!('0' <= *value && *value <= '9') && *value != '.' && *value != '-' && *value != '/') { strm << "Expected values for parameter " << name - << " is a double. It was given argument '" << value << "'"; + << " is a double. It was given argument '" << _value << "'"; throw default_exception(strm.str()); } } From 1059d226e44d9ad65285e5e1a9aba58856a2ed57 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 21 Oct 2014 13:25:19 -0700 Subject: [PATCH 488/509] add default statement instead of incomplete cases Signed-off-by: Nikolaj Bjorner --- src/util/gparams.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 83157a0b0..adffe6bde 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -261,8 +261,7 @@ public: throw default_exception(strm.str()); } break; - case CPK_SYMBOL: - case CPK_STRING: + default: break; } } From ae6121525a9c6dd8ba079001bd2fcfad4015d81b Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 22 Oct 2014 13:57:07 +0100 Subject: [PATCH 489/509] Z3Py: improve readability of Z3 exceptions Signed-off-by: Nuno Lopes --- src/api/python/z3types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3types.py b/src/api/python/z3types.py index 593312d68..5d59368ff 100644 --- a/src/api/python/z3types.py +++ b/src/api/python/z3types.py @@ -4,7 +4,7 @@ class Z3Exception(Exception): def __init__(self, value): self.value = value def __str__(self): - return repr(self.value) + return str(self.value) class ContextObj(ctypes.c_void_p): def __init__(self, context): self._as_parameter_ = context From 65ce445b7e209728c725997e140191ab16454bf2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 22 Oct 2014 08:24:49 -0700 Subject: [PATCH 490/509] update Java API Signed-off-by: Nikolaj Bjorner --- src/api/java/Context.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 6b6c63ac3..cc5c07c5e 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2911,20 +2911,6 @@ public class Context extends IDisposable Native.updateParamValue(nCtx(), id, value); } - /** - * Get a configuration parameter. Returns null if the parameter - * value does not exist. - **/ - public String getParamValue(String id) throws Z3Exception - { - Native.StringPtr res = new Native.StringPtr(); - boolean r = Native.getParamValue(nCtx(), id, res); - if (!r) - return null; - else - return res.value; - } - long m_ctx = 0; long nCtx() From d91a114b8086d3f3d68d354b80bc62a9e7d5b3d6 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 22 Oct 2014 16:29:13 +0100 Subject: [PATCH 491/509] Java API: removed Z3_get_param_value as in other APIs. Signed-off-by: Christoph M. Wintersteiger --- src/api/java/Context.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 6b6c63ac3..41a98e3d9 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2904,27 +2904,13 @@ public class Context extends IDisposable * configuration parameters can be obtained using the Z3 executable: * z3.exe -ini? Only a few configuration parameters are mutable * once the context is created. An exception is thrown when trying to modify - * an immutable parameter. + * an immutable parameter. **/ public void updateParamValue(String id, String value) throws Z3Exception { Native.updateParamValue(nCtx(), id, value); } - /** - * Get a configuration parameter. Returns null if the parameter - * value does not exist. - **/ - public String getParamValue(String id) throws Z3Exception - { - Native.StringPtr res = new Native.StringPtr(); - boolean r = Native.getParamValue(nCtx(), id, res); - if (!r) - return null; - else - return res.value; - } - long m_ctx = 0; long nCtx() From 4304012971ef5293a09d73b010bd3263d5b9d9a5 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 22 Oct 2014 16:55:08 +0100 Subject: [PATCH 492/509] Java API: copyright notices Signed-off-by: Christoph M. Wintersteiger --- src/api/java/AST.java | 19 +++++++++++++++---- src/api/java/ASTDecRefQueue.java | 18 +++++++++++++++--- src/api/java/ASTMap.java | 19 +++++++++++++++---- src/api/java/ASTVector.java | 19 +++++++++++++++---- src/api/java/AlgebraicNum.java | 19 +++++++++++++++---- src/api/java/ApplyResult.java | 19 +++++++++++++++---- src/api/java/ApplyResultDecRefQueue.java | 18 +++++++++++++++--- src/api/java/ArithExpr.java | 19 +++++++++++++++---- src/api/java/ArithSort.java | 19 +++++++++++++++---- src/api/java/ArrayExpr.java | 19 +++++++++++++++---- src/api/java/ArraySort.java | 19 +++++++++++++++---- src/api/java/AstMapDecRefQueue.java | 18 +++++++++++++++--- src/api/java/AstVectorDecRefQueue.java | 18 +++++++++++++++--- src/api/java/BitVecExpr.java | 19 +++++++++++++++---- src/api/java/BitVecNum.java | 19 +++++++++++++++---- src/api/java/BitVecSort.java | 20 ++++++++++++++++---- src/api/java/BoolExpr.java | 19 +++++++++++++++---- src/api/java/BoolSort.java | 19 +++++++++++++++---- src/api/java/Constructor.java | 19 +++++++++++++++---- src/api/java/ConstructorList.java | 19 +++++++++++++++---- src/api/java/Context.java | 19 +++++++++++++++---- src/api/java/DatatypeExpr.java | 19 +++++++++++++++---- src/api/java/DatatypeSort.java | 19 +++++++++++++++---- src/api/java/EnumSort.java | 19 +++++++++++++++---- src/api/java/Expr.java | 19 +++++++++++++++---- src/api/java/FiniteDomainSort.java | 19 +++++++++++++++---- src/api/java/Fixedpoint.java | 19 +++++++++++++++---- src/api/java/FixedpointDecRefQueue.java | 18 +++++++++++++++--- src/api/java/FuncDecl.java | 19 +++++++++++++++---- src/api/java/FuncInterp.java | 19 +++++++++++++++---- src/api/java/FuncInterpDecRefQueue.java | 18 +++++++++++++++--- src/api/java/FuncInterpEntryDecRefQueue.java | 18 +++++++++++++++--- src/api/java/Global.java | 18 +++++++++++++++--- src/api/java/Goal.java | 19 +++++++++++++++---- src/api/java/GoalDecRefQueue.java | 18 +++++++++++++++--- src/api/java/IDecRefQueue.java | 19 +++++++++++++++---- src/api/java/IntExpr.java | 19 +++++++++++++++---- src/api/java/IntNum.java | 19 +++++++++++++++---- src/api/java/IntSort.java | 19 +++++++++++++++---- src/api/java/IntSymbol.java | 19 +++++++++++++++---- src/api/java/InterpolationContext.java | 18 ++++++++++++++++-- src/api/java/ListSort.java | 19 +++++++++++++++---- src/api/java/Log.java | 19 +++++++++++++++---- src/api/java/Model.java | 19 +++++++++++++++---- src/api/java/ModelDecRefQueue.java | 18 +++++++++++++++--- src/api/java/ParamDescrs.java | 19 +++++++++++++++---- src/api/java/ParamDescrsDecRefQueue.java | 18 +++++++++++++++--- src/api/java/Params.java | 20 ++++++++++++++++---- src/api/java/ParamsDecRefQueue.java | 18 +++++++++++++++--- src/api/java/Pattern.java | 19 +++++++++++++++---- src/api/java/Probe.java | 19 +++++++++++++++---- src/api/java/ProbeDecRefQueue.java | 18 +++++++++++++++--- src/api/java/Quantifier.java | 19 +++++++++++++++---- src/api/java/RatNum.java | 19 +++++++++++++++---- src/api/java/RealExpr.java | 19 +++++++++++++++---- src/api/java/RealSort.java | 19 +++++++++++++++---- src/api/java/RelationSort.java | 19 +++++++++++++++---- src/api/java/SetSort.java | 19 +++++++++++++++---- src/api/java/Solver.java | 19 +++++++++++++++---- src/api/java/SolverDecRefQueue.java | 18 +++++++++++++++--- src/api/java/Sort.java | 19 +++++++++++++++---- src/api/java/Statistics.java | 19 +++++++++++++++---- src/api/java/StatisticsDecRefQueue.java | 18 +++++++++++++++--- src/api/java/Status.java | 19 +++++++++++++++---- src/api/java/StringSymbol.java | 19 +++++++++++++++---- src/api/java/Symbol.java | 19 +++++++++++++++---- src/api/java/Tactic.java | 19 +++++++++++++++---- src/api/java/TacticDecRefQueue.java | 18 +++++++++++++++--- src/api/java/TupleSort.java | 19 +++++++++++++++---- src/api/java/UninterpretedSort.java | 19 +++++++++++++++---- src/api/java/Version.java | 19 +++++++++++++++---- src/api/java/Z3Exception.java | 19 +++++++++++++++---- src/api/java/Z3Object.java | 19 +++++++++++++++---- 73 files changed, 1098 insertions(+), 274 deletions(-) diff --git a/src/api/java/AST.java b/src/api/java/AST.java index fa5cd8284..1f5463ec7 100644 --- a/src/api/java/AST.java +++ b/src/api/java/AST.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from AST.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + AST.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ASTDecRefQueue.java b/src/api/java/ASTDecRefQueue.java index e0711363d..6ae84eb41 100644 --- a/src/api/java/ASTDecRefQueue.java +++ b/src/api/java/ASTDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ASTDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ASTMap.java b/src/api/java/ASTMap.java index dbe7fbd02..6a4e6d56f 100644 --- a/src/api/java/ASTMap.java +++ b/src/api/java/ASTMap.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from ASTMap.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ASTMap.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ASTVector.java b/src/api/java/ASTVector.java index 39e32f5d5..07b7dbf56 100644 --- a/src/api/java/ASTVector.java +++ b/src/api/java/ASTVector.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from ASTVector.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ASTVector.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/AlgebraicNum.java b/src/api/java/AlgebraicNum.java index eaeae933d..340f37f80 100644 --- a/src/api/java/AlgebraicNum.java +++ b/src/api/java/AlgebraicNum.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from AlgebraicNum.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + AlgebraicNum.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ApplyResult.java b/src/api/java/ApplyResult.java index e6c6b89fd..c550c05ae 100644 --- a/src/api/java/ApplyResult.java +++ b/src/api/java/ApplyResult.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from ApplyResult.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ApplyResult.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ApplyResultDecRefQueue.java b/src/api/java/ApplyResultDecRefQueue.java index 275f4b8f0..78f74d6cc 100644 --- a/src/api/java/ApplyResultDecRefQueue.java +++ b/src/api/java/ApplyResultDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ApplyResultDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ArithExpr.java b/src/api/java/ArithExpr.java index c429f2322..83ec35d01 100644 --- a/src/api/java/ArithExpr.java +++ b/src/api/java/ArithExpr.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from ArithExpr.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - * **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ArithExpr.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ArithSort.java b/src/api/java/ArithSort.java index 5e1780539..2346d9b74 100644 --- a/src/api/java/ArithSort.java +++ b/src/api/java/ArithSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from ArithSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ArithSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ArrayExpr.java b/src/api/java/ArrayExpr.java index b3bbb8d75..2d5a5a273 100644 --- a/src/api/java/ArrayExpr.java +++ b/src/api/java/ArrayExpr.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from ArrayExpr.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ArrayExpr.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ArraySort.java b/src/api/java/ArraySort.java index 2ab8a9750..a371fa3cb 100644 --- a/src/api/java/ArraySort.java +++ b/src/api/java/ArraySort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from ArraySort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ArraySort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/AstMapDecRefQueue.java b/src/api/java/AstMapDecRefQueue.java index f4c6b2ab5..a4e02d29f 100644 --- a/src/api/java/AstMapDecRefQueue.java +++ b/src/api/java/AstMapDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + AstMapDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/AstVectorDecRefQueue.java b/src/api/java/AstVectorDecRefQueue.java index bdabcdcb1..e0c7988a9 100644 --- a/src/api/java/AstVectorDecRefQueue.java +++ b/src/api/java/AstVectorDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + AstVectorDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/BitVecExpr.java b/src/api/java/BitVecExpr.java index 9602ea3a0..24b1cfdf3 100644 --- a/src/api/java/BitVecExpr.java +++ b/src/api/java/BitVecExpr.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from BitVecExpr.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + BitVecExpr.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/BitVecNum.java b/src/api/java/BitVecNum.java index 2dd0dd75a..69ac5dadc 100644 --- a/src/api/java/BitVecNum.java +++ b/src/api/java/BitVecNum.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from BitVecNum.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + BitVecNum.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/BitVecSort.java b/src/api/java/BitVecSort.java index c2ec4c26e..be406c806 100644 --- a/src/api/java/BitVecSort.java +++ b/src/api/java/BitVecSort.java @@ -1,8 +1,20 @@ /** - * This file was automatically generated from BitVecSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + BitVecSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ + package com.microsoft.z3; /** diff --git a/src/api/java/BoolExpr.java b/src/api/java/BoolExpr.java index 40786f76d..99453496a 100644 --- a/src/api/java/BoolExpr.java +++ b/src/api/java/BoolExpr.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from BoolExpr.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + BoolExpr.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/BoolSort.java b/src/api/java/BoolSort.java index 03711289a..85ca0a7f7 100644 --- a/src/api/java/BoolSort.java +++ b/src/api/java/BoolSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from BoolSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + BoolSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Constructor.java b/src/api/java/Constructor.java index 0c53da73c..4813c2b0a 100644 --- a/src/api/java/Constructor.java +++ b/src/api/java/Constructor.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Constructor.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Constructor.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ConstructorList.java b/src/api/java/ConstructorList.java index a33276ebb..315a2e535 100644 --- a/src/api/java/ConstructorList.java +++ b/src/api/java/ConstructorList.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from ConstructorList.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ConstructorList.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 41a98e3d9..4fbd79be2 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Context.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Context.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/DatatypeExpr.java b/src/api/java/DatatypeExpr.java index 63cb02f80..806ceacab 100644 --- a/src/api/java/DatatypeExpr.java +++ b/src/api/java/DatatypeExpr.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from DatatypeExpr.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + DatatypeExpr.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/DatatypeSort.java b/src/api/java/DatatypeSort.java index f7b2f7d32..9c339d932 100644 --- a/src/api/java/DatatypeSort.java +++ b/src/api/java/DatatypeSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from DatatypeSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + DatatypeSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/EnumSort.java b/src/api/java/EnumSort.java index c0fb6d1d6..9715b9a97 100644 --- a/src/api/java/EnumSort.java +++ b/src/api/java/EnumSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from EnumSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + EnumSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Expr.java b/src/api/java/Expr.java index f7edc6a2b..3773e749d 100644 --- a/src/api/java/Expr.java +++ b/src/api/java/Expr.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Expr.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Expr.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/FiniteDomainSort.java b/src/api/java/FiniteDomainSort.java index a8ba0d8c3..4cb2ab917 100644 --- a/src/api/java/FiniteDomainSort.java +++ b/src/api/java/FiniteDomainSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from FiniteDomainSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + FiniteDomainSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Fixedpoint.java b/src/api/java/Fixedpoint.java index 4710368aa..57e1e105a 100644 --- a/src/api/java/Fixedpoint.java +++ b/src/api/java/Fixedpoint.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Fixedpoint.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Fixedpoint.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/FixedpointDecRefQueue.java b/src/api/java/FixedpointDecRefQueue.java index e52389b85..2ea40dd2c 100644 --- a/src/api/java/FixedpointDecRefQueue.java +++ b/src/api/java/FixedpointDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + FixedpointDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/FuncDecl.java b/src/api/java/FuncDecl.java index ada44bbd7..573ebd102 100644 --- a/src/api/java/FuncDecl.java +++ b/src/api/java/FuncDecl.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from FuncDecl.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + FuncDecl.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/FuncInterp.java b/src/api/java/FuncInterp.java index b7fa118e9..243ade71f 100644 --- a/src/api/java/FuncInterp.java +++ b/src/api/java/FuncInterp.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from FuncInterp.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + FuncInterp.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/FuncInterpDecRefQueue.java b/src/api/java/FuncInterpDecRefQueue.java index e2814a9b8..8bdad4ad5 100644 --- a/src/api/java/FuncInterpDecRefQueue.java +++ b/src/api/java/FuncInterpDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + FuncInterpDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/FuncInterpEntryDecRefQueue.java b/src/api/java/FuncInterpEntryDecRefQueue.java index 61488ec74..494c2695c 100644 --- a/src/api/java/FuncInterpEntryDecRefQueue.java +++ b/src/api/java/FuncInterpEntryDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + FuncInterpEntryDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Global.java b/src/api/java/Global.java index 33a828fb8..b97e48721 100644 --- a/src/api/java/Global.java +++ b/src/api/java/Global.java @@ -1,7 +1,19 @@ /** - * Global.java - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Global.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Goal.java b/src/api/java/Goal.java index c486a57b3..aba8b0149 100644 --- a/src/api/java/Goal.java +++ b/src/api/java/Goal.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Goal.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Goal.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/GoalDecRefQueue.java b/src/api/java/GoalDecRefQueue.java index 0c7b8f5d9..45992170f 100644 --- a/src/api/java/GoalDecRefQueue.java +++ b/src/api/java/GoalDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + GoalDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/IDecRefQueue.java b/src/api/java/IDecRefQueue.java index ddb679910..2190f8e4d 100644 --- a/src/api/java/IDecRefQueue.java +++ b/src/api/java/IDecRefQueue.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from IDecRefQueue.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + IDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/IntExpr.java b/src/api/java/IntExpr.java index 78cc15f90..2e90c3cbf 100644 --- a/src/api/java/IntExpr.java +++ b/src/api/java/IntExpr.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from IntExpr.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + IntExpr.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/IntNum.java b/src/api/java/IntNum.java index ebf237a2e..0fdcf1aa6 100644 --- a/src/api/java/IntNum.java +++ b/src/api/java/IntNum.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from IntNum.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + IntNum.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/IntSort.java b/src/api/java/IntSort.java index 44e48ccd1..bcfc730c4 100644 --- a/src/api/java/IntSort.java +++ b/src/api/java/IntSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from IntSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + IntSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/IntSymbol.java b/src/api/java/IntSymbol.java index 113b507c3..b0f1af685 100644 --- a/src/api/java/IntSymbol.java +++ b/src/api/java/IntSymbol.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from IntSymbol.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + IntSymbol.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/InterpolationContext.java b/src/api/java/InterpolationContext.java index 067871324..3e9996b3e 100644 --- a/src/api/java/InterpolationContext.java +++ b/src/api/java/InterpolationContext.java @@ -1,6 +1,20 @@ /** - * - */ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + InterpolationContext.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ + package com.microsoft.z3; import java.util.Map; diff --git a/src/api/java/ListSort.java b/src/api/java/ListSort.java index af1645187..52cb1a179 100644 --- a/src/api/java/ListSort.java +++ b/src/api/java/ListSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from ListSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ListSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Log.java b/src/api/java/Log.java index 99581cedb..254da1bc6 100644 --- a/src/api/java/Log.java +++ b/src/api/java/Log.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Log.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Log.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Model.java b/src/api/java/Model.java index 32247eb4a..11eed201e 100644 --- a/src/api/java/Model.java +++ b/src/api/java/Model.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Model.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Model.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ModelDecRefQueue.java b/src/api/java/ModelDecRefQueue.java index a0542714e..1200b29be 100644 --- a/src/api/java/ModelDecRefQueue.java +++ b/src/api/java/ModelDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ModelDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ParamDescrs.java b/src/api/java/ParamDescrs.java index 3b3e76e51..4afae9f76 100644 --- a/src/api/java/ParamDescrs.java +++ b/src/api/java/ParamDescrs.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from ParamDescrs.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ParamDescrs.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ParamDescrsDecRefQueue.java b/src/api/java/ParamDescrsDecRefQueue.java index 70806041d..a29565e8e 100644 --- a/src/api/java/ParamDescrsDecRefQueue.java +++ b/src/api/java/ParamDescrsDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ParamDescrsDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Params.java b/src/api/java/Params.java index cf3d8c759..bbe6d66cf 100644 --- a/src/api/java/Params.java +++ b/src/api/java/Params.java @@ -1,8 +1,20 @@ /** - * This file was automatically generated from Params.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Params.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ + package com.microsoft.z3; diff --git a/src/api/java/ParamsDecRefQueue.java b/src/api/java/ParamsDecRefQueue.java index 361fdf133..7d71feb8f 100644 --- a/src/api/java/ParamsDecRefQueue.java +++ b/src/api/java/ParamsDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ParamDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Pattern.java b/src/api/java/Pattern.java index cd9340c9d..797b387d0 100644 --- a/src/api/java/Pattern.java +++ b/src/api/java/Pattern.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Pattern.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Pattern.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Probe.java b/src/api/java/Probe.java index b68b0266f..17ad81b5c 100644 --- a/src/api/java/Probe.java +++ b/src/api/java/Probe.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Probe.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Probe.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/ProbeDecRefQueue.java b/src/api/java/ProbeDecRefQueue.java index 0ae0b0e8e..fda5d37b4 100644 --- a/src/api/java/ProbeDecRefQueue.java +++ b/src/api/java/ProbeDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + ProbeDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Quantifier.java b/src/api/java/Quantifier.java index 78a0fc03f..e9aeefcca 100644 --- a/src/api/java/Quantifier.java +++ b/src/api/java/Quantifier.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Quantifier.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Quantifier.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/RatNum.java b/src/api/java/RatNum.java index e1cb5b346..f937ea593 100644 --- a/src/api/java/RatNum.java +++ b/src/api/java/RatNum.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from RatNum.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + RatNum.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/RealExpr.java b/src/api/java/RealExpr.java index 20d603d9a..6188e2999 100644 --- a/src/api/java/RealExpr.java +++ b/src/api/java/RealExpr.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from RealExpr.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + RealExpr.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/RealSort.java b/src/api/java/RealSort.java index ce7295ead..d76823a0d 100644 --- a/src/api/java/RealSort.java +++ b/src/api/java/RealSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from RealSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + RealSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/RelationSort.java b/src/api/java/RelationSort.java index bdc4d7804..77f6c595b 100644 --- a/src/api/java/RelationSort.java +++ b/src/api/java/RelationSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from RelationSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + RelationSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/SetSort.java b/src/api/java/SetSort.java index a94a34b26..69126933a 100644 --- a/src/api/java/SetSort.java +++ b/src/api/java/SetSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from SetSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + SetSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Solver.java b/src/api/java/Solver.java index 3827de07a..e129189f9 100644 --- a/src/api/java/Solver.java +++ b/src/api/java/Solver.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Solver.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Solver.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/SolverDecRefQueue.java b/src/api/java/SolverDecRefQueue.java index 1c715716a..993c99621 100644 --- a/src/api/java/SolverDecRefQueue.java +++ b/src/api/java/SolverDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + SolverDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Sort.java b/src/api/java/Sort.java index 381b9b0ae..7bcc7ce7e 100644 --- a/src/api/java/Sort.java +++ b/src/api/java/Sort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Sort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Sort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Statistics.java b/src/api/java/Statistics.java index c36020bd6..315feeaa2 100644 --- a/src/api/java/Statistics.java +++ b/src/api/java/Statistics.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Statistics.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Statistics.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/StatisticsDecRefQueue.java b/src/api/java/StatisticsDecRefQueue.java index d16bf3710..db3e32c86 100644 --- a/src/api/java/StatisticsDecRefQueue.java +++ b/src/api/java/StatisticsDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + StatisticsDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Status.java b/src/api/java/Status.java index e37631070..a08f47909 100644 --- a/src/api/java/Status.java +++ b/src/api/java/Status.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Status.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Status.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/StringSymbol.java b/src/api/java/StringSymbol.java index 951051aa0..09273c946 100644 --- a/src/api/java/StringSymbol.java +++ b/src/api/java/StringSymbol.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from StringSymbol.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + StringSymbol.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Symbol.java b/src/api/java/Symbol.java index 177409ec8..856c9b57e 100644 --- a/src/api/java/Symbol.java +++ b/src/api/java/Symbol.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Symbol.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Symbol.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Tactic.java b/src/api/java/Tactic.java index e62096715..6573a1407 100644 --- a/src/api/java/Tactic.java +++ b/src/api/java/Tactic.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Tactic.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Tactic.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/TacticDecRefQueue.java b/src/api/java/TacticDecRefQueue.java index 50fba156e..5557d03b8 100644 --- a/src/api/java/TacticDecRefQueue.java +++ b/src/api/java/TacticDecRefQueue.java @@ -1,7 +1,19 @@ /** - * Copyright (c) 2012 Microsoft Corporation - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + TacticDecRefQueue.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/TupleSort.java b/src/api/java/TupleSort.java index 554581d0f..523f8d676 100644 --- a/src/api/java/TupleSort.java +++ b/src/api/java/TupleSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from TupleSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + TupleSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/UninterpretedSort.java b/src/api/java/UninterpretedSort.java index 51df17543..07e4707f5 100644 --- a/src/api/java/UninterpretedSort.java +++ b/src/api/java/UninterpretedSort.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from UninterpretedSort.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + UninterpretedSort.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Version.java b/src/api/java/Version.java index b96fb50f9..3f019390b 100644 --- a/src/api/java/Version.java +++ b/src/api/java/Version.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Version.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Version.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Z3Exception.java b/src/api/java/Z3Exception.java index 24dc586d4..2522db627 100644 --- a/src/api/java/Z3Exception.java +++ b/src/api/java/Z3Exception.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Z3Exception.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Z3Exception.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; diff --git a/src/api/java/Z3Object.java b/src/api/java/Z3Object.java index 6c2c11e25..03cfcd625 100644 --- a/src/api/java/Z3Object.java +++ b/src/api/java/Z3Object.java @@ -1,8 +1,19 @@ /** - * This file was automatically generated from Z3Object.cs - * w/ further modifications by: - * @author Christoph M. Wintersteiger (cwinter) - **/ +Copyright (c) 2012-2014 Microsoft Corporation + +Module Name: + + Z3Object.java + +Abstract: + +Author: + + @author Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +**/ package com.microsoft.z3; From 6e18f44d991344fc444b21c13e4c2233334ab6d8 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 22 Oct 2014 10:42:23 -0700 Subject: [PATCH 493/509] fixed error check in read_interpolation_problem --- src/api/api_interp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index dbf68da38..63ecc6c24 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -617,7 +617,7 @@ extern "C" { } for (unsigned j = 0; j < num - 1; j++) - if (read_parents[j] == SHRT_MIN){ + if (read_parents[j] == SHRT_MAX){ read_error << "formula " << j + 1 << ": unreferenced"; goto fail; } @@ -717,4 +717,4 @@ Z3_lbool Z3_API Z3_interpolate(__in Z3_context ctx, __in unsigned incremental, __in unsigned num_theory, __in_ecount(num_theory) Z3_ast *theory); -#endif \ No newline at end of file +#endif From 5454e389355f3a98e82ccd03815cf058458190fd Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 22 Oct 2014 10:43:04 -0700 Subject: [PATCH 494/509] replaced check_interpolants option with interp.check --- src/cmd_context/basic_cmds.cpp | 6 ------ src/cmd_context/cmd_context.cpp | 8 -------- src/cmd_context/cmd_context.h | 2 -- src/cmd_context/context_params.cpp | 5 ----- src/cmd_context/context_params.h | 1 - src/cmd_context/interpolant_cmds.cpp | 2 +- src/interp/iz3params.pyg | 1 + 7 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 836ade9ce..a80c5bc6c 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -241,7 +241,6 @@ protected: symbol m_produce_models; symbol m_produce_assignments; symbol m_produce_interpolants; - symbol m_check_interpolants; symbol m_regular_output_channel; symbol m_diagnostic_output_channel; symbol m_random_seed; @@ -256,7 +255,6 @@ protected: s == m_print_success || s == m_print_warning || s == m_expand_definitions || s == m_interactive_mode || s == m_produce_proofs || s == m_produce_unsat_cores || s == m_produce_models || s == m_produce_assignments || s == m_produce_interpolants || - s == m_check_interpolants || s == m_regular_output_channel || s == m_diagnostic_output_channel || s == m_random_seed || s == m_verbosity || s == m_global_decls; } @@ -275,7 +273,6 @@ public: m_produce_models(":produce-models"), m_produce_assignments(":produce-assignments"), m_produce_interpolants(":produce-interpolants"), - m_check_interpolants(":check-interpolants"), m_regular_output_channel(":regular-output-channel"), m_diagnostic_output_channel(":diagnostic-output-channel"), m_random_seed(":random-seed"), @@ -347,9 +344,6 @@ class set_option_cmd : public set_get_option_cmd { check_not_initialized(ctx, m_produce_interpolants); ctx.set_produce_interpolants(to_bool(value)); } - else if (m_option == m_check_interpolants) { - ctx.set_check_interpolants(to_bool(value)); - } else if (m_option == m_produce_unsat_cores) { check_not_initialized(ctx, m_produce_unsat_cores); ctx.set_produce_unsat_cores(to_bool(value)); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index ce2838aba..731608524 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -397,10 +397,6 @@ void cmd_context::set_produce_interpolants(bool f) { // set_solver_factory(mk_smt_solver_factory()); } -void cmd_context::set_check_interpolants(bool f) { - m_params.m_check_interpolants = f; -} - bool cmd_context::produce_models() const { return m_params.m_model; } @@ -414,10 +410,6 @@ bool cmd_context::produce_interpolants() const { return m_params.m_proof; } -bool cmd_context::check_interpolants() const { - return m_params.m_check_interpolants; -} - bool cmd_context::produce_unsat_cores() const { return m_params.m_unsat_core; } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index e34975183..8ad07e8cc 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -279,7 +279,6 @@ public: bool produce_models() const; bool produce_proofs() const; bool produce_interpolants() const; - bool check_interpolants() const; bool produce_unsat_cores() const; bool well_sorted_check_enabled() const; bool validate_model_enabled() const; @@ -287,7 +286,6 @@ public: void set_produce_unsat_cores(bool flag); void set_produce_proofs(bool flag); void set_produce_interpolants(bool flag); - void set_check_interpolants(bool flag); bool produce_assignments() const { return m_produce_assignments; } void set_produce_assignments(bool flag) { m_produce_assignments = flag; } void set_status(status st) { m_status = st; } diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index 254a1d931..f87c8d264 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -61,9 +61,6 @@ void context_params::set(char const * param, char const * value) { else if (p == "proof") { set_bool(m_proof, param, value); } - else if (p == "check_interpolants") { - set_bool(m_check_interpolants, param, value); - } else if (p == "model") { set_bool(m_model, param, value); } @@ -105,7 +102,6 @@ void context_params::updt_params(params_ref const & p) { m_well_sorted_check = p.get_bool("type_check", p.get_bool("well_sorted_check", true)); m_auto_config = p.get_bool("auto_config", true); m_proof = p.get_bool("proof", false); - m_check_interpolants = p.get_bool("check_interpolants", false); m_model = p.get_bool("model", true); m_model_validate = p.get_bool("model_validate", false); m_trace = p.get_bool("trace", false); @@ -120,7 +116,6 @@ void context_params::collect_param_descrs(param_descrs & d) { d.insert("well_sorted_check", CPK_BOOL, "type checker", "true"); d.insert("type_check", CPK_BOOL, "type checker (alias for well_sorted_check)", "true"); d.insert("auto_config", CPK_BOOL, "use heuristics to automatically select solver and configure it", "true"); - d.insert("check_interpolants", CPK_BOOL, "check correctness of interpolants", "false"); d.insert("model_validate", CPK_BOOL, "validate models produced by solvers", "false"); d.insert("trace", CPK_BOOL, "trace generation for VCC", "false"); d.insert("trace_file_name", CPK_STRING, "trace out file name (see option 'trace')", "z3.log"); diff --git a/src/cmd_context/context_params.h b/src/cmd_context/context_params.h index e26fd3fe5..506e559db 100644 --- a/src/cmd_context/context_params.h +++ b/src/cmd_context/context_params.h @@ -30,7 +30,6 @@ public: bool m_auto_config; bool m_proof; bool m_interpolants; - bool m_check_interpolants; bool m_debug_ref_count; bool m_trace; std::string m_trace_file_name; diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index cb83b52f6..42646a99d 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -65,7 +65,7 @@ static void show_interpolant_and_maybe_check(cmd_context & ctx, s.cleanup(); // verify, for the paranoid... - if(check || ctx.check_interpolants()){ + if(check || interp_params(m_params).check()){ std::ostringstream err; ast_manager &_m = ctx.m(); diff --git a/src/interp/iz3params.pyg b/src/interp/iz3params.pyg index 5b574c859..3116a18db 100644 --- a/src/interp/iz3params.pyg +++ b/src/interp/iz3params.pyg @@ -2,4 +2,5 @@ def_module_params('interp', description='interpolation parameters', export=True, params=(('profile', BOOL, False, '(INTERP) profile interpolation'), + ('check', BOOL, False, '(INTERP) check interpolants'), )) From 31a017e99e1569b3913bd260816a3b9a45d44b3f Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 22 Oct 2014 19:47:50 +0100 Subject: [PATCH 495/509] FPA: standard function names consistency, improved error messages, bugfixes. Signed-off-by: Christoph M. Wintersteiger --- src/ast/float_decl_plugin.cpp | 308 +++++++++++++++------------- src/ast/float_decl_plugin.h | 45 ++-- src/ast/fpa/fpa2bv_converter.cpp | 88 ++++++-- src/ast/fpa/fpa2bv_converter.h | 9 +- src/ast/fpa/fpa2bv_rewriter.h | 18 +- src/ast/rewriter/float_rewriter.cpp | 41 ++-- src/ast/rewriter/float_rewriter.h | 9 +- 7 files changed, 303 insertions(+), 215 deletions(-) diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index 5466df069..f51101d79 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -40,11 +40,10 @@ void float_decl_plugin::set_manager(ast_manager * m, family_id id) { SASSERT(m_int_sort != 0); // arith_decl_plugin must be installed before float_decl_plugin. m_manager->inc_ref(m_int_sort); - if (m_manager->has_plugin(symbol("bv"))) { - // bv plugin is optional, so m_bv_plugin may be 0 + // BV is not optional anymore. + SASSERT(m_manager->has_plugin(symbol("bv"))); m_bv_fid = m_manager->mk_family_id("bv"); m_bv_plugin = static_cast(m_manager->get_plugin(m_bv_fid)); - } } float_decl_plugin::~float_decl_plugin() { @@ -103,6 +102,18 @@ bool float_decl_plugin::is_value(expr * n, mpf & val) { m_fm.mk_nan(ebits, sbits, val); return true; } + else if (is_app_of(n, m_family_id, OP_FLOAT_PLUS_ZERO)) { + unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); + unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); + m_fm.mk_pzero(ebits, sbits, val); + return true; + } + else if (is_app_of(n, m_family_id, OP_FLOAT_MINUS_ZERO)) { + unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); + unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); + m_fm.mk_nzero(ebits, sbits, val); + return true; + } return false; } @@ -156,7 +167,7 @@ sort * float_decl_plugin::mk_float_sort(unsigned ebits, unsigned sbits) { parameter ps[2] = { p1, p2 }; sort_size sz; sz = sort_size::mk_very_big(); // TODO: refine - return m_manager->mk_sort(symbol("FP"), sort_info(m_family_id, FLOAT_SORT, sz, 2, ps)); + return m_manager->mk_sort(symbol("FloatingPoint"), sort_info(m_family_id, FLOAT_SORT, sz, 2, ps)); } sort * float_decl_plugin::mk_rm_sort() { @@ -176,6 +187,14 @@ sort * float_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete return mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); case ROUNDING_MODE_SORT: return mk_rm_sort(); + case FLOAT16_SORT: + return mk_float_sort(5, 11); + case FLOAT32_SORT: + return mk_float_sort(8, 24); + case FLOAT64_SORT: + return mk_float_sort(11, 53); + case FLOAT128_SORT: + return mk_float_sort(15, 133); default: m_manager->raise_exception("unknown floating point theory sort"); return 0; @@ -229,17 +248,18 @@ func_decl * float_decl_plugin::mk_float_const_decl(decl_kind k, unsigned num_par unsigned ebits = s->get_parameter(0).get_int(); unsigned sbits = s->get_parameter(1).get_int(); scoped_mpf val(m_fm); - if (k == OP_FLOAT_NAN) { - m_fm.mk_nan(ebits, sbits, val); + + switch (k) + { + case OP_FLOAT_NAN: m_fm.mk_nan(ebits, sbits, val); SASSERT(m_fm.is_nan(val)); + break; + case OP_FLOAT_MINUS_INF: m_fm.mk_ninf(ebits, sbits, val); break; + case OP_FLOAT_PLUS_INF: m_fm.mk_pinf(ebits, sbits, val); break; + case OP_FLOAT_MINUS_ZERO: m_fm.mk_nzero(ebits, sbits, val); break; + case OP_FLOAT_PLUS_ZERO: m_fm.mk_pzero(ebits, sbits, val); break; } - else if (k == OP_FLOAT_MINUS_INF) { - m_fm.mk_ninf(ebits, sbits, val); - } - else { - SASSERT(k == OP_FLOAT_PLUS_INF); - m_fm.mk_pinf(ebits, sbits, val); - } + return mk_value_decl(val); } @@ -248,14 +268,14 @@ func_decl * float_decl_plugin::mk_bin_rel_decl(decl_kind k, unsigned num_paramet if (arity != 2) m_manager->raise_exception("invalid number of arguments to floating point relation"); if (domain[0] != domain[1] || !is_float_sort(domain[0])) - m_manager->raise_exception("sort mismatch"); + m_manager->raise_exception("sort mismatch, expected equal FloatingPoint sorts as arguments"); symbol name; switch (k) { - case OP_FLOAT_EQ: name = "=="; break; - case OP_FLOAT_LT: name = "<"; break; - case OP_FLOAT_GT: name = ">"; break; - case OP_FLOAT_LE: name = "<="; break; - case OP_FLOAT_GE: name = ">="; break; + case OP_FLOAT_EQ: name = "fp.eq"; break; + case OP_FLOAT_LT: name = "fp.lt"; break; + case OP_FLOAT_GT: name = "fp.gt"; break; + case OP_FLOAT_LE: name = "fp.lte"; break; + case OP_FLOAT_GE: name = "fp.gte"; break; default: UNREACHABLE(); break; @@ -270,17 +290,18 @@ func_decl * float_decl_plugin::mk_unary_rel_decl(decl_kind k, unsigned num_param if (arity != 1) m_manager->raise_exception("invalid number of arguments to floating point relation"); if (!is_float_sort(domain[0])) - m_manager->raise_exception("sort mismatch"); + m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); symbol name; switch (k) { - case OP_FLOAT_IS_ZERO: name = "isZero"; break; - case OP_FLOAT_IS_NZERO: name = "isNZero"; break; - case OP_FLOAT_IS_PZERO: name = "isPZero"; break; - case OP_FLOAT_IS_SIGN_MINUS: name = "isSignMinus"; break; - case OP_FLOAT_IS_NAN: name = "isNaN"; break; - case OP_FLOAT_IS_INF: name = "isInfinite"; break; - case OP_FLOAT_IS_NORMAL: name = "isNormal"; break; - case OP_FLOAT_IS_SUBNORMAL: name = "isSubnormal"; break; + case OP_FLOAT_IS_ZERO: name = "fp.isZero"; break; + case OP_FLOAT_IS_NZERO: name = "fp.isNZero"; break; + case OP_FLOAT_IS_PZERO: name = "fp.isPZero"; break; + case OP_FLOAT_IS_NEGATIVE: name = "fp.isNegative"; break; + case OP_FLOAT_IS_POSITIVE: name = "fp.isPositive"; break; + case OP_FLOAT_IS_NAN: name = "fp.isNaN"; break; + case OP_FLOAT_IS_INF: name = "fp.isInfinite"; break; + case OP_FLOAT_IS_NORMAL: name = "fp.isNormal"; break; + case OP_FLOAT_IS_SUBNORMAL: name = "fp.isSubnormal"; break; default: UNREACHABLE(); break; @@ -293,11 +314,11 @@ func_decl * float_decl_plugin::mk_unary_decl(decl_kind k, unsigned num_parameter if (arity != 1) m_manager->raise_exception("invalid number of arguments to floating point operator"); if (!is_float_sort(domain[0])) - m_manager->raise_exception("sort mismatch"); + m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); symbol name; switch (k) { - case OP_FLOAT_ABS: name = "abs"; break; - case OP_FLOAT_UMINUS: name = "-"; break; + case OP_FLOAT_ABS: name = "fp.abs"; break; + case OP_FLOAT_NEG: name = "fp.neg"; break; default: UNREACHABLE(); break; @@ -310,12 +331,12 @@ func_decl * float_decl_plugin::mk_binary_decl(decl_kind k, unsigned num_paramete if (arity != 2) m_manager->raise_exception("invalid number of arguments to floating point operator"); if (domain[0] != domain[1] || !is_float_sort(domain[0])) - m_manager->raise_exception("sort mismatch"); + m_manager->raise_exception("sort mismatch, expected arguments of equal FloatingPoint sorts"); symbol name; switch (k) { - case OP_FLOAT_REM: name = "remainder"; break; - case OP_FLOAT_MIN: name = "fp.min"; break; - case OP_FLOAT_MAX: name = "fp.max"; break; + case OP_FLOAT_REM: name = "fp.rem"; break; + case OP_FLOAT_MIN: name = "fp.min"; break; + case OP_FLOAT_MAX: name = "fp.max"; break; default: UNREACHABLE(); break; @@ -327,14 +348,16 @@ func_decl * float_decl_plugin::mk_rm_binary_decl(decl_kind k, unsigned num_param unsigned arity, sort * const * domain, sort * range) { if (arity != 3) m_manager->raise_exception("invalid number of arguments to floating point operator"); - if (!is_rm_sort(domain[0]) || domain[1] != domain[2] || !is_float_sort(domain[1])) - m_manager->raise_exception("sort mismatch"); + if (!is_rm_sort(domain[0])) + m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); + if (domain[1] != domain[2] || !is_float_sort(domain[1])) + m_manager->raise_exception("sort mismatch, expected arguments 1 and 2 of equal FloatingPoint sorts"); symbol name; switch (k) { - case OP_FLOAT_ADD: name = "+"; break; - case OP_FLOAT_SUB: name = "-"; break; - case OP_FLOAT_MUL: name = "*"; break; - case OP_FLOAT_DIV: name = "/"; break; + case OP_FLOAT_ADD: name = "fp.add"; break; + case OP_FLOAT_SUB: name = "fp.sub"; break; + case OP_FLOAT_MUL: name = "fp.mul"; break; + case OP_FLOAT_DIV: name = "fp.div"; break; default: UNREACHABLE(); break; @@ -346,12 +369,14 @@ func_decl * float_decl_plugin::mk_rm_unary_decl(decl_kind k, unsigned num_parame unsigned arity, sort * const * domain, sort * range) { if (arity != 2) m_manager->raise_exception("invalid number of arguments to floating point operator"); - if (!is_rm_sort(domain[0]) || !is_float_sort(domain[1])) - m_manager->raise_exception("sort mismatch"); + if (!is_rm_sort(domain[0])) + m_manager->raise_exception("sort mismatch, expected RoundingMode as first argument"); + if (!is_float_sort(domain[1])) + m_manager->raise_exception("sort mismatch, expected FloatingPoint as second argument"); symbol name; switch (k) { - case OP_FLOAT_SQRT: name = "squareRoot"; break; - case OP_FLOAT_ROUND_TO_INTEGRAL: name = "roundToIntegral"; break; + case OP_FLOAT_SQRT: name = "fp.sqrt"; break; + case OP_FLOAT_ROUND_TO_INTEGRAL: name = "fp.roundToIntegral"; break; default: UNREACHABLE(); break; @@ -359,13 +384,15 @@ func_decl * float_decl_plugin::mk_rm_unary_decl(decl_kind k, unsigned num_parame return m_manager->mk_func_decl(name, arity, domain, domain[1], func_decl_info(m_family_id, k)); } -func_decl * float_decl_plugin::mk_fused_ma(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * float_decl_plugin::mk_fma(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 4) m_manager->raise_exception("invalid number of arguments to fused_ma operator"); - if (!is_rm_sort(domain[0]) || domain[1] != domain[2] || domain[1] != domain[3] || !is_float_sort(domain[1])) - m_manager->raise_exception("sort mismatch"); - symbol name("fusedMA"); + if (!is_rm_sort(domain[0])) + m_manager->raise_exception("sort mismatch, expected RoundingMode as first argument"); + if (domain[1] != domain[2] || domain[1] != domain[3] || !is_float_sort(domain[1])) + m_manager->raise_exception("sort mismatch, expected arguments 1,2,3 of equal FloatingPoint sort"); + symbol name("fp.fma"); return m_manager->mk_func_decl(name, arity, domain, domain[1], func_decl_info(m_family_id, k)); } @@ -375,12 +402,13 @@ func_decl * float_decl_plugin::mk_to_float(decl_kind k, unsigned num_parameters, is_sort_of(domain[0], m_bv_fid, BV_SORT) && is_sort_of(domain[1], m_bv_fid, BV_SORT) && is_sort_of(domain[2], m_bv_fid, BV_SORT)) { - // When the bv_decl_plugin is installed, then we know how to convert 3 bit-vectors into a float! + // 3 BVs -> 1 FP sort * fp = mk_float_sort(domain[2]->get_parameter(0).get_int(), domain[1]->get_parameter(0).get_int()+1); - symbol name("asFloat"); + symbol name("fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (m_bv_plugin && arity == 1 && is_sort_of(domain[0], m_bv_fid, BV_SORT)) { + // 1 BV -> 1 FP if (num_parameters != 2) m_manager->raise_exception("invalid number of parameters to to_fp"); if (!parameters[0].is_int() || !parameters[1].is_int()) @@ -389,37 +417,67 @@ func_decl * float_decl_plugin::mk_to_float(decl_kind k, unsigned num_parameters, int sbits = parameters[1].get_int(); sort * fp = mk_float_sort(ebits, sbits); - symbol name("asFloat"); + symbol name("to_fp"); + return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); + } + else if (m_bv_plugin && arity == 2 && + is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && + is_sort_of(domain[1], m_bv_fid, BV_SORT)) { + // Rounding + 1 BV -> 1 FP + if (num_parameters != 2) + m_manager->raise_exception("invalid number of parameters to to_fp"); + if (!parameters[0].is_int() || !parameters[1].is_int()) + m_manager->raise_exception("invalid parameter type to to_fp"); + int ebits = parameters[0].get_int(); + int sbits = parameters[1].get_int(); + + sort * fp = mk_float_sort(ebits, sbits); + symbol name("to_fp"); + return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); + } + else if (arity == 2 && + is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && + is_sort_of(domain[1], m_family_id, FLOAT_SORT)) { + // Rounding + 1 FP -> 1 FP + if (num_parameters != 2) + m_manager->raise_exception("invalid number of parameters to to_fp"); + if (!parameters[0].is_int() || !parameters[1].is_int()) + m_manager->raise_exception("invalid parameter type to to_fp"); + int ebits = parameters[0].get_int(); + int sbits = parameters[1].get_int(); + if (!is_rm_sort(domain[0])) + m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); + if (!is_sort_of(domain[1], m_family_id, FLOAT_SORT)) + m_manager->raise_exception("sort mismatch, expected second argument of FloatingPoint sort"); + + sort * fp = mk_float_sort(ebits, sbits); + symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else { - // .. Otherwise we only know how to convert rationals/reals. + // 1 Real -> 1 FP if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) - m_manager->raise_exception("expecting two integer parameters to asFloat"); + m_manager->raise_exception("expecting two integer parameters to to_fp"); if (arity != 2 && arity != 3) - m_manager->raise_exception("invalid number of arguments to asFloat operator"); + m_manager->raise_exception("invalid number of arguments to to_fp operator"); if (arity == 3 && domain[2] != m_int_sort) - m_manager->raise_exception("sort mismatch"); - if (!is_rm_sort(domain[0]) || - !(domain[1] == m_real_sort || is_sort_of(domain[1], m_family_id, FLOAT_SORT))) - m_manager->raise_exception("sort mismatch"); + m_manager->raise_exception("sort mismatch, expected second argument of Int sort"); + if (domain[1] != m_real_sort) + m_manager->raise_exception("sort mismatch, expected second argument of Real sort"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); - symbol name("asFloat"); + symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } } -func_decl * float_decl_plugin::mk_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * float_decl_plugin::mk_float_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { - if (!m_bv_plugin) - m_manager->raise_exception("asIEEEBV unsupported; use a logic with BV support"); if (arity != 1) m_manager->raise_exception("invalid number of arguments to asIEEEBV"); if (!is_float_sort(domain[0])) - m_manager->raise_exception("sort mismatch"); - - // When the bv_decl_plugin is installed, then we know how to convert a float to an IEEE bit-vector. + m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); + unsigned float_sz = domain[0]->get_parameter(0).get_int() + domain[0]->get_parameter(1).get_int(); parameter ps[] = { parameter(float_sz) }; sort * bv_srt = m_bv_plugin->mk_sort(m_bv_fid, 1, ps); @@ -429,41 +487,34 @@ func_decl * float_decl_plugin::mk_to_ieee_bv(decl_kind k, unsigned num_parameter func_decl * float_decl_plugin::mk_from3bv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { - if (!m_bv_plugin) - m_manager->raise_exception("fp unsupported; use a logic with BV support"); if (arity != 3) m_manager->raise_exception("invalid number of arguments to fp"); if (!is_sort_of(domain[0], m_bv_fid, BV_SORT) || !is_sort_of(domain[1], m_bv_fid, BV_SORT) || !is_sort_of(domain[2], m_bv_fid, BV_SORT)) - m_manager->raise_exception("sort mismtach"); + m_manager->raise_exception("sort mismatch"); sort * fp = mk_float_sort(domain[1]->get_parameter(0).get_int(), domain[2]->get_parameter(0).get_int() + 1); symbol name("fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k)); } -func_decl * float_decl_plugin::mk_to_fp_unsigned(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * float_decl_plugin::mk_to_ubv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (!m_bv_plugin) m_manager->raise_exception("to_fp_unsigned unsupported; use a logic with BV support"); if (arity != 2) m_manager->raise_exception("invalid number of arguments to to_fp_unsigned"); if (is_rm_sort(domain[0])) - m_manager->raise_exception("sort mismtach"); + m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (!is_sort_of(domain[1], m_bv_fid, BV_SORT)) - m_manager->raise_exception("sort mismtach"); + m_manager->raise_exception("sort mismatch, expected second argument of BV sort"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); - symbol name("to_fp_unsigned"); + symbol name("fp.t_ubv"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k)); } -func_decl * float_decl_plugin::mk_to_ubv(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range) { - NOT_IMPLEMENTED_YET(); -} - func_decl * float_decl_plugin::mk_to_sbv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { NOT_IMPLEMENTED_YET(); @@ -498,14 +549,15 @@ func_decl * float_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_FLOAT_IS_ZERO: case OP_FLOAT_IS_NZERO: case OP_FLOAT_IS_PZERO: - case OP_FLOAT_IS_SIGN_MINUS: + case OP_FLOAT_IS_NEGATIVE: + case OP_FLOAT_IS_POSITIVE: case OP_FLOAT_IS_NAN: case OP_FLOAT_IS_INF: case OP_FLOAT_IS_NORMAL: case OP_FLOAT_IS_SUBNORMAL: return mk_unary_rel_decl(k, num_parameters, parameters, arity, domain, range); case OP_FLOAT_ABS: - case OP_FLOAT_UMINUS: + case OP_FLOAT_NEG: return mk_unary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FLOAT_REM: case OP_FLOAT_MIN: @@ -517,20 +569,18 @@ func_decl * float_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters return mk_rm_binary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FLOAT_SUB: if (arity == 1) - return mk_unary_decl(OP_FLOAT_UMINUS, num_parameters, parameters, arity, domain, range); + return mk_unary_decl(OP_FLOAT_NEG, num_parameters, parameters, arity, domain, range); else return mk_rm_binary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FLOAT_SQRT: case OP_FLOAT_ROUND_TO_INTEGRAL: return mk_rm_unary_decl(k, num_parameters, parameters, arity, domain, range); - case OP_FLOAT_FUSED_MA: - return mk_fused_ma(k, num_parameters, parameters, arity, domain, range); - case OP_TO_IEEE_BV: - return mk_to_ieee_bv(k, num_parameters, parameters, arity, domain, range); + case OP_FLOAT_FMA: + return mk_fma(k, num_parameters, parameters, arity, domain, range); + case OP_FLOAT_TO_IEEE_BV: + return mk_float_to_ieee_bv(k, num_parameters, parameters, arity, domain, range); case OP_FLOAT_FP: return mk_from3bv(k, num_parameters, parameters, arity, domain, range); - case OP_FLOAT_TO_FP_UNSIGNED: - return mk_to_fp_unsigned(k, num_parameters, parameters, arity, domain, range); case OP_FLOAT_TO_UBV: return mk_to_ubv(k, num_parameters, parameters, arity, domain, range); case OP_FLOAT_TO_SBV: @@ -544,8 +594,11 @@ func_decl * float_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters } void float_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { - op_names.push_back(builtin_name("plusInfinity", OP_FLOAT_PLUS_INF)); - op_names.push_back(builtin_name("minusInfinity", OP_FLOAT_MINUS_INF)); + // These are the operators from the final draft of the SMT FloatingPoint standard + op_names.push_back(builtin_name("+oo", OP_FLOAT_PLUS_INF)); + op_names.push_back(builtin_name("-oo", OP_FLOAT_MINUS_INF)); + op_names.push_back(builtin_name("+zero", OP_FLOAT_PLUS_ZERO)); + op_names.push_back(builtin_name("-zero", OP_FLOAT_MINUS_ZERO)); op_names.push_back(builtin_name("NaN", OP_FLOAT_NAN)); op_names.push_back(builtin_name("roundNearestTiesToEven", OP_RM_NEAREST_TIES_TO_EVEN)); @@ -554,46 +607,6 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("roundTowardNegative", OP_RM_TOWARD_NEGATIVE)); op_names.push_back(builtin_name("roundTowardZero", OP_RM_TOWARD_ZERO)); - op_names.push_back(builtin_name("+", OP_FLOAT_ADD)); - op_names.push_back(builtin_name("-", OP_FLOAT_SUB)); - op_names.push_back(builtin_name("/", OP_FLOAT_DIV)); - op_names.push_back(builtin_name("*", OP_FLOAT_MUL)); - - op_names.push_back(builtin_name("abs", OP_FLOAT_ABS)); - op_names.push_back(builtin_name("remainder", OP_FLOAT_REM)); - op_names.push_back(builtin_name("fusedMA", OP_FLOAT_FUSED_MA)); - op_names.push_back(builtin_name("squareRoot", OP_FLOAT_SQRT)); - op_names.push_back(builtin_name("roundToIntegral", OP_FLOAT_ROUND_TO_INTEGRAL)); - - op_names.push_back(builtin_name("==", OP_FLOAT_EQ)); - - op_names.push_back(builtin_name("<", OP_FLOAT_LT)); - op_names.push_back(builtin_name(">", OP_FLOAT_GT)); - op_names.push_back(builtin_name("<=", OP_FLOAT_LE)); - op_names.push_back(builtin_name(">=", OP_FLOAT_GE)); - - op_names.push_back(builtin_name("isNaN", OP_FLOAT_IS_NAN)); - op_names.push_back(builtin_name("isInfinite", OP_FLOAT_IS_INF)); - op_names.push_back(builtin_name("isZero", OP_FLOAT_IS_ZERO)); - op_names.push_back(builtin_name("isNZero", OP_FLOAT_IS_NZERO)); - op_names.push_back(builtin_name("isPZero", OP_FLOAT_IS_PZERO)); - op_names.push_back(builtin_name("isNormal", OP_FLOAT_IS_NORMAL)); - op_names.push_back(builtin_name("isSubnormal", OP_FLOAT_IS_SUBNORMAL)); - op_names.push_back(builtin_name("isSignMinus", OP_FLOAT_IS_SIGN_MINUS)); - - // Disabled min/max, clashes with user-defined min/max functions - // op_names.push_back(builtin_name("min", OP_FLOAT_MIN)); - // op_names.push_back(builtin_name("max", OP_FLOAT_MAX)); - - op_names.push_back(builtin_name("asFloat", OP_TO_FLOAT)); - - if (m_bv_plugin) - op_names.push_back(builtin_name("asIEEEBV", OP_TO_IEEE_BV)); - - // These are the operators from the final draft of the SMT FloatingPoints standard - op_names.push_back(builtin_name("+oo", OP_FLOAT_PLUS_INF)); - op_names.push_back(builtin_name("-oo", OP_FLOAT_MINUS_INF)); - op_names.push_back(builtin_name("RNE", OP_RM_NEAREST_TIES_TO_EVEN)); op_names.push_back(builtin_name("RNA", OP_RM_NEAREST_TIES_TO_AWAY)); op_names.push_back(builtin_name("RTP", OP_RM_TOWARD_POSITIVE)); @@ -601,44 +614,47 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("RTZ", OP_RM_TOWARD_ZERO)); op_names.push_back(builtin_name("fp.abs", OP_FLOAT_ABS)); - op_names.push_back(builtin_name("fp.neg", OP_FLOAT_UMINUS)); + op_names.push_back(builtin_name("fp.neg", OP_FLOAT_NEG)); op_names.push_back(builtin_name("fp.add", OP_FLOAT_ADD)); op_names.push_back(builtin_name("fp.sub", OP_FLOAT_SUB)); op_names.push_back(builtin_name("fp.mul", OP_FLOAT_MUL)); op_names.push_back(builtin_name("fp.div", OP_FLOAT_DIV)); - op_names.push_back(builtin_name("fp.fma", OP_FLOAT_FUSED_MA)); + op_names.push_back(builtin_name("fp.fma", OP_FLOAT_FMA)); op_names.push_back(builtin_name("fp.sqrt", OP_FLOAT_SQRT)); op_names.push_back(builtin_name("fp.rem", OP_FLOAT_REM)); - op_names.push_back(builtin_name("fp.eq", OP_FLOAT_EQ)); + op_names.push_back(builtin_name("fp.roundToIntegral", OP_FLOAT_ROUND_TO_INTEGRAL)); + op_names.push_back(builtin_name("fp.min", OP_FLOAT_MIN)); + op_names.push_back(builtin_name("fp.max", OP_FLOAT_MAX)); op_names.push_back(builtin_name("fp.leq", OP_FLOAT_LE)); op_names.push_back(builtin_name("fp.lt", OP_FLOAT_LT)); op_names.push_back(builtin_name("fp.geq", OP_FLOAT_GE)); op_names.push_back(builtin_name("fp.gt", OP_FLOAT_GT)); + op_names.push_back(builtin_name("fp.eq", OP_FLOAT_EQ)); + op_names.push_back(builtin_name("fp.isNormal", OP_FLOAT_IS_NORMAL)); op_names.push_back(builtin_name("fp.isSubnormal", OP_FLOAT_IS_SUBNORMAL)); op_names.push_back(builtin_name("fp.isZero", OP_FLOAT_IS_ZERO)); op_names.push_back(builtin_name("fp.isInfinite", OP_FLOAT_IS_INF)); op_names.push_back(builtin_name("fp.isNaN", OP_FLOAT_IS_NAN)); - op_names.push_back(builtin_name("fp.min", OP_FLOAT_MIN)); - op_names.push_back(builtin_name("fp.max", OP_FLOAT_MAX)); - op_names.push_back(builtin_name("to_fp", OP_TO_FLOAT)); + op_names.push_back(builtin_name("fp.isNegative", OP_FLOAT_IS_NEGATIVE)); + op_names.push_back(builtin_name("fp.isPositive", OP_FLOAT_IS_POSITIVE)); - if (m_bv_plugin) { - op_names.push_back(builtin_name("fp", OP_FLOAT_FP)); - op_names.push_back(builtin_name("to_fp_unsigned", OP_FLOAT_TO_FP_UNSIGNED)); - op_names.push_back(builtin_name("fp.to_ubv", OP_FLOAT_TO_UBV)); - op_names.push_back(builtin_name("fp.to_sbv", OP_FLOAT_TO_SBV)); - } + op_names.push_back(builtin_name("fp", OP_FLOAT_FP)); + op_names.push_back(builtin_name("fp.to_ubv", OP_FLOAT_TO_UBV)); + op_names.push_back(builtin_name("fp.to_sbv", OP_FLOAT_TO_SBV)); - // op_names.push_back(builtin_name("fp.toReal", ?)); + op_names.push_back(builtin_name("to_fp", OP_TO_FLOAT)); } void float_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { - sort_names.push_back(builtin_name("FP", FLOAT_SORT)); + sort_names.push_back(builtin_name("FloatingPoint", FLOAT_SORT)); sort_names.push_back(builtin_name("RoundingMode", ROUNDING_MODE_SORT)); - // In the SMT FPA final draft, FP is called FloatingPoint - sort_names.push_back(builtin_name("FloatingPoint", FLOAT_SORT)); + // The final theory supports three common FloatingPoint sorts + sort_names.push_back(builtin_name("Float16", FLOAT16_SORT)); + sort_names.push_back(builtin_name("Float32", FLOAT32_SORT)); + sort_names.push_back(builtin_name("Float64", FLOAT64_SORT)); + sort_names.push_back(builtin_name("Float128", FLOAT128_SORT)); } expr * float_decl_plugin::get_some_value(sort * s) { @@ -662,6 +678,8 @@ bool float_decl_plugin::is_value(app * e) const { case OP_FLOAT_VALUE: case OP_FLOAT_PLUS_INF: case OP_FLOAT_MINUS_INF: + case OP_FLOAT_PLUS_ZERO: + case OP_FLOAT_MINUS_ZERO: case OP_FLOAT_NAN: return true; case OP_TO_FLOAT: diff --git a/src/ast/float_decl_plugin.h b/src/ast/float_decl_plugin.h index 58c37080d..c7ec04932 100644 --- a/src/ast/float_decl_plugin.h +++ b/src/ast/float_decl_plugin.h @@ -27,7 +27,11 @@ Revision History: enum float_sort_kind { FLOAT_SORT, - ROUNDING_MODE_SORT + ROUNDING_MODE_SORT, + FLOAT16_SORT, + FLOAT32_SORT, + FLOAT64_SORT, + FLOAT128_SORT }; enum float_op_kind { @@ -37,22 +41,23 @@ enum float_op_kind { OP_RM_TOWARD_NEGATIVE, OP_RM_TOWARD_ZERO, - OP_FLOAT_VALUE, OP_FLOAT_PLUS_INF, OP_FLOAT_MINUS_INF, OP_FLOAT_NAN, + OP_FLOAT_PLUS_ZERO, + OP_FLOAT_MINUS_ZERO, OP_FLOAT_ADD, OP_FLOAT_SUB, - OP_FLOAT_UMINUS, + OP_FLOAT_NEG, OP_FLOAT_MUL, OP_FLOAT_DIV, OP_FLOAT_REM, OP_FLOAT_ABS, OP_FLOAT_MIN, OP_FLOAT_MAX, - OP_FLOAT_FUSED_MA, // x*y + z + OP_FLOAT_FMA, // x*y + z OP_FLOAT_SQRT, OP_FLOAT_ROUND_TO_INTEGRAL, @@ -68,13 +73,14 @@ enum float_op_kind { OP_FLOAT_IS_SUBNORMAL, OP_FLOAT_IS_PZERO, OP_FLOAT_IS_NZERO, - OP_FLOAT_IS_SIGN_MINUS, + OP_FLOAT_IS_NEGATIVE, + OP_FLOAT_IS_POSITIVE, OP_TO_FLOAT, - OP_TO_IEEE_BV, + OP_FLOAT_TO_IEEE_BV, OP_FLOAT_FP, - OP_FLOAT_TO_FP_UNSIGNED, + OP_FLOAT_TO_FP, OP_FLOAT_TO_UBV, OP_FLOAT_TO_SBV, OP_FLOAT_TO_REAL, @@ -125,16 +131,14 @@ class float_decl_plugin : public decl_plugin { unsigned arity, sort * const * domain, sort * range); func_decl * mk_rm_unary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); - func_decl * mk_fused_ma(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_fma(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_float(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); - func_decl * mk_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_float_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); func_decl * mk_from3bv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); - func_decl * mk_to_fp_unsigned(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_ubv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_sbv(decl_kind k, unsigned num_parameters, parameter const * parameters, @@ -195,6 +199,7 @@ public: family_id get_fid() const { return m_fid; } family_id get_family_id() const { return m_fid; } arith_util & au() { return m_a_util; } + float_decl_plugin & plugin() { return *m_plugin; } sort * mk_float_sort(unsigned ebits, unsigned sbits); sort * mk_rm_sort() { return m().mk_sort(m_fid, ROUNDING_MODE_SORT); } @@ -242,16 +247,16 @@ public: app * mk_mul(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FLOAT_MUL, arg1, arg2, arg3); } app * mk_sub(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FLOAT_SUB, arg1, arg2, arg3); } app * mk_div(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FLOAT_DIV, arg1, arg2, arg3); } - app * mk_uminus(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_UMINUS, arg1); } + app * mk_neg(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_NEG, arg1); } app * mk_rem(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_REM, arg1, arg2); } app * mk_max(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_MAX, arg1, arg2); } app * mk_min(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_MIN, arg1, arg2); } app * mk_abs(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_ABS, arg1); } app * mk_sqrt(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_SQRT, arg1, arg2); } app * mk_round(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_ROUND_TO_INTEGRAL, arg1, arg2); } - app * mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4) { + app * mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg4) { expr * args[4] = { arg1, arg2, arg3, arg4 }; - return m().mk_app(m_fid, OP_FLOAT_FUSED_MA, 4, args); + return m().mk_app(m_fid, OP_FLOAT_FMA, 4, args); } app * mk_float_eq(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_EQ, arg1, arg2); } @@ -267,11 +272,13 @@ public: app * mk_is_subnormal(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_SUBNORMAL, arg1); } app * mk_is_nzero(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_NZERO, arg1); } app * mk_is_pzero(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_PZERO, arg1); } - app * mk_is_sign_minus(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_SIGN_MINUS, arg1); } + app * mk_is_sign_minus(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_NEGATIVE, arg1); } + app * mk_is_positive(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_POSITIVE, arg1); } + app * mk_is_negative(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_NEGATIVE, arg1); } - bool is_uminus(expr * a) { return is_app_of(a, m_fid, OP_FLOAT_UMINUS); } + bool is_neg(expr * a) { return is_app_of(a, m_fid, OP_FLOAT_NEG); } - app * mk_to_ieee_bv(expr * arg1) { return m().mk_app(m_fid, OP_TO_IEEE_BV, arg1); } + app * mk_float_to_ieee_bv(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_TO_IEEE_BV, arg1); } }; #endif diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index d6f362902..5f92b4d94 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -136,12 +136,13 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { unsigned sbits = m_util.get_sbits(srt); expr_ref sgn(m), s(m), e(m); - sort_ref s_sgn(m), s_sig(m), s_exp(m); - s_sgn = m_bv_util.mk_sort(1); - s_sig = m_bv_util.mk_sort(sbits-1); - s_exp = m_bv_util.mk_sort(ebits); #ifdef Z3DEBUG + sort_ref s_sgn(m), s_sig(m), s_exp(m); + s_sgn = m_bv_util.mk_sort(1); + s_sig = m_bv_util.mk_sort(sbits - 1); + s_exp = m_bv_util.mk_sort(ebits); + std::string p("fpa2bv"); std::string name = f->get_name().str(); @@ -149,9 +150,17 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { s = m.mk_fresh_const((p + "_sig_" + name).c_str(), s_sig); e = m.mk_fresh_const((p + "_exp_" + name).c_str(), s_exp); #else - sgn = m.mk_fresh_const(0, s_sgn); - s = m.mk_fresh_const(0, s_sig); - e = m.mk_fresh_const(0, s_exp); + expr_ref bv(m); + unsigned bv_sz = 1 + ebits + (sbits - 1); + bv = m.mk_fresh_const(0, m_bv_util.mk_sort(bv_sz)); + + sgn = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, bv); + e = m_bv_util.mk_extract(bv_sz - 2, sbits - 1, bv); + s = m_bv_util.mk_extract(sbits - 2, 0, bv); + + SASSERT(m_bv_util.get_bv_size(sgn) == 1); + SASSERT(m_bv_util.get_bv_size(s) == sbits-1); + SASSERT(m_bv_util.get_bv_size(e) == ebits); #endif mk_triple(sgn, s, e, result); @@ -595,12 +604,12 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_sub(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); expr_ref t(m); - mk_uminus(f, 1, &args[2], t); + mk_neg(f, 1, &args[2], t); expr * nargs[3] = { args[0], args[1], t }; mk_add(f, 3, nargs, result); } -void fpa2bv_converter::mk_uminus(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { +void fpa2bv_converter::mk_neg(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr * sgn, * s, * e; split(args[0], sgn, s, e); @@ -906,7 +915,7 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, TRACE("fpa2bv_div", tout << "DIV = " << mk_ismt2_pp(result, m) << std::endl; ); } -void fpa2bv_converter::mk_remainder(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { +void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); // Remainder is always exact, so there is no rounding mode. @@ -1114,7 +1123,7 @@ void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, mk_triple(r_sgn, r_sig, r_exp, result); } -void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { +void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 4); // fusedma means (x * y) + z @@ -1835,9 +1844,18 @@ void fpa2bv_converter::mk_is_subnormal(func_decl * f, unsigned num, expr * const mk_is_denormal(args[0], result); } -void fpa2bv_converter::mk_is_sign_minus(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { +void fpa2bv_converter::mk_is_negative(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_neg(args[0], result); + TRACE("fpa2bv_is_negative", tout << "result = " << mk_ismt2_pp(result, m) << std::endl;); +} + +void fpa2bv_converter::mk_is_positive(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 1); + expr_ref t1(m), t2(m); + mk_is_nan(args[0], t1); + mk_is_pos(args[0], t2); + result = m.mk_and(m.mk_not(t1), t2); } void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -2123,7 +2141,7 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr * sgn, * s, * e; - split(args[0], sgn, s, e); + split(args[0], sgn, s, e); result = m_bv_util.mk_concat(m_bv_util.mk_concat(sgn, e), s); } @@ -2132,19 +2150,55 @@ void fpa2bv_converter::mk_fp(func_decl * f, unsigned num, expr * const * args, e mk_triple(args[0], args[2], args[1], result); } -void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - NOT_IMPLEMENTED_YET(); -} - void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(f->get_num_parameters() == 1); + SASSERT(f->get_parameter(0).is_int()); + + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + int width = f->get_parameter(0).get_int(); + + expr * rm = args[0]; + expr * x = args[1]; + + expr * sgn, *s, *e; + split(x, sgn, s, e); + NOT_IMPLEMENTED_YET(); } void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(f->get_num_parameters() == 1); + SASSERT(f->get_parameter(0).is_int()); + + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + int width = f->get_parameter(0).get_int(); + + expr * rm = args[0]; + expr * x = args[1]; + + expr * sgn, *s, *e; + split(x, sgn, s, e); + NOT_IMPLEMENTED_YET(); } void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 1); + + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + int width = f->get_parameter(0).get_int(); + + expr * rm = args[0]; + expr * x = args[1]; + + expr * sgn, *s, *e; + split(x, sgn, s, e); + NOT_IMPLEMENTED_YET(); } diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index dcb508ffd..eb539d8ae 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -94,14 +94,14 @@ public: void mk_add(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_sub(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_uminus(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_neg(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_mul(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_remainder(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_rem(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_abs(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_min(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_fusedma(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_fma(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_sqrt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_round_to_integral(func_decl * f, unsigned num, expr * const * args, expr_ref & result); @@ -114,7 +114,8 @@ public: void mk_is_zero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_nzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_pzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_is_sign_minus(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_is_negative(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_is_positive(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_nan(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_normal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); diff --git a/src/ast/fpa/fpa2bv_rewriter.h b/src/ast/fpa/fpa2bv_rewriter.h index 225aad668..7a245b71a 100644 --- a/src/ast/fpa/fpa2bv_rewriter.h +++ b/src/ast/fpa/fpa2bv_rewriter.h @@ -65,8 +65,6 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { bool max_steps_exceeded(unsigned num_steps) const { cooperate("fpa2bv"); - if (memory::get_allocation_size() > m_max_memory) - throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return num_steps > m_max_steps; } @@ -117,17 +115,19 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { case OP_FLOAT_VALUE: m_conv.mk_value(f, num, args, result); return BR_DONE; case OP_FLOAT_PLUS_INF: m_conv.mk_plus_inf(f, result); return BR_DONE; case OP_FLOAT_MINUS_INF: m_conv.mk_minus_inf(f, result); return BR_DONE; + case OP_FLOAT_PLUS_ZERO: m_conv.mk_pzero(f, result); return BR_DONE; + case OP_FLOAT_MINUS_ZERO: m_conv.mk_nzero(f, result); return BR_DONE; case OP_FLOAT_NAN: m_conv.mk_nan(f, result); return BR_DONE; case OP_FLOAT_ADD: m_conv.mk_add(f, num, args, result); return BR_DONE; case OP_FLOAT_SUB: m_conv.mk_sub(f, num, args, result); return BR_DONE; - case OP_FLOAT_UMINUS: m_conv.mk_uminus(f, num, args, result); return BR_DONE; + case OP_FLOAT_NEG: m_conv.mk_neg(f, num, args, result); return BR_DONE; case OP_FLOAT_MUL: m_conv.mk_mul(f, num, args, result); return BR_DONE; case OP_FLOAT_DIV: m_conv.mk_div(f, num, args, result); return BR_DONE; - case OP_FLOAT_REM: m_conv.mk_remainder(f, num, args, result); return BR_DONE; + case OP_FLOAT_REM: m_conv.mk_rem(f, num, args, result); return BR_DONE; case OP_FLOAT_ABS: m_conv.mk_abs(f, num, args, result); return BR_DONE; case OP_FLOAT_MIN: m_conv.mk_min(f, num, args, result); return BR_DONE; case OP_FLOAT_MAX: m_conv.mk_max(f, num, args, result); return BR_DONE; - case OP_FLOAT_FUSED_MA: m_conv.mk_fusedma(f, num, args, result); return BR_DONE; + case OP_FLOAT_FMA: m_conv.mk_fma(f, num, args, result); return BR_DONE; case OP_FLOAT_SQRT: m_conv.mk_sqrt(f, num, args, result); return BR_DONE; case OP_FLOAT_ROUND_TO_INTEGRAL: m_conv.mk_round_to_integral(f, num, args, result); return BR_DONE; case OP_FLOAT_EQ: m_conv.mk_float_eq(f, num, args, result); return BR_DONE; @@ -142,18 +142,18 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { case OP_FLOAT_IS_INF: m_conv.mk_is_inf(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_NORMAL: m_conv.mk_is_normal(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_SUBNORMAL: m_conv.mk_is_subnormal(f, num, args, result); return BR_DONE; - case OP_FLOAT_IS_SIGN_MINUS: m_conv.mk_is_sign_minus(f, num, args, result); return BR_DONE; + case OP_FLOAT_IS_POSITIVE: m_conv.mk_is_positive(f, num, args, result); return BR_DONE; + case OP_FLOAT_IS_NEGATIVE: m_conv.mk_is_negative(f, num, args, result); return BR_DONE; case OP_TO_FLOAT: m_conv.mk_to_float(f, num, args, result); return BR_DONE; - case OP_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE; + case OP_FLOAT_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE; case OP_FLOAT_FP: m_conv.mk_fp(f, num, args, result); return BR_DONE; - case OP_FLOAT_TO_FP_UNSIGNED: m_conv.mk_to_fp_unsigned(f, num, args, result); return BR_DONE; case OP_FLOAT_TO_UBV: m_conv.mk_to_ubv(f, num, args, result); return BR_DONE; case OP_FLOAT_TO_SBV: m_conv.mk_to_sbv(f, num, args, result); return BR_DONE; case OP_FLOAT_TO_REAL: m_conv.mk_to_real(f, num, args, result); return BR_DONE; default: TRACE("fpa2bv", tout << "unsupported operator: " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); - throw tactic_exception("NYI"); + NOT_IMPLEMENTED_YET(); } } diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index a4212d579..7dc34dd11 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -36,17 +36,17 @@ br_status float_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c br_status st = BR_FAILED; SASSERT(f->get_family_id() == get_fid()); switch (f->get_decl_kind()) { - case OP_TO_FLOAT: st = mk_to_float(f, num_args, args, result); break; + case OP_TO_FLOAT: st = mk_to_fp(f, num_args, args, result); break; case OP_FLOAT_ADD: SASSERT(num_args == 3); st = mk_add(args[0], args[1], args[2], result); break; case OP_FLOAT_SUB: SASSERT(num_args == 3); st = mk_sub(args[0], args[1], args[2], result); break; - case OP_FLOAT_UMINUS: SASSERT(num_args == 1); st = mk_uminus(args[0], result); break; + case OP_FLOAT_NEG: SASSERT(num_args == 1); st = mk_neg(args[0], result); break; case OP_FLOAT_MUL: SASSERT(num_args == 3); st = mk_mul(args[0], args[1], args[2], result); break; case OP_FLOAT_DIV: SASSERT(num_args == 3); st = mk_div(args[0], args[1], args[2], result); break; case OP_FLOAT_REM: SASSERT(num_args == 2); st = mk_rem(args[0], args[1], result); break; case OP_FLOAT_ABS: SASSERT(num_args == 1); st = mk_abs(args[0], result); break; case OP_FLOAT_MIN: SASSERT(num_args == 2); st = mk_min(args[0], args[1], result); break; case OP_FLOAT_MAX: SASSERT(num_args == 2); st = mk_max(args[0], args[1], result); break; - case OP_FLOAT_FUSED_MA: SASSERT(num_args == 4); st = mk_fused_ma(args[0], args[1], args[2], args[3], result); break; + case OP_FLOAT_FMA: SASSERT(num_args == 4); st = mk_fma(args[0], args[1], args[2], args[3], result); break; case OP_FLOAT_SQRT: SASSERT(num_args == 2); st = mk_sqrt(args[0], args[1], result); break; case OP_FLOAT_ROUND_TO_INTEGRAL: SASSERT(num_args == 2); st = mk_round(args[0], args[1], result); break; @@ -62,10 +62,10 @@ br_status float_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c case OP_FLOAT_IS_INF: SASSERT(num_args == 1); st = mk_is_inf(args[0], result); break; case OP_FLOAT_IS_NORMAL: SASSERT(num_args == 1); st = mk_is_normal(args[0], result); break; case OP_FLOAT_IS_SUBNORMAL: SASSERT(num_args == 1); st = mk_is_subnormal(args[0], result); break; - case OP_FLOAT_IS_SIGN_MINUS: SASSERT(num_args == 1); st = mk_is_sign_minus(args[0], result); break; - case OP_TO_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(args[0], result); break; + case OP_FLOAT_IS_NEGATIVE: SASSERT(num_args == 1); st = mk_is_negative(args[0], result); break; + case OP_FLOAT_IS_POSITIVE: SASSERT(num_args == 1); st = mk_is_positive(args[0], result); break; + case OP_FLOAT_TO_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(args[0], result); break; case OP_FLOAT_FP: SASSERT(num_args == 3); st = mk_fp(args[0], args[1], args[2], result); break; - case OP_FLOAT_TO_FP_UNSIGNED: SASSERT(num_args == 2); st = mk_to_fp_unsigned(args[0], args[1], result); break; case OP_FLOAT_TO_UBV: SASSERT(num_args == 2); st = mk_to_ubv(args[0], args[1], result); break; case OP_FLOAT_TO_SBV: SASSERT(num_args == 2); st = mk_to_sbv(args[0], args[1], result); break; case OP_FLOAT_TO_REAL: SASSERT(num_args == 1); st = mk_to_real(args[0], result); break; @@ -73,7 +73,7 @@ br_status float_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c return st; } -br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { +br_status float_rewriter::mk_to_fp(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_num_parameters() == 2); SASSERT(f->get_parameter(0).is_int()); SASSERT(f->get_parameter(1).is_int()); @@ -154,7 +154,7 @@ br_status float_rewriter::mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref br_status float_rewriter::mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { // a - b = a + (-b) - result = m_util.mk_add(arg1, arg2, m_util.mk_uminus(arg3)); + result = m_util.mk_add(arg1, arg2, m_util.mk_neg(arg3)); return BR_REWRITE2; } @@ -188,7 +188,7 @@ br_status float_rewriter::mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref return BR_FAILED; } -br_status float_rewriter::mk_uminus(expr * arg1, expr_ref & result) { +br_status float_rewriter::mk_neg(expr * arg1, expr_ref & result) { if (m_util.is_nan(arg1)) { // -nan --> nan result = arg1; @@ -204,7 +204,7 @@ br_status float_rewriter::mk_uminus(expr * arg1, expr_ref & result) { result = m_util.mk_plus_inf(m().get_sort(arg1)); return BR_DONE; } - if (m_util.is_uminus(arg1)) { + if (m_util.is_neg(arg1)) { // - - a --> a result = to_app(arg1)->get_arg(0); return BR_DONE; @@ -239,7 +239,7 @@ br_status float_rewriter::mk_abs(expr * arg1, expr_ref & result) { return BR_DONE; } result = m().mk_ite(m_util.mk_is_sign_minus(arg1), - m_util.mk_uminus(arg1), + m_util.mk_neg(arg1), arg1); return BR_REWRITE2; } @@ -284,7 +284,7 @@ br_status float_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) { return BR_REWRITE_FULL; } -br_status float_rewriter::mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result) { +br_status float_rewriter::mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_value(arg1, rm)) { scoped_mpf v2(m_util.fm()), v3(m_util.fm()), v4(m_util.fm()); @@ -480,7 +480,7 @@ br_status float_rewriter::mk_is_subnormal(expr * arg1, expr_ref & result) { return BR_FAILED; } -br_status float_rewriter::mk_is_sign_minus(expr * arg1, expr_ref & result) { +br_status float_rewriter::mk_is_negative(expr * arg1, expr_ref & result) { scoped_mpf v(m_util.fm()); if (m_util.is_value(arg1, v)) { result = (m_util.fm().is_neg(v)) ? m().mk_true() : m().mk_false(); @@ -490,6 +490,17 @@ br_status float_rewriter::mk_is_sign_minus(expr * arg1, expr_ref & result) { return BR_FAILED; } +br_status float_rewriter::mk_is_positive(expr * arg1, expr_ref & result) { + scoped_mpf v(m_util.fm()); + if (m_util.is_value(arg1, v)) { + result = (m_util.fm().is_neg(v) || m_util.fm().is_nan(v)) ? m().mk_false() : m().mk_true(); + return BR_DONE; + } + + return BR_FAILED; +} + + // This the SMT = br_status float_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) { scoped_mpf v1(m_util.fm()), v2(m_util.fm()); @@ -532,10 +543,6 @@ br_status float_rewriter::mk_fp(expr * arg1, expr * arg2, expr * arg3, expr_ref return BR_FAILED; } -br_status float_rewriter::mk_to_fp_unsigned(expr * arg1, expr * arg2, expr_ref & result) { - return BR_FAILED; -} - br_status float_rewriter::mk_to_ubv(expr * arg1, expr * arg2, expr_ref & result) { return BR_FAILED; } diff --git a/src/ast/rewriter/float_rewriter.h b/src/ast/rewriter/float_rewriter.h index 0f44d227c..4d8cec856 100644 --- a/src/ast/rewriter/float_rewriter.h +++ b/src/ast/rewriter/float_rewriter.h @@ -45,17 +45,16 @@ public: br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result); - br_status mk_to_float(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); - br_status mk_uminus(expr * arg1, expr_ref & result); + br_status mk_neg(expr * arg1, expr_ref & result); br_status mk_rem(expr * arg1, expr * arg2, expr_ref & result); br_status mk_abs(expr * arg1, expr_ref & result); br_status mk_min(expr * arg1, expr * arg2, expr_ref & result); br_status mk_max(expr * arg1, expr * arg2, expr_ref & result); - br_status mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result); + br_status mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result); br_status mk_sqrt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_round(expr * arg1, expr * arg2, expr_ref & result); br_status mk_float_eq(expr * arg1, expr * arg2, expr_ref & result); @@ -70,10 +69,12 @@ public: br_status mk_is_inf(expr * arg1, expr_ref & result); br_status mk_is_normal(expr * arg1, expr_ref & result); br_status mk_is_subnormal(expr * arg1, expr_ref & result); - br_status mk_is_sign_minus(expr * arg1, expr_ref & result); + br_status mk_is_negative(expr * arg1, expr_ref & result); + br_status mk_is_positive(expr * arg1, expr_ref & result); br_status mk_to_ieee_bv(expr * arg1, expr_ref & result); + br_status mk_to_fp(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_fp(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_to_fp_unsigned(expr * arg1, expr * arg2, expr_ref & result); br_status mk_to_ubv(expr * arg1, expr * arg2, expr_ref & result); From 5adfbe885733f67b0d1f5f2a7d051d1a0c60b921 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 22 Oct 2014 21:57:57 +0100 Subject: [PATCH 496/509] Z3Py: Fix test output Signed-off-by: Nuno Lopes --- src/api/python/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 470280630..f2318021f 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -38,7 +38,7 @@ Z3 exceptions: ... n = x + y ... except Z3Exception as ex: ... print("failed: %s" % ex) -failed: 'sort mismatch' +failed: sort mismatch """ from z3core import * from z3types import * From a6bee82ef8dd8ad49e90bdc80c796436e1ccaca0 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 23 Oct 2014 17:10:31 +0100 Subject: [PATCH 497/509] Interpolation API: fixed some memory leaks Signed-off-by: Christoph M. Wintersteiger --- src/interp/iz3proof_itp.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index e89aa1d6d..52ddcd64f 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -3078,6 +3078,11 @@ public: m().dec_ref(add_pos); m().dec_ref(rewrite_A); m().dec_ref(rewrite_B); + m().dec_ref(normal_step); + m().dec_ref(normal_chain); + m().dec_ref(normal); + m().dec_ref(sforall); + m().dec_ref(sexists); } }; From 6a27d93776fce414f4c4a04fb8358361a2a07d70 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 23 Oct 2014 17:20:55 +0100 Subject: [PATCH 498/509] Fixed memory leaks in interpolation API Signed-off-by: Christoph M. Wintersteiger --- src/api/api_interp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 63ecc6c24..dd689dcb0 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -273,6 +273,9 @@ extern "C" { 0 // ignore params for now ); + for (unsigned i = 0; i < cnsts.size(); i++) + _m.dec_ref(cnsts[i]); + Z3_lbool status = of_lbool(_status); Z3_ast_vector_ref *v = 0; From fd0920eb36c308f935b43a0bb4ee5cd910ffbd3f Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 23 Oct 2014 19:15:26 +0100 Subject: [PATCH 499/509] Updated release notes Signed-off-by: Christoph M. Wintersteiger --- RELEASE_NOTES | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 643c9b26f..4be65b260 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -3,6 +3,15 @@ RELEASE NOTES Version 4.3.2 ============= +- Added preliminary support for the theory of floating point numbers (tactics qffpa, qffpabv, and logics QF_FPA, QF_FPABV). + +- Added the interpolation features of iZ3, which are now integrated into the Z3. + +- Fixed a multitude of bugs and inconsistencies that were reported to us either in person, by email, or on Codeplex. Of those that we do have records of, we would like to express our gratitude to: + Vladimir Klebanov, Konrad Jamrozik, Nuno Lopes, Carsten Ruetz, Esteban Pavese, Tomer Weiss, Ilya Mironov, Gabriele Paganelli, Levent Erkok, Fabian Emmes, David Cok, Etienne Kneuss, Arlen Cox, Matt Lewis, Carsten Otto, Paul Jackson, David Monniaux, Markus Rabe, Martin Pluecker, Jasmin Blanchette, Jules Villard, Andrew Gacek, George Karpenkov, Joerg Pfaehler, and Pablo Aledo + as well as the following Codeplex users that either reported bugs or took part in discussions: +xor88, parno, gario, Bauna, GManNickG, hanwentao, dinu09, fhowar, Cici, chinissai, barak_cohen, tvalentyn, krikunts, sukyoung, daramos, snedunuri, rajtendulkar, sonertari, nick8325, dvitek, amdragon, Beatgodes, dmonniaux, nickolai, DameNingen, mangpo, ttsiodras, blurium, sbrickey, pcodemod, indranilsaha, apanda, hougaardj, yoff, EfForEffort, Ansotegui, scottgw, viorelpreoteasa, idudka, c2855337, gario, jnfoster, omarmrivas, switicus, vosandi, foens, yzwwf, Heizmann, znajem, ilyagri, hougaardj, cliguda, rgrig, 92c849c1ccc707173, edmcman, cipher1024, MichaelvW, hellok, n00b42, ic3guy, Adorf, tvcsantos, zilongwang, Elarnon, immspw, jbridge99, danliew, zverlov, petross, jmh93, dradorf, fniksic, Heyji, cxcfan, henningg, wxlfrank, rvprasad, MovGP0, jackie1015, cowang, ffaghih, sanpra1989, gzchenyin, baitman, xjtulixiangyang, andreis, trucnguyenlam, erizzi, hanhchi, qsp, windypan, vadave, gradanne, SamWot, gsingh93, manjeetdahiya, zverlov, and RaLa + - New parameter setting infrastructure. Now, it is possible to set parameter for Z3 internal modules. Several parameter names changed. Execute `z3 -p` for the new parameter list. - Added get_version() and get_version_string() to Z3Py From 7d196201dc00db41693d53abb63104462001792b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Oct 2014 12:33:20 +0100 Subject: [PATCH 500/509] fixed warnings Signed-off-by: Christoph M. Wintersteiger --- .gitignore | 1 + src/util/mpz.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 647529b4f..7757275f3 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,4 @@ src/util/version.h src/api/java/Native.cpp src/api/java/Native.java src/api/java/enumerations/*.java +*.bak diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index bd7f30a76..94df9f78d 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -1535,7 +1535,7 @@ bool mpz_manager::is_power_of_two(mpz const & a, unsigned & shift) { return false; if (is_small(a)) { if (::is_power_of_two(a.m_val)) { - shift = ::log2(a.m_val); + shift = ::log2((unsigned)a.m_val); return true; } else { @@ -1838,7 +1838,7 @@ unsigned mpz_manager::log2(mpz const & a) { if (is_nonpos(a)) return 0; if (is_small(a)) - return ::log2(a.m_val); + return ::log2((unsigned)a.m_val); #ifndef _MP_GMP COMPILE_TIME_ASSERT(sizeof(digit_t) == 8 || sizeof(digit_t) == 4); mpz_cell * c = a.m_ptr; @@ -1860,7 +1860,7 @@ unsigned mpz_manager::mlog2(mpz const & a) { if (is_nonneg(a)) return 0; if (is_small(a)) - return ::log2(-a.m_val); + return ::log2((unsigned)-a.m_val); #ifndef _MP_GMP COMPILE_TIME_ASSERT(sizeof(digit_t) == 8 || sizeof(digit_t) == 4); mpz_cell * c = a.m_ptr; From e0c42f589225883c03438f6c5f9ad1fe9368708a Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Oct 2014 14:43:01 +0100 Subject: [PATCH 501/509] Java API bugfix Signed-off-by: Christoph M. Wintersteiger --- scripts/update_api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index e08adf111..2fbf42cca 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -297,11 +297,13 @@ def param2javaw(p): k = param_kind(p) if k == OUT: return "jobject" - if k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY: + elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY: if param_type(p) == INT or param_type(p) == UINT: return "jintArray" else: return "jlongArray" + elif k == OUT_MANAGED_ARRAY: + return "jlong"; else: return type2javaw(param_type(p)) From 4d62ff6b9fe241e76ae64e8fe4322df1980a8aaf Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Oct 2014 15:53:52 +0100 Subject: [PATCH 502/509] Spelling. Thanks to codeplex user regehr for reporting this. Signed-off-by: Christoph M. Wintersteiger --- src/api/python/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index f2318021f..e58e47640 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -1398,7 +1398,7 @@ def BoolVector(prefix, sz, ctx=None): return [ Bool('%s__%s' % (prefix, i)) for i in range(sz) ] def FreshBool(prefix='b', ctx=None): - """Return a fresh Bolean constant in the given context using the given prefix. + """Return a fresh Boolean constant in the given context using the given prefix. If `ctx=None`, then the global context is used. From 6e159bd442a8c338ebd9d94d489cef51eb18f25a Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Oct 2014 15:54:49 +0100 Subject: [PATCH 503/509] updated release notes Signed-off-by: Christoph M. Wintersteiger --- RELEASE_NOTES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 4be65b260..fa4857e32 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -10,7 +10,7 @@ Version 4.3.2 - Fixed a multitude of bugs and inconsistencies that were reported to us either in person, by email, or on Codeplex. Of those that we do have records of, we would like to express our gratitude to: Vladimir Klebanov, Konrad Jamrozik, Nuno Lopes, Carsten Ruetz, Esteban Pavese, Tomer Weiss, Ilya Mironov, Gabriele Paganelli, Levent Erkok, Fabian Emmes, David Cok, Etienne Kneuss, Arlen Cox, Matt Lewis, Carsten Otto, Paul Jackson, David Monniaux, Markus Rabe, Martin Pluecker, Jasmin Blanchette, Jules Villard, Andrew Gacek, George Karpenkov, Joerg Pfaehler, and Pablo Aledo as well as the following Codeplex users that either reported bugs or took part in discussions: -xor88, parno, gario, Bauna, GManNickG, hanwentao, dinu09, fhowar, Cici, chinissai, barak_cohen, tvalentyn, krikunts, sukyoung, daramos, snedunuri, rajtendulkar, sonertari, nick8325, dvitek, amdragon, Beatgodes, dmonniaux, nickolai, DameNingen, mangpo, ttsiodras, blurium, sbrickey, pcodemod, indranilsaha, apanda, hougaardj, yoff, EfForEffort, Ansotegui, scottgw, viorelpreoteasa, idudka, c2855337, gario, jnfoster, omarmrivas, switicus, vosandi, foens, yzwwf, Heizmann, znajem, ilyagri, hougaardj, cliguda, rgrig, 92c849c1ccc707173, edmcman, cipher1024, MichaelvW, hellok, n00b42, ic3guy, Adorf, tvcsantos, zilongwang, Elarnon, immspw, jbridge99, danliew, zverlov, petross, jmh93, dradorf, fniksic, Heyji, cxcfan, henningg, wxlfrank, rvprasad, MovGP0, jackie1015, cowang, ffaghih, sanpra1989, gzchenyin, baitman, xjtulixiangyang, andreis, trucnguyenlam, erizzi, hanhchi, qsp, windypan, vadave, gradanne, SamWot, gsingh93, manjeetdahiya, zverlov, and RaLa +xor88, parno, gario, Bauna, GManNickG, hanwentao, dinu09, fhowar, Cici, chinissai, barak_cohen, tvalentyn, krikunts, sukyoung, daramos, snedunuri, rajtendulkar, sonertari, nick8325, dvitek, amdragon, Beatgodes, dmonniaux, nickolai, DameNingen, mangpo, ttsiodras, blurium, sbrickey, pcodemod, indranilsaha, apanda, hougaardj, yoff, EfForEffort, Ansotegui, scottgw, viorelpreoteasa, idudka, c2855337, gario, jnfoster, omarmrivas, switicus, vosandi, foens, yzwwf, Heizmann, znajem, ilyagri, hougaardj, cliguda, rgrig, 92c849c1ccc707173, edmcman, cipher1024, MichaelvW, hellok, n00b42, ic3guy, Adorf, tvcsantos, zilongwang, Elarnon, immspw, jbridge99, danliew, zverlov, petross, jmh93, dradorf, fniksic, Heyji, cxcfan, henningg, wxlfrank, rvprasad, MovGP0, jackie1015, cowang, ffaghih, sanpra1989, gzchenyin, baitman, xjtulixiangyang, andreis, trucnguyenlam, erizzi, hanhchi, qsp, windypan, vadave, gradanne, SamWot, gsingh93, manjeetdahiya, zverlov, RaLa, and regehr. - New parameter setting infrastructure. Now, it is possible to set parameter for Z3 internal modules. Several parameter names changed. Execute `z3 -p` for the new parameter list. From cc99e967866de3b975ce9facf08e02cec0df1db7 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Oct 2014 18:00:36 +0100 Subject: [PATCH 504/509] Java API Cleanup Signed-off-by: Christoph M. Wintersteiger --- examples/java/README | 10 ++++------ src/api/java/README | 3 +++ src/api/java/manifest | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/java/README b/examples/java/README index fa1f20a63..6da4958e4 100644 --- a/examples/java/README +++ b/examples/java/README @@ -1,7 +1,5 @@ -### This is work-in-progress and does not work yet. - -Small example using the Z3 Java bindings. -To build the example execute +A small example using the Z3 Java bindings. +To build the example, configure Z3 with the --java option to scripts/mk_make.py, build via make examples in the build directory. @@ -11,5 +9,5 @@ which can be run on Windows via On Linux and FreeBSD, we must use LD_LIBRARY_PATH=. java -cp com.microsoft.z3.jar:. JavaExample - - \ No newline at end of file +On OSX, the corresponding option is DYLD_LIBRARY_PATH: + DYLD_LIBRARY_PATH=. java -cp com.microsoft.z3.jar:. JavaExample diff --git a/src/api/java/README b/src/api/java/README index 7afe098fb..c9dbcf8f7 100644 --- a/src/api/java/README +++ b/src/api/java/README @@ -1,3 +1,6 @@ Java bindings ------------- +The Java bindings will be included in the Z3 build if it is configured with +the option --java to python scripts/mk_make.py. This will produce the +com.microsoft.z3.jar package in the build directory. diff --git a/src/api/java/manifest b/src/api/java/manifest index b54445cf2..88eac9a3a 100644 --- a/src/api/java/manifest +++ b/src/api/java/manifest @@ -1,2 +1,2 @@ Manifest-Version: 1.0 -Created-By: 4.3.0 (Microsoft Research LTD.) +Created-By: 4.3.2 (Microsoft Research LTD.) From 60cf1d5a4f24802a283f3b427d70b578b1b2989c Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Oct 2014 18:02:58 +0100 Subject: [PATCH 505/509] Update copyright notices Signed-off-by: Christoph M. Wintersteiger --- src/api/dotnet/Properties/AssemblyInfo | 2 +- src/api/ml/z3_theory_stubs.c | 2 +- src/shell/main.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/dotnet/Properties/AssemblyInfo b/src/api/dotnet/Properties/AssemblyInfo index ee0e3f354..001264e79 100644 --- a/src/api/dotnet/Properties/AssemblyInfo +++ b/src/api/dotnet/Properties/AssemblyInfo @@ -12,7 +12,7 @@ using System.Security.Permissions; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft Corporation")] [assembly: AssemblyProduct("Z3")] -[assembly: AssemblyCopyright("Copyright © Microsoft Corporation 2006")] +[assembly: AssemblyCopyright("Copyright (C) 2006-2014 Microsoft Corporation")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/src/api/ml/z3_theory_stubs.c b/src/api/ml/z3_theory_stubs.c index d17a1790d..a48b94553 100644 --- a/src/api/ml/z3_theory_stubs.c +++ b/src/api/ml/z3_theory_stubs.c @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2010 Microsoft Corporation +Copyright (c) Microsoft Corporation Module Name: diff --git a/src/shell/main.cpp b/src/shell/main.cpp index f23bc470c..0eb612f35 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -65,7 +65,7 @@ void display_usage() { #ifdef Z3GITHASH std::cout << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH); #endif - std::cout << "]. (C) Copyright 2006-2013 Microsoft Corp.\n"; + std::cout << "]. (C) Copyright 2006-2014 Microsoft Corp.\n"; std::cout << "Usage: z3 [options] [-file:]file\n"; std::cout << "\nInput format:\n"; std::cout << " -smt use parser for SMT input format.\n"; From 2f9b3c42eb0e629fdb9c88a2a41863e73e8ffb09 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Oct 2014 19:43:36 +0100 Subject: [PATCH 506/509] Java API cleanup Signed-off-by: Christoph M. Wintersteiger --- examples/java/README | 1 + src/api/java/mk_java.py | 463 ---------------------------------------- 2 files changed, 1 insertion(+), 463 deletions(-) delete mode 100644 src/api/java/mk_java.py diff --git a/examples/java/README b/examples/java/README index 6da4958e4..1939afc49 100644 --- a/examples/java/README +++ b/examples/java/README @@ -1,4 +1,5 @@ A small example using the Z3 Java bindings. + To build the example, configure Z3 with the --java option to scripts/mk_make.py, build via make examples in the build directory. diff --git a/src/api/java/mk_java.py b/src/api/java/mk_java.py deleted file mode 100644 index 87f3e274a..000000000 --- a/src/api/java/mk_java.py +++ /dev/null @@ -1,463 +0,0 @@ -###################################################### -# Copyright (c) 2012 Microsoft Corporation -# -# Auxiliary scripts for generating Java bindings -# from the managed API. -# -# Author: Christoph M. Wintersteiger (cwinter) -###################################################### - - -### -# DO NOT USE THIS SCRIPT! -# This script creates a rough draft of a Java API from -# the managed API, but does not automated the process. -### - -CS="../dotnet/" -EXT=".cs" -EXCLUDE=["Enumerations.cs", "Native.cs", "AssemblyInfo.cs"] -OUTDIR="com/Microsoft/Z3/" -ENUMS_FILE = "Enumerations.cs" - -import os -import fileinput -import string -import re - -EXCLUDE_METHODS = [ [ "Context.cs", "public Expr MkNumeral(ulong" ], - [ "Context.cs", "public Expr MkNumeral(uint" ], - [ "Context.cs", "public RatNum MkReal(ulong" ], - [ "Context.cs", "public RatNum MkReal(uint" ], - [ "Context.cs", "public IntNum MkInt(ulong" ], - [ "Context.cs", "public IntNum MkInt(uint" ], - [ "Context.cs", "public BitVecNum MkBV(ulong" ], - [ "Context.cs", "public BitVecNum MkBV(uint" ], - ] - -ENUMS = [] - -def mk_java_bindings(): - print "Generating Java bindings (from C# bindings in " + CS + ")..." - print "Finding enumerations in " + ENUMS_FILE + "..." - find_enums(ENUMS_FILE) - for root, dirs, files in os.walk(CS): - for fn in files: - if not fn in EXCLUDE and fn.endswith(EXT): - translate(fn) - -def subst_getters(s, getters): - for g in getters: - s = s.replace(g, g + "()") - -def type_replace(s): - s = s.replace(" bool", " boolean") - s = s.replace("(bool", "(boolean") - s = s.replace("uint", "int") - s = s.replace("ulong", "long") - s = s.replace("string", "String") - s = s.replace("IntPtr", "long") - s = s.replace("Dictionary<", "Map<") - s = s.replace("UInt64", "long") - s = s.replace("Int64", "long") - s = s.replace("List", "LinkedList") - s = s.replace("System.Exception", "Exception") - return s - -def rename_native(s): - while s.find("Native.Z3") != -1: - i0 = s.find("Native.Z3") - i1 = s.find("(", i0) - c0 = s[:i0] - c1 = s[i0:i1] - c1 = c1.replace("Native.Z3_", "Native.") - c2 = s[i1:] - lc_callback = lambda pat: pat.group("id").upper() - c1 = re.sub("_(?P\w)", lc_callback, c1) - s = c0 + c1 + c2 - return s - -def find_enums(fn): - for line in fileinput.input(os.path.join(CS, fn)): - s = string.rstrip(string.lstrip(line)) - if s.startswith("public enum"): - ENUMS.append(s.split(" ")[2]) - - -def enum_replace(line): - for e in ENUMS: - if line.find("case") != -1: - line = line.replace(e + ".", "") - elif line.find("== (int)") != -1 or line.find("!= (int)") != -1: - line = re.sub("\(int\)" + e + "\.(?P[A-Z0-9_]*)", e + ".\g.toInt()", line) - elif line.find("==") != -1 or line.find("!=") != -1: - line = re.sub(e + "\.(?P[A-Z0-9_]*)", e + ".\g", line) - else: - # line = re.sub("\(\(" + e + "\)(?P.*\(.*)\)", "(" + e + ".values()[\g])", line) - line = re.sub("\(" + e + "\)(?P.*\(.*\))", e + ".fromInt(\g)", line) - return line - -def replace_generals(a): - a = re.sub(" NativeObject", " NativeObject()", a) - a = re.sub("\.NativeObject", ".NativeObject()", a) - a = re.sub("(?P[\.\(])Id", "\gId()", a) - a = a.replace("(Context ==", "(Context() ==") - a = a.replace("(Context,", "(Context(),") - a = a.replace("Context.", "Context().") - a = a.replace(".nCtx", ".nCtx()") - a = a.replace("(nCtx", "(nCtx()") - a = re.sub("Context\(\).(?P[^_]*)_DRQ", "Context().\g_DRQ()", a) - a = re.sub("ASTKind", "ASTKind()", a) - a = re.sub("IsExpr(?P[ ;])", "IsExpr()\g", a) - a = re.sub("IsNumeral(?P[ ;])", "IsNumeral()\g", a) - a = re.sub("IsInt(?P[ ;])", "IsInt()\g", a) - a = re.sub("IsReal(?P[ ;])", "IsReal()\g", a) - a = re.sub("IsVar(?P[ ;\)])", "IsVar()\g", a) - a = re.sub("FuncDecl.DeclKind", "FuncDecl().DeclKind()", a) - a = re.sub("FuncDecl.DomainSize", "FuncDecl().DomainSize()", a) - a = re.sub("(?P[=&]) Num(?P[a-zA-Z]*)", "\g Num\g()", a) - a = re.sub("= Denominator", "= Denominator()", a) - a = re.sub(", BoolSort(?P[\)\.])", ", BoolSort()\g", a) - a = re.sub(", RealSort(?P[\)\.])", ", RealSort()\g", a) - a = re.sub(", IntSort(?P[\)\.])", ", IntSort()\g", a) - a = a.replace("? 1 : 0", "? true : false") - if a.find("Native.") != -1 and a.find("!= 0") != -1: - a = a.replace("!= 0", "") - if a.find("Native.") != -1 and a.find("== 0") != -1: - a = a.replace("== 0", "^ true") - return a - -def translate(filename): - tgtfn = OUTDIR + filename.replace(EXT, ".java") - print "Translating " + filename + " to " + tgtfn - tgt = open(tgtfn, "w") - in_header = 0 - in_class = 0 - in_static_class = 0 - in_javadoc = 0 - lastindent = 0 - skip_brace = 0 - in_getter = "" - in_getter_type = "" - in_unsupported = 0 - getters = [] - in_bracket_op = 0 - in_getter_get = 0 - in_getter_set = 0 - had_ulong_res = 0 - in_enum = 0 - missing_foreach_brace = 0 - foreach_opened_brace = 0 - for line in fileinput.input(os.path.join(CS, filename)): - s = string.rstrip(string.lstrip(line)) - if in_javadoc: - if s.startswith("///"): - lastindent = line.find(s); - if s.startswith("/// "): - pass - else: - a = line - a = a.replace("", "") - a = a.replace("", "") - a = a.replace("///"," *") - a = a.replace("","@return ") - a = a.replace("","") - tgt.write(a) - else: - t = "" - for i in range(0, lastindent): - t += " " - tgt.write(t + " **/\n") - in_javadoc = 0 - - # for i in range(0, len(EXCLUDE_METHODS)): - # if filename == EXCLUDE_METHODS[i][0] and s.startswith(EXCLUDE_METHODS[i][1]): - # tgt.write(t + "/* Not translated because it would translate to a function with clashing types. */\n") - # in_unsupported = 1 - # break - - - if in_unsupported: - if s == "}": - in_unsupported = 0 - elif not in_javadoc: - if not in_header and s.find("/*++") != -1: - in_header = 1 - tgt.write("/**\n") - elif in_header and s.startswith("--*/"): - in_header = 0 - tgt.write(" * This file was automatically generated from " + filename + " \n") - tgt.write(" **/\n") - tgt.write("\npackage com.Microsoft.Z3;\n\n") - tgt.write("import java.math.BigInteger;\n") - tgt.write("import java.util.*;\n") - tgt.write("import java.lang.Exception;\n") - tgt.write("import com.Microsoft.Z3.Enumerations.*;\n") - elif in_header == 1: - # tgt.write(" * " + line.replace(filename, tgtfn)) - pass - elif s.startswith("using"): - if s.find("System.Diagnostics.Contracts") == -1: - tgt.write("/* " + s + " */\n") - elif s.startswith("namespace"): - pass - elif s.startswith("public") and s.find("operator") != -1 and (s.find("==") != -1 or s.find("!=") != -1): - t = "" - for i in range(0, line.find(s)+1): - t += " " - tgt.write(t + "/* Overloaded operators are not translated. */\n") - in_unsupported = 1 - elif s.startswith("public enum"): - tgt.write(line.replace("enum", "class")) - in_enum = 1 - elif in_enum == 1: - if s == "}": - tgt.write(line) - in_enum = 0 - else: - line = re.sub("(?P.*)\W*=\W*(?P[^\n,])", "public static final int \g = \g;", line) - tgt.write(line.replace(",","")) - elif s.startswith("public class") or s.startswith("internal class") or s.startswith("internal abstract class"): - a = line.replace(":", "extends").replace("internal ", "") - a = a.replace(", IComparable", "") - a = type_replace(a) - tgt.write(a) - in_class = 1 - in_static_class = 0 - elif s.startswith("public static class") or s.startswith("abstract class"): - tgt.write(line.replace(":", "extends").replace("static", "final")) - in_class = 1 - in_static_class = 1 - elif s.startswith("/// "): - tgt.write(line.replace("/// ", "/**")) - in_javadoc = 1 - elif skip_brace and s == "{": - skip_brace = 0 - elif ((s.find("public") != -1 or s.find("protected") != -1) and s.find("class") == -1 and s.find("event") == -1 and s.find("(") == -1) or s.startswith("internal virtual IntPtr NativeObject") or s.startswith("internal Context Context"): - if (s.startswith("new")): - s = s[3:] - s = s.replace("internal virtual", "") - s = s.replace("internal", "") - tokens = s.split(" ") - # print "TOKENS: " + str(len(tokens)) - if len(tokens) == 3: - in_getter = tokens[2] - in_getter_type = type_replace((tokens[0] + " " + tokens[1])) - if in_static_class: - in_getter_type = in_getter_type.replace("static", "") - lastindent = line.find(s) - skip_brace = 1 - getters.append(in_getter) - elif len(tokens) == 4: - if tokens[2].startswith("this["): - in_bracket_op = 1 - in_getter = type_replace(tokens[2]).replace("this[", "get(") - in_getter += " " + tokens[3].replace("]", ")") - in_getter_type = type_replace(tokens[0] + " " + tokens[1]) - else: - in_getter = tokens[3] - in_getter_type = type_replace(tokens[0] + " " + tokens[1] + " " + tokens[2]) - if in_static_class: - in_getter_type = in_getter_type.replace("static", "") - lastindent = line.find(s) - skip_brace = 1 - getters.append(in_getter) - else: - in_getter = tokens[2] - in_getter_type = type_replace(tokens[0] + " " + tokens[1]) - if tokens[2].startswith("this["): - lastindent = line.find(s) - t = "" - for i in range(0, lastindent): t += " " - tgt.write(t + "/* operator this[] not translated */\n ") - in_unsupported = 1 - else: - if in_static_class: - in_getter_type = in_getter_type.replace("static", "") - rest = s[s.find("get ") + 4:-1] - subst_getters(rest, getters) - rest = type_replace(rest) - rest = rename_native(rest) - rest = replace_generals(rest) - rest = enum_replace(rest) - t = "" - for i in range(0, lastindent): - t += " " - tgt.write(t + in_getter_type + " " + in_getter + "() " + rest + "\n") - if rest.find("}") == -1: - in_getter_get = 1 - else: - getters.append(in_getter) - in_getter = "" - in_getter_type = "" - print "ACC: " + s + " --> " + in_getter - elif s.find("{ get {") != -1: - line = type_replace(line) - line = line.replace("internal ", "") - d = line[0:line.find("{ get")] - rest = line[line.find("{ get")+5:] - rest = rest.replace("} }", "}") - rest = re.sub("Contract.\w+\([\s\S]*\);", "", rest) - subst_getters(rest, getters) - rest = rename_native(rest) - rest = replace_generals(rest) - rest = enum_replace(rest) - if in_bracket_op: - tgt.write(d + rest) - else: - tgt.write(d + "()" + rest) - print "ACC: " + s + " --> " + in_getter - elif in_getter != "" and s.startswith("get"): - t = "" - for i in range(0, lastindent): - t += " " - if len(s) > 3: rest = s[3:] - else: rest = "" - subst_getters(rest, getters) - rest = type_replace(rest) - rest = rename_native(rest) - rest = replace_generals(rest) - rest = enum_replace(rest) - if in_bracket_op: - tgt.write(t + in_getter_type + " " + in_getter + " " + rest + "\n") - else: - tgt.write(t + in_getter_type + " " + in_getter + "() " + rest + "\n") - if rest.find("}") == -1: - in_getter_get = 1 - elif in_getter != "" and s.startswith("set"): - t = "" - for i in range(0, lastindent): - t += " " - if len(s) > 3: rest = type_replace(s[3:]) - else: rest = "" - subst_getters(rest, getters) - rest = rest.replace("(Integer)value", "Integer(value)") - rest = type_replace(rest) - rest = rename_native(rest) - rest = replace_generals(rest) - rest = enum_replace(rest) - ac_acc = in_getter_type[:in_getter_type.find(' ')] - ac_type = in_getter_type[in_getter_type.find(' ')+1:] - if in_bracket_op: - in_getter = in_getter.replace("get", "set").replace(")", "") - tgt.write(t + ac_acc + " void " + in_getter + ", " + ac_type + " value) " + rest + "\n") - else: - tgt.write(t + ac_acc + " void set" + in_getter + "(" + ac_type + " value) " + rest + "\n") - if rest.find("}") == -1: - in_getter_set = 1 - elif in_getter != "" and in_getter_get == 1 and s == "}": - tgt.write(line) - in_getter_get = 0 - elif in_getter != "" and in_getter_set == 1 and s == "}": - tgt.write(line) - in_getter_set = 0 - elif in_getter != "" and in_getter_get == 0 and in_getter_set == 0 and s == "}": - in_getter = "" - in_getter_type == "" - in_bracket_op = 0 - skip_brace = 0 - elif s.startswith("uint ") and s.find("=") == -1: - line = line.replace("uint", "Integer", line) - line = re.sub("(?P\w+)(?P[,;])", "\g\g", line) - tgt.write(line); - elif (not in_class and (s.startswith("{") or s.startswith("}"))) or s.startswith("[") or s.startswith("#"): - # tgt.write("// Skipping: " + s) - pass - elif line == "}\n": - pass - else: - # indent = line.find(s) - # tgt.write("// LINE: " + line) - if line.find(": base") != -1: - line = re.sub(": base\((?P

[^\{]*)\)", "{ super(\g

);", line) - line = line[4:] - obraces = line.count("{") - cbraces = line.count("}") - mbraces = obraces - cbraces - # tgt.write("// obraces = " + str(obraces) + "\n") - if obraces == 1: - skip_brace = 1 - else: - for i in range(0, mbraces): - line = line.replace("\n", "}\n") - if (s.find("public") != -1 or s.find("protected") != -1 or s.find("internal") != -1) and s.find("(") != -1: - line = re.sub(" = [\w.]+(?P[,;\)])", "\g", line) - a = type_replace(line) - a = enum_replace(a) - a = re.sub("(?P[\(, ])params ", "\g", a) - a = a.replace("base.", "super.") - a = re.sub("Contract.\w+\([\s\S]*\);", "", a) - a = rename_native(a) - a = re.sub("~\w+\(\)", "protected void finalize()", a) - - if missing_foreach_brace == 1: - # a = a.replace("\n", " // checked " + str(foreach_opened_brace) + "\n") - if foreach_opened_brace == 0 and a.find("{") != -1: - foreach_opened_brace = 1 - elif foreach_opened_brace == 0 and a.find("}") == -1: - a = a.replace("\n", "}}\n") - foreach_opened_brace = 0 - missing_foreach_brace = 0 - elif foreach_opened_brace == 1 and a.find("}") != -1: - a = a.replace("\n", "}}\n") - foreach_opened_brace = 0 - missing_foreach_brace = 0 - -# if a.find("foreach") != -1: -# missing_foreach_brace = 1 -# a = re.sub("foreach\s*\((?P[\w <>,]+)\s+(?P\w+)\s+in\s+(?P\w+)\)", - # "{ Iterator fe_i = \g.iterator(); while (fe_i.hasNext()) { \g \g = (long)fe_i.next(); ", -# a) - a = re.sub("foreach\s*\((?P[\w <>,]+)\s+(?P\w+)\s+in\s+(?P\w+)\)", - "for (\g \g: \g)", - a) - if a.find("long o: m_queue") != -1: - a = a.replace("long", "Long") - a = a.replace("readonly ", "") - a = a.replace("const ", "final ") - a = a.replace("String ToString", "String toString") - a = a.replace(".ToString", ".toString") - a = a.replace("internal ", "") - a = a.replace("new static", "static") - a = a.replace("new public", "public") - a = a.replace("override ", "") - a = a.replace("virtual ", "") - a = a.replace("o as AST", "(AST) o") - a = a.replace("o as Sort", "(Sort) o") - a = a.replace("other as AST", "(AST) other") - a = a.replace("o as FuncDecl", "(FuncDecl) o") - a = a.replace("IntPtr obj", "long obj") - a = a.replace("IntPtr o", "long o") - a = a.replace("new long()", "0") - a = a.replace("long.Zero", "0") - a = a.replace("object o", "Object o") - a = a.replace("object other", "Object other") - a = a.replace("IntPtr res = IntPtr.Zero;", "Native.IntPtr res = new Native.IntPtr();") - a = a.replace("out res", "res") - a = a.replace("GC.ReRegisterForFinalize(m_ctx);", "") - a = a.replace("GC.SuppressFinalize(this);", "") - a = a.replace(".Length", ".length") - a = a.replace("m_queue.Count", "m_queue.size()") - a = a.replace("m_queue.Add", "m_queue.add") - a = a.replace("m_queue.Clear", "m_queue.clear") - a = a.replace("for (long ", "for (int ") - a = a.replace("ReferenceEquals(Context, ctx)", "Context() == ctx") - a = a.replace("BigInteger.Parse", "new BigInteger") - if had_ulong_res == 0 and a.find("ulong res = 0") != -1: - a = a.replace("ulong res = 0;", "LongPtr res = new LongPtr();") - elif had_ulong_res == 1: - a = a.replace("ref res)", "res)") - if a.find("return res;") != -1: - a = a.replace("return res;", "return res.value;") - had_ulong_res = 0 - a = a.replace("lock (", "synchronized (") - if in_static_class: - a = a.replace("static", "") - a = re.sub("ref (?P\w+)", "\g", a) - subst_getters(a, getters) - a = re.sub("NativeObject = (?P.*);", "setNativeObject(\g);", a) - a = replace_generals(a) - tgt.write(a) - tgt.close() - -mk_java_bindings() From ddebb4a69d8c6105187063e3647136d720e3c41c Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Oct 2014 19:45:21 +0100 Subject: [PATCH 507/509] Documentation fixes Signed-off-by: Christoph M. Wintersteiger --- doc/mk_api_doc.py | 8 ++++++++ doc/z3api.dox | 14 +++++++++----- src/api/java/AST.java | 20 ++------------------ src/api/java/Expr.java | 2 +- src/api/java/Global.java | 5 ++--- src/api/java/Sort.java | 19 +------------------ src/api/z3_algebraic.h | 16 +++++++++++++++- src/api/z3_api.h | 7 ++----- src/api/z3_interp.h | 18 +++++++++++++++++- src/api/z3_polynomial.h | 16 ++++++++++++++++ src/api/z3_rcf.h | 15 +++++++++++++++ 11 files changed, 88 insertions(+), 52 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index 6e530243a..633f96c6b 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -27,6 +27,10 @@ try: shutil.copyfile('website.dox', 'tmp/website.dox') shutil.copyfile('../src/api/python/z3.py', 'tmp/z3py.py') cleanup_API('../src/api/z3_api.h', 'tmp/z3_api.h') + cleanup_API('../src/api/z3_algebraic.h', 'tmp/z3_algebraic.h') + cleanup_API('../src/api/z3_polynomial.h', 'tmp/z3_polynomial.h') + cleanup_API('../src/api/z3_rcf.h', 'tmp/z3_rcf.h') + cleanup_API('../src/api/z3_interp.h', 'tmp/z3_interp.h') print "Removed annotations from z3_api.h." try: @@ -38,6 +42,10 @@ try: exit(1) print "Generated C and .NET API documentation." os.remove('tmp/z3_api.h') + os.remove('tmp/z3_algebraic.h') + os.remove('tmp/z3_polynomial.h') + os.remove('tmp/z3_rcf.h') + os.remove('tmp/z3_interp.h') print "Removed temporary file z3_api.h." os.remove('tmp/website.dox') print "Removed temporary file website.dox" diff --git a/doc/z3api.dox b/doc/z3api.dox index 0233e6d83..dbaa3c8b8 100644 --- a/doc/z3api.dox +++ b/doc/z3api.dox @@ -363,7 +363,7 @@ TYPEDEF_HIDES_STRUCT = NO # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. -SYMBOL_CACHE_SIZE = 0 +# SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given @@ -708,7 +708,11 @@ INPUT_ENCODING = UTF-8 # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = website.dox \ - z3_api.h \ + z3_api.h \ + z3_algebraic.h \ + z3_polynomial.h \ + z3_rcf.h \ + z3_interp.h \ z3++.h \ z3py.py \ ApplyResult.cs \ @@ -1477,13 +1481,13 @@ XML_OUTPUT = xml # which can be used by a validating XML parser to check the # syntax of the XML files. -XML_SCHEMA = +# XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. -XML_DTD = +# XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting @@ -1699,7 +1703,7 @@ DOT_NUM_THREADS = 0 # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. -DOT_FONTNAME = FreeSans +# DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. diff --git a/src/api/java/AST.java b/src/api/java/AST.java index 1f5463ec7..a201d5697 100644 --- a/src/api/java/AST.java +++ b/src/api/java/AST.java @@ -24,28 +24,12 @@ import com.microsoft.z3.enumerations.Z3_ast_kind; **/ public class AST extends Z3Object { - /** - * Comparison operator. An AST An - * AST - * - * @return True if and are from - * the same context and represent the same sort; false otherwise. - **/ - /* Overloaded operators are not translated. */ - - /** - * Comparison operator. An AST An - * AST - * - * @return True if and are not - * from the same context or represent different sorts; false - * otherwise. - **/ /* Overloaded operators are not translated. */ /** * Object comparison. - **/ + * another AST + **/ public boolean equals(Object o) { AST casted = null; diff --git a/src/api/java/Expr.java b/src/api/java/Expr.java index 3773e749d..a4c669dad 100644 --- a/src/api/java/Expr.java +++ b/src/api/java/Expr.java @@ -40,7 +40,7 @@ public class Expr extends AST /** * Returns a simplified version of the expression * A set of - * parameters to configure the simplifier + * parameters a Params object to configure the simplifier * **/ public Expr simplify(Params p) throws Z3Exception diff --git a/src/api/java/Global.java b/src/api/java/Global.java index b97e48721..c93f46c88 100644 --- a/src/api/java/Global.java +++ b/src/api/java/Global.java @@ -50,8 +50,7 @@ public final class Global /** * Get a global (or module) parameter. * - * Returns null if the parameter does not exist. - * The caller must invoke #Z3_global_param_del_value to delete the value returned at \c param_value. + * Returns null if the parameter parameter id does not exist. * This function cannot be invoked simultaneously from different threads without synchronization. * The result string stored in param_value is stored in a shared location. * @@ -70,7 +69,7 @@ public final class Global * * This command will not affect already created objects (such as tactics and solvers) * - * @seealso SetParameter + * **/ public static void resetParameters() { diff --git a/src/api/java/Sort.java b/src/api/java/Sort.java index 7bcc7ce7e..7d89428c6 100644 --- a/src/api/java/Sort.java +++ b/src/api/java/Sort.java @@ -25,27 +25,10 @@ import com.microsoft.z3.enumerations.Z3_sort_kind; **/ public class Sort extends AST { - /** - * Comparison operator. A Sort A - * Sort - * - * @return True if and are from - * the same context and represent the same sort; false otherwise. - **/ /* Overloaded operators are not translated. */ /** - * Comparison operator. A Sort A - * Sort - * - * @return True if and are not - * from the same context or represent different sorts; false - * otherwise. - **/ - /* Overloaded operators are not translated. */ - - /** - * Equality operator for objects of type Sort. + * Equality operator for objects of type Sort. * * @return **/ diff --git a/src/api/z3_algebraic.h b/src/api/z3_algebraic.h index eef791170..8b6d97aa8 100644 --- a/src/api/z3_algebraic.h +++ b/src/api/z3_algebraic.h @@ -23,7 +23,19 @@ Notes: #ifdef __cplusplus extern "C" { -#endif // __cplusplus +#endif // __cplusplus + + /** + \defgroup capi C API + + */ + + /*@{*/ + + /** + @name Algebraic Numbers API + */ + /*@{*/ /** \brief Return Z3_TRUE if \c can be used as value in the Z3 real algebraic @@ -218,6 +230,8 @@ extern "C" { */ int Z3_API Z3_algebraic_eval(__in Z3_context c, __in Z3_ast p, __in unsigned n, __in Z3_ast a[]); + /*@}*/ + /*@}*/ #ifdef __cplusplus }; diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 2e3ddf4b4..248d2161e 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -771,7 +771,6 @@ typedef enum The premises of the rules is a sequence of clauses. The first clause argument is the main clause of the rule. - One literal from the second, third, .. clause is resolved with a literal from the first (main) clause. Premises of the rules are of the form @@ -1143,8 +1142,8 @@ typedef enum { - Z3_FILE_ACCESS_ERRROR: A file could not be accessed. - Z3_INVALID_USAGE: API call is invalid in the current state. - Z3_INTERNAL_FATAL: An error internal to Z3 occurred. - - Z3_DEC_REF_ERROR: Trying to decrement the reference counter of an AST that was deleted or the reference counter was not initialized\mlonly.\endmlonly\conly with #Z3_inc_ref. - - Z3_EXCEPTION: Internal Z3 exception. Additional details can be retrieved using \mlonly #Z3_get_error_msg. \endmlonly \conly #Z3_get_error_msg_ex. + - Z3_DEC_REF_ERROR: Trying to decrement the reference counter of an AST that was deleted or the reference counter was not initialized \mlonly.\endmlonly \conly with #Z3_inc_ref. + - Z3_EXCEPTION: Internal Z3 exception. Additional details can be retrieved using #Z3_get_error_msg. */ typedef enum { @@ -1287,8 +1286,6 @@ extern "C" { \sa Z3_global_param_set - The caller must invoke #Z3_global_param_del_value to delete the value returned at \c param_value. - \remark This function cannot be invoked simultaneously from different threads without synchronization. The result string stored in param_value is stored in shared location. diff --git a/src/api/z3_interp.h b/src/api/z3_interp.h index 729851988..6ae26c0ed 100644 --- a/src/api/z3_interp.h +++ b/src/api/z3_interp.h @@ -24,7 +24,14 @@ extern "C" { #endif // __cplusplus /** - @name Interpolation + \defgroup capi C API + + */ + + /*@{*/ + + /** + @name Interpolation API */ /*@{*/ @@ -184,6 +191,8 @@ extern "C" { \param cnsts Returns sequence of formulas (do not free) \param parents Returns the parents vector (or NULL for sequence) \param error Returns an error message in case of failure (do not free the string) + \param num_theory Number of theory terms + \param theory Theory terms Returns true on success. @@ -232,6 +241,8 @@ extern "C" { \param parents The parents vector (or NULL for sequence) \param interps The interpolant to check \param error Returns an error message if interpolant incorrect (do not free the string) + \param num_theory Number of theory terms + \param theory Theory terms Return value is Z3_L_TRUE if interpolant is verified, Z3_L_FALSE if incorrect, and Z3_L_UNDEF if unknown. @@ -258,6 +269,8 @@ extern "C" { \param cnsts Array of constraints \param parents The parents vector (or NULL for sequence) \param filename The file name to write + \param num_theory Number of theory terms + \param theory Theory terms def_API('Z3_write_interpolation_problem', VOID, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(STRING), _in(UINT), _in_array(5, AST))) */ @@ -270,6 +283,9 @@ extern "C" { __in unsigned num_theory, __in_ecount(num_theory) Z3_ast theory[]); + /*@}*/ + /*@}*/ + #ifdef __cplusplus }; #endif // __cplusplus diff --git a/src/api/z3_polynomial.h b/src/api/z3_polynomial.h index 614e654f9..f55a63dd4 100644 --- a/src/api/z3_polynomial.h +++ b/src/api/z3_polynomial.h @@ -24,6 +24,19 @@ Notes: extern "C" { #endif // __cplusplus + /** + \defgroup capi C API + + */ + + /*@{*/ + + + /** + @name Polynomials API + */ + /*@{*/ + /** \brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x. @@ -38,6 +51,9 @@ extern "C" { Z3_ast_vector Z3_API Z3_polynomial_subresultants(__in Z3_context c, __in Z3_ast p, __in Z3_ast q, __in Z3_ast x); + /*@}*/ + /*@}*/ + #ifdef __cplusplus }; #endif // __cplusplus diff --git a/src/api/z3_rcf.h b/src/api/z3_rcf.h index e2b4b7e05..04fe40253 100644 --- a/src/api/z3_rcf.h +++ b/src/api/z3_rcf.h @@ -25,6 +25,18 @@ Notes: #ifdef __cplusplus extern "C" { #endif // __cplusplus + + /** + \defgroup capi C API + + */ + + /*@{*/ + + /** + @name Real Closed Fields API + */ + /*@{*/ /** \brief Delete a RCF numeral created using the RCF API. @@ -192,6 +204,9 @@ extern "C" { */ void Z3_API Z3_rcf_get_numerator_denominator(__in Z3_context c, __in Z3_rcf_num a, __out Z3_rcf_num * n, __out Z3_rcf_num * d); + /*@}*/ + /*@}*/ + #ifdef __cplusplus }; #endif // __cplusplus From da71d5ee0157b2c5d521e13b90139d4c3e7c68d8 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 24 Oct 2014 11:53:03 -0700 Subject: [PATCH 508/509] unlimit stack on linux/mac --- src/interp/iz3interp.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 0d394090f..7a0090981 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -513,3 +513,26 @@ void interpolation_options_struct::apply(iz3base &b){ b.set_option((*it).first,(*it).second); } +// On linux and mac, unlimit stack space so we get recursion + +#if defined(_WINDOWS) || defined(_CYGWIN) + +#else + +#include +#include + +class iz3stack_unlimiter { +public: + iz3stack_unlimiter() { + struct rlimit rl = {RLIM_INFINITY, RLIM_INFINITY}; + setrlimit(RLIMIT_STACK, &rl); + // nothing to be done if above fails + } +}; + +// initializing this will unlimit stack + +iz3stack_unlimiter the_iz3stack_unlimiter; + +#endif From 0713535fa6a3a5fb510d5e05f7bf7bff44b3b6d9 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Oct 2014 21:00:23 +0100 Subject: [PATCH 509/509] Documentation website fixes. Signed-off-by: Christoph M. Wintersteiger --- .gitignore | 2 ++ doc/website.dox | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7757275f3..65a69bf51 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,5 @@ src/api/java/Native.cpp src/api/java/Native.java src/api/java/enumerations/*.java *.bak +doc/api +doc/code diff --git a/doc/website.dox b/doc/website.dox index 6936b4f77..d048dab0c 100644 --- a/doc/website.dox +++ b/doc/website.dox @@ -15,5 +15,5 @@ - .NET API - Java API - Python API (also available in pydoc format). - - Try Z3 online at RiSE4Fun using Python or SMT 2.0. + - Try Z3 online at RiSE4Fun. */