From 68fb01c20684f60b1fbb065190cdd7b3242ab4f1 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 3 Mar 2013 20:45:58 -0800 Subject: [PATCH 001/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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/179] 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 418f148ecf4e8c405ddd844f665f077018af1c9e Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 4 Jun 2013 18:22:54 -0700 Subject: [PATCH 031/179] 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 032/179] 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 033/179] 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 034/179] 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 40fe1f6e999b5fb902c035c005b4af73e727dcb0 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 7 Jun 2013 11:50:01 -0700 Subject: [PATCH 035/179] 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 036/179] 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 037/179] 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 30a4627a1eab8e49418c39a030472e8a477805e3 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 10 Jun 2013 14:46:15 -0700 Subject: [PATCH 038/179] 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 886128c98996e5217e8d34ea13c8f48552c7dacd Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 14 Jun 2013 16:33:51 -0700 Subject: [PATCH 039/179] 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 040/179] 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 041/179] 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 042/179] 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 efb6b2453e55203900b21731ada58d7a42c8cfe6 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 24 Jun 2013 15:34:42 -0700 Subject: [PATCH 043/179] 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 7cc6ff0a4c8248204f4209f87f62acdc2c5a8443 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 25 Jun 2013 12:25:41 -0700 Subject: [PATCH 044/179] 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 4d939c07a34f440f87be91a8882fda13a31bfaab Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 27 Jun 2013 11:28:38 -0700 Subject: [PATCH 045/179] 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 046/179] 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 047/179] 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 048/179] 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 a7ed218636eca7f22478dcd4fbd8475e8151f681 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 7 Aug 2013 13:16:46 -0700 Subject: [PATCH 049/179] 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 050/179] 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 051/179] 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 052/179] 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 053/179] 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 054/179] 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 055/179] 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 056/179] 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 057/179] 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 058/179] 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 059/179] 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 060/179] 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 061/179] 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 062/179] 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 063/179] 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 064/179] 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 065/179] 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 066/179] 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 067/179] 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 068/179] 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 069/179] 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 070/179] 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 071/179] 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 072/179] 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 073/179] 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 074/179] 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 075/179] 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 076/179] 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 077/179] 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 078/179] 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 079/179] 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 080/179] 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 081/179] 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 082/179] 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 083/179] 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 084/179] 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 085/179] 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 086/179] 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 087/179] 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 088/179] 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 089/179] 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 090/179] 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 091/179] 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 092/179] 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 093/179] 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 094/179] 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 095/179] 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 096/179] 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 097/179] 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 098/179] 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 099/179] 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 100/179] 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 101/179] 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 102/179] 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 103/179] 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 104/179] 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 105/179] 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 106/179] 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 107/179] 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 108/179] 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 109/179] 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 110/179] 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 111/179] 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 112/179] 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 113/179] 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 114/179] 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 115/179] 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 116/179] 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 117/179] 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 118/179] 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 119/179] 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 120/179] 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 121/179] 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 122/179] 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 123/179] 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 124/179] 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 125/179] 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 126/179] 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 127/179] 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 128/179] 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 129/179] 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 130/179] 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 131/179] 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 132/179] 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 133/179] 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 134/179] 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 135/179] 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 136/179] 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 137/179] 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 138/179] 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 139/179] 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 140/179] 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 141/179] 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 142/179] 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 143/179] 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 144/179] 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 145/179] 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 146/179] 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 147/179] 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 148/179] 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 149/179] 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 150/179] 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 151/179] 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 152/179] 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 153/179] 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 154/179] 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 155/179] 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 156/179] 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 157/179] 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 158/179] 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 159/179] 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 160/179] 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 161/179] 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 162/179] 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 163/179] 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 164/179] 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 165/179] 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 166/179] 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 167/179] 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 168/179] 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 169/179] 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 170/179] 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 171/179] 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 172/179] 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 173/179] 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 174/179] 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 175/179] 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 176/179] 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 177/179] 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 178/179] 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 61385c8489b7fda11b518a67fe308ea3cfe28c3d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Nov 2013 09:54:37 -0800 Subject: [PATCH 179/179] 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"