diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..b86ed5df7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +src/api/dotnet/Properties/AssemblyInfo.cs text eol=crlf diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 7aaaf2f8d..643c9b26f 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -63,6 +63,8 @@ Version 4.3.2 - Fixed bug reported at http://z3.codeplex.com/workitem/23 (Thanks to Paul Jackson). +- Fixed bug reported at http://stackoverflow.com/questions/15226944/segmentation-fault-in-z3 (Thanks to Tianhai Liu). + Version 4.3.1 ============= diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index ab5d0132c..77f7702f2 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -906,6 +906,72 @@ void enum_sort_example() { std::cout << "2: " << result_goal.as_expr() << std::endl; } +void expr_vector_example() { + std::cout << "expr_vector example\n"; + context c; + const unsigned N = 10; + + expr_vector x(c); + + for (unsigned i = 0; i < N; i++) { + std::stringstream x_name; + x_name << "x_" << i; + x.push_back(c.int_const(x_name.str().c_str())); + } + + solver s(c); + for (unsigned i = 0; i < N; i++) { + s.add(x[i] >= 1); + } + + std::cout << s << "\n" << "solving...\n" << s.check() << "\n"; + model m = s.get_model(); + std::cout << "solution\n" << m; +} + +void exists_expr_vector_example() { + std::cout << "exists expr_vector example\n"; + context c; + const unsigned N = 10; + + expr_vector xs(c); + expr x(c); + expr b(c); + b = c.bool_val(true); + + for (unsigned i = 0; i < N; i++) { + std::stringstream x_name; + x_name << "x_" << i; + x = c.int_const(x_name.str().c_str()); + xs.push_back(x); + b = b && x >= 0; + } + + expr ex(c); + ex = exists(xs, b); + std::cout << ex << std::endl; +} + +void substitute_example() { + std::cout << "substitute example\n"; + context c; + expr x(c); + x = c.int_const("x"); + expr f(c); + f = (x == 2) || (x == 1); + std::cout << f << std::endl; + + expr two(c), three(c); + two = c.int_val(2); + three = c.int_val(3); + Z3_ast from[] = { two }; + Z3_ast to[] = { three }; + expr new_f(c); + new_f = to_expr(c, Z3_substitute(c, f, 1, from, to)); + + std::cout << new_f << std::endl; +} + int main() { try { demorgan(); std::cout << "\n"; @@ -937,10 +1003,13 @@ int main() { tactic_example9(); std::cout << "\n"; tactic_qe(); std::cout << "\n"; tst_visit(); std::cout << "\n"; - incremental_example1(); std::cout << "\n"; - incremental_example2(); std::cout << "\n"; - incremental_example3(); std::cout << "\n"; + incremental_example1(); std::cout << "\n"; + incremental_example2(); std::cout << "\n"; + incremental_example3(); std::cout << "\n"; enum_sort_example(); std::cout << "\n"; + expr_vector_example(); std::cout << "\n"; + exists_expr_vector_example(); std::cout << "\n"; + substitute_example(); std::cout << "\n"; std::cout << "done\n"; } catch (exception & ex) { diff --git a/examples/dotnet/Program.cs b/examples/dotnet/Program.cs index 2085de296..4361cab96 100644 --- a/examples/dotnet/Program.cs +++ b/examples/dotnet/Program.cs @@ -474,7 +474,7 @@ namespace test_mapi cells_c[i] = new BoolExpr[9]; for (uint j = 0; j < 9; j++) cells_c[i][j] = ctx.MkAnd(ctx.MkLe(ctx.MkInt(1), X[i][j]), - ctx.MkLe(X[i][j], ctx.MkInt(9))); + ctx.MkLe(X[i][j], ctx.MkInt(9))); } // each row contains a digit at most once @@ -485,7 +485,13 @@ namespace test_mapi // each column contains a digit at most once BoolExpr[] cols_c = new BoolExpr[9]; for (uint j = 0; j < 9; j++) - cols_c[j] = ctx.MkDistinct(X[j]); + { + IntExpr[] column = new IntExpr[9]; + for (uint i = 0; i < 9; i++) + column[i] = X[i][j]; + + cols_c[j] = ctx.MkDistinct(column); + } // each 3x3 square contains a digit at most once BoolExpr[][] sq_c = new BoolExpr[3][]; @@ -2087,7 +2093,7 @@ namespace test_mapi { QuantifierExample3(ctx); QuantifierExample4(ctx); - } + } Log.Close(); if (Log.isOpen()) 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..d0e174914 --- /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 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); + 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..c97225f4c --- /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 const *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 5682cc5d7..6e352f06f 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') @@ -55,11 +56,19 @@ 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('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', 'duality']) - 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'], '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') + add_lib('tab', ['muz', 'transforms'], 'muz/tab') + add_lib('bmc', ['muz', 'transforms'], 'muz/bmc') + add_lib('duality_intf', ['muz', 'transforms', 'duality'], 'muz/duality') + add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf'], 'muz/fp') + add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') - add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', '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') # add_dll('foci2', ['util'], 'interp/foci2stub', # dll_name='foci2', @@ -82,6 +91,7 @@ def init_project_def(): # Examples add_cpp_example('cpp_example', 'c++') add_cpp_example('iz3', 'interp') + 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 79c1877ff..9295c949f 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -292,7 +292,7 @@ def check_java(): print("Finding jni.h...") if JNI_HOME != None: - if not os.path.exists(path.join(JNI_HOME, 'jni.h')): + if not os.path.exists(os.path.join(JNI_HOME, 'jni.h')): raise MKException("Failed to detect jni.h '%s'; the environment variable JNI_HOME is probably set to the wrong path." % os.path.join(JNI_HOME)) else: # Search for jni.h in the library directories... @@ -952,6 +952,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': @@ -1117,20 +1120,19 @@ class DotNetDLLComponent(Component): cs_fp_files.append(os.path.join(self.to_src_dir, self.assembly_info_dir, cs_file)) cs_files.append(os.path.join(self.assembly_info_dir, cs_file)) dllfile = '%s.dll' % self.dll_name - out.write('%s:' % dllfile) + out.write('%s: %s$(SO_EXT)' % (dllfile, get_component(Z3_DLL_COMPONENT).dll_name)) for cs_file in cs_fp_files: out.write(' ') out.write(cs_file) out.write('\n') - out.write(' cd %s && csc /noconfig /unsafe+ /nowarn:1701,1702 /nostdlib+ /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:mscorlib.dll /reference:System.Core.dll /reference:System.dll /reference:System.Numerics.dll /debug+ /debug:full /filealign:512 /optimize- /out:%s.dll /target:library' % (self.to_src_dir, self.dll_name)) + out.write(' csc /noconfig /unsafe+ /nowarn:1701,1702 /nostdlib+ /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:mscorlib.dll /reference:System.Core.dll /reference:System.dll /reference:System.Numerics.dll /debug+ /debug:full /filealign:512 /optimize- /linkresource:%s.dll /out:%s.dll /target:library' % (get_component(Z3_DLL_COMPONENT).dll_name, self.dll_name)) + if VS_X64: + out.write(' /platform:x64') + else: + out.write(' /platform:x86') for cs_file in cs_files: - out.write(' ') - out.write(cs_file) + out.write(' %s' % os.path.join(self.to_src_dir, cs_file)) out.write('\n') - # HACK - win_to_src_dir = self.to_src_dir.replace('/', '\\') - out.write(' move %s\n' % os.path.join(win_to_src_dir, dllfile)) - out.write(' move %s.pdb\n' % os.path.join(win_to_src_dir, self.dll_name)) out.write('%s: %s\n\n' % (self.name, dllfile)) return @@ -1256,7 +1258,7 @@ class CppExampleComponent(ExampleComponent): out.write(' ') out.write(os.path.join(self.to_ex_dir, cppfile)) out.write('\n') - out.write('\t%s $(EXAMP_DEBUG_FLAG) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), exefile)) + out.write('\t%s $(OS_DEFINES) $(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) @@ -1428,7 +1430,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) @@ -1476,6 +1479,7 @@ def mk_config(): print('Java Compiler: %s' % JAVAC) else: global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG + OS_DEFINES = "" ARITH = "internal" check_ar() CXX = find_cxx_compiler() @@ -1526,18 +1530,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: @@ -1573,6 +1580,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/api_algebraic.cpp b/src/api/api_algebraic.cpp index 7716cbb59..d03a6aff4 100644 --- a/src/api/api_algebraic.cpp +++ b/src/api/api_algebraic.cpp @@ -373,7 +373,7 @@ extern "C" { scoped_anum_vector roots(_am); { cancel_eh eh(_am); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); vector_var2anum v2a(as); _am.isolate_roots(_p, v2a, roots); @@ -408,7 +408,7 @@ extern "C" { } { cancel_eh eh(_am); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); vector_var2anum v2a(as); int r = _am.eval_sign_at(_p, v2a); diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 5e8b2b861..4c45cfa6b 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -46,7 +46,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_int_symbol(c, i); RESET_ERROR_CODE(); - if (i < 0 || (unsigned)i >= (SIZE_MAX >> PTR_ALIGNMENT)) { + if (i < 0 || (size_t)i >= (SIZE_MAX >> PTR_ALIGNMENT)) { SET_ERROR_CODE(Z3_IOB); return 0; } @@ -682,7 +682,7 @@ extern "C" { th_rewriter m_rw(m, p); expr_ref result(m); cancel_eh eh(m_rw); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); @@ -1072,6 +1072,16 @@ extern "C" { case OP_BV2INT: return Z3_OP_BV2INT; case OP_CARRY: return Z3_OP_CARRY; case OP_XOR3: return Z3_OP_XOR3; + case OP_BSMUL_NO_OVFL: + case OP_BUMUL_NO_OVFL: + case OP_BSMUL_NO_UDFL: + case OP_BSDIV_I: + case OP_BUDIV_I: + case OP_BSREM_I: + case OP_BUREM_I: + case OP_BSMOD_I: + + return Z3_OP_UNINTERPRETED; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; diff --git a/src/api/api_bv.cpp b/src/api/api_bv.cpp index 8126c8e2a..07d4fda18 100644 --- a/src/api/api_bv.cpp +++ b/src/api/api_bv.cpp @@ -121,10 +121,20 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ unsigned sz = Z3_get_bv_sort_size(c, s); rational max_bound = power(rational(2), sz); Z3_ast bound = Z3_mk_numeral(c, max_bound.to_string().c_str(), int_s); - Z3_ast pred = Z3_mk_bvslt(c, n, Z3_mk_int(c, 0, s)); + Z3_inc_ref(c, bound); + Z3_ast zero = Z3_mk_int(c, 0, s); + Z3_inc_ref(c, zero); + Z3_ast pred = Z3_mk_bvslt(c, n, zero); + Z3_inc_ref(c, pred); // if n <_sigend 0 then r - s^sz else r Z3_ast args[2] = { r, bound }; - Z3_ast res = Z3_mk_ite(c, pred, Z3_mk_sub(c, 2, args), r); + Z3_ast sub = Z3_mk_sub(c, 2, args); + Z3_inc_ref(c, sub); + Z3_ast res = Z3_mk_ite(c, pred, sub, r); + Z3_dec_ref(c, bound); + Z3_dec_ref(c, pred); + Z3_dec_ref(c, sub); + Z3_dec_ref(c, zero); RETURN_Z3(res); } else { @@ -156,7 +166,14 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } - return Z3_mk_bvshl(c, Z3_mk_int64(c, 1, s), Z3_mk_int64(c, sz - 1, s)); + Z3_ast x = Z3_mk_int64(c, 1, s); + Z3_inc_ref(c, x); + Z3_ast y = Z3_mk_int64(c, sz - 1, s); + Z3_inc_ref(c, y); + Z3_ast result = Z3_mk_bvshl(c, x, y); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + return result; Z3_CATCH_RETURN(0); } @@ -177,17 +194,40 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ RESET_ERROR_CODE(); if (is_signed) { Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); + Z3_inc_ref(c, zero); Z3_ast r = Z3_mk_bvadd(c, t1, t2); - Z3_ast args[2] = { Z3_mk_bvslt(c, zero, t1), Z3_mk_bvslt(c, zero, t2) }; + Z3_inc_ref(c, r); + Z3_ast l1 = Z3_mk_bvslt(c, zero, t1); + Z3_inc_ref(c, l1); + Z3_ast l2 = Z3_mk_bvslt(c, zero, t2); + Z3_inc_ref(c, l2); + Z3_ast args[2] = { l1, l2 }; Z3_ast args_pos = Z3_mk_and(c, 2, args); - return Z3_mk_implies(c, args_pos, Z3_mk_bvslt(c, zero, r)); + Z3_inc_ref(c, args_pos); + Z3_ast result = Z3_mk_implies(c, args_pos, Z3_mk_bvslt(c, zero, r)); + Z3_dec_ref(c, r); + Z3_dec_ref(c, l1); + Z3_dec_ref(c, l2); + Z3_dec_ref(c, args_pos); + Z3_dec_ref(c, zero); + return result; } else { unsigned sz = Z3_get_bv_sort_size(c, Z3_get_sort(c, t1)); t1 = Z3_mk_zero_ext(c, 1, t1); + Z3_inc_ref(c, t1); t2 = Z3_mk_zero_ext(c, 1, t2); + Z3_inc_ref(c, t2); Z3_ast r = Z3_mk_bvadd(c, t1, t2); - return Z3_mk_eq(c, Z3_mk_extract(c, sz, sz, r), Z3_mk_int(c, 0, Z3_mk_bv_sort(c, 1))); + Z3_inc_ref(c, r); + Z3_ast ex = Z3_mk_extract(c, sz, sz, r); + Z3_inc_ref(c, ex); + Z3_ast result = Z3_mk_eq(c, ex, Z3_mk_int(c, 0, Z3_mk_bv_sort(c, 1))); + Z3_dec_ref(c, t1); + Z3_dec_ref(c, t2); + Z3_dec_ref(c, ex); + Z3_dec_ref(c, r); + return result; } Z3_CATCH_RETURN(0); } @@ -197,10 +237,26 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_TRY; RESET_ERROR_CODE(); Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); + Z3_inc_ref(c, zero); Z3_ast r = Z3_mk_bvadd(c, t1, t2); - Z3_ast args[2] = { Z3_mk_bvslt(c, t1, zero), Z3_mk_bvslt(c, t2, zero) }; + Z3_inc_ref(c, r); + Z3_ast l1 = Z3_mk_bvslt(c, t1, zero); + Z3_inc_ref(c, l1); + Z3_ast l2 = Z3_mk_bvslt(c, t2, zero); + Z3_inc_ref(c, l2); + Z3_ast args[2] = { l1, l2 }; Z3_ast args_neg = Z3_mk_and(c, 2, args); - return Z3_mk_implies(c, args_neg, Z3_mk_bvslt(c, r, zero)); + Z3_inc_ref(c, args_neg); + Z3_ast lt = Z3_mk_bvslt(c, r, zero); + Z3_inc_ref(c, lt); + Z3_ast result = Z3_mk_implies(c, args_neg, lt); + Z3_dec_ref(c, lt); + Z3_dec_ref(c, l1); + Z3_dec_ref(c, l2); + Z3_dec_ref(c, r); + Z3_dec_ref(c, args_neg); + Z3_dec_ref(c, zero); + return result; Z3_CATCH_RETURN(0); } @@ -208,12 +264,28 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_ast Z3_API Z3_mk_bvsub_no_overflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); - Z3_sort s = Z3_get_sort(c, t2); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); + Z3_inc_ref(c, minus_t2); + Z3_sort s = Z3_get_sort(c, t2); Z3_ast min = Z3_mk_bvsmin(c, s); - return Z3_mk_ite(c, Z3_mk_eq(c, t2, min), - Z3_mk_bvslt(c, t1, Z3_mk_int(c, 0, s)), - Z3_mk_bvadd_no_overflow(c, t1, minus_t2, true)); + Z3_inc_ref(c, min); + Z3_ast x = Z3_mk_eq(c, t2, min); + Z3_inc_ref(c, x); + Z3_ast zero = Z3_mk_int(c, 0, s); + Z3_inc_ref(c, zero); + Z3_ast y = Z3_mk_bvslt(c, t1, zero); + Z3_inc_ref(c, y); + Z3_ast z = Z3_mk_bvadd_no_overflow(c, t1, minus_t2, true); + Z3_inc_ref(c, z); + Z3_ast result = Z3_mk_ite(c, x, y, z); + mk_c(c)->save_ast_trail(to_app(result)); + Z3_dec_ref(c, minus_t2); + Z3_dec_ref(c, min); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + Z3_dec_ref(c, z); + Z3_dec_ref(c, zero); + return result; Z3_CATCH_RETURN(0); } @@ -222,10 +294,19 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ RESET_ERROR_CODE(); if (is_signed) { Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); - if (Z3_get_error_code(c) != Z3_OK) return 0; + Z3_inc_ref(c, zero); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); - if (Z3_get_error_code(c) != Z3_OK) return 0; - return Z3_mk_implies(c, Z3_mk_bvslt(c, zero, t2), Z3_mk_bvadd_no_underflow(c, t1, minus_t2)); + Z3_inc_ref(c, minus_t2); + Z3_ast x = Z3_mk_bvslt(c, zero, t2); + Z3_inc_ref(c, x); + Z3_ast y = Z3_mk_bvadd_no_underflow(c, t1, minus_t2); + Z3_inc_ref(c, y); + Z3_ast result = Z3_mk_implies(c, x, y); + Z3_dec_ref(c, zero); + Z3_dec_ref(c, minus_t2); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + return result; } else { return Z3_mk_bvule(c, t2, t1); @@ -267,12 +348,24 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_TRY; RESET_ERROR_CODE(); Z3_sort s = Z3_get_sort(c, t1); - if (Z3_get_error_code(c) != Z3_OK) return 0; Z3_ast min = Z3_mk_bvmsb(c, s); - if (Z3_get_error_code(c) != Z3_OK) return 0; - Z3_ast args[2] = { Z3_mk_eq(c, t1, min), - Z3_mk_eq(c, t2, Z3_mk_int(c, -1, s)) }; - return Z3_mk_not(c, Z3_mk_and(c, 2, args)); + Z3_inc_ref(c, min); + Z3_ast x = Z3_mk_eq(c, t1, min); + Z3_inc_ref(c, x); + Z3_ast y = Z3_mk_int(c, -1, s); + Z3_inc_ref(c, y); + Z3_ast z = Z3_mk_eq(c, t2, y); + Z3_inc_ref(c, z); + Z3_ast args[2] = { x, z }; + Z3_ast u = Z3_mk_and(c, 2, args); + Z3_inc_ref(c, u); + Z3_ast result = Z3_mk_not(c, u); + Z3_dec_ref(c, min); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + Z3_dec_ref(c, z); + Z3_dec_ref(c, u); + return result; Z3_CATCH_RETURN(0); } 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); } } diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index 28fe3ed33..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" @@ -29,104 +28,126 @@ Revision History: #include"dl_cmds.h" #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 { - - fixedpoint_context::fixedpoint_context(ast_manager& m, smt_params& p) : - m_state(0), - m_reduce_app(0), - m_reduce_assign(0), - m_context(m, p), - m_trail(m) {} - - - void fixedpoint_context::set_state(void* state) { - SASSERT(!m_state); - m_state = state; - symbol name("datalog_relation"); - ast_manager& m = m_context.get_manager(); - if (!m.has_plugin(name)) { - m.register_plugin(name, alloc(datalog::dl_decl_plugin)); - } - datalog::relation_manager& r = m_context.get_rel_context().get_rmanager(); - r.register_plugin(alloc(datalog::external_relation_plugin, *this, r)); - } - - void fixedpoint_context::reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { - expr* r = 0; - if (m_reduce_app) { - m_reduce_app(m_state, f, num_args, args, &r); - result = r; - m_trail.push_back(f); - for (unsigned i = 0; i < num_args; ++i) { - m_trail.push_back(args[i]); - } - m_trail.push_back(r); - } - // allow fallthrough. - if (r == 0) { + + class fixedpoint_context : public datalog::external_relation_context { + void * m_state; + reduce_app_callback_fptr m_reduce_app; + reduce_assign_callback_fptr m_reduce_assign; + datalog::register_engine m_register_engine; + datalog::context m_context; + ast_ref_vector m_trail; + public: + fixedpoint_context(ast_manager& m, smt_params& p): + m_state(0), + m_reduce_app(0), + m_reduce_assign(0), + m_context(m, m_register_engine, p), + m_trail(m) {} + + virtual ~fixedpoint_context() {} + family_id get_family_id() const { return const_cast(m_context).get_decl_util().get_family_id(); } + void set_state(void* state) { + SASSERT(!m_state); + m_state = state; + symbol name("datalog_relation"); ast_manager& m = m_context.get_manager(); - result = m.mk_app(f, num_args, args); - } - } - - // overwrite terms passed in outs vector with values computed by function. - void fixedpoint_context::reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { - if (m_reduce_assign) { - m_trail.push_back(f); - for (unsigned i = 0; i < num_args; ++i) { - m_trail.push_back(args[i]); + if (!m.has_plugin(name)) { + m.register_plugin(name, alloc(datalog::dl_decl_plugin)); + } + datalog::rel_context_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)); } - m_reduce_assign(m_state, f, num_args, args, num_out, outs); } - } - - - void fixedpoint_context::add_rule(expr* rule, symbol const& name) { - m_context.add_rule(rule, name); - } - - void fixedpoint_context::update_rule(expr* rule, symbol const& name) { - m_context.update_rule(rule, name); - } - - void fixedpoint_context::add_table_fact(func_decl* r, unsigned num_args, unsigned args[]) { - m_context.add_table_fact(r, num_args, args); - } - - unsigned fixedpoint_context::get_num_levels(func_decl* pred) { - return m_context.get_num_levels(pred); - } - - expr_ref fixedpoint_context::get_cover_delta(int level, func_decl* pred) { - return m_context.get_cover_delta(level, pred); - } - - void fixedpoint_context::add_cover(int level, func_decl* pred, expr* predicate) { - m_context.add_cover(level, pred, predicate); - } - - std::string fixedpoint_context::get_last_status() { - datalog::execution_result status = m_context.get_status(); - switch(status) { - case datalog::INPUT_ERROR: - return "input error"; - case datalog::OK: - return "ok"; - case datalog::TIMEOUT: - return "timeout"; - default: - UNREACHABLE(); - return "unknown"; + void set_reduce_app(reduce_app_callback_fptr f) { + m_reduce_app = f; } - } - - std::string fixedpoint_context::to_string(unsigned num_queries, expr*const* queries) { - std::stringstream str; - m_context.display_smt2(num_queries, queries, str); - return str.str(); - } - + void set_reduce_assign(reduce_assign_callback_fptr f) { + m_reduce_assign = f; + } + virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { + expr* r = 0; + if (m_reduce_app) { + m_reduce_app(m_state, f, num_args, args, &r); + result = r; + m_trail.push_back(f); + for (unsigned i = 0; i < num_args; ++i) { + m_trail.push_back(args[i]); + } + m_trail.push_back(r); + } + // allow fallthrough. + if (r == 0) { + ast_manager& m = m_context.get_manager(); + result = m.mk_app(f, num_args, args); + } + } + virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { + if (m_reduce_assign) { + m_trail.push_back(f); + for (unsigned i = 0; i < num_args; ++i) { + m_trail.push_back(args[i]); + } + m_reduce_assign(m_state, f, num_args, args, num_out, outs); + } + } + datalog::context& ctx() { return m_context; } + void add_rule(expr* rule, symbol const& name) { + m_context.add_rule(rule, name); + } + void update_rule(expr* rule, symbol const& name) { + m_context.update_rule(rule, name); + } + void add_table_fact(func_decl* r, unsigned num_args, unsigned args[]) { + m_context.add_table_fact(r, num_args, args); + } + std::string get_last_status() { + datalog::execution_result status = m_context.get_status(); + switch(status) { + case datalog::INPUT_ERROR: + return "input error"; + case datalog::OK: + return "ok"; + case datalog::TIMEOUT: + return "timeout"; + case datalog::APPROX: + return "approximated"; + default: + UNREACHABLE(); + return "unknown"; + } + } + std::string to_string(unsigned num_queries, expr*const* queries) { + std::stringstream str; + m_context.display_smt2(num_queries, queries, str); + return str.str(); + } + void cancel() { + m_context.cancel(); + } + void reset_cancel() { + m_context.reset_cancel(); + } + unsigned get_num_levels(func_decl* pred) { + return m_context.get_num_levels(pred); + } + expr_ref get_cover_delta(int level, func_decl* pred) { + return m_context.get_cover_delta(level, pred); + } + void add_cover(int level, func_decl* pred, expr* predicate) { + m_context.add_cover(level, pred, predicate); + } + void collect_param_descrs(param_descrs & p) { m_context.collect_params(p); } + void updt_params(params_ref const& p) { m_context.updt_params(p); } + }; }; extern "C" { @@ -266,7 +287,7 @@ extern "C" { lbool r = l_undef; cancel_eh eh(*to_fixedpoint_ref(d)); unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); try { @@ -291,7 +312,7 @@ extern "C" { lbool r = l_undef; unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); cancel_eh eh(*to_fixedpoint_ref(d)); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); try { @@ -358,7 +379,7 @@ extern "C" { v->m_ast_vector.push_back(coll.m_queries[i].get()); } for (unsigned i = 0; i < coll.m_rels.size(); ++i) { - to_fixedpoint_ref(d)->ctx().register_predicate(coll.m_rels[i].get()); + to_fixedpoint_ref(d)->ctx().register_predicate(coll.m_rels[i].get(), true); } for (unsigned i = 0; i < coll.m_rules.size(); ++i) { to_fixedpoint_ref(d)->add_rule(coll.m_rules[i].get(), coll.m_names[i]); @@ -415,7 +436,7 @@ extern "C" { void Z3_API Z3_fixedpoint_register_relation(Z3_context c,Z3_fixedpoint d, Z3_func_decl f) { Z3_TRY; LOG_Z3_fixedpoint_register_relation(c, d, f); - to_fixedpoint_ref(d)->ctx().register_predicate(to_func_decl(f)); + to_fixedpoint_ref(d)->ctx().register_predicate(to_func_decl(f), true); Z3_CATCH; } diff --git a/src/api/api_datalog.h b/src/api/api_datalog.h index 51dbb72ec..f5bfada33 100644 --- a/src/api/api_datalog.h +++ b/src/api/api_datalog.h @@ -22,48 +22,14 @@ Revision History: #include"z3.h" #include"ast.h" #include"smt_params.h" -#include"dl_external_relation.h" -#include"dl_decl_plugin.h" #include"smt_kernel.h" #include"api_util.h" -#include"dl_context.h" typedef void (*reduce_app_callback_fptr)(void*, func_decl*, unsigned, expr*const*, expr**); typedef void (*reduce_assign_callback_fptr)(void*, func_decl*, unsigned, expr*const*, unsigned, expr*const*); namespace api { - - class fixedpoint_context : public datalog::external_relation_context { - void * m_state; - reduce_app_callback_fptr m_reduce_app; - reduce_assign_callback_fptr m_reduce_assign; - datalog::context m_context; - ast_ref_vector m_trail; - public: - fixedpoint_context(ast_manager& m, smt_params& p); - virtual ~fixedpoint_context() {} - family_id get_family_id() const { return const_cast(m_context).get_decl_util().get_family_id(); } - void set_state(void* state); - void set_reduce_app(reduce_app_callback_fptr f) { m_reduce_app = f; } - void set_reduce_assign(reduce_assign_callback_fptr f) { m_reduce_assign = f; } - virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result); - virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs); - datalog::context& ctx() { return m_context; } - void add_rule(expr* rule, symbol const& name); - void update_rule(expr* rule, symbol const& name); - void add_table_fact(func_decl* r, unsigned num_args, unsigned args[]); - std::string get_last_status(); - std::string to_string(unsigned num_queries, expr*const* queries); - void cancel() { m_context.cancel(); } - void reset_cancel() { m_context.reset_cancel(); } - - unsigned get_num_levels(func_decl* pred); - expr_ref get_cover_delta(int level, func_decl* pred); - void add_cover(int level, func_decl* pred, expr* predicate); - void collect_param_descrs(param_descrs & p) { m_context.collect_params(p); } - void updt_params(params_ref const& p) { m_context.updt_params(p); } - - }; + class fixedpoint_context; }; diff --git a/src/api/api_polynomial.cpp b/src/api/api_polynomial.cpp index 3148f972b..25d4ca292 100644 --- a/src/api/api_polynomial.cpp +++ b/src/api/api_polynomial.cpp @@ -67,7 +67,7 @@ extern "C" { expr_ref _r(mk_c(c)->m()); { cancel_eh eh(pm); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); pm.psc_chain(_p, _q, v_x, rs); } 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/api/api_solver.cpp b/src/api/api_solver.cpp index 9ace149af..ac30a0c21 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -243,7 +243,7 @@ extern "C" { unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", false); cancel_eh eh(*to_solver_ref(s)); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); lbool result; { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); diff --git a/src/api/api_solver_old.cpp b/src/api/api_solver_old.cpp index c89f89873..e0533fbd9 100644 --- a/src/api/api_solver_old.cpp +++ b/src/api/api_solver_old.cpp @@ -73,7 +73,7 @@ extern "C" { RESET_ERROR_CODE(); CHECK_SEARCHING(c); cancel_eh eh(mk_c(c)->get_smt_kernel()); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); flet _model(mk_c(c)->fparams().m_model, true); lbool result; try { @@ -123,7 +123,7 @@ extern "C" { expr * const* _assumptions = to_exprs(assumptions); flet _model(mk_c(c)->fparams().m_model, true); cancel_eh eh(mk_c(c)->get_smt_kernel()); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); lbool result; result = mk_c(c)->get_smt_kernel().check(num_assumptions, _assumptions); if (result != l_false && m) { diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index 5bce218e6..911360047 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -410,7 +410,7 @@ extern "C" { to_tactic_ref(t)->updt_params(p); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); diff --git a/src/api/api_util.h b/src/api/api_util.h index 58abf97bf..b49ef8315 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -29,7 +29,6 @@ Revision History: #define Z3_CATCH_RETURN_NO_HANDLE(VAL) } catch (z3_exception &) { return VAL; } #define CHECK_REF_COUNT(a) (reinterpret_cast(a)->get_ref_count() > 0) -#define VALIDATE(a) SASSERT(!a || CHECK_REF_COUNT(a)) namespace api { // Generic wrapper for ref-count objects exposed by the API @@ -44,30 +43,30 @@ namespace api { }; }; -inline ast * to_ast(Z3_ast a) { VALIDATE(a); return reinterpret_cast(a); } +inline ast * to_ast(Z3_ast a) { return reinterpret_cast(a); } inline Z3_ast of_ast(ast* a) { return reinterpret_cast(a); } -inline expr * to_expr(Z3_ast a) { VALIDATE(a); return reinterpret_cast(a); } +inline expr * to_expr(Z3_ast a) { return reinterpret_cast(a); } inline Z3_ast of_expr(expr* e) { return reinterpret_cast(e); } inline expr * const * to_exprs(Z3_ast const* a) { return reinterpret_cast(a); } inline Z3_ast * const * of_exprs(expr* const* e) { return reinterpret_cast(e); } -inline app * to_app(Z3_app a) { VALIDATE(a); return reinterpret_cast(a); } -inline app * to_app(Z3_ast a) { VALIDATE(a); return reinterpret_cast(a); } +inline app * to_app(Z3_app a) { return reinterpret_cast(a); } +inline app * to_app(Z3_ast a) { return reinterpret_cast(a); } inline Z3_app of_app(app* a) { return reinterpret_cast(a); } -inline app * const* to_apps(Z3_ast const* a) { VALIDATE(a); return reinterpret_cast(a); } +inline app * const* to_apps(Z3_ast const* a) { return reinterpret_cast(a); } inline ast * const * to_asts(Z3_ast const* a) { return reinterpret_cast(a); } -inline sort * to_sort(Z3_sort a) { VALIDATE(a); return reinterpret_cast(a); } +inline sort * to_sort(Z3_sort a) { return reinterpret_cast(a); } inline Z3_sort of_sort(sort* s) { return reinterpret_cast(s); } inline sort * const * to_sorts(Z3_sort const* a) { return reinterpret_cast(a); } inline Z3_sort const * of_sorts(sort* const* s) { return reinterpret_cast(s); } -inline func_decl * to_func_decl(Z3_func_decl a) { VALIDATE(a); return reinterpret_cast(a); } +inline func_decl * to_func_decl(Z3_func_decl a) { return reinterpret_cast(a); } inline Z3_func_decl of_func_decl(func_decl* f) { return reinterpret_cast(f); } inline func_decl * const * to_func_decls(Z3_func_decl const* f) { return reinterpret_cast(f); } @@ -75,7 +74,7 @@ inline func_decl * const * to_func_decls(Z3_func_decl const* f) { return reinter inline symbol to_symbol(Z3_symbol s) { return symbol::mk_symbol_from_c_ptr(reinterpret_cast(s)); } inline Z3_symbol of_symbol(symbol s) { return reinterpret_cast(const_cast(s.c_ptr())); } -inline Z3_pattern of_pattern(ast* a) { VALIDATE(a); return reinterpret_cast(a); } +inline Z3_pattern of_pattern(ast* a) { return reinterpret_cast(a); } inline app* to_pattern(Z3_pattern p) { return reinterpret_cast(p); } inline Z3_lbool of_lbool(lbool b) { return static_cast(b); } diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 43975fd04..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); @@ -249,6 +251,8 @@ namespace z3 { array & operator=(array const & s); public: array(unsigned sz):m_size(sz) { m_array = new T[sz]; } + template + array(ast_vector_tpl const & v); ~array() { delete[] m_array; } unsigned size() const { return m_size; } T & operator[](int i) { assert(0 <= i); assert(static_cast(i) < m_size); return m_array[i]; } @@ -427,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; @@ -872,7 +877,18 @@ namespace z3 { \brief Return a simplified version of this expression. The parameter \c p is a set of parameters for the Z3 simplifier. */ expr simplify(params const & p) const { Z3_ast r = Z3_simplify_ex(ctx(), m_ast, p); check_error(); return expr(ctx(), r); } - }; + + /** + \brief Apply substitution. Replace src expressions by dst. + */ + expr substitute(expr_vector const& src, expr_vector const& dst); + + /** + \brief Apply substitution. Replace bound variables by expressions. + */ + expr substitute(expr_vector const& dst); + + }; /** \brief Wraps a Z3_ast as an expr object. It also checks for errors. @@ -928,49 +944,6 @@ namespace z3 { inline expr udiv(expr const & a, int b) { return udiv(a, a.ctx().num_val(b, a.get_sort())); } inline expr udiv(int a, expr const & b) { return udiv(b.ctx().num_val(a, b.get_sort()), b); } - // Basic functions for creating quantified formulas. - // The C API should be used for creating quantifiers with patterns, weights, many variables, etc. - inline expr forall(expr const & x, expr const & b) { - check_context(x, b); - Z3_app vars[] = {(Z3_app) x}; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr forall(expr const & x1, expr const & x2, expr const & b) { - check_context(x1, b); check_context(x2, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x, expr const & b) { - check_context(x, b); - Z3_app vars[] = {(Z3_app) x}; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x1, expr const & x2, expr const & b) { - check_context(x1, b); check_context(x2, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - template class cast_ast; template<> class cast_ast { @@ -1032,6 +1005,67 @@ namespace z3 { friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } }; + template + template + array::array(ast_vector_tpl const & v) { + m_array = new T[v.size()]; + m_size = v.size(); + for (unsigned i = 0; i < m_size; i++) { + m_array[i] = v[i]; + } + } + + // Basic functions for creating quantified formulas. + // The C API should be used for creating quantifiers with patterns, weights, many variables, etc. + inline expr forall(expr const & x, expr const & b) { + check_context(x, b); + Z3_app vars[] = {(Z3_app) x}; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr const & x1, expr const & x2, expr const & b) { + check_context(x1, b); check_context(x2, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr_vector const & xs, expr const & b) { + array vars(xs); + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x, expr const & b) { + check_context(x, b); + Z3_app vars[] = {(Z3_app) x}; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x1, expr const & x2, expr const & b) { + check_context(x1, b); check_context(x2, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr_vector const & xs, expr const & b) { + array vars(xs); + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + class func_entry : public object { Z3_func_entry m_entry; void init(Z3_func_entry e) { @@ -1274,7 +1308,7 @@ namespace z3 { expr as_expr() const { unsigned n = size(); if (n == 0) - return ctx().bool_val(false); + return ctx().bool_val(true); else if (n == 1) return operator[](0); else { @@ -1485,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); @@ -1571,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(); @@ -1680,6 +1740,30 @@ namespace z3 { d.check_error(); return expr(d.ctx(), r); } + + inline expr expr::substitute(expr_vector const& src, expr_vector const& dst) { + assert(src.size() == dst.size()); + array _src(src.size()); + array _dst(dst.size()); + for (unsigned i = 0; i < src.size(); ++i) { + _src[i] = src[i]; + _dst[i] = dst[i]; + } + Z3_ast r = Z3_substitute(ctx(), m_ast, src.size(), _src.ptr(), _dst.ptr()); + check_error(); + return expr(ctx(), r); + } + + inline expr expr::substitute(expr_vector const& dst) { + array _dst(dst.size()); + for (unsigned i = 0; i < dst.size(); ++i) { + _dst[i] = dst[i]; + } + Z3_ast r = Z3_substitute_vars(ctx(), m_ast, dst.size(), _dst.ptr()); + check_error(); + return expr(ctx(), r); + } + }; diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 35fe6611d..ae8b233d1 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -44,6 +44,21 @@ namespace Microsoft.Z3 /// /// Constructor. /// + /// + /// The following parameters can be set: + /// - proof (Boolean) Enable proof generation + /// - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting + /// - trace (Boolean) Tracing support for VCC + /// - trace_file_name (String) Trace out file for VCC traces + /// - timeout (unsigned) default timeout (in milliseconds) used for solvers + /// - well_sorted_check type checker + /// - auto_config use heuristics to automatically select solver and configure it + /// - model model generation for solvers, this parameter can be overwritten when creating a solver + /// - model_validate validate models produced by solvers + /// - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver + /// Note that in previous versions of Z3, this constructor was also used to set global and module parameters. + /// For this purpose we should now use + /// public Context(Dictionary settings) : base() { @@ -288,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) { @@ -300,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/dotnet/Microsoft.Z3.csproj b/src/api/dotnet/Microsoft.Z3.csproj index 8f6e34517..9eb9eb660 100644 --- a/src/api/dotnet/Microsoft.Z3.csproj +++ b/src/api/dotnet/Microsoft.Z3.csproj @@ -399,4 +399,4 @@ --> - \ No newline at end of file + diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index 274f8dc4a..555a2466f 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -210,7 +210,7 @@ namespace Microsoft.Z3 public Status Check(params Expr[] assumptions) { Z3_lbool r; - if (assumptions == null) + if (assumptions == null || assumptions.Length == 0) r = (Z3_lbool)Native.Z3_solver_check(Context.nCtx, NativeObject); else r = (Z3_lbool)Native.Z3_solver_check_assumptions(Context.nCtx, NativeObject, (uint)assumptions.Length, AST.ArrayToNative(assumptions)); diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 5277dab38..6b6c63ac3 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -27,6 +27,21 @@ public class Context extends IDisposable /** * Constructor. + * + * The following parameters can be set: + * - proof (Boolean) Enable proof generation + * - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting + * - trace (Boolean) Tracing support for VCC + * - trace_file_name (String) Trace out file for VCC traces + * - timeout (unsigned) default timeout (in milliseconds) used for solvers + * - well_sorted_check type checker + * - auto_config use heuristics to automatically select solver and configure it + * - model model generation for solvers, this parameter can be overwritten when creating a solver + * - model_validate validate models produced by solvers + * - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver + * Note that in previous versions of Z3, this constructor was also used to set global and + * module parameters. For this purpose we should now use + * **/ public Context(Map settings) throws Z3Exception { diff --git a/src/api/java/Status.java b/src/api/java/Status.java index 117618426..e37631070 100644 --- a/src/api/java/Status.java +++ b/src/api/java/Status.java @@ -12,7 +12,7 @@ package com.microsoft.z3; public enum Status { // / Used to signify an unsatisfiable status. - UNSATISFIABLE(1), + UNSATISFIABLE(-1), // / Used to signify an unknown status. UNKNOWN(0), diff --git a/src/api/ml/README-test-win b/src/api/ml/README-test-win index d9be0174b..0e8b73ccd 100644 --- a/src/api/ml/README-test-win +++ b/src/api/ml/README-test-win @@ -1,23 +1,23 @@ -This directory contains scripts to build the test application using -OCaml. You also need CamlIDL to be able to generate the OCaml API. - -- To download OCaml: - http://caml.inria.fr/ocaml/ - -- To download CamlIDL: - http://forge.ocamlcore.org/projects/camlidl/ - -- One must build the OCaml library before compiling the example. - Go to directory ../ocaml - -- Use 'build-test.cmd' to build the test application using the OCaml compiler. - -Remark: The OCaml and C compiler tool chains must be configured in your environment. -Running from the Visual Studio Command Prompt configures the Microsoft C compiler. - -- The script 'exec.cmd' adds the bin directory to the path. So, - test_mlapi.exe can find z3.dll. - - - - +This directory contains scripts to build the test application using +OCaml. You also need CamlIDL to be able to generate the OCaml API. + +- To download OCaml: + http://caml.inria.fr/ocaml/ + +- To download CamlIDL: + http://forge.ocamlcore.org/projects/camlidl/ + +- One must build the OCaml library before compiling the example. + Go to directory ../ocaml + +- Use 'build-test.cmd' to build the test application using the OCaml compiler. + +Remark: The OCaml and C compiler tool chains must be configured in your environment. +Running from the Visual Studio Command Prompt configures the Microsoft C compiler. + +- The script 'exec.cmd' adds the bin directory to the path. So, + test_mlapi.exe can find z3.dll. + + + + diff --git a/src/api/ml/README-win b/src/api/ml/README-win index 91d2faa4f..82ddc2652 100644 --- a/src/api/ml/README-win +++ b/src/api/ml/README-win @@ -1,23 +1,23 @@ -The OCaml API for Z3 was tested using OCaml 3.12.1. -You also need CamlIDL to be able to generate the OCaml API. - -- To download OCaml: - http://caml.inria.fr/ocaml/ - -- To download CamlIDL: - http://forge.ocamlcore.org/projects/camlidl/ - -- To build the OCaml API for Z3: - .\build-lib.cmd - -Remark: The OCaml and C compiler tool chains must be configured in your environment. -Running from the Visual Studio Command Prompt configures the Microsoft C compiler. - -Remark: Building the OCaml API copies some pathnames into files, -so the OCaml API must be recompiled if the Z3 library files are moved. - -See ..\examples\ocaml\build-test.cmd for an example of how to compile and link with Z3. - -Acknowledgements: -The OCaml interface for Z3 was written by Josh Berdine and Jakob Lichtenberg. -Many thanks to them! +The OCaml API for Z3 was tested using OCaml 3.12.1. +You also need CamlIDL to be able to generate the OCaml API. + +- To download OCaml: + http://caml.inria.fr/ocaml/ + +- To download CamlIDL: + http://forge.ocamlcore.org/projects/camlidl/ + +- To build the OCaml API for Z3: + .\build-lib.cmd + +Remark: The OCaml and C compiler tool chains must be configured in your environment. +Running from the Visual Studio Command Prompt configures the Microsoft C compiler. + +Remark: Building the OCaml API copies some pathnames into files, +so the OCaml API must be recompiled if the Z3 library files are moved. + +See ..\examples\ocaml\build-test.cmd for an example of how to compile and link with Z3. + +Acknowledgements: +The OCaml interface for Z3 was written by Josh Berdine and Jakob Lichtenberg. +Many thanks to them! diff --git a/src/api/ml/build-lib.cmd b/src/api/ml/build-lib.cmd index 93d667a34..7cf1bbcbd 100755 --- a/src/api/ml/build-lib.cmd +++ b/src/api/ml/build-lib.cmd @@ -1,3 +1,3 @@ -@echo off - -call .\compile_mlapi.cmd ..\include ..\bin ..\bin +@echo off + +call .\compile_mlapi.cmd ..\include ..\bin ..\bin diff --git a/src/api/ml/build-test.cmd b/src/api/ml/build-test.cmd index 7b80dc795..13a752dbb 100755 --- a/src/api/ml/build-test.cmd +++ b/src/api/ml/build-test.cmd @@ -1,19 +1,19 @@ -@echo off - -if not exist ..\..\ocaml\z3.cmxa ( - echo "YOU MUST BUILD OCAML API! Go to directory ..\ocaml" - goto :EOF -) - -REM ocaml (>= 3.11) calls the linker through flexlink -ocamlc -version >> ocaml_version -set /p OCAML_VERSION= = 3.11) calls the linker through flexlink +ocamlc -version >> ocaml_version +set /p OCAML_VERSION= >> 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. @@ -4069,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/api/z3_api.h b/src/api/z3_api.h index 6d8f34c6c..b29a9c87d 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -703,7 +703,7 @@ typedef enum over Boolean connectives 'and' and 'or'. - - Z3_OP_PR_NFF_NEG: Proof for a (negative) NNF step. Examples: + - Z3_OP_PR_NNF_NEG: Proof for a (negative) NNF step. Examples: \nicebox{ T1: (not s_1) ~ r_1 ... @@ -780,11 +780,11 @@ typedef enum } or \nicebox{ - (=> (and ln+1 ln+2 .. ln+m) l0) + (=> (and l1 l2 .. ln) l0) } or in the most general (ground) form: \nicebox{ - (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln-1)) + (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln)) } In other words we use the following (Prolog style) convention for Horn implications: @@ -800,7 +800,7 @@ typedef enum general non-ground form is: \nicebox{ - (forall (vars) (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln-1))) + (forall (vars) (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln))) } The hyper-resolution rule takes a sequence of parameters. @@ -1724,6 +1724,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 diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index 079516145..acdb10bf6 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -26,6 +26,7 @@ Notes: void register_z3_replayer_cmds(z3_replayer & in); void throw_invalid_reference() { + TRACE("z3_replayer", tout << "invalid argument reference\n";); throw z3_replayer_exception("invalid argument reference"); } diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index d048bb2f7..cd33c7782 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -268,6 +268,8 @@ public: bool is_int_real(expr const * n) const { return is_int_real(get_sort(n)); } MATCH_UNARY(is_uminus); + MATCH_UNARY(is_to_real); + MATCH_UNARY(is_to_int); MATCH_BINARY(is_sub); MATCH_BINARY(is_add); MATCH_BINARY(is_mul); diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index da3c80f06..9de402082 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -300,7 +300,7 @@ std::ostream & operator<<(std::ostream & out, func_decl_info const & info) { // // ----------------------------------- -char const * g_ast_kind_names[] = {"application", "variable", "quantifier", "sort", "function declaration" }; +static char const * g_ast_kind_names[] = {"application", "variable", "quantifier", "sort", "function declaration" }; char const * get_ast_kind_name(ast_kind k) { return g_ast_kind_names[k]; @@ -2765,7 +2765,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro app const * cls = to_app(f1); unsigned num_args = cls->get_num_args(); #ifdef Z3DEBUG - vector found; + svector found; #endif for (unsigned i = 0; i < num_args; i++) { expr * lit = cls->get_arg(i); diff --git a/src/ast/ast.h b/src/ast/ast.h index 2753d0006..2c3843587 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1194,7 +1194,6 @@ enum pattern_op_kind { heurisitic quantifier instantiation. */ class pattern_decl_plugin : public decl_plugin { - sort * m_list; public: virtual decl_plugin * mk_fresh() { return alloc(pattern_decl_plugin); } diff --git a/src/ast/ast_lt.cpp b/src/ast/ast_lt.cpp index 1bf153f4f..2fba4f7a6 100644 --- a/src/ast/ast_lt.cpp +++ b/src/ast/ast_lt.cpp @@ -134,6 +134,16 @@ bool lt(ast * n1, ast * n2) { } } +bool is_sorted(unsigned num, expr * const * ns) { + for (unsigned i = 1; i < num; i++) { + ast * prev = ns[i-1]; + ast * curr = ns[i]; + if (lt(curr, prev)) + return false; + } + return true; +} + bool lex_lt(unsigned num, ast * const * n1, ast * const * n2) { for (unsigned i = 0; i < num; i ++) { if (n1[i] == n2[i]) diff --git a/src/ast/ast_lt.h b/src/ast/ast_lt.h index 5eb268997..b405ab229 100644 --- a/src/ast/ast_lt.h +++ b/src/ast/ast_lt.h @@ -22,6 +22,7 @@ Revision History: class ast; bool lt(ast * n1, ast * n2); +bool is_sorted(unsigned num, expr * const * ns); struct ast_to_lt { bool operator()(ast * n1, ast * n2) const { return lt(n1, n2); } diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 5819e3930..f684879ae 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -260,6 +260,7 @@ class smt_printer { else { m_out << sym << "["; } + for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (p.is_ast()) { @@ -642,9 +643,7 @@ class smt_printer { m_out << m_var_names[m_num_var_names - idx - 1]; } else { - if (!m_is_smt2) { - m_out << "?" << idx; - } + m_out << "?" << idx; } } diff --git a/src/ast/ast_util.cpp b/src/ast/ast_util.cpp index 8b0b3fa04..d77deca01 100644 --- a/src/ast/ast_util.cpp +++ b/src/ast/ast_util.cpp @@ -1,3 +1,21 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + ast_util.cpp + +Abstract: + + Helper functions + +Author: + + Leonardo de Moura (leonardo) 2007-06-08. + +Revision History: + +--*/ #include "ast_util.h" app * mk_list_assoc_app(ast_manager & m, func_decl * f, unsigned num_args, expr * const * args) { @@ -138,3 +156,38 @@ expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx) { SASSERT(m.is_or(cls)); return to_app(cls)->get_arg(idx); } + +expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args) { + if (num_args == 0) + return m.mk_true(); + else if (num_args == 1) + return args[0]; + else + return m.mk_and(num_args, args); +} + +expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args) { + if (num_args == 0) + return m.mk_false(); + else if (num_args == 1) + return args[0]; + else + return m.mk_or(num_args, args); +} + +expr * mk_not(ast_manager & m, expr * arg) { + expr * atom; + if (m.is_not(arg, atom)) + return atom; + else + return m.mk_not(arg); +} + +expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) { + expr_ref_buffer new_diseqs(m); + for (unsigned i = 0; i < num_args; i++) { + for (unsigned j = i + 1; j < num_args; j++) + new_diseqs.push_back(m.mk_not(m.mk_eq(args[i], args[j]))); + } + return mk_and(m, new_diseqs.size(), new_diseqs.c_ptr()); +} diff --git a/src/ast/ast_util.h b/src/ast/ast_util.h index c7f54da01..1e813b1b0 100644 --- a/src/ast/ast_util.h +++ b/src/ast/ast_util.h @@ -95,5 +95,36 @@ bool is_clause(ast_manager & m, expr * n); unsigned get_clause_num_literals(ast_manager & m, expr * cls); expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx); +// ----------------------------------- +// +// Goodies for creating Boolean expressions +// +// ----------------------------------- + +/** + Return (and args[0] ... args[num_args-1]) if num_args >= 2 + Return args[0] if num_args == 1 + Return true if num_args == 0 + */ +expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args); + +/** + Return (or args[0] ... args[num_args-1]) if num_args >= 2 + Return args[0] if num_args == 1 + Return false if num_args == 0 + */ +expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args); + +/** + Return a if arg = (not a) + Retur (not arg) otherwise + */ +expr * mk_not(ast_manager & m, expr * arg); + +/** + Return the expression (and (not (= args[0] args[1])) (not (= args[0] args[2])) ... (not (= args[num_args-2] args[num_args-1]))) +*/ +expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args); + #endif /* _AST_UTIL_H_ */ diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 0ef3b60d6..f1c61619a 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -210,6 +210,10 @@ func_decl * bv_decl_plugin::mk_unary(ptr_vector & decls, decl_kind k, func_decl * bv_decl_plugin::mk_int2bv(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain) { + if (bv_size == 0) { + m_manager->raise_exception("bit-vector size must be greater than zero"); + } + force_ptr_array_size(m_int2bv, bv_size + 1); if (arity != 1) { @@ -415,6 +419,9 @@ func_decl * bv_decl_plugin::mk_num_decl(unsigned num_parameters, parameter const return 0; } unsigned bv_size = parameters[1].get_int(); + if (bv_size == 0) { + m_manager->raise_exception("bit-vector size must be greater than zero"); + } // TODO: sign an error if the parameters[0] is out of range, that is, it is a value not in [0, 2^{bv_size}) // This cannot be enforced now, since some Z3 modules try to generate these invalid numerals. // After SMT-COMP, I should find all offending modules. @@ -766,7 +773,7 @@ bool bv_recognizers::is_zero(expr const * n) const { return decl->get_parameter(0).get_rational().is_zero(); } -bool bv_recognizers::is_extract(expr const* e, unsigned& low, unsigned& high, expr*& b) { +bool bv_recognizers::is_extract(expr const* e, unsigned& low, unsigned& high, expr*& b) const { if (!is_extract(e)) return false; low = get_extract_low(e); high = get_extract_high(e); @@ -774,7 +781,7 @@ bool bv_recognizers::is_extract(expr const* e, unsigned& low, unsigned& high, ex return true; } -bool bv_recognizers::is_bv2int(expr const* e, expr*& r) { +bool bv_recognizers::is_bv2int(expr const* e, expr*& r) const { if (!is_bv2int(e)) return false; r = to_app(e)->get_arg(0); return true; diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index 8ea90f844..c5ebfb2d9 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -288,10 +288,10 @@ public: bool is_extract(expr const * e) const { return is_app_of(e, get_fid(), OP_EXTRACT); } unsigned get_extract_high(func_decl const * f) const { return f->get_parameter(0).get_int(); } unsigned get_extract_low(func_decl const * f) const { return f->get_parameter(1).get_int(); } - unsigned get_extract_high(expr const * n) { SASSERT(is_extract(n)); return get_extract_high(to_app(n)->get_decl()); } - unsigned get_extract_low(expr const * n) { SASSERT(is_extract(n)); return get_extract_low(to_app(n)->get_decl()); } - bool is_extract(expr const * e, unsigned & low, unsigned & high, expr * & b); - bool is_bv2int(expr const * e, expr * & r); + unsigned get_extract_high(expr const * n) const { SASSERT(is_extract(n)); return get_extract_high(to_app(n)->get_decl()); } + unsigned get_extract_low(expr const * n) const { SASSERT(is_extract(n)); return get_extract_low(to_app(n)->get_decl()); } + bool is_extract(expr const * e, unsigned & low, unsigned & high, expr * & b) const; + bool is_bv2int(expr const * e, expr * & r) const; bool is_bv_add(expr const * e) const { return is_app_of(e, get_fid(), OP_BADD); } bool is_bv_sub(expr const * e) const { return is_app_of(e, get_fid(), OP_BSUB); } bool is_bv_mul(expr const * e) const { return is_app_of(e, get_fid(), OP_BMUL); } 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/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index 8ac19c11c..6d0823a24 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -326,6 +326,7 @@ namespace datalog { } unsigned index0; sort* last_sort = 0; + SASSERT(num_params > 0); for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (!p.is_int()) { @@ -636,9 +637,13 @@ namespace datalog { app* dl_decl_util::mk_numeral(uint64 value, sort* s) { if (is_finite_sort(s)) { + uint64 sz = 0; + if (try_get_size(s, sz) && sz <= value) { + m.raise_exception("value is out of bounds"); + } parameter params[2] = { parameter(rational(value, rational::ui64())), parameter(s) }; return m.mk_const(m.mk_func_decl(m_fid, OP_DL_CONSTANT, 2, params, 0, (sort*const*)0)); - } + } if (m_arith.is_int(s) || m_arith.is_real(s)) { return m_arith.mk_numeral(rational(value, rational::ui64()), s); } @@ -652,13 +657,15 @@ 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; } - bool dl_decl_util::is_numeral(expr* e, uint64& v) const { + bool dl_decl_util::is_numeral(const expr* e, uint64& v) const { if (is_numeral(e)) { - app* c = to_app(e); + const app* c = to_app(e); SASSERT(c->get_decl()->get_num_parameters() == 2); parameter const& p = c->get_decl()->get_parameter(0); SASSERT(p.is_rational()); diff --git a/src/ast/dl_decl_plugin.h b/src/ast/dl_decl_plugin.h index 559aff7bd..a662de578 100644 --- a/src/ast/dl_decl_plugin.h +++ b/src/ast/dl_decl_plugin.h @@ -169,11 +169,11 @@ namespace datalog { app* mk_le(expr* a, expr* b); - bool is_lt(expr* a) { return is_app_of(a, m_fid, OP_DL_LT); } + bool is_lt(const expr* a) const { return is_app_of(a, m_fid, OP_DL_LT); } - bool is_numeral(expr* c) const { return is_app_of(c, m_fid, OP_DL_CONSTANT); } + bool is_numeral(const expr* c) const { return is_app_of(c, m_fid, OP_DL_CONSTANT); } - bool is_numeral(expr* e, uint64& v) const; + bool is_numeral(const expr* e, uint64& v) const; // // Utilities for extracting constants diff --git a/src/ast/expr_abstract.cpp b/src/ast/expr_abstract.cpp index 6deb4bf45..0569eb360 100644 --- a/src/ast/expr_abstract.cpp +++ b/src/ast/expr_abstract.cpp @@ -20,52 +20,50 @@ Notes: #include "expr_abstract.h" #include "map.h" -void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { - ast_ref_vector pinned(m); - ptr_vector stack; - obj_map map; +void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { + expr * curr = 0, *b = 0; SASSERT(n->get_ref_count() > 0); - stack.push_back(n); + m_stack.push_back(n); for (unsigned i = 0; i < num_bound; ++i) { b = bound[i]; expr* v = m.mk_var(base + num_bound - i - 1, m.get_sort(b)); - pinned.push_back(v); - map.insert(b, v); + m_pinned.push_back(v); + m_map.insert(b, v); } - while(!stack.empty()) { - curr = stack.back(); - if (map.contains(curr)) { - stack.pop_back(); + while(!m_stack.empty()) { + curr = m_stack.back(); + if (m_map.contains(curr)) { + m_stack.pop_back(); continue; } switch(curr->get_kind()) { case AST_VAR: { - map.insert(curr, curr); - stack.pop_back(); + m_map.insert(curr, curr); + m_stack.pop_back(); break; } case AST_APP: { app* a = to_app(curr); bool all_visited = true; - ptr_vector args; + m_args.reset(); for (unsigned i = 0; i < a->get_num_args(); ++i) { - if (!map.find(a->get_arg(i), b)) { - stack.push_back(a->get_arg(i)); + if (!m_map.find(a->get_arg(i), b)) { + m_stack.push_back(a->get_arg(i)); all_visited = false; } else { - args.push_back(b); + m_args.push_back(b); } } if (all_visited) { - b = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); - pinned.push_back(b); - map.insert(curr, b); - stack.pop_back(); + b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); + m_pinned.push_back(b); + m_map.insert(curr, b); + m_stack.pop_back(); } break; } @@ -81,17 +79,24 @@ void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* cons } expr_abstract(m, new_base, num_bound, bound, q->get_expr(), result1); b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get()); - pinned.push_back(b); - map.insert(curr, b); - stack.pop_back(); + m_pinned.push_back(b); + m_map.insert(curr, b); + m_stack.pop_back(); break; } default: UNREACHABLE(); } } - if (!map.find(n, b)) { - UNREACHABLE(); - } + VERIFY (m_map.find(n, b)); result = b; + m_pinned.reset(); + m_map.reset(); + m_stack.reset(); + m_args.reset(); +} + +void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { + expr_abstractor abs(m); + abs(base, num_bound, bound, n, result); } diff --git a/src/ast/expr_abstract.h b/src/ast/expr_abstract.h index c6ec7973b..3d9f3960f 100644 --- a/src/ast/expr_abstract.h +++ b/src/ast/expr_abstract.h @@ -21,6 +21,17 @@ Notes: #include"ast.h" +class expr_abstractor { + ast_manager& m; + expr_ref_vector m_pinned; + ptr_vector m_stack, m_args; + obj_map m_map; + +public: + expr_abstractor(ast_manager& m): m(m), m_pinned(m) {} + void operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); +}; + void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); #endif diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index f2d6591dc..4aab6ab32 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -200,6 +200,7 @@ func_decl * float_decl_plugin::mk_float_const_decl(decl_kind k, unsigned num_par } else { m_manager->raise_exception("sort of floating point constant was not specified"); + UNREACHABLE(); } SASSERT(is_sort_of(s, m_family_id, FLOAT_SORT)); @@ -254,7 +255,11 @@ func_decl * float_decl_plugin::mk_unary_rel_decl(decl_kind k, unsigned num_param case OP_FLOAT_IS_ZERO: name = "isZero"; break; case OP_FLOAT_IS_NZERO: name = "isNZero"; break; case OP_FLOAT_IS_PZERO: name = "isPZero"; break; - case OP_FLOAT_IS_SIGN_MINUS: name = "isSignMinus"; break; + case OP_FLOAT_IS_SIGN_MINUS: name = "isSignMinus"; break; + case OP_FLOAT_IS_NAN: name = "isNaN"; break; + case OP_FLOAT_IS_INF: name = "isInfinite"; break; + case OP_FLOAT_IS_NORMAL: name = "isNormal"; break; + case OP_FLOAT_IS_SUBNORMAL: name = "isSubnormal"; break; default: UNREACHABLE(); break; @@ -353,27 +358,22 @@ func_decl * float_decl_plugin::mk_to_float(decl_kind k, unsigned num_parameters, sort * fp = mk_float_sort(domain[2]->get_parameter(0).get_int(), domain[1]->get_parameter(0).get_int()+1); symbol name("asFloat"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); - } + } else { // .. Otherwise we only know how to convert rationals/reals. if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to asFloat"); if (arity != 2 && arity != 3) m_manager->raise_exception("invalid number of arguments to asFloat operator"); - if (!is_rm_sort(domain[0]) || domain[1] != m_real_sort) + if (arity == 3 && domain[2] != m_int_sort) + m_manager->raise_exception("sort mismatch"); + if (!is_rm_sort(domain[0]) || + !(domain[1] == m_real_sort || is_sort_of(domain[1], m_family_id, FLOAT_SORT))) m_manager->raise_exception("sort mismatch"); - if (arity == 2) { - sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); - symbol name("asFloat"); - return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); - } - else { - if (domain[2] != m_int_sort) - m_manager->raise_exception("sort mismatch"); - sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); - symbol name("asFloat"); - return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); - } + + sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); + symbol name("asFloat"); + return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } } @@ -419,6 +419,10 @@ func_decl * float_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_FLOAT_IS_NZERO: case OP_FLOAT_IS_PZERO: case OP_FLOAT_IS_SIGN_MINUS: + case OP_FLOAT_IS_NAN: + case OP_FLOAT_IS_INF: + case OP_FLOAT_IS_NORMAL: + case OP_FLOAT_IS_SUBNORMAL: return mk_unary_rel_decl(k, num_parameters, parameters, arity, domain, range); case OP_FLOAT_ABS: case OP_FLOAT_UMINUS: @@ -477,9 +481,13 @@ void float_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("<=", OP_FLOAT_LE)); op_names.push_back(builtin_name(">=", OP_FLOAT_GE)); + op_names.push_back(builtin_name("isNaN", OP_FLOAT_IS_NAN)); + op_names.push_back(builtin_name("isInfinite", OP_FLOAT_IS_INF)); op_names.push_back(builtin_name("isZero", OP_FLOAT_IS_ZERO)); op_names.push_back(builtin_name("isNZero", OP_FLOAT_IS_NZERO)); op_names.push_back(builtin_name("isPZero", OP_FLOAT_IS_PZERO)); + op_names.push_back(builtin_name("isNormal", OP_FLOAT_IS_NORMAL)); + op_names.push_back(builtin_name("isSubnormal", OP_FLOAT_IS_SUBNORMAL)); op_names.push_back(builtin_name("isSignMinus", OP_FLOAT_IS_SIGN_MINUS)); op_names.push_back(builtin_name("min", OP_FLOAT_MIN)); @@ -496,6 +504,15 @@ void float_decl_plugin::get_sort_names(svector & sort_names, symbo sort_names.push_back(builtin_name("RoundingMode", ROUNDING_MODE_SORT)); } +expr * float_decl_plugin::get_some_value(sort * s) { + SASSERT(s->is_sort_of(m_family_id, FLOAT_SORT)); + mpf tmp; + m_fm.mk_nan(s->get_parameter(0).get_int(), s->get_parameter(1).get_int(), tmp); + expr * res = this->mk_value(tmp); + m_fm.del(tmp); + return res; +} + bool float_decl_plugin::is_value(app * e) const { if (e->get_family_id() != m_family_id) return false; diff --git a/src/ast/float_decl_plugin.h b/src/ast/float_decl_plugin.h index 6f1ef5ec2..f1b60a91b 100644 --- a/src/ast/float_decl_plugin.h +++ b/src/ast/float_decl_plugin.h @@ -61,9 +61,13 @@ enum float_op_kind { OP_FLOAT_GT, OP_FLOAT_LE, OP_FLOAT_GE, + OP_FLOAT_IS_NAN, + OP_FLOAT_IS_INF, OP_FLOAT_IS_ZERO, - OP_FLOAT_IS_NZERO, + OP_FLOAT_IS_NORMAL, + OP_FLOAT_IS_SUBNORMAL, OP_FLOAT_IS_PZERO, + OP_FLOAT_IS_NZERO, OP_FLOAT_IS_SIGN_MINUS, OP_TO_FLOAT, @@ -140,6 +144,7 @@ public: unsigned arity, sort * const * domain, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); + virtual expr * get_some_value(sort * s); virtual bool is_value(app* e) const; virtual bool is_unique_value(app* e) const { return is_value(e); } @@ -222,7 +227,7 @@ public: app * mk_rem(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_REM, arg1, arg2); } app * mk_max(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_MAX, arg1, arg2); } app * mk_min(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_MIN, arg1, arg2); } - app * mk_abs(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_ABS, arg1, arg2); } + app * mk_abs(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_ABS, arg1); } app * mk_sqrt(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_SQRT, arg1, arg2); } app * mk_round(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_ROUND_TO_INTEGRAL, arg1, arg2); } app * mk_fused_ma(expr * arg1, expr * arg2, expr * arg3, expr * arg4) { @@ -236,7 +241,11 @@ public: app * mk_le(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_LE, arg1, arg2); } app * mk_ge(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FLOAT_GE, arg1, arg2); } + app * mk_is_nan(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_NAN, arg1); } + app * mk_is_inf(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_INF, arg1); } app * mk_is_zero(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_ZERO, arg1); } + app * mk_is_normal(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_NORMAL, arg1); } + app * mk_is_subnormal(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_SUBNORMAL, arg1); } app * mk_is_nzero(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_NZERO, arg1); } app * mk_is_pzero(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_PZERO, arg1); } app * mk_is_sign_minus(expr * arg1) { return m().mk_app(m_fid, OP_FLOAT_IS_SIGN_MINUS, arg1); } diff --git a/src/ast/func_decl_dependencies.cpp b/src/ast/func_decl_dependencies.cpp index 5e054d5d9..162efb0dd 100644 --- a/src/ast/func_decl_dependencies.cpp +++ b/src/ast/func_decl_dependencies.cpp @@ -79,7 +79,6 @@ void func_decl_dependencies::collect_ng_func_decls(expr * n, func_decl_set * s) */ class func_decl_dependencies::top_sort { enum color { OPEN, IN_PROGRESS, CLOSED }; - ast_manager & m_manager; dependency_graph & m_deps; typedef obj_map color_map; @@ -177,7 +176,7 @@ class func_decl_dependencies::top_sort { } public: - top_sort(ast_manager & m, dependency_graph & deps):m_manager(m), m_deps(deps) {} + top_sort(dependency_graph & deps) : m_deps(deps) {} bool operator()(func_decl * new_decl) { @@ -198,7 +197,7 @@ bool func_decl_dependencies::insert(func_decl * f, func_decl_set * s) { m_deps.insert(f, s); - top_sort cycle_detector(m_manager, m_deps); + top_sort cycle_detector(m_deps); if (cycle_detector(f)) { m_deps.erase(f); dealloc(s); diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp index e9b9c831e..bd21aa1ac 100644 --- a/src/ast/macros/macro_util.cpp +++ b/src/ast/macros/macro_util.cpp @@ -597,8 +597,9 @@ void hint_to_macro_head(ast_manager & m, app * head, unsigned num_decls, app_ref is_hint_head(head, vars) must also return true */ bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) { - TRACE("macro_util_hint", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n" - << mk_pp(exception, m_manager) << "\n";); + TRACE("macro_util_hint", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n"; + if (exception) tout << mk_pp(exception, m_manager); else tout << ""; + tout << "\n";); ptr_buffer vars; if (!is_hint_head(head, vars)) { TRACE("macro_util_hint", tout << "failed because head is not hint head\n";); @@ -792,7 +793,10 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest); expr_ref def(m_manager); mk_sub(rhs, rest, def); - add_arith_macro_candidate(to_app(arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); + // If is_poly_hint, rhs may contain variables that do not occur in to_app(arg). + // So, we should re-check. + if (!_is_poly_hint || is_poly_hint(def, to_app(arg), 0)) + add_arith_macro_candidate(to_app(arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); } else if (is_times_minus_one(arg, neg_arg) && is_app(neg_arg)) { f = to_app(neg_arg)->get_decl(); @@ -810,7 +814,10 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest); expr_ref def(m_manager); mk_sub(rest, rhs, def); - add_arith_macro_candidate(to_app(neg_arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); + // If is_poly_hint, rhs may contain variables that do not occur in to_app(neg_arg). + // So, we should re-check. + if (!_is_poly_hint || is_poly_hint(def, to_app(neg_arg), 0)) + add_arith_macro_candidate(to_app(neg_arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); } } } diff --git a/src/ast/macros/quasi_macros.cpp b/src/ast/macros/quasi_macros.cpp index 1b0f0b621..084a33ebf 100644 --- a/src/ast/macros/quasi_macros.cpp +++ b/src/ast/macros/quasi_macros.cpp @@ -22,10 +22,9 @@ Revision History: #include"uint_set.h" #include"var_subst.h" -quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm, basic_simplifier_plugin & p, simplifier & s) : +quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm, simplifier & s) : m_manager(m), m_macro_manager(mm), - m_bsimp(p), m_simplifier(s), m_new_vars(m), m_new_eqs(m), diff --git a/src/ast/macros/quasi_macros.h b/src/ast/macros/quasi_macros.h index 1731774b2..c5e6b6d4f 100644 --- a/src/ast/macros/quasi_macros.h +++ b/src/ast/macros/quasi_macros.h @@ -32,7 +32,6 @@ class quasi_macros { ast_manager & m_manager; macro_manager & m_macro_manager; - basic_simplifier_plugin & m_bsimp; simplifier & m_simplifier; occurrences_map m_occurrences; ptr_vector m_todo; @@ -57,7 +56,7 @@ class quasi_macros { void apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); public: - quasi_macros(ast_manager & m, macro_manager & mm, basic_simplifier_plugin & p, simplifier & s); + quasi_macros(ast_manager & m, macro_manager & mm, simplifier & s); ~quasi_macros(); /** diff --git a/src/ast/pattern/database.smt2 b/src/ast/pattern/database.smt2 index 9021e44f0..c42c9c25f 100644 --- a/src/ast/pattern/database.smt2 +++ b/src/ast/pattern/database.smt2 @@ -119,13 +119,13 @@ :pattern (?select (?select (?asElems e) a) i)))) (assert (forall ((x Int) (f Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?fClosedTime f))) 0) + (or (<= (+ a0 (* (- 1) (?fClosedTime f))) 0) (not (= (?isAllocated x a0) 1)) (= (?isAllocated (?select f x) a0) 1)) :pattern (?isAllocated (?select f x) a0)))) (assert (forall ((a Int) (e Int) (i Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?eClosedTime e))) 0) + (or (<= (+ a0 (* (- 1) (?eClosedTime e))) 0) (not (= (?isAllocated a a0) 1)) (= (?isAllocated (?select (?select e a) i) a0) 1)) :pattern (?isAllocated (?select (?select e a) i) a0)))) @@ -281,13 +281,13 @@ :pattern (IntsAllocated h (?StructGet_ s f))))) (assert (forall ((x Int) (f Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?fClosedTime f))) 0) + (or (<= (+ a0 (* (- 1) (?fClosedTime f))) 0) (not (?isAllocated_ x a0)) (?isAllocated_ (?select f x) a0)) :pattern (?isAllocated_ (?select f x) a0)))) (assert (forall ((a Int) (e Int) (i Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?eClosedTime e))) 0) + (or (<= (+ a0 (* (- 1) (?eClosedTime e))) 0) (not (?isAllocated_ a a0)) (?isAllocated_ (?select (?select e a) i) a0)) :pattern (?isAllocated_ (?select (?select e a) i) a0)))) diff --git a/src/ast/recurse_expr.h b/src/ast/recurse_expr.h index 9cb71872b..6b3220d40 100644 --- a/src/ast/recurse_expr.h +++ b/src/ast/recurse_expr.h @@ -30,7 +30,7 @@ class recurse_expr : public Visitor { vector m_results2; bool is_cached(expr * n) const { T c; return m_cache.find(n, c); } - T get_cached(expr * n) const { T c; m_cache.find(n, c); return c; } + T get_cached(expr * n) const { return m_cache.find(n); } void cache_result(expr * n, T c) { m_cache.insert(n, c); } void visit(expr * n, bool & visited); 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) diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index 0bb7378f4..1f4418fd5 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -25,6 +25,7 @@ void array_rewriter::updt_params(params_ref const & _p) { array_rewriter_params p(_p); m_sort_store = p.sort_store(); m_expand_select_store = p.expand_select_store(); + m_expand_store_eq = p.expand_store_eq(); } void array_rewriter::get_param_descrs(param_descrs & r) { @@ -365,3 +366,40 @@ br_status array_rewriter::mk_set_subset(expr * arg1, expr * arg2, expr_ref & res return BR_REWRITE3; } +br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { + if (!m_expand_store_eq) { + return BR_FAILED; + } + expr* lhs1 = lhs; + while (m_util.is_store(lhs1)) { + lhs1 = to_app(lhs1)->get_arg(0); + } + expr* rhs1 = rhs; + while (m_util.is_store(rhs1)) { + rhs1 = to_app(rhs1)->get_arg(0); + } + if (lhs1 != rhs1) { + return BR_FAILED; + } + ptr_buffer fmls, args; + expr* e; + expr_ref tmp1(m()), tmp2(m()); +#define MK_EQ() \ + while (m_util.is_store(e)) { \ + args.push_back(lhs); \ + args.append(to_app(e)->get_num_args()-2,to_app(e)->get_args()+1); \ + mk_select(args.size(), args.c_ptr(), tmp1); \ + args[0] = rhs; \ + mk_select(args.size(), args.c_ptr(), tmp2); \ + fmls.push_back(m().mk_eq(tmp1, tmp2)); \ + e = to_app(e)->get_arg(0); \ + args.reset(); \ + } \ + + e = lhs; + MK_EQ(); + e = rhs; + MK_EQ(); + result = m().mk_and(fmls.size(), fmls.c_ptr()); + return BR_REWRITE_FULL; +} diff --git a/src/ast/rewriter/array_rewriter.h b/src/ast/rewriter/array_rewriter.h index f1362802d..5f20f61ba 100644 --- a/src/ast/rewriter/array_rewriter.h +++ b/src/ast/rewriter/array_rewriter.h @@ -31,12 +31,14 @@ class array_rewriter { array_util m_util; bool m_sort_store; bool m_expand_select_store; + bool m_expand_store_eq; template lbool compare_args(unsigned num_args, expr * const * args1, expr * const * args2); public: array_rewriter(ast_manager & m, params_ref const & p = params_ref()): m_util(m) { updt_params(p); + } ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } @@ -60,6 +62,7 @@ public: br_status mk_set_complement(expr * arg, expr_ref & result); br_status mk_set_difference(expr * arg1, expr * arg2, expr_ref & result); br_status mk_set_subset(expr * arg1, expr * arg2, expr_ref & result); + br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); }; #endif diff --git a/src/ast/rewriter/array_rewriter_params.pyg b/src/ast/rewriter/array_rewriter_params.pyg index 2e3ae9f6a..a43fadecf 100644 --- a/src/ast/rewriter/array_rewriter_params.pyg +++ b/src/ast/rewriter/array_rewriter_params.pyg @@ -2,4 +2,5 @@ def_module_params(module_name='rewriter', class_name='array_rewriter_params', export=True, params=(("expand_select_store", BOOL, False, "replace a (select (store ...) ...) term by an if-then-else term"), + ("expand_store_eq", BOOL, False, "reduce (store ...) = (store ...) with a common base into selects"), ("sort_store", BOOL, False, "sort nested stores when the indices are known to be different"))) diff --git a/src/ast/rewriter/ast_counter.cpp b/src/ast/rewriter/ast_counter.cpp new file mode 100644 index 000000000..6f49a232f --- /dev/null +++ b/src/ast/rewriter/ast_counter.cpp @@ -0,0 +1,175 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + ast_counter.cpp + +Abstract: + + Routines for counting features of terms, such as free variables. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-18. + +Revision History: + +--*/ + +#include "ast_counter.h" +#include "var_subst.h" + +void counter::update(unsigned el, int delta) { + int & counter = get(el); + SASSERT(!m_stay_non_negative || counter>=0); + SASSERT(!m_stay_non_negative || static_cast(counter)>=-delta); + counter += delta; +} + +int & counter::get(unsigned el) { + return m_data.insert_if_not_there2(el, 0)->get_data().m_value; +} + +counter & counter::count(unsigned sz, const unsigned * els, int delta) { + for(unsigned i=0; im_value>0 ) { + cnt++; + } + } + return cnt; +} + +void counter::collect_positive(uint_set & acc) const { + iterator eit = begin(); + iterator eend = end(); + for(; eit!=eend; ++eit) { + if(eit->m_value>0) { acc.insert(eit->m_key); } + } +} + +bool counter::get_max_positive(unsigned & res) const { + bool found = false; + iterator eit = begin(); + iterator eend = end(); + for(; eit!=eend; ++eit) { + if( eit->m_value>0 && (!found || eit->m_key>res) ) { + found = true; + res = eit->m_key; + } + } + return found; +} + +unsigned counter::get_max_positive() const { + unsigned max_pos; + VERIFY(get_max_positive(max_pos)); + return max_pos; +} + +int counter::get_max_counter_value() const { + int res = 0; + iterator eit = begin(); + iterator eend = end(); + for (; eit!=eend; ++eit) { + if( eit->m_value>res ) { + res = eit->m_value; + } + } + return res; +} + +void var_counter::count_vars(ast_manager & m, const app * pred, int coef) { + unsigned n = pred->get_num_args(); + for (unsigned i = 0; i < n; i++) { + m_sorts.reset(); + m_todo.reset(); + m_mark.reset(); + ::get_free_vars(m_mark, m_todo, pred->get_arg(i), m_sorts); + for (unsigned j = 0; j < m_sorts.size(); ++j) { + if (m_sorts[j]) { + update(j, 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(); + if (m_visited.is_marked(e)) { + continue; + } + m_visited.mark(e, true); + switch(e->get_kind()) { + case AST_QUANTIFIER: { + qs.push_back(to_quantifier(e)); + break; + } + case AST_VAR: { + if (to_var(e)->get_idx() >= max_var) { + has_var = true; + max_var = to_var(e)->get_idx(); + } + break; + } + case AST_APP: { + app* a = to_app(e); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + m_todo.push_back(a->get_arg(i)); + } + break; + } + default: + UNREACHABLE(); + break; + } + } + 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; +} + + +unsigned var_counter::get_max_var(expr* e) { + bool has_var = false; + m_todo.push_back(e); + return get_max_var(has_var); +} + +unsigned var_counter::get_next_var(expr* e) { + bool has_var = false; + m_todo.push_back(e); + unsigned mv = get_max_var(has_var); + if (has_var) mv++; + return mv; +} + diff --git a/src/ast/rewriter/ast_counter.h b/src/ast/rewriter/ast_counter.h new file mode 100644 index 000000000..e7251079f --- /dev/null +++ b/src/ast/rewriter/ast_counter.h @@ -0,0 +1,109 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + ast_counter.h + +Abstract: + + Routines for counting features of terms, such as free variables. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-18. + Krystof Hoder (t-khoder) 2010-10-10. + +Revision History: + + Hoisted from dl_util.h 2013-03-18. + +--*/ + + +#ifndef _AST_COUNTER_H_ +#define _AST_COUNTER_H_ + +#include "ast.h" +#include "map.h" +#include "uint_set.h" + +class counter { +protected: + typedef u_map map_impl; + map_impl m_data; + const bool m_stay_non_negative; +public: + typedef map_impl::iterator iterator; + + counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} + + void reset() { m_data.reset(); } + iterator begin() const { return m_data.begin(); } + iterator end() const { return m_data.end(); } + void update(unsigned el, int delta); + int & get(unsigned el); + + /** + \brief Increase values of elements in \c els by \c delta. + + The function returns a reference to \c *this to allow for expressions like + counter().count(sz, arr).get_positive_count() + */ + counter & count(unsigned sz, const unsigned * els, int delta = 1); + counter & count(const unsigned_vector & els, int delta = 1) { + return count(els.size(), els.c_ptr(), delta); + } + + void collect_positive(uint_set & acc) const; + unsigned get_positive_count() const; + + bool get_max_positive(unsigned & res) const; + unsigned get_max_positive() const; + + /** + Since the default counter value of a counter is zero, the result is never negative. + */ + int get_max_counter_value() const; +}; + +class var_counter : public counter { +protected: + ptr_vector m_sorts; + expr_fast_mark1 m_visited; + ptr_vector m_todo; + ast_mark m_mark; + unsigned_vector m_scopes; + unsigned get_max_var(bool & has_var); +public: + var_counter(bool stay_non_negative = true): counter(stay_non_negative) {} + void count_vars(ast_manager & m, const app * t, int coef = 1); + unsigned get_max_var(expr* e); + unsigned get_next_var(expr* e); +}; + +class ast_counter { + typedef obj_map map_impl; + map_impl m_data; + bool m_stay_non_negative; + public: + typedef map_impl::iterator iterator; + + ast_counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} + + iterator begin() const { return m_data.begin(); } + iterator end() const { return m_data.end(); } + + int & get(ast * el) { + return m_data.insert_if_not_there2(el, 0)->get_data().m_value; + } + void update(ast * el, int delta){ + get(el) += delta; + SASSERT(!m_stay_non_negative || get(el) >= 0); + } + + void inc(ast * el) { update(el, 1); } + void dec(ast * el) { update(el, -1); } +}; + +#endif diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 44efe0199..11b09003b 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -64,6 +64,7 @@ void bv_rewriter::updt_local_params(params_ref const & _p) { m_split_concat_eq = p.split_concat_eq(); m_udiv2mul = p.udiv2mul(); m_bvnot2arith = p.bvnot2arith(); + m_bv_sort_ac = p.bv_sort_ac(); m_mkbv2num = _p.get_bool("mkbv2num", false); } @@ -1315,7 +1316,7 @@ br_status bv_rewriter::mk_bv_or(unsigned num, expr * const * args, expr_ref & re return BR_REWRITE2; } - if (!flattened && !merged && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero()))) { + if (!flattened && !merged && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero())) && (!m_bv_sort_ac || is_sorted(num, args))) { return BR_FAILED; } @@ -1331,6 +1332,8 @@ br_status bv_rewriter::mk_bv_or(unsigned num, expr * const * args, expr_ref & re result = new_args[0]; return BR_DONE; default: + if (m_bv_sort_ac) + std::sort(new_args.begin(), new_args.end(), ast_to_lt()); result = m_util.mk_bv_or(new_args.size(), new_args.c_ptr()); return BR_DONE; } @@ -1456,7 +1459,8 @@ br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & r return BR_REWRITE3; } - if (!merged && !flattened && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero() && v1 != (rational::power_of_two(sz) - numeral(1))))) + if (!merged && !flattened && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero() && v1 != (rational::power_of_two(sz) - numeral(1)))) && + (!m_bv_sort_ac || is_sorted(num, args))) return BR_FAILED; ptr_buffer new_args; @@ -1497,6 +1501,8 @@ br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & r } __fallthrough; default: + if (m_bv_sort_ac) + std::sort(new_args.begin(), new_args.end(), ast_to_lt()); result = m_util.mk_bv_xor(new_args.size(), new_args.c_ptr()); return BR_DONE; } diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index a94fd18c1..6d8c21666 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -68,6 +68,7 @@ class bv_rewriter : public poly_rewriter { bool m_split_concat_eq; bool m_udiv2mul; bool m_bvnot2arith; + bool m_bv_sort_ac; bool is_zero_bit(expr * x, unsigned idx); diff --git a/src/ast/rewriter/bv_rewriter_params.pyg b/src/ast/rewriter/bv_rewriter_params.pyg index d9ee9f7a3..5feece753 100644 --- a/src/ast/rewriter/bv_rewriter_params.pyg +++ b/src/ast/rewriter/bv_rewriter_params.pyg @@ -8,5 +8,6 @@ def_module_params(module_name='rewriter', ("elim_sign_ext", BOOL, True, "expand sign-ext operator using concat and extract"), ("hi_div0", BOOL, True, "use the 'hardware interpretation' for division by zero (for bit-vector terms)"), ("mul2concat", BOOL, False, "replace multiplication by a power of two into a concatenation"), - ("bvnot2arith", BOOL, False, "replace (bvnot x) with (bvsub -1 x)") + ("bvnot2arith", BOOL, False, "replace (bvnot x) with (bvsub -1 x)"), + ("bv_sort_ac", BOOL, False, "sort the arguments of all AC operators") )) diff --git a/src/muz_qe/expr_safe_replace.cpp b/src/ast/rewriter/expr_safe_replace.cpp similarity index 92% rename from src/muz_qe/expr_safe_replace.cpp rename to src/ast/rewriter/expr_safe_replace.cpp index b3b4d5138..a4886ad1a 100644 --- a/src/muz_qe/expr_safe_replace.cpp +++ b/src/ast/rewriter/expr_safe_replace.cpp @@ -100,3 +100,16 @@ void expr_safe_replace::operator()(expr* e, expr_ref& res) { } res = cache.find(e); } + +void expr_safe_replace::reset() { + m_src.reset(); + m_dst.reset(); + m_subst.reset(); +} + +void expr_safe_replace::apply_substitution(expr* s, expr* def, expr_ref& t) { + reset(); + insert(s, def); + (*this)(t, t); + reset(); +} diff --git a/src/muz_qe/expr_safe_replace.h b/src/ast/rewriter/expr_safe_replace.h similarity index 89% rename from src/muz_qe/expr_safe_replace.h rename to src/ast/rewriter/expr_safe_replace.h index 6af819596..ad626d18f 100644 --- a/src/muz_qe/expr_safe_replace.h +++ b/src/ast/rewriter/expr_safe_replace.h @@ -38,6 +38,10 @@ public: void operator()(expr_ref& e) { (*this)(e.get(), e); } void operator()(expr* src, expr_ref& e); + + void apply_substitution(expr* s, expr* def, expr_ref& t); + + void reset(); }; #endif /* __EXPR_SAFE_REPLACE_H__ */ diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index 482e280e3..0a4c3fc4e 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -58,6 +58,10 @@ br_status float_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c case OP_FLOAT_IS_ZERO: SASSERT(num_args == 1); st = mk_is_zero(args[0], result); break; case OP_FLOAT_IS_NZERO: SASSERT(num_args == 1); st = mk_is_nzero(args[0], result); break; case OP_FLOAT_IS_PZERO: SASSERT(num_args == 1); st = mk_is_pzero(args[0], result); break; + case OP_FLOAT_IS_NAN: SASSERT(num_args == 1); st = mk_is_nan(args[0], result); break; + case OP_FLOAT_IS_INF: SASSERT(num_args == 1); st = mk_is_inf(args[0], result); break; + case OP_FLOAT_IS_NORMAL: SASSERT(num_args == 1); st = mk_is_normal(args[0], result); break; + case OP_FLOAT_IS_SUBNORMAL: SASSERT(num_args == 1); st = mk_is_subnormal(args[0], result); break; case OP_FLOAT_IS_SIGN_MINUS: SASSERT(num_args == 1); st = mk_is_sign_minus(args[0], result); break; case OP_TO_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(args[0], result); break; } @@ -77,14 +81,27 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c return BR_FAILED; rational q; - if (!m_util.au().is_numeral(args[1], q)) + mpf q_mpf; + if (m_util.au().is_numeral(args[1], q)) { + TRACE("fp_rewriter", tout << "q: " << q << std::endl; ); + mpf v; + m_util.fm().set(v, ebits, sbits, rm, q.to_mpq()); + result = m_util.mk_value(v); + m_util.fm().del(v); + // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); + return BR_DONE; + } + else if (m_util.is_value(args[1], q_mpf)) { + TRACE("fp_rewriter", tout << "q: " << m_util.fm().to_string(q_mpf) << std::endl; ); + mpf v; + m_util.fm().set(v, ebits, sbits, rm, q_mpf); + result = m_util.mk_value(v); + m_util.fm().del(v); + // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); + return BR_DONE; + } + else return BR_FAILED; - - mpf v; - m_util.fm().set(v, ebits, sbits, rm, q.to_mpq()); - result = m_util.mk_value(v); - m_util.fm().del(v); - return BR_DONE; } else if (num_args == 3 && m_util.is_rm(m().get_sort(args[0])) && @@ -104,11 +121,11 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c return BR_FAILED; TRACE("fp_rewriter", tout << "q: " << q << ", e: " << e << "\n";); - mpf v; m_util.fm().set(v, ebits, sbits, rm, q.to_mpq(), e.to_mpq().numerator()); result = m_util.mk_value(v); - m_util.fm().del(v); + m_util.fm().del(v); + // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } else { @@ -217,8 +234,7 @@ br_status float_rewriter::mk_abs(expr * arg1, expr_ref & result) { result = arg1; return BR_DONE; } - sort * s = m().get_sort(arg1); - result = m().mk_ite(m_util.mk_lt(arg1, m_util.mk_pzero(s)), + result = m().mk_ite(m_util.mk_is_sign_minus(arg1), m_util.mk_uminus(arg1), arg1); return BR_REWRITE2; @@ -234,13 +250,13 @@ br_status float_rewriter::mk_min(expr * arg1, expr * arg2, expr_ref & result) { return BR_DONE; } // expand as using ite's - result = m().mk_ite(mk_eq_nan(arg1), + result = m().mk_ite(m().mk_or(mk_eq_nan(arg1), m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2))), arg2, m().mk_ite(mk_eq_nan(arg2), arg1, m().mk_ite(m_util.mk_lt(arg1, arg2), - arg1, - arg2))); + arg1, + arg2))); return BR_REWRITE_FULL; } @@ -254,7 +270,7 @@ br_status float_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) { return BR_DONE; } // expand as using ite's - result = m().mk_ite(mk_eq_nan(arg1), + result = m().mk_ite(m().mk_or(mk_eq_nan(arg1), m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2))), arg2, m().mk_ite(mk_eq_nan(arg2), arg1, @@ -420,6 +436,46 @@ br_status float_rewriter::mk_is_pzero(expr * arg1, expr_ref & result) { return BR_FAILED; } +br_status float_rewriter::mk_is_nan(expr * arg1, expr_ref & result) { + scoped_mpf v(m_util.fm()); + if (m_util.is_value(arg1, v)) { + result = (m_util.fm().is_nan(v)) ? m().mk_true() : m().mk_false(); + return BR_DONE; + } + + return BR_FAILED; +} + +br_status float_rewriter::mk_is_inf(expr * arg1, expr_ref & result) { + scoped_mpf v(m_util.fm()); + if (m_util.is_value(arg1, v)) { + result = (m_util.fm().is_inf(v)) ? m().mk_true() : m().mk_false(); + return BR_DONE; + } + + return BR_FAILED; +} + +br_status float_rewriter::mk_is_normal(expr * arg1, expr_ref & result) { + scoped_mpf v(m_util.fm()); + if (m_util.is_value(arg1, v)) { + result = (m_util.fm().is_normal(v)) ? m().mk_true() : m().mk_false(); + return BR_DONE; + } + + return BR_FAILED; +} + +br_status float_rewriter::mk_is_subnormal(expr * arg1, expr_ref & result) { + scoped_mpf v(m_util.fm()); + if (m_util.is_value(arg1, v)) { + result = (m_util.fm().is_denormal(v)) ? m().mk_true() : m().mk_false(); + return BR_DONE; + } + + return BR_FAILED; +} + br_status float_rewriter::mk_is_sign_minus(expr * arg1, expr_ref & result) { scoped_mpf v(m_util.fm()); if (m_util.is_value(arg1, v)) { diff --git a/src/ast/rewriter/float_rewriter.h b/src/ast/rewriter/float_rewriter.h index 7c86a5bc3..4a0879d4b 100644 --- a/src/ast/rewriter/float_rewriter.h +++ b/src/ast/rewriter/float_rewriter.h @@ -66,6 +66,10 @@ public: br_status mk_is_zero(expr * arg1, expr_ref & result); br_status mk_is_nzero(expr * arg1, expr_ref & result); br_status mk_is_pzero(expr * arg1, expr_ref & result); + br_status mk_is_nan(expr * arg1, expr_ref & result); + br_status mk_is_inf(expr * arg1, expr_ref & result); + br_status mk_is_normal(expr * arg1, expr_ref & result); + br_status mk_is_subnormal(expr * arg1, expr_ref & result); br_status mk_is_sign_minus(expr * arg1, expr_ref & result); br_status mk_to_ieee_bv(expr * arg1, expr_ref & result); diff --git a/src/ast/rewriter/mk_simplified_app.cpp b/src/ast/rewriter/mk_simplified_app.cpp index da615e195..a46e71582 100644 --- a/src/ast/rewriter/mk_simplified_app.cpp +++ b/src/ast/rewriter/mk_simplified_app.cpp @@ -62,6 +62,8 @@ struct mk_simplified_app::imp { st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); + else if (s_fid == m_ar_rw.get_fid()) + st = m_ar_rw.mk_eq_core(args[0], args[1], result); if (st != BR_FAILED) return st; diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h index 290144c75..036221f96 100644 --- a/src/ast/rewriter/poly_rewriter_def.h +++ b/src/ast/rewriter/poly_rewriter_def.h @@ -1,932 +1,932 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - poly_rewriter_def.h - -Abstract: - - Basic rewriting rules for Polynomials. - -Author: - - Leonardo (leonardo) 2011-04-08 - -Notes: - ---*/ -#include"poly_rewriter.h" -#include"poly_rewriter_params.hpp" -#include"ast_lt.h" -#include"ast_ll_pp.h" -#include"ast_smt2_pp.h" - -template -char const * poly_rewriter::g_ste_blowup_msg = "sum of monomials blowup"; - - -template -void poly_rewriter::updt_params(params_ref const & _p) { - poly_rewriter_params p(_p); - m_flat = p.flat(); - m_som = p.som(); - m_hoist_mul = p.hoist_mul(); - m_hoist_cmul = p.hoist_cmul(); - m_som_blowup = p.som_blowup(); -} - -template -void poly_rewriter::get_param_descrs(param_descrs & r) { - poly_rewriter_params::collect_param_descrs(r); -} - -template -expr * poly_rewriter::mk_add_app(unsigned num_args, expr * const * args) { - switch (num_args) { - case 0: return mk_numeral(numeral(0)); - case 1: return args[0]; - default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args); - } -} - -// t = (^ x y) --> return x, and set k = y if k is an integer >= 1 -// Otherwise return t and set k = 1 -template -expr * poly_rewriter::get_power_body(expr * t, rational & k) { - if (!is_power(t)) { - k = rational(1); - return t; - } - if (is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k > rational(1)) { - return to_app(t)->get_arg(0); - } - k = rational(1); - return t; -} - -template -expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) { - switch (num_args) { - case 0: - return mk_numeral(numeral(1)); - case 1: - return args[0]; - default: - if (use_power()) { - rational k_prev; - expr * prev = get_power_body(args[0], k_prev); - rational k; - ptr_buffer new_args; -#define PUSH_POWER() { \ - if (k_prev.is_one()) { \ - new_args.push_back(prev); \ - } \ - else { \ - expr * pargs[2] = { prev, mk_numeral(k_prev) }; \ - new_args.push_back(m().mk_app(get_fid(), power_decl_kind(), 2, pargs)); \ - } \ - } - - for (unsigned i = 1; i < num_args; i++) { - expr * arg = get_power_body(args[i], k); - if (arg == prev) { - k_prev += k; - } - else { - PUSH_POWER(); - prev = arg; - k_prev = k; - } - } - PUSH_POWER(); - SASSERT(new_args.size() > 0); - if (new_args.size() == 1) { - return new_args[0]; - } - else { - return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.c_ptr()); - } - } - else { - return m().mk_app(get_fid(), mul_decl_kind(), num_args, args); - } - } -} - -template -expr * poly_rewriter::mk_mul_app(numeral const & c, expr * arg) { - if (c.is_one()) { - return arg; - } - else { - expr * new_args[2] = { mk_numeral(c), arg }; - return mk_mul_app(2, new_args); - } -} - -template -br_status poly_rewriter::mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args >= 2); - // only try to apply flattening if it is not already in one of the flat monomial forms - // - (* c x) - // - (* c (* x_1 ... x_n)) - if (num_args != 2 || !is_numeral(args[0]) || (is_mul(args[1]) && is_numeral(to_app(args[1])->get_arg(0)))) { - unsigned i; - for (i = 0; i < num_args; i++) { - if (is_mul(args[i])) - break; - } - if (i < num_args) { - // input has nested monomials. - ptr_buffer flat_args; - // we need the todo buffer to handle: (* (* c (* x_1 ... x_n)) (* d (* y_1 ... y_n))) - ptr_buffer todo; - flat_args.append(i, args); - for (unsigned j = i; j < num_args; j++) { - if (is_mul(args[j])) { - todo.push_back(args[j]); - while (!todo.empty()) { - expr * curr = todo.back(); - todo.pop_back(); - if (is_mul(curr)) { - unsigned k = to_app(curr)->get_num_args(); - while (k > 0) { - --k; - todo.push_back(to_app(curr)->get_arg(k)); - } - } - else { - flat_args.push_back(curr); - } - } - } - else { - flat_args.push_back(args[j]); - } - } - TRACE("poly_rewriter", - tout << "flat mul:\n"; - for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n"; - tout << "---->\n"; - for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n";); - br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.c_ptr(), result); - if (st == BR_FAILED) { - result = mk_mul_app(flat_args.size(), flat_args.c_ptr()); - return BR_DONE; - } - return st; - } - } - return mk_nflat_mul_core(num_args, args, result); -} - - -template -struct poly_rewriter::mon_pw_lt { - poly_rewriter & m_owner; - mon_pw_lt(poly_rewriter & o):m_owner(o) {} - - bool operator()(expr * n1, expr * n2) const { - rational k; - return lt(m_owner.get_power_body(n1, k), - m_owner.get_power_body(n2, k)); - } -}; - - -template -br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args >= 2); - // cheap case - numeral a; - if (num_args == 2 && is_numeral(args[0], a) && !a.is_one() && !a.is_zero() && - (is_var(args[1]) || to_app(args[1])->get_decl()->get_family_id() != get_fid())) - return BR_FAILED; - numeral c(1); - unsigned num_coeffs = 0; - unsigned num_add = 0; - expr * var = 0; - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg, a)) { - num_coeffs++; - c *= a; - } - else { - var = arg; - if (is_add(arg)) - num_add++; - } - } - - normalize(c); - // (* c_1 ... c_n) --> c_1*...*c_n - if (num_coeffs == num_args) { - result = mk_numeral(c); - return BR_DONE; - } - - // (* s ... 0 ... r) --> 0 - if (c.is_zero()) { - result = mk_numeral(c); - return BR_DONE; - } - - if (num_coeffs == num_args - 1) { - SASSERT(var != 0); - // (* c_1 ... c_n x) --> x if c_1*...*c_n == 1 - if (c.is_one()) { - result = var; - return BR_DONE; - } - - numeral c_prime; - if (is_mul(var)) { - // apply basic simplification even when flattening is not enabled. - // (* c1 (* c2 x)) --> (* c1*c2 x) - if (to_app(var)->get_num_args() == 2 && is_numeral(to_app(var)->get_arg(0), c_prime)) { - c *= c_prime; - normalize(c); - result = mk_mul_app(c, to_app(var)->get_arg(1)); - return BR_REWRITE1; - } - else { - // var is a power-product - return BR_FAILED; - } - } - - if (num_add == 0 || m_hoist_cmul) { - SASSERT(!is_add(var) || m_hoist_cmul); - if (num_args == 2 && args[1] == var) { - DEBUG_CODE({ - numeral c_prime; - SASSERT(is_numeral(args[0], c_prime) && c == c_prime); - }); - // it is already simplified - return BR_FAILED; - } - - // (* c_1 ... c_n x) --> (* c_1*...*c_n x) - result = mk_mul_app(c, var); - return BR_DONE; - } - else { - SASSERT(is_add(var)); - // (* c_1 ... c_n (+ t_1 ... t_m)) --> (+ (* c_1*...*c_n t_1) ... (* c_1*...*c_n t_m)) - ptr_buffer new_add_args; - unsigned num = to_app(var)->get_num_args(); - for (unsigned i = 0; i < num; i++) { - new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i))); - } - result = mk_add_app(new_add_args.size(), new_add_args.c_ptr()); - TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";); - return BR_REWRITE2; - } - } - - SASSERT(num_coeffs <= num_args - 2); - - if (!m_som || num_add == 0) { - ptr_buffer new_args; - expr * prev = 0; - bool ordered = true; - for (unsigned i = 0; i < num_args; i++) { - expr * curr = args[i]; - if (is_numeral(curr)) - continue; - if (prev != 0 && lt(curr, prev)) - ordered = false; - new_args.push_back(curr); - prev = curr; - } - TRACE("poly_rewriter", - for (unsigned i = 0; i < new_args.size(); i++) { - if (i > 0) - tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); - tout << mk_ismt2_pp(new_args[i], m()); - } - tout << "\nordered: " << ordered << "\n";); - if (ordered && num_coeffs == 0 && !use_power()) - return BR_FAILED; - if (!ordered) { - if (use_power()) - std::sort(new_args.begin(), new_args.end(), mon_pw_lt(*this)); - else - std::sort(new_args.begin(), new_args.end(), ast_to_lt()); - TRACE("poly_rewriter", - tout << "after sorting:\n"; - for (unsigned i = 0; i < new_args.size(); i++) { - if (i > 0) - tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); - tout << mk_ismt2_pp(new_args[i], m()); - } - tout << "\n";); - } - SASSERT(new_args.size() >= 2); - result = mk_mul_app(new_args.size(), new_args.c_ptr()); - result = mk_mul_app(c, result); - TRACE("poly_rewriter", tout << "mk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";); - return BR_DONE; - } - - SASSERT(m_som && num_add > 0); - - sbuffer szs; - sbuffer it; - sbuffer sums; - for (unsigned i = 0; i < num_args; i ++) { - it.push_back(0); - expr * arg = args[i]; - if (is_add(arg)) { - sums.push_back(const_cast(to_app(arg)->get_args())); - szs.push_back(to_app(arg)->get_num_args()); - } - else { - sums.push_back(const_cast(args + i)); - szs.push_back(1); - SASSERT(sums.back()[0] == arg); - } - } - expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception - ptr_buffer m_args; - TRACE("som", tout << "starting som...\n";); - do { - TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " "; - tout << "\n";); - if (sum.size() > m_som_blowup) - throw rewriter_exception(g_ste_blowup_msg); - m_args.reset(); - for (unsigned i = 0; i < num_args; i++) { - expr * const * v = sums[i]; - expr * arg = v[it[i]]; - m_args.push_back(arg); - } - sum.push_back(mk_mul_app(m_args.size(), m_args.c_ptr())); - } - while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); - result = mk_add_app(sum.size(), sum.c_ptr()); - return BR_REWRITE2; -} - -template -br_status poly_rewriter::mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { - unsigned i; - for (i = 0; i < num_args; i++) { - if (is_add(args[i])) - break; - } - if (i < num_args) { - // has nested ADDs - ptr_buffer flat_args; - flat_args.append(i, args); - for (; i < num_args; i++) { - expr * arg = args[i]; - // Remark: all rewrites are depth 1. - if (is_add(arg)) { - unsigned num = to_app(arg)->get_num_args(); - for (unsigned j = 0; j < num; j++) - flat_args.push_back(to_app(arg)->get_arg(j)); - } - else { - flat_args.push_back(arg); - } - } - br_status st = mk_nflat_add_core(flat_args.size(), flat_args.c_ptr(), result); - if (st == BR_FAILED) { - result = mk_add_app(flat_args.size(), flat_args.c_ptr()); - return BR_DONE; - } - return st; - } - return mk_nflat_add_core(num_args, args, result); -} - -template -inline expr * poly_rewriter::get_power_product(expr * t) { - if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0))) - return to_app(t)->get_arg(1); - return t; -} - -template -inline expr * poly_rewriter::get_power_product(expr * t, numeral & a) { - if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0), a)) - return to_app(t)->get_arg(1); - a = numeral(1); - return t; -} - -template -bool poly_rewriter::is_mul(expr * t, numeral & c, expr * & pp) { - if (!is_mul(t) || to_app(t)->get_num_args() != 2) - return false; - if (!is_numeral(to_app(t)->get_arg(0), c)) - return false; - pp = to_app(t)->get_arg(1); - return true; -} - -template -struct poly_rewriter::hoist_cmul_lt { - poly_rewriter & m_r; - hoist_cmul_lt(poly_rewriter & r):m_r(r) {} - - bool operator()(expr * t1, expr * t2) const { - expr * pp1, * pp2; - numeral c1, c2; - bool is_mul1 = m_r.is_mul(t1, c1, pp1); - bool is_mul2 = m_r.is_mul(t2, c2, pp2); - if (!is_mul1 && is_mul2) - return true; - if (is_mul1 && !is_mul2) - return false; - if (!is_mul1 && !is_mul2) - return t1->get_id() < t2->get_id(); - if (c1 < c2) - return true; - if (c1 > c2) - return false; - return pp1->get_id() < pp2->get_id(); - } -}; - -template -void poly_rewriter::hoist_cmul(expr_ref_buffer & args) { - unsigned sz = args.size(); - std::sort(args.c_ptr(), args.c_ptr() + sz, hoist_cmul_lt(*this)); - numeral c, c_prime; - ptr_buffer pps; - expr * pp, * pp_prime; - unsigned j = 0; - unsigned i = 0; - while (i < sz) { - expr * mon = args[i]; - if (is_mul(mon, c, pp) && i < sz - 1) { - expr * mon_prime = args[i+1]; - if (is_mul(mon_prime, c_prime, pp_prime) && c == c_prime) { - // found target - pps.reset(); - pps.push_back(pp); - pps.push_back(pp_prime); - i += 2; - while (i < sz && is_mul(args[i], c_prime, pp_prime) && c == c_prime) { - pps.push_back(pp_prime); - i++; - } - SASSERT(is_numeral(to_app(mon)->get_arg(0), c_prime) && c == c_prime); - expr * mul_args[2] = { to_app(mon)->get_arg(0), mk_add_app(pps.size(), pps.c_ptr()) }; - args.set(j, mk_mul_app(2, mul_args)); - j++; - continue; - } - } - args.set(j, mon); - j++; - i++; - } - args.resize(j); -} - -template -br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args >= 2); - numeral c; - unsigned num_coeffs = 0; - numeral a; - expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args - expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once - bool has_multiple = false; - expr * prev = 0; - bool ordered = true; - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg, a)) { - num_coeffs++; - c += a; - } - else { - // arg is not a numeral - if (m_sort_sums && ordered) { - if (prev != 0 && lt(arg, prev)) - ordered = false; - prev = arg; - } - } - - arg = get_power_product(arg); - if (visited.is_marked(arg)) { - multiple.mark(arg); - has_multiple = true; - } - else { - visited.mark(arg); - } - } - normalize(c); - SASSERT(m_sort_sums || ordered); - TRACE("sort_sums", - tout << "ordered: " << ordered << "\n"; - for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";); - - if (has_multiple) { - // expensive case - buffer coeffs; - m_expr2pos.reset(); - // compute the coefficient of power products that occur multiple times. - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) - continue; - unsigned pos; - if (m_expr2pos.find(pp, pos)) { - coeffs[pos] += a; - } - else { - m_expr2pos.insert(pp, coeffs.size()); - coeffs.push_back(a); - } - } - expr_ref_buffer new_args(m()); - if (!c.is_zero()) { - new_args.push_back(mk_numeral(c)); - } - // copy power products with non zero coefficients to new_args - visited.reset(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg); - if (!multiple.is_marked(pp)) { - new_args.push_back(arg); - } - else if (!visited.is_marked(pp)) { - visited.mark(pp); - unsigned pos = UINT_MAX; - m_expr2pos.find(pp, pos); - SASSERT(pos != UINT_MAX); - a = coeffs[pos]; - normalize(a); - if (!a.is_zero()) - new_args.push_back(mk_mul_app(a, pp)); - } - } - if (m_hoist_cmul) { - hoist_cmul(new_args); - } - else if (m_sort_sums) { - TRACE("sort_sums_bug", tout << "new_args.size(): " << new_args.size() << "\n";); - if (c.is_zero()) - std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); - else - std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); - } - result = mk_add_app(new_args.size(), new_args.c_ptr()); - if (hoist_multiplication(result)) { - return BR_REWRITE_FULL; - } - return BR_DONE; - } - else { - SASSERT(!has_multiple); - if (ordered && !m_hoist_mul && !m_hoist_cmul) { - if (num_coeffs == 0) - return BR_FAILED; - if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero()) - return BR_FAILED; - } - expr_ref_buffer new_args(m()); - if (!c.is_zero()) - new_args.push_back(mk_numeral(c)); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg)) - continue; - new_args.push_back(arg); - } - if (m_hoist_cmul) { - hoist_cmul(new_args); - } - else if (!ordered) { - if (c.is_zero()) - std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); - else - std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); - } - result = mk_add_app(new_args.size(), new_args.c_ptr()); - if (hoist_multiplication(result)) { - return BR_REWRITE_FULL; - } - return BR_DONE; - } -} - - -template -br_status poly_rewriter::mk_uminus(expr * arg, expr_ref & result) { - numeral a; - set_curr_sort(m().get_sort(arg)); - if (is_numeral(arg, a)) { - a.neg(); - normalize(a); - result = mk_numeral(a); - return BR_DONE; - } - else { - result = mk_mul_app(numeral(-1), arg); - return BR_REWRITE1; - } -} - -template -br_status poly_rewriter::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args > 0); - if (num_args == 1) { - result = args[0]; - return BR_DONE; - } - set_curr_sort(m().get_sort(args[0])); - expr * minus_one = mk_numeral(numeral(-1)); - ptr_buffer new_args; - new_args.push_back(args[0]); - for (unsigned i = 1; i < num_args; i++) { - expr * aux_args[2] = { minus_one, args[i] }; - new_args.push_back(mk_mul_app(2, aux_args)); - } - result = mk_add_app(new_args.size(), new_args.c_ptr()); - return BR_REWRITE2; -} - -/** - \brief Cancel/Combine monomials that occur is the left and right hand sides. - - \remark If move = true, then all non-constant monomials are moved to the left-hand-side. -*/ -template -br_status poly_rewriter::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) { - set_curr_sort(m().get_sort(lhs)); - unsigned lhs_sz; - expr * const * lhs_monomials = get_monomials(lhs, lhs_sz); - unsigned rhs_sz; - expr * const * rhs_monomials = get_monomials(rhs, rhs_sz); - - expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in lhs or rhs - expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once - bool has_multiple = false; - - numeral c(0); - numeral a; - unsigned num_coeffs = 0; - - for (unsigned i = 0; i < lhs_sz; i++) { - expr * arg = lhs_monomials[i]; - if (is_numeral(arg, a)) { - c += a; - num_coeffs++; - } - else { - visited.mark(get_power_product(arg)); - } - } - - if (move && num_coeffs == 0 && is_numeral(rhs)) - return BR_FAILED; - - for (unsigned i = 0; i < rhs_sz; i++) { - expr * arg = rhs_monomials[i]; - if (is_numeral(arg, a)) { - c -= a; - num_coeffs++; - } - else { - expr * pp = get_power_product(arg); - if (visited.is_marked(pp)) { - multiple.mark(pp); - has_multiple = true; - } - } - } - - normalize(c); - - if (!has_multiple && num_coeffs <= 1) { - if (move) { - if (is_numeral(rhs)) - return BR_FAILED; - } - else { - if (num_coeffs == 0 || is_numeral(rhs)) - return BR_FAILED; - } - } - - buffer coeffs; - m_expr2pos.reset(); - for (unsigned i = 0; i < lhs_sz; i++) { - expr * arg = lhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) - continue; - unsigned pos; - if (m_expr2pos.find(pp, pos)) { - coeffs[pos] += a; - } - else { - m_expr2pos.insert(pp, coeffs.size()); - coeffs.push_back(a); - } - } - - for (unsigned i = 0; i < rhs_sz; i++) { - expr * arg = rhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) - continue; - unsigned pos = UINT_MAX; - m_expr2pos.find(pp, pos); - SASSERT(pos != UINT_MAX); - coeffs[pos] -= a; - } - - - ptr_buffer new_lhs_monomials; - new_lhs_monomials.push_back(0); // save space for coefficient if needed - // copy power products with non zero coefficients to new_lhs_monomials - visited.reset(); - for (unsigned i = 0; i < lhs_sz; i++) { - expr * arg = lhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg); - if (!multiple.is_marked(pp)) { - new_lhs_monomials.push_back(arg); - } - else if (!visited.is_marked(pp)) { - visited.mark(pp); - unsigned pos = UINT_MAX; - m_expr2pos.find(pp, pos); - SASSERT(pos != UINT_MAX); - a = coeffs[pos]; - if (!a.is_zero()) - new_lhs_monomials.push_back(mk_mul_app(a, pp)); - } - } - - ptr_buffer new_rhs_monomials; - new_rhs_monomials.push_back(0); // save space for coefficient if needed - for (unsigned i = 0; i < rhs_sz; i++) { - expr * arg = rhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) { - if (move) { - if (!a.is_zero()) { - if (a.is_minus_one()) { - new_lhs_monomials.push_back(pp); - } - else { - a.neg(); - SASSERT(!a.is_one()); - expr * args[2] = { mk_numeral(a), pp }; - new_lhs_monomials.push_back(mk_mul_app(2, args)); - } - } - } - else { - new_rhs_monomials.push_back(arg); - } - } - } - - bool c_at_rhs = false; - if (move) { - if (m_sort_sums) { - // + 1 to skip coefficient - std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), ast_to_lt()); - } - c_at_rhs = true; - } - else if (new_rhs_monomials.size() == 1) { // rhs is empty - c_at_rhs = true; - } - else if (new_lhs_monomials.size() > 1) { - c_at_rhs = true; - } - - if (c_at_rhs) { - c.neg(); - normalize(c); - new_rhs_monomials[0] = mk_numeral(c); - lhs_result = mk_add_app(new_lhs_monomials.size() - 1, new_lhs_monomials.c_ptr() + 1); - rhs_result = mk_add_app(new_rhs_monomials.size(), new_rhs_monomials.c_ptr()); - } - else { - new_lhs_monomials[0] = mk_numeral(c); - lhs_result = mk_add_app(new_lhs_monomials.size(), new_lhs_monomials.c_ptr()); - rhs_result = mk_add_app(new_rhs_monomials.size() - 1, new_rhs_monomials.c_ptr() + 1); - } - return BR_DONE; -} - -#define TO_BUFFER(_tester_, _buffer_, _e_) \ - _buffer_.push_back(_e_); \ - for (unsigned _i = 0; _i < _buffer_.size(); ) { \ - expr* _e = _buffer_[_i]; \ - if (_tester_(_e)) { \ - app* a = to_app(_e); \ - _buffer_[_i] = a->get_arg(0); \ - for (unsigned _j = 1; _j < a->get_num_args(); ++_j) { \ - _buffer_.push_back(a->get_arg(_j)); \ - } \ - } \ - else { \ - ++_i; \ - } \ - } \ - -template -bool poly_rewriter::hoist_multiplication(expr_ref& som) { - if (!m_hoist_mul) { - return false; - } - ptr_buffer adds, muls; - TO_BUFFER(is_add, adds, som); - buffer valid(adds.size(), true); - obj_map mul_map; - unsigned j; - bool change = false; - for (unsigned k = 0; k < adds.size(); ++k) { - expr* e = adds[k]; - muls.reset(); - TO_BUFFER(is_mul, muls, e); - for (unsigned i = 0; i < muls.size(); ++i) { - e = muls[i]; - if (is_numeral(e)) { - continue; - } - if (mul_map.find(e, j) && valid[j] && j != k) { - m_curr_sort = m().get_sort(adds[k]); - adds[j] = merge_muls(adds[j], adds[k]); - adds[k] = mk_numeral(rational(0)); - valid[j] = false; - valid[k] = false; - change = true; - break; - } - else { - mul_map.insert(e, k); - } - } - } - if (!change) { - return false; - } - - som = mk_add_app(adds.size(), adds.c_ptr()); - - - return true; -} - -template -expr* poly_rewriter::merge_muls(expr* x, expr* y) { - ptr_buffer m1, m2; - TO_BUFFER(is_mul, m1, x); - TO_BUFFER(is_mul, m2, y); - unsigned k = 0; - for (unsigned i = 0; i < m1.size(); ++i) { - x = m1[i]; - bool found = false; - unsigned j; - for (j = k; j < m2.size(); ++j) { - found = m2[j] == x; - if (found) break; - } - if (found) { - std::swap(m1[i],m1[k]); - std::swap(m2[j],m2[k]); - ++k; - } - } - m_curr_sort = m().get_sort(x); - SASSERT(k > 0); - SASSERT(m1.size() >= k); - SASSERT(m2.size() >= k); - expr* args[2] = { mk_mul_app(m1.size()-k, m1.c_ptr()+k), - mk_mul_app(m2.size()-k, m2.c_ptr()+k) }; - if (k == m1.size()) { - m1.push_back(0); - } - m1[k] = mk_add_app(2, args); - return mk_mul_app(k+1, m1.c_ptr()); -} +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + poly_rewriter_def.h + +Abstract: + + Basic rewriting rules for Polynomials. + +Author: + + Leonardo (leonardo) 2011-04-08 + +Notes: + +--*/ +#include"poly_rewriter.h" +#include"poly_rewriter_params.hpp" +#include"ast_lt.h" +#include"ast_ll_pp.h" +#include"ast_smt2_pp.h" + +template +char const * poly_rewriter::g_ste_blowup_msg = "sum of monomials blowup"; + + +template +void poly_rewriter::updt_params(params_ref const & _p) { + poly_rewriter_params p(_p); + m_flat = p.flat(); + m_som = p.som(); + m_hoist_mul = p.hoist_mul(); + m_hoist_cmul = p.hoist_cmul(); + m_som_blowup = p.som_blowup(); +} + +template +void poly_rewriter::get_param_descrs(param_descrs & r) { + poly_rewriter_params::collect_param_descrs(r); +} + +template +expr * poly_rewriter::mk_add_app(unsigned num_args, expr * const * args) { + switch (num_args) { + case 0: return mk_numeral(numeral(0)); + case 1: return args[0]; + default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args); + } +} + +// t = (^ x y) --> return x, and set k = y if k is an integer >= 1 +// Otherwise return t and set k = 1 +template +expr * poly_rewriter::get_power_body(expr * t, rational & k) { + if (!is_power(t)) { + k = rational(1); + return t; + } + if (is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k > rational(1)) { + return to_app(t)->get_arg(0); + } + k = rational(1); + return t; +} + +template +expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) { + switch (num_args) { + case 0: + return mk_numeral(numeral(1)); + case 1: + return args[0]; + default: + if (use_power()) { + rational k_prev; + expr * prev = get_power_body(args[0], k_prev); + rational k; + ptr_buffer new_args; +#define PUSH_POWER() { \ + if (k_prev.is_one()) { \ + new_args.push_back(prev); \ + } \ + else { \ + expr * pargs[2] = { prev, mk_numeral(k_prev) }; \ + new_args.push_back(m().mk_app(get_fid(), power_decl_kind(), 2, pargs)); \ + } \ + } + + for (unsigned i = 1; i < num_args; i++) { + expr * arg = get_power_body(args[i], k); + if (arg == prev) { + k_prev += k; + } + else { + PUSH_POWER(); + prev = arg; + k_prev = k; + } + } + PUSH_POWER(); + SASSERT(new_args.size() > 0); + if (new_args.size() == 1) { + return new_args[0]; + } + else { + return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.c_ptr()); + } + } + else { + return m().mk_app(get_fid(), mul_decl_kind(), num_args, args); + } + } +} + +template +expr * poly_rewriter::mk_mul_app(numeral const & c, expr * arg) { + if (c.is_one()) { + return arg; + } + else { + expr * new_args[2] = { mk_numeral(c), arg }; + return mk_mul_app(2, new_args); + } +} + +template +br_status poly_rewriter::mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args >= 2); + // only try to apply flattening if it is not already in one of the flat monomial forms + // - (* c x) + // - (* c (* x_1 ... x_n)) + if (num_args != 2 || !is_numeral(args[0]) || (is_mul(args[1]) && is_numeral(to_app(args[1])->get_arg(0)))) { + unsigned i; + for (i = 0; i < num_args; i++) { + if (is_mul(args[i])) + break; + } + if (i < num_args) { + // input has nested monomials. + ptr_buffer flat_args; + // we need the todo buffer to handle: (* (* c (* x_1 ... x_n)) (* d (* y_1 ... y_n))) + ptr_buffer todo; + flat_args.append(i, args); + for (unsigned j = i; j < num_args; j++) { + if (is_mul(args[j])) { + todo.push_back(args[j]); + while (!todo.empty()) { + expr * curr = todo.back(); + todo.pop_back(); + if (is_mul(curr)) { + unsigned k = to_app(curr)->get_num_args(); + while (k > 0) { + --k; + todo.push_back(to_app(curr)->get_arg(k)); + } + } + else { + flat_args.push_back(curr); + } + } + } + else { + flat_args.push_back(args[j]); + } + } + TRACE("poly_rewriter", + tout << "flat mul:\n"; + for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n"; + tout << "---->\n"; + for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n";); + br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.c_ptr(), result); + if (st == BR_FAILED) { + result = mk_mul_app(flat_args.size(), flat_args.c_ptr()); + return BR_DONE; + } + return st; + } + } + return mk_nflat_mul_core(num_args, args, result); +} + + +template +struct poly_rewriter::mon_pw_lt { + poly_rewriter & m_owner; + mon_pw_lt(poly_rewriter & o):m_owner(o) {} + + bool operator()(expr * n1, expr * n2) const { + rational k; + return lt(m_owner.get_power_body(n1, k), + m_owner.get_power_body(n2, k)); + } +}; + + +template +br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args >= 2); + // cheap case + numeral a; + if (num_args == 2 && is_numeral(args[0], a) && !a.is_one() && !a.is_zero() && + (is_var(args[1]) || to_app(args[1])->get_decl()->get_family_id() != get_fid())) + return BR_FAILED; + numeral c(1); + unsigned num_coeffs = 0; + unsigned num_add = 0; + expr * var = 0; + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg, a)) { + num_coeffs++; + c *= a; + } + else { + var = arg; + if (is_add(arg)) + num_add++; + } + } + + normalize(c); + // (* c_1 ... c_n) --> c_1*...*c_n + if (num_coeffs == num_args) { + result = mk_numeral(c); + return BR_DONE; + } + + // (* s ... 0 ... r) --> 0 + if (c.is_zero()) { + result = mk_numeral(c); + return BR_DONE; + } + + if (num_coeffs == num_args - 1) { + SASSERT(var != 0); + // (* c_1 ... c_n x) --> x if c_1*...*c_n == 1 + if (c.is_one()) { + result = var; + return BR_DONE; + } + + numeral c_prime; + if (is_mul(var)) { + // apply basic simplification even when flattening is not enabled. + // (* c1 (* c2 x)) --> (* c1*c2 x) + if (to_app(var)->get_num_args() == 2 && is_numeral(to_app(var)->get_arg(0), c_prime)) { + c *= c_prime; + normalize(c); + result = mk_mul_app(c, to_app(var)->get_arg(1)); + return BR_REWRITE1; + } + else { + // var is a power-product + return BR_FAILED; + } + } + + if (num_add == 0 || m_hoist_cmul) { + SASSERT(!is_add(var) || m_hoist_cmul); + if (num_args == 2 && args[1] == var) { + DEBUG_CODE({ + numeral c_prime; + SASSERT(is_numeral(args[0], c_prime) && c == c_prime); + }); + // it is already simplified + return BR_FAILED; + } + + // (* c_1 ... c_n x) --> (* c_1*...*c_n x) + result = mk_mul_app(c, var); + return BR_DONE; + } + else { + SASSERT(is_add(var)); + // (* c_1 ... c_n (+ t_1 ... t_m)) --> (+ (* c_1*...*c_n t_1) ... (* c_1*...*c_n t_m)) + ptr_buffer new_add_args; + unsigned num = to_app(var)->get_num_args(); + for (unsigned i = 0; i < num; i++) { + new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i))); + } + result = mk_add_app(new_add_args.size(), new_add_args.c_ptr()); + TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";); + return BR_REWRITE2; + } + } + + SASSERT(num_coeffs <= num_args - 2); + + if (!m_som || num_add == 0) { + ptr_buffer new_args; + expr * prev = 0; + bool ordered = true; + for (unsigned i = 0; i < num_args; i++) { + expr * curr = args[i]; + if (is_numeral(curr)) + continue; + if (prev != 0 && lt(curr, prev)) + ordered = false; + new_args.push_back(curr); + prev = curr; + } + TRACE("poly_rewriter", + for (unsigned i = 0; i < new_args.size(); i++) { + if (i > 0) + tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); + tout << mk_ismt2_pp(new_args[i], m()); + } + tout << "\nordered: " << ordered << "\n";); + if (ordered && num_coeffs == 0 && !use_power()) + return BR_FAILED; + if (!ordered) { + if (use_power()) + std::sort(new_args.begin(), new_args.end(), mon_pw_lt(*this)); + else + std::sort(new_args.begin(), new_args.end(), ast_to_lt()); + TRACE("poly_rewriter", + tout << "after sorting:\n"; + for (unsigned i = 0; i < new_args.size(); i++) { + if (i > 0) + tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); + tout << mk_ismt2_pp(new_args[i], m()); + } + tout << "\n";); + } + SASSERT(new_args.size() >= 2); + result = mk_mul_app(new_args.size(), new_args.c_ptr()); + result = mk_mul_app(c, result); + TRACE("poly_rewriter", tout << "mk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";); + return BR_DONE; + } + + SASSERT(m_som && num_add > 0); + + sbuffer szs; + sbuffer it; + sbuffer sums; + for (unsigned i = 0; i < num_args; i ++) { + it.push_back(0); + expr * arg = args[i]; + if (is_add(arg)) { + sums.push_back(const_cast(to_app(arg)->get_args())); + szs.push_back(to_app(arg)->get_num_args()); + } + else { + sums.push_back(const_cast(args + i)); + szs.push_back(1); + SASSERT(sums.back()[0] == arg); + } + } + expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception + ptr_buffer m_args; + TRACE("som", tout << "starting som...\n";); + do { + TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " "; + tout << "\n";); + if (sum.size() > m_som_blowup) + throw rewriter_exception(g_ste_blowup_msg); + m_args.reset(); + for (unsigned i = 0; i < num_args; i++) { + expr * const * v = sums[i]; + expr * arg = v[it[i]]; + m_args.push_back(arg); + } + sum.push_back(mk_mul_app(m_args.size(), m_args.c_ptr())); + } + while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); + result = mk_add_app(sum.size(), sum.c_ptr()); + return BR_REWRITE2; +} + +template +br_status poly_rewriter::mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { + unsigned i; + for (i = 0; i < num_args; i++) { + if (is_add(args[i])) + break; + } + if (i < num_args) { + // has nested ADDs + ptr_buffer flat_args; + flat_args.append(i, args); + for (; i < num_args; i++) { + expr * arg = args[i]; + // Remark: all rewrites are depth 1. + if (is_add(arg)) { + unsigned num = to_app(arg)->get_num_args(); + for (unsigned j = 0; j < num; j++) + flat_args.push_back(to_app(arg)->get_arg(j)); + } + else { + flat_args.push_back(arg); + } + } + br_status st = mk_nflat_add_core(flat_args.size(), flat_args.c_ptr(), result); + if (st == BR_FAILED) { + result = mk_add_app(flat_args.size(), flat_args.c_ptr()); + return BR_DONE; + } + return st; + } + return mk_nflat_add_core(num_args, args, result); +} + +template +inline expr * poly_rewriter::get_power_product(expr * t) { + if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0))) + return to_app(t)->get_arg(1); + return t; +} + +template +inline expr * poly_rewriter::get_power_product(expr * t, numeral & a) { + if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0), a)) + return to_app(t)->get_arg(1); + a = numeral(1); + return t; +} + +template +bool poly_rewriter::is_mul(expr * t, numeral & c, expr * & pp) { + if (!is_mul(t) || to_app(t)->get_num_args() != 2) + return false; + if (!is_numeral(to_app(t)->get_arg(0), c)) + return false; + pp = to_app(t)->get_arg(1); + return true; +} + +template +struct poly_rewriter::hoist_cmul_lt { + poly_rewriter & m_r; + hoist_cmul_lt(poly_rewriter & r):m_r(r) {} + + bool operator()(expr * t1, expr * t2) const { + expr * pp1, * pp2; + numeral c1, c2; + bool is_mul1 = m_r.is_mul(t1, c1, pp1); + bool is_mul2 = m_r.is_mul(t2, c2, pp2); + if (!is_mul1 && is_mul2) + return true; + if (is_mul1 && !is_mul2) + return false; + if (!is_mul1 && !is_mul2) + return t1->get_id() < t2->get_id(); + if (c1 < c2) + return true; + if (c1 > c2) + return false; + return pp1->get_id() < pp2->get_id(); + } +}; + +template +void poly_rewriter::hoist_cmul(expr_ref_buffer & args) { + unsigned sz = args.size(); + std::sort(args.c_ptr(), args.c_ptr() + sz, hoist_cmul_lt(*this)); + numeral c, c_prime; + ptr_buffer pps; + expr * pp, * pp_prime; + unsigned j = 0; + unsigned i = 0; + while (i < sz) { + expr * mon = args[i]; + if (is_mul(mon, c, pp) && i < sz - 1) { + expr * mon_prime = args[i+1]; + if (is_mul(mon_prime, c_prime, pp_prime) && c == c_prime) { + // found target + pps.reset(); + pps.push_back(pp); + pps.push_back(pp_prime); + i += 2; + while (i < sz && is_mul(args[i], c_prime, pp_prime) && c == c_prime) { + pps.push_back(pp_prime); + i++; + } + SASSERT(is_numeral(to_app(mon)->get_arg(0), c_prime) && c == c_prime); + expr * mul_args[2] = { to_app(mon)->get_arg(0), mk_add_app(pps.size(), pps.c_ptr()) }; + args.set(j, mk_mul_app(2, mul_args)); + j++; + continue; + } + } + args.set(j, mon); + j++; + i++; + } + args.resize(j); +} + +template +br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args >= 2); + numeral c; + unsigned num_coeffs = 0; + numeral a; + expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args + expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once + bool has_multiple = false; + expr * prev = 0; + bool ordered = true; + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg, a)) { + num_coeffs++; + c += a; + } + else { + // arg is not a numeral + if (m_sort_sums && ordered) { + if (prev != 0 && lt(arg, prev)) + ordered = false; + prev = arg; + } + } + + arg = get_power_product(arg); + if (visited.is_marked(arg)) { + multiple.mark(arg); + has_multiple = true; + } + else { + visited.mark(arg); + } + } + normalize(c); + SASSERT(m_sort_sums || ordered); + TRACE("sort_sums", + tout << "ordered: " << ordered << "\n"; + for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";); + + if (has_multiple) { + // expensive case + buffer coeffs; + m_expr2pos.reset(); + // compute the coefficient of power products that occur multiple times. + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) + continue; + unsigned pos; + if (m_expr2pos.find(pp, pos)) { + coeffs[pos] += a; + } + else { + m_expr2pos.insert(pp, coeffs.size()); + coeffs.push_back(a); + } + } + expr_ref_buffer new_args(m()); + if (!c.is_zero()) { + new_args.push_back(mk_numeral(c)); + } + // copy power products with non zero coefficients to new_args + visited.reset(); + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg); + if (!multiple.is_marked(pp)) { + new_args.push_back(arg); + } + else if (!visited.is_marked(pp)) { + visited.mark(pp); + unsigned pos = UINT_MAX; + m_expr2pos.find(pp, pos); + SASSERT(pos != UINT_MAX); + a = coeffs[pos]; + normalize(a); + if (!a.is_zero()) + new_args.push_back(mk_mul_app(a, pp)); + } + } + if (m_hoist_cmul) { + hoist_cmul(new_args); + } + else if (m_sort_sums) { + TRACE("sort_sums_bug", tout << "new_args.size(): " << new_args.size() << "\n";); + if (c.is_zero()) + std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); + else + std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); + } + result = mk_add_app(new_args.size(), new_args.c_ptr()); + if (hoist_multiplication(result)) { + return BR_REWRITE_FULL; + } + return BR_DONE; + } + else { + SASSERT(!has_multiple); + if (ordered && !m_hoist_mul && !m_hoist_cmul) { + if (num_coeffs == 0) + return BR_FAILED; + if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero()) + return BR_FAILED; + } + expr_ref_buffer new_args(m()); + if (!c.is_zero()) + new_args.push_back(mk_numeral(c)); + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg)) + continue; + new_args.push_back(arg); + } + if (m_hoist_cmul) { + hoist_cmul(new_args); + } + else if (!ordered) { + if (c.is_zero()) + std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); + else + std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); + } + result = mk_add_app(new_args.size(), new_args.c_ptr()); + if (hoist_multiplication(result)) { + return BR_REWRITE_FULL; + } + return BR_DONE; + } +} + + +template +br_status poly_rewriter::mk_uminus(expr * arg, expr_ref & result) { + numeral a; + set_curr_sort(m().get_sort(arg)); + if (is_numeral(arg, a)) { + a.neg(); + normalize(a); + result = mk_numeral(a); + return BR_DONE; + } + else { + result = mk_mul_app(numeral(-1), arg); + return BR_REWRITE1; + } +} + +template +br_status poly_rewriter::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args > 0); + if (num_args == 1) { + result = args[0]; + return BR_DONE; + } + set_curr_sort(m().get_sort(args[0])); + expr * minus_one = mk_numeral(numeral(-1)); + ptr_buffer new_args; + new_args.push_back(args[0]); + for (unsigned i = 1; i < num_args; i++) { + expr * aux_args[2] = { minus_one, args[i] }; + new_args.push_back(mk_mul_app(2, aux_args)); + } + result = mk_add_app(new_args.size(), new_args.c_ptr()); + return BR_REWRITE2; +} + +/** + \brief Cancel/Combine monomials that occur is the left and right hand sides. + + \remark If move = true, then all non-constant monomials are moved to the left-hand-side. +*/ +template +br_status poly_rewriter::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) { + set_curr_sort(m().get_sort(lhs)); + unsigned lhs_sz; + expr * const * lhs_monomials = get_monomials(lhs, lhs_sz); + unsigned rhs_sz; + expr * const * rhs_monomials = get_monomials(rhs, rhs_sz); + + expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in lhs or rhs + expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once + bool has_multiple = false; + + numeral c(0); + numeral a; + unsigned num_coeffs = 0; + + for (unsigned i = 0; i < lhs_sz; i++) { + expr * arg = lhs_monomials[i]; + if (is_numeral(arg, a)) { + c += a; + num_coeffs++; + } + else { + visited.mark(get_power_product(arg)); + } + } + + if (move && num_coeffs == 0 && is_numeral(rhs)) + return BR_FAILED; + + for (unsigned i = 0; i < rhs_sz; i++) { + expr * arg = rhs_monomials[i]; + if (is_numeral(arg, a)) { + c -= a; + num_coeffs++; + } + else { + expr * pp = get_power_product(arg); + if (visited.is_marked(pp)) { + multiple.mark(pp); + has_multiple = true; + } + } + } + + normalize(c); + + if (!has_multiple && num_coeffs <= 1) { + if (move) { + if (is_numeral(rhs)) + return BR_FAILED; + } + else { + if (num_coeffs == 0 || is_numeral(rhs)) + return BR_FAILED; + } + } + + buffer coeffs; + m_expr2pos.reset(); + for (unsigned i = 0; i < lhs_sz; i++) { + expr * arg = lhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) + continue; + unsigned pos; + if (m_expr2pos.find(pp, pos)) { + coeffs[pos] += a; + } + else { + m_expr2pos.insert(pp, coeffs.size()); + coeffs.push_back(a); + } + } + + for (unsigned i = 0; i < rhs_sz; i++) { + expr * arg = rhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) + continue; + unsigned pos = UINT_MAX; + m_expr2pos.find(pp, pos); + SASSERT(pos != UINT_MAX); + coeffs[pos] -= a; + } + + + ptr_buffer new_lhs_monomials; + new_lhs_monomials.push_back(0); // save space for coefficient if needed + // copy power products with non zero coefficients to new_lhs_monomials + visited.reset(); + for (unsigned i = 0; i < lhs_sz; i++) { + expr * arg = lhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg); + if (!multiple.is_marked(pp)) { + new_lhs_monomials.push_back(arg); + } + else if (!visited.is_marked(pp)) { + visited.mark(pp); + unsigned pos = UINT_MAX; + m_expr2pos.find(pp, pos); + SASSERT(pos != UINT_MAX); + a = coeffs[pos]; + if (!a.is_zero()) + new_lhs_monomials.push_back(mk_mul_app(a, pp)); + } + } + + ptr_buffer new_rhs_monomials; + new_rhs_monomials.push_back(0); // save space for coefficient if needed + for (unsigned i = 0; i < rhs_sz; i++) { + expr * arg = rhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) { + if (move) { + if (!a.is_zero()) { + if (a.is_minus_one()) { + new_lhs_monomials.push_back(pp); + } + else { + a.neg(); + SASSERT(!a.is_one()); + expr * args[2] = { mk_numeral(a), pp }; + new_lhs_monomials.push_back(mk_mul_app(2, args)); + } + } + } + else { + new_rhs_monomials.push_back(arg); + } + } + } + + bool c_at_rhs = false; + if (move) { + if (m_sort_sums) { + // + 1 to skip coefficient + std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), ast_to_lt()); + } + c_at_rhs = true; + } + else if (new_rhs_monomials.size() == 1) { // rhs is empty + c_at_rhs = true; + } + else if (new_lhs_monomials.size() > 1) { + c_at_rhs = true; + } + + if (c_at_rhs) { + c.neg(); + normalize(c); + new_rhs_monomials[0] = mk_numeral(c); + lhs_result = mk_add_app(new_lhs_monomials.size() - 1, new_lhs_monomials.c_ptr() + 1); + rhs_result = mk_add_app(new_rhs_monomials.size(), new_rhs_monomials.c_ptr()); + } + else { + new_lhs_monomials[0] = mk_numeral(c); + lhs_result = mk_add_app(new_lhs_monomials.size(), new_lhs_monomials.c_ptr()); + rhs_result = mk_add_app(new_rhs_monomials.size() - 1, new_rhs_monomials.c_ptr() + 1); + } + return BR_DONE; +} + +#define TO_BUFFER(_tester_, _buffer_, _e_) \ + _buffer_.push_back(_e_); \ + for (unsigned _i = 0; _i < _buffer_.size(); ) { \ + expr* _e = _buffer_[_i]; \ + if (_tester_(_e)) { \ + app* a = to_app(_e); \ + _buffer_[_i] = a->get_arg(0); \ + for (unsigned _j = 1; _j < a->get_num_args(); ++_j) { \ + _buffer_.push_back(a->get_arg(_j)); \ + } \ + } \ + else { \ + ++_i; \ + } \ + } \ + +template +bool poly_rewriter::hoist_multiplication(expr_ref& som) { + if (!m_hoist_mul) { + return false; + } + ptr_buffer adds, muls; + TO_BUFFER(is_add, adds, som); + buffer valid(adds.size(), true); + obj_map mul_map; + unsigned j; + bool change = false; + for (unsigned k = 0; k < adds.size(); ++k) { + expr* e = adds[k]; + muls.reset(); + TO_BUFFER(is_mul, muls, e); + for (unsigned i = 0; i < muls.size(); ++i) { + e = muls[i]; + if (is_numeral(e)) { + continue; + } + if (mul_map.find(e, j) && valid[j] && j != k) { + m_curr_sort = m().get_sort(adds[k]); + adds[j] = merge_muls(adds[j], adds[k]); + adds[k] = mk_numeral(rational(0)); + valid[j] = false; + valid[k] = false; + change = true; + break; + } + else { + mul_map.insert(e, k); + } + } + } + if (!change) { + return false; + } + + som = mk_add_app(adds.size(), adds.c_ptr()); + + + return true; +} + +template +expr* poly_rewriter::merge_muls(expr* x, expr* y) { + ptr_buffer m1, m2; + TO_BUFFER(is_mul, m1, x); + TO_BUFFER(is_mul, m2, y); + unsigned k = 0; + for (unsigned i = 0; i < m1.size(); ++i) { + x = m1[i]; + bool found = false; + unsigned j; + for (j = k; j < m2.size(); ++j) { + found = m2[j] == x; + if (found) break; + } + if (found) { + std::swap(m1[i],m1[k]); + std::swap(m2[j],m2[k]); + ++k; + } + } + m_curr_sort = m().get_sort(x); + SASSERT(k > 0); + SASSERT(m1.size() >= k); + SASSERT(m2.size() >= k); + expr* args[2] = { mk_mul_app(m1.size()-k, m1.c_ptr()+k), + mk_mul_app(m2.size()-k, m2.c_ptr()+k) }; + if (k == m1.size()) { + m1.push_back(0); + } + m1[k] = mk_add_app(2, args); + return mk_mul_app(k+1, m1.c_ptr()); +} diff --git a/src/ast/rewriter/quant_hoist.cpp b/src/ast/rewriter/quant_hoist.cpp index b64e71866..dd9062828 100644 --- a/src/ast/rewriter/quant_hoist.cpp +++ b/src/ast/rewriter/quant_hoist.cpp @@ -25,7 +25,8 @@ Revision History: #include "bool_rewriter.h" #include "var_subst.h" #include "ast_pp.h" - +#include "ast_counter.h" +#include "expr_safe_replace.h" // // Bring quantifiers of common type into prenex form. @@ -42,7 +43,7 @@ public: void operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result) { quantifier_type qt = Q_none_pos; - pull_quantifiers(fml, qt, vars, result); + pull_quantifier(fml, qt, vars, result); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); @@ -52,7 +53,7 @@ public: void pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result) { quantifier_type qt = Q_exists_pos; - pull_quantifiers(fml, qt, vars, result); + pull_quantifier(fml, qt, vars, result); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); @@ -61,7 +62,7 @@ public: void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars) { quantifier_type qt = is_forall?Q_forall_pos:Q_exists_pos; expr_ref result(m); - pull_quantifiers(fml, qt, vars, result); + pull_quantifier(fml, qt, vars, result); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); @@ -78,7 +79,57 @@ public: expr * const * exprs = (expr* const*) (vars.c_ptr() + vars.size()- nd); instantiate(m, q, exprs, result); } - + + unsigned pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names) { + unsigned index = var_counter().get_next_var(fml); + while (is_quantifier(fml) && (is_forall == to_quantifier(fml)->is_forall())) { + quantifier* q = to_quantifier(fml); + index += q->get_num_decls(); + if (names) { + names->append(q->get_num_decls(), q->get_decl_names()); + } + if (sorts) { + sorts->append(q->get_num_decls(), q->get_decl_sorts()); + } + fml = q->get_expr(); + } + if (!has_quantifiers(fml)) { + return index; + } + app_ref_vector vars(m); + pull_quantifier(is_forall, fml, vars); + if (vars.empty()) { + return index; + } + // replace vars by de-bruijn indices + expr_safe_replace rep(m); + svector bound_names; + ptr_vector bound_sorts; + for (unsigned i = 0; i < vars.size(); ++i) { + app* v = vars[i].get(); + if (names) { + bound_names.push_back(v->get_decl()->get_name()); + } + if (sorts) { + bound_sorts.push_back(m.get_sort(v)); + } + rep.insert(v, m.mk_var(index++, m.get_sort(v))); + } + if (names && !bound_names.empty()) { + bound_names.reverse(); + bound_names.append(*names); + names->reset(); + names->append(bound_names); + } + if (sorts && !bound_sorts.empty()) { + bound_sorts.reverse(); + bound_sorts.append(*sorts); + sorts->reset(); + sorts->append(bound_sorts); + } + rep(fml); + return index; + } private: @@ -143,7 +194,7 @@ private: } - void pull_quantifiers(expr* fml, quantifier_type& qt, app_ref_vector& vars, expr_ref& result) { + void pull_quantifier(expr* fml, quantifier_type& qt, app_ref_vector& vars, expr_ref& result) { if (!has_quantifiers(fml)) { result = fml; @@ -159,7 +210,7 @@ private: if (m.is_and(fml)) { num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { - pull_quantifiers(a->get_arg(i), qt, vars, tmp); + pull_quantifier(a->get_arg(i), qt, vars, tmp); args.push_back(tmp); } m_rewriter.mk_and(args.size(), args.c_ptr(), result); @@ -167,25 +218,25 @@ private: else if (m.is_or(fml)) { num_args = to_app(fml)->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { - pull_quantifiers(to_app(fml)->get_arg(i), qt, vars, tmp); + pull_quantifier(to_app(fml)->get_arg(i), qt, vars, tmp); args.push_back(tmp); } m_rewriter.mk_or(args.size(), args.c_ptr(), result); } else if (m.is_not(fml)) { - pull_quantifiers(to_app(fml)->get_arg(0), negate(qt), vars, tmp); + pull_quantifier(to_app(fml)->get_arg(0), negate(qt), vars, tmp); negate(qt); result = m.mk_not(tmp); } else if (m.is_implies(fml)) { - pull_quantifiers(to_app(fml)->get_arg(0), negate(qt), vars, tmp); + pull_quantifier(to_app(fml)->get_arg(0), negate(qt), vars, tmp); negate(qt); - pull_quantifiers(to_app(fml)->get_arg(1), qt, vars, result); + pull_quantifier(to_app(fml)->get_arg(1), qt, vars, result); result = m.mk_implies(tmp, result); } else if (m.is_ite(fml)) { - pull_quantifiers(to_app(fml)->get_arg(1), qt, vars, tmp); - pull_quantifiers(to_app(fml)->get_arg(2), qt, vars, result); + pull_quantifier(to_app(fml)->get_arg(1), qt, vars, tmp); + pull_quantifier(to_app(fml)->get_arg(2), qt, vars, result); result = m.mk_ite(to_app(fml)->get_arg(0), tmp, result); } else { @@ -203,7 +254,7 @@ private: } set_quantifier_type(qt, q->is_forall()); extract_quantifier(q, vars, tmp); - pull_quantifiers(tmp, qt, vars, result); + pull_quantifier(tmp, qt, vars, result); break; } case AST_VAR: @@ -215,6 +266,7 @@ private: break; } } + }; quantifier_hoister::quantifier_hoister(ast_manager& m) { @@ -237,3 +289,6 @@ void quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, app_ref_ m_impl->pull_quantifier(is_forall, fml, vars); } +unsigned quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names) { + return m_impl->pull_quantifier(is_forall, fml, sorts, names); +} diff --git a/src/ast/rewriter/quant_hoist.h b/src/ast/rewriter/quant_hoist.h index 878f7840d..50cbd1ba4 100644 --- a/src/ast/rewriter/quant_hoist.h +++ b/src/ast/rewriter/quant_hoist.h @@ -59,6 +59,15 @@ public: The list of variables is empty if there are no top-level universal/existential quantifier. */ void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars); + + /** + \brief Pull top-most universal (is_forall true) or existential (is_forall=false) quantifier up. + Return an expression with de-Bruijn indices and the list of names that were used. + Return index of maximal variable. + */ + + unsigned pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names); + }; #endif diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 966544b78..5d19c53e3 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -169,7 +169,9 @@ struct th_rewriter_cfg : public default_rewriter_cfg { st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); - + else if (s_fid == m_ar_rw.get_fid()) + st = m_ar_rw.mk_eq_core(args[0], args[1], result); + if (st != BR_FAILED) return st; } diff --git a/src/ast/rewriter/var_subst.cpp b/src/ast/rewriter/var_subst.cpp index 3ebc0b573..930267dad 100644 --- a/src/ast/rewriter/var_subst.cpp +++ b/src/ast/rewriter/var_subst.cpp @@ -17,7 +17,6 @@ Notes: --*/ #include"var_subst.h" -#include"used_vars.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"ast_smt2_pp.h" @@ -40,7 +39,7 @@ void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, exp tout << mk_ismt2_pp(result, m_reducer.m()) << "\n";); } -void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { +void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { SASSERT(is_well_sorted(m, q)); if (is_ground(q->get_expr())) { // ignore patterns if the body is a ground formula. @@ -51,17 +50,17 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { result = q; return; } - used_vars used; - used.process(q->get_expr()); + m_used.reset(); + m_used.process(q->get_expr()); unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) - used.process(q->get_pattern(i)); + m_used.process(q->get_pattern(i)); unsigned num_no_patterns = q->get_num_no_patterns(); for (unsigned i = 0; i < num_no_patterns; i++) - used.process(q->get_no_pattern(i)); + m_used.process(q->get_no_pattern(i)); unsigned num_decls = q->get_num_decls(); - if (used.uses_all_vars(num_decls)) { + if (m_used.uses_all_vars(num_decls)) { q->set_no_unused_vars(); result = q; return; @@ -70,7 +69,7 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { ptr_buffer used_decl_sorts; buffer used_decl_names; for (unsigned i = 0; i < num_decls; ++i) { - if (used.contains(num_decls - i - 1)) { + if (m_used.contains(num_decls - i - 1)) { used_decl_sorts.push_back(q->get_decl_sort(i)); used_decl_names.push_back(q->get_decl_name(i)); } @@ -79,10 +78,10 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { unsigned num_removed = 0; expr_ref_buffer var_mapping(m); int next_idx = 0; - unsigned sz = used.get_max_found_var_idx_plus_1(); + unsigned sz = m_used.get_max_found_var_idx_plus_1(); for (unsigned i = 0; i < num_decls; ++i) { - sort * s = used.contains(i); + sort * s = m_used.contains(i); if (s) { var_mapping.push_back(m.mk_var(next_idx, s)); next_idx++; @@ -95,7 +94,7 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { // (VAR 0) is in the first position of var_mapping. for (unsigned i = num_decls; i < sz; i++) { - sort * s = used.contains(i); + sort * s = m_used.contains(i); if (s) var_mapping.push_back(m.mk_var(i - num_removed, s)); else @@ -110,9 +109,8 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { std::reverse(var_mapping.c_ptr(), var_mapping.c_ptr() + var_mapping.size()); expr_ref new_expr(m); - var_subst subst(m); - subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr(), new_expr); + m_subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr(), new_expr); if (num_removed == num_decls) { result = new_expr; @@ -124,11 +122,11 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { expr_ref_buffer new_no_patterns(m); for (unsigned i = 0; i < num_patterns; i++) { - subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); + m_subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); new_patterns.push_back(tmp); } for (unsigned i = 0; i < num_no_patterns; i++) { - subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); + m_subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); new_no_patterns.push_back(tmp); } @@ -145,7 +143,12 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { num_no_patterns, new_no_patterns.c_ptr()); to_quantifier(result)->set_no_unused_vars(); - SASSERT(is_well_sorted(m, result)); + SASSERT(is_well_sorted(m, result)); +} + +void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { + unused_vars_eliminator el(m); + el(q, result); } void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result) { @@ -161,9 +164,7 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";); } -static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector& sorts) { - ast_mark mark; - ptr_vector todo; +static void get_free_vars_offset(ast_mark& mark, ptr_vector& todo, unsigned offset, expr* e, ptr_vector& sorts) { todo.push_back(e); while (!todo.empty()) { e = todo.back(); @@ -175,7 +176,9 @@ static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector& sor switch(e->get_kind()) { case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); - get_free_vars_offset(q->get_expr(), offset+q->get_num_decls(), sorts); + ast_mark mark1; + ptr_vector todo1; + get_free_vars_offset(mark1, todo1, offset+q->get_num_decls(), q->get_expr(), sorts); break; } case AST_VAR: { @@ -207,5 +210,11 @@ static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector& sor void get_free_vars(expr* e, ptr_vector& sorts) { - get_free_vars_offset(e, 0, sorts); + ast_mark mark; + ptr_vector todo; + get_free_vars_offset(mark, todo, 0, e, sorts); +} + +void get_free_vars(ast_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts) { + get_free_vars_offset(mark, todo, 0, e, sorts); } diff --git a/src/ast/rewriter/var_subst.h b/src/ast/rewriter/var_subst.h index 9f3c13c0c..ffc21e691 100644 --- a/src/ast/rewriter/var_subst.h +++ b/src/ast/rewriter/var_subst.h @@ -20,6 +20,7 @@ Notes: #define _VAR_SUBST_H_ #include"rewriter.h" +#include"used_vars.h" /** \brief Alias for var_shifter class. @@ -53,6 +54,15 @@ public: /** \brief Eliminate the unused variables from \c q. Store the result in \c r. */ +class unused_vars_eliminator { + ast_manager& m; + var_subst m_subst; + used_vars m_used; +public: + unused_vars_eliminator(ast_manager& m): m(m), m_subst(m) {} + void operator()(quantifier* q, expr_ref& r); +}; + void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & r); /** @@ -73,6 +83,8 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref */ void get_free_vars(expr* e, ptr_vector& sorts); +void get_free_vars(ast_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts); + #endif diff --git a/src/ast/scoped_proof.h b/src/ast/scoped_proof.h new file mode 100644 index 000000000..e37290c03 --- /dev/null +++ b/src/ast/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/ast/simplifier/bv_simplifier_plugin.cpp b/src/ast/simplifier/bv_simplifier_plugin.cpp index 3ef55baba..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) << " "; @@ -636,7 +636,31 @@ bool bv_simplifier_plugin::try_mk_extract(unsigned high, unsigned low, expr * ar if (!all_found) { return false; } - result = m_manager.mk_app(m_fid, a->get_decl_kind(), new_args.size(), new_args.c_ptr()); + // We should not use mk_app because it does not guarantee that the result would be in simplified form. + // result = m_manager.mk_app(m_fid, a->get_decl_kind(), new_args.size(), new_args.c_ptr()); + if (is_app_of(a, m_fid, OP_BAND)) + mk_bv_and(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BOR)) + mk_bv_or(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BXOR)) + mk_bv_xor(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BNOR)) + mk_bv_nor(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BNAND)) + mk_bv_nand(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BNOT)) { + SASSERT(new_args.size() == 1); + mk_bv_not(new_args[0], result); + } + else if (is_app_of(a, m_fid, OP_BADD)) + mk_add(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BMUL)) + mk_mul(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BSUB)) + mk_sub(new_args.size(), new_args.c_ptr(), result); + else { + UNREACHABLE(); + } return true; } else if (m_manager.is_ite(a)) { @@ -747,16 +771,16 @@ void bv_simplifier_plugin::mk_bv_eq(expr* a1, expr* a2, expr_ref& result) { expr * arg1 = *it1; expr * arg2 = *it2; TRACE("expr_bv_util", tout << "low1: " << low1 << " low2: " << low2 << "\n"; - ast_ll_pp(tout, m_manager, arg1); - ast_ll_pp(tout, m_manager, arg2);); + tout << mk_pp(arg1, m_manager) << "\n"; + tout << mk_pp(arg2, m_manager) << "\n";); unsigned sz1 = get_bv_size(arg1); unsigned sz2 = get_bv_size(arg2); SASSERT(low1 < sz1 && low2 < sz2); unsigned rsz1 = sz1 - low1; unsigned rsz2 = sz2 - low2; TRACE("expr_bv_util", tout << "rsz1: " << rsz1 << " rsz2: " << rsz2 << "\n"; - ast_ll_pp(tout, m_manager, arg1); ast_ll_pp(tout, m_manager, arg2);); - + tout << mk_pp(arg1, m_manager) << "\n"; + tout << mk_pp(arg2, m_manager) << "\n";); if (rsz1 == rsz2) { mk_extract(sz1 - 1, low1, arg1, lhs); @@ -826,9 +850,9 @@ void bv_simplifier_plugin::mk_eq_core(expr * arg1, expr * arg2, expr_ref & resul } m_bsimp.mk_and(tmps.size(), tmps.c_ptr(), result); TRACE("mk_eq_bb", - ast_ll_pp(tout, m_manager, arg1); - ast_ll_pp(tout, m_manager, arg2); - ast_ll_pp(tout, m_manager, result);); + tout << mk_pp(arg1, m_manager) << "\n"; + tout << mk_pp(arg2, m_manager) << "\n"; + tout << mk_pp(result, m_manager) << "\n";); return; } #endif diff --git a/src/ast/simplifier/poly_simplifier_plugin.cpp b/src/ast/simplifier/poly_simplifier_plugin.cpp index 13e5748dc..d64123e7b 100644 --- a/src/ast/simplifier/poly_simplifier_plugin.cpp +++ b/src/ast/simplifier/poly_simplifier_plugin.cpp @@ -18,6 +18,7 @@ Author: #include"ast_pp.h" #include"ast_util.h" #include"ast_smt2_pp.h" +#include"ast_ll_pp.h" poly_simplifier_plugin::poly_simplifier_plugin(symbol const & fname, ast_manager & m, decl_kind add, decl_kind mul, decl_kind uminus, decl_kind sub, decl_kind num): @@ -173,7 +174,7 @@ void poly_simplifier_plugin::mk_monomial(unsigned num_args, expr * * args, expr_ result = args[0]; break; default: - std::sort(args, args + num_args, monomial_element_lt_proc(*this)); + std::stable_sort(args, args + num_args, monomial_element_lt_proc(*this)); result = mk_mul(num_args, args); SASSERT(wf_monomial(result)); break; @@ -284,6 +285,7 @@ bool poly_simplifier_plugin::merge_monomials(bool inv, expr * n1, expr * n2, exp else result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k1), b); } + TRACE("merge_monomials", tout << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\n" << mk_pp(result, m_manager) << "\n";); return true; } @@ -465,7 +467,9 @@ void poly_simplifier_plugin::mk_sum_of_monomials(expr_ref_vector & monomials, ex result = monomials.get(0); break; default: { - std::sort(monomials.c_ptr(), monomials.c_ptr() + monomials.size(), monomial_lt_proc(*this)); + TRACE("mk_sum_sort", tout << "before\n"; for (unsigned i = 0; i < monomials.size(); i++) tout << mk_ll_pp(monomials.get(i), m_manager) << "\n";); + std::stable_sort(monomials.c_ptr(), monomials.c_ptr() + monomials.size(), monomial_lt_proc(*this)); + TRACE("mk_sum_sort", tout << "after\n"; for (unsigned i = 0; i < monomials.size(); i++) tout << mk_ll_pp(monomials.get(i), m_manager) << "\n";); if (is_simple_sum_of_monomials(monomials)) { mk_sum_of_monomials_core(monomials.size(), monomials.c_ptr(), result); return; diff --git a/src/ast/substitution/matcher.cpp b/src/ast/substitution/matcher.cpp index a5560c6a2..ce9bdcb3e 100644 --- a/src/ast/substitution/matcher.cpp +++ b/src/ast/substitution/matcher.cpp @@ -18,10 +18,6 @@ Revision History: --*/ #include"matcher.h" -matcher::matcher(ast_manager & m): - m_manager(m) { -} - bool matcher::operator()(expr * e1, expr * e2, substitution & s) { reset(); m_subst = &s; diff --git a/src/ast/substitution/matcher.h b/src/ast/substitution/matcher.h index 1a71a51ed..84a62e874 100644 --- a/src/ast/substitution/matcher.h +++ b/src/ast/substitution/matcher.h @@ -30,7 +30,6 @@ class matcher { typedef pair_hash, obj_ptr_hash > expr_pair_hash; typedef hashtable > cache; - ast_manager & m_manager; substitution * m_subst; // cache m_cache; svector m_todo; @@ -38,7 +37,7 @@ class matcher { void reset(); public: - matcher(ast_manager & m); + matcher() {} /** \brief Return true if e2 is an instance of e1. diff --git a/src/ast/substitution/substitution.cpp b/src/ast/substitution/substitution.cpp index 11bca0133..be293c5a8 100644 --- a/src/ast/substitution/substitution.cpp +++ b/src/ast/substitution/substitution.cpp @@ -148,7 +148,7 @@ void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, e expr * arg = to_app(e)->get_arg(i); expr * new_arg; - m_apply_cache.find(expr_offset(arg, off), new_arg); + VERIFY(m_apply_cache.find(expr_offset(arg, off), new_arg)); new_args.push_back(new_arg); if (arg != new_arg) has_new_args = true; diff --git a/src/cmd_context/README b/src/cmd_context/README index c44a00690..feb3abd20 100644 --- a/src/cmd_context/README +++ b/src/cmd_context/README @@ -1,2 +1,2 @@ Command context provides the infrastructure for executing commands in front-ends such as SMT-LIB 2.0. -It is also provides the solver abstraction to plugin solvers in this kind of front-end. \ No newline at end of file +It is also provides the solver abstraction to plugin solvers in this kind of front-end. diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 0e75a56ba..527230e16 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -584,7 +584,7 @@ public: ctx.regular_stream() << "(:authors \"Leonardo de Moura and Nikolaj Bjorner\")" << std::endl; } else if (opt == m_version) { - ctx.regular_stream() << "(:version \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "\")" << std::endl; + ctx.regular_stream() << "(:version \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER << "\")" << std::endl; } else if (opt == m_status) { ctx.regular_stream() << "(:status " << ctx.get_status() << ")" << std::endl; diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 0cff3b970..376e2a14f 100644 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -517,6 +517,9 @@ void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& r 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); @@ -528,7 +531,8 @@ void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& r 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; - r = -r; + if(conseq_neg) + r = -r; } rats[i-1] = r; } diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index e49182073..218651520 100644 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -364,7 +364,7 @@ class iz3mgr { return cook(to_func_decl(s)->get_parameters()[idx].get_ast()); } - enum lemma_theory {ArithTheory,UnknownTheory}; + enum lemma_theory {ArithTheory,ArrayTheory,UnknownTheory}; lemma_theory get_theory_lemma_theory(const ast &proof){ symb s = sym(proof); @@ -374,6 +374,8 @@ class iz3mgr { std::string foo(p0.bare_str()); if(foo == "arith") return ArithTheory; + if(foo == "array") + return ArrayTheory; return UnknownTheory; } diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index a4b742d7d..f460dfaff 100644 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -68,6 +68,10 @@ class iz3proof_itp_impl : public iz3proof_itp { and yields a proof of false. */ symb leq2eq; + /* Equality to inequality. eq2leq(p, q) takes a proof p of x=y, and + a proof q ~(x <= y) and and yields a proof of false. */ + symb eq2leq; + /* 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; @@ -85,6 +89,9 @@ class iz3proof_itp_impl : public iz3proof_itp { and q of ~Q and returns a proof of false. */ symb modpon; + /* This represents a lack of a proof */ + ast no_proof; + // This is used to represent an infinitessimal value ast epsilon; @@ -102,13 +109,15 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } - ast make_contra_node(const ast &pf, const std::vector &lits){ + ast make_contra_node(const ast &pf, const std::vector &lits, int pfok = -1){ 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 bar; + if(pfok & (1 << i)) bar = make(rotate_sum,lits[i],pf); + else bar = no_proof; ast foo = make(contra,bar,lits[i]); reslits.push_back(foo); } @@ -129,6 +138,11 @@ class iz3proof_itp_impl : public iz3proof_itp { return pv->range_contained(r,rng) ? LitA : LitB; } + bool term_common(const ast &t){ + prover::range r = pv->ast_scope(t); + return pv->ranges_intersect(r,rng) && !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 @@ -267,24 +281,59 @@ class iz3proof_itp_impl : public iz3proof_itp { 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 thing = pf == no_proof ? no_proof : subst_term_and_simp(ph,pf,arg(f,0)); ast newf = make(contra,thing,arg(f,1)); res.push_back(newf); } } } + + bool is_negative_equality(const ast &e){ + if(op(e) == Not){ + opr o = op(arg(e,0)); + return o == Equal || o == Iff; + } + return false; + } - ast resolve_contra(const ast &pivot1, const ast &conj1, + int count_negative_equalities(const std::vector &resolvent){ + int res = 0; + for(unsigned i = 0; i < resolvent.size(); i++) + if(is_negative_equality(arg(resolvent[i],1))) + res++; + return res; + } + + ast resolve_contra_nf(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(count_negative_equalities(resolvent) > 1) + throw proof_error(); 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); } - + 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) + return resolve_contra_nf(pivot2, conj2, pivot1, conj1); + return resolve_with_quantifier(pivot1, conj1, pivot2, conj2); + } + + bool is_contra_itp(const ast &pivot1, ast itp2, ast &pivot2){ if(op(itp2) == And){ int nargs = num_args(itp2); @@ -408,7 +457,7 @@ class iz3proof_itp_impl : public iz3proof_itp { } /* This is where the real work happens. Here, we simplify the - proof obtained by cut elimination, obtaining a interpolant. */ + proof obtained by cut elimination, obtaining an interpolant. */ struct cannot_simplify {}; hash_map simplify_memo; @@ -424,19 +473,23 @@ class iz3proof_itp_impl : public iz3proof_itp { if(bar.second){ int nargs = num_args(e); std::vector args(nargs); - for(int i = 0; i < nargs; i++) + bool placeholder_arg = false; + for(int i = 0; i < nargs; i++){ 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 == Idiv) res = mk_idiv(args[0],args[1]); - else if(f == Uninterpreted){ + 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); + else if(g == sum) res = simplify_sum(args); #if 0 else if(g == cong) res = simplify_cong(args); else if(g == modpon) res = simplify_modpon(args); @@ -462,17 +515,26 @@ class iz3proof_itp_impl : public iz3proof_itp { 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 == eq2leq) return simplify_rotate_eq2leq(pl,args[0],pf); if(g == cong) return simplify_rotate_cong(pl,args[0],pf); + if(g == modpon) return simplify_rotate_modpon(pl,args[0],pf); // if(g == symm) return simplify_rotate_symm(pl,args[0],pf); } throw cannot_simplify(); } + ast simplify_sum(std::vector &args){ + ast cond = mk_true(); + ast ineq = args[0]; + if(!is_ineq(ineq)) throw cannot_simplify(); + sum_cond_ineq(ineq,cond,args[1],args[2]); + return my_implies(cond,ineq); + } + 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; + return rotate_sum_rec(pl,pf,cond,ineq); } void sum_cond_ineq(ast &ineq, ast &cond, const ast &coeff2, const ast &ineq2){ @@ -481,18 +543,26 @@ class iz3proof_itp_impl : public iz3proof_itp { 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 if(is_ineq(ineq2)) + linear_comb(ineq,coeff2,ineq2); else throw cannot_simplify(); } + bool is_ineq(const ast &ineq){ + opr o = op(ineq); + if(o == Not) o = op(arg(ineq,0)); + return o == Leq || o == Lt || o == Geq || o == Gt; + } + // 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(pf == pl) + return my_implies(cond,simplify_ineq(ineq)); if(op(pf) == Uninterpreted && sym(pf) == sum){ if(arg(pf,2) == pl){ sum_cond_ineq(ineq,cond,make_int("1"),arg(pf,0)); @@ -510,25 +580,57 @@ 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 = arg(pf,1); - ast yleqx = arg(pf,2); + 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,make(Plus,x,get_ineq_rhs(xleqy))); + itpeq = make(Equal,x,z3_simplify(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); + 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 = my_and(cond,ineq); + cond = z3_simplify(my_and(cond,ineq)); return my_implies(cond,itpeq); } throw cannot_simplify(); } + ast round_ineq(const ast &ineq){ + if(!is_ineq(ineq)) + throw cannot_simplify(); + ast res = simplify_ineq(ineq); + if(op(res) == Lt) + res = make(Leq,arg(res,0),make(Sub,arg(res,1),make_int("1"))); + return res; + } + + ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){ + 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")))); + } + throw cannot_simplify(); + } + + 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(); + return simplify_modpon(args); + } + throw cannot_simplify(); + } + ast get_ineq_rhs(const ast &ineq2){ opr o = op(ineq2); if(o == Implies) @@ -564,8 +666,8 @@ class iz3proof_itp_impl : public iz3proof_itp { 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))); + if(is_equivrel(equa)) + return my_implies(cond,make(op(equa),arg(equa,1),arg(equa,0))); throw cannot_simplify(); } @@ -575,12 +677,120 @@ class iz3proof_itp_impl : public iz3proof_itp { 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)); + 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(); } + bool is_equivrel(const ast &p){ + opr o = op(p); + return o == Equal || o == Iff; + } + + struct rewrites_failed{}; + + /* Suppose p in Lang(B) and A |- p -> q and B |- q -> r. Return a z in Lang(B) such that + B |- p -> z and A |- z -> q. Collect any side conditions in "rules". */ + + ast commute_rewrites(const ast &p, const ast &q, const ast &r, ast &rules){ + if(q == r) + return p; + if(p == q) + return r; + else { + ast rew = make(Equal,q,r); + if(get_term_type(rew) == LitB){ + apply_common_rewrites(p,p,q,rules); // A rewrites must be over comon vocab + return r; + } + } + if(sym(p) != sym(q) || sym(q) != sym(r)) + throw rewrites_failed(); + int nargs = num_args(p); + if(nargs != num_args(q) || nargs != num_args(r)) + throw rewrites_failed(); + std::vector args; args.resize(nargs); + for(int i = 0; i < nargs; i++) + args[i] = commute_rewrites(arg(p,i),arg(q,i),arg(r,i),rules); + return clone(p,args); + } + + ast apply_common_rewrites(const ast &p, const ast &q, const ast &r, ast &rules){ + if(q == r) + return p; + ast rew = make(Equal,q,r); + if(term_common(rew)){ + if(p != q) + throw rewrites_failed(); + rules = my_and(rules,rew); + return r; + } + if(sym(p) != sym(q) || sym(q) != sym(r)) + return p; + int nargs = num_args(p); + if(nargs != num_args(q) || nargs != num_args(r)) + return p; + std::vector args; args.resize(nargs); + for(int i = 0; i < nargs; i++) + args[i] = apply_common_rewrites(arg(p,i),arg(q,i),arg(r,i),rules); + return clone(p,args); + } + + ast apply_all_rewrites(const ast &p, const ast &q, const ast &r){ + if(q == r) + return p; + if(p == q) + return r; + if(sym(p) != sym(q) || sym(q) != sym(r)) + throw rewrites_failed(); + int nargs = num_args(p); + if(nargs != num_args(q) || nargs != num_args(r)) + throw rewrites_failed(); + std::vector args; args.resize(nargs); + for(int i = 0; i < nargs; i++) + args[i] = apply_all_rewrites(arg(p,i),arg(q,i),arg(r,i)); + return clone(p,args); + } + 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); @@ -617,6 +827,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast triv_interp(const symb &rule, const std::vector &premises){ std::vector ps; ps.resize(premises.size()); std::vector conjs; + int mask = 0; for(unsigned i = 0; i < ps.size(); i++){ ast p = premises[i]; switch(get_term_type(p)){ @@ -627,12 +838,14 @@ class iz3proof_itp_impl : public iz3proof_itp { ps[i] = mk_true(); break; default: - ps[i] = get_placeholder(p); + ps[i] = get_placeholder(p); // can only prove consequent! + if(i == ps.size()-1) + mask |= (1 << conjs.size()); conjs.push_back(p); } } ast ref = make(rule,ps); - ast res = make_contra_node(ref,conjs); + ast res = make_contra_node(ref,conjs,mask); return res; } @@ -689,8 +902,30 @@ class iz3proof_itp_impl : public iz3proof_itp { /** Make an axiom node. The conclusion must be an instance of an axiom. */ virtual node make_axiom(const std::vector &conclusion){ - throw proof_error(); -} + prover::range frng = pv->range_full(); + int nargs = conclusion.size(); + std::vector largs(nargs); + std::vector eqs; + std::vector pfs; + + for(int i = 0; i < nargs; i++){ + ast argpf; + ast lit = conclusion[i]; + largs[i] = localize_term(lit,frng,argpf); + frng = pv->range_glb(frng,pv->ast_scope(largs[i])); + if(largs[i] != lit){ + eqs.push_back(make_equiv(largs[i],lit)); + pfs.push_back(argpf); + } + } + + int frame = pv->range_max(frng); + ast itp = make_assumption(frame,largs); + + for(unsigned i = 0; i < eqs.size(); i++) + itp = make_mp(eqs[i],itp,pfs[i]); + return itp; + } /** Make a Contra node. This rule takes a derivation of the form Gamma |- False and produces |- \/~Gamma. */ @@ -880,6 +1115,45 @@ class iz3proof_itp_impl : public iz3proof_itp { return itp; } + int find_congruence_position(const ast &p, const ast &con){ + // find the argument position of x and y + const ast &x = arg(p,0); + const ast &y = arg(p,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)) + return i; + throw proof_error(); + } + + /** Make a congruence node. This takes derivations of |- x_i1 = y_i1, |- x_i2 = y_i2,... + and produces |- f(...x_i1...x_i2...) = f(...y_i1...y_i2...) */ + + node make_congruence(const std::vector &p, const ast &con, const std::vector &prems){ + if(p.size() == 0) + throw proof_error(); + if(p.size() == 1) + return make_congruence(p[0],con,prems[0]); + ast thing = con; + ast res = mk_true(); + for(unsigned i = 0; i < p.size(); i++){ + int pos = find_congruence_position(p[i],thing); + ast next = subst_in_arg_pos(pos,arg(p[i],1),arg(thing,0)); + ast goal = make(op(thing),arg(thing,0),next); + ast equa = make_congruence(p[i],goal,prems[i]); + if(i == 0) + res = equa; + else { + ast trace = make(op(con),arg(con,0),arg(thing,0)); + ast equiv = make(Iff,trace,make(op(trace),arg(trace,0),next)); + ast foo = make_congruence(goal,equiv,equa); + res = make_mp(equiv,res,foo); + } + thing = make(op(thing),next,arg(thing,1)); + } + return res; + } + /* Interpolate a mixed congruence axiom. */ virtual ast make_mixed_congruence(const ast &x, const ast &y, const ast &p, const ast &con, const ast &prem1){ @@ -1037,7 +1311,7 @@ class iz3proof_itp_impl : public iz3proof_itp { get_placeholder(mk_not(con)), get_placeholder(xleqy), get_placeholder(yleqx)), - conjs); + conjs,1); } } return itp; @@ -1054,6 +1328,7 @@ 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); @@ -1065,6 +1340,12 @@ class iz3proof_itp_impl : public iz3proof_itp { 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); + itp = make(eq2leq,get_placeholder(conjs[0]),get_placeholder(conjs[1])); + itp = make_contra_node(itp,conjs); } } return itp; @@ -1083,10 +1364,17 @@ class iz3proof_itp_impl : public iz3proof_itp { itp = mk_true(); break; default: { + std::vector conjs; conjs.resize(2); + conjs[0] = tleqc; + 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); @@ -1095,6 +1383,130 @@ class iz3proof_itp_impl : public iz3proof_itp { } + + // create a fresh variable for localization + ast fresh_localization_var(const ast &term, int frame){ + std::ostringstream s; + s << "%" << (localization_vars.size()); + ast var = make_var(s.str().c_str(),get_type(term)); + pv->sym_range(sym(var)) = pv->range_full(); // make this variable global + localization_vars.push_back(LocVar(var,term,frame)); + return var; + } + + 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 + hash_map localization_map; // maps terms to their localization vars + hash_map localization_pf_map; // maps terms to proofs of their localizations + + /* "localize" a term e to a given frame range, creating new symbols to + represent non-local subterms. This returns the localized version e_l, + as well as a proof thet e_l = l. + */ + + ast make_refl(const ast &e){ + return mk_true(); // TODO: is this right? + } + + + ast make_equiv(const ast &x, const ast &y){ + if(get_type(x) == bool_type()) + return make(Iff,x,y); + else + return make(Equal,x,y); + } + + ast localize_term(ast e, const prover::range &rng, ast &pf){ + ast orig_e = e; + pf = make_refl(e); // proof that e = e + + prover::range erng = pv->ast_scope(e); + if(!(erng.lo > erng.hi) && pv->ranges_intersect(pv->ast_scope(e),rng)){ + return e; // this term occurs in range, so it's O.K. + } + + hash_map::iterator it = localization_map.find(e); + if(it != localization_map.end()){ + pf = localization_pf_map[e]; + 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) */){ + 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); + } + } + + e = clone(e,largs); + if(pfs.size()) + pf = make_congruence(eqs,make_equiv(e,orig_e),pfs); + // assert(is_local(e)); + } + + if(pv->ranges_intersect(pv->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 = pv->range_near(pv->ast_scope(e),rng); + + ast new_var = fresh_localization_var(e,frame); + localization_map[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); + localization_pf_map[orig_e] = pf; + return new_var; + } + + node make_resolution(ast pivot, node premise1, node premise2) { + std::vector lits; + return make_resolution(pivot,lits,premise1,premise2); + } + + ast resolve_with_quantifier(const ast &pivot1, const ast &conj1, + const ast &pivot2, const ast &conj2){ + if(is_not(arg(pivot1,1))) + return resolve_with_quantifier(pivot2,conj2,pivot1,conj1); + ast eqpf; + ast P = arg(pivot1,1); + ast Ploc = localize_term(P, rng, eqpf); + ast pPloc = make_hypothesis(Ploc); + ast pP = make_mp(make(Iff,Ploc,P),pPloc,eqpf); + ast rP = make_resolution(P,conj1,pP); + ast nP = mk_not(P); + ast nPloc = mk_not(Ploc); + ast neqpf = make_congruence(make(Iff,Ploc,P),make(Iff,nPloc,nP),eqpf); + ast npPloc = make_hypothesis(nPloc); + ast npP = make_mp(make(Iff,nPloc,nP),npPloc,neqpf); + ast nrP = make_resolution(nP,conj2,npP); + ast res = make_resolution(Ploc,rP,nrP); + return res; + } ast get_contra_coeff(const ast &f){ ast c = arg(f,0); @@ -1129,6 +1541,15 @@ class iz3proof_itp_impl : public iz3proof_itp { return l; } + 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') + return true; + } + return false; + } + public: iz3proof_itp_impl(prover *p, const prover::range &r, bool w) : iz3proof_itp(*p) @@ -1144,11 +1565,13 @@ public: sum = function("@sum",3,boolintbooldom,bool_type()); rotate_sum = function("@rotsum",2,boolbooldom,bool_type()); leq2eq = function("@leq2eq",3,boolboolbooldom,bool_type()); + eq2leq = function("@eq2leq",2,boolbooldom,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()); + no_proof = make_var("@nop",bool_type()); } }; diff --git a/src/interp/iz3proof_itp.h b/src/interp/iz3proof_itp.h index a50fa184a..1d5579b75 100644 --- a/src/interp/iz3proof_itp.h +++ b/src/interp/iz3proof_itp.h @@ -94,6 +94,11 @@ class iz3proof_itp : public iz3mgr { virtual node make_congruence(const ast &xi_eq_yi, const ast &con, const ast &prem1) = 0; + /** Make a congruence node. This takes derivations of |- x_i1 = y_i1, |- x_i2 = y_i2,... + and produces |- f(...x_i1...x_i2...) = f(...y_i1...y_i2...) */ + + virtual node make_congruence(const std::vector &xi_eq_yi, const ast &con, const std::vector &prems) = 0; + /** Make a modus-ponens node. This takes derivations of |- x and |- x = y and produces |- y */ diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index f6f72d920..042dbe192 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -895,7 +895,7 @@ public: my_cons.push_back(mk_not(arg(con,i))); my_coeffs.push_back(farkas_coeffs[i]); } - ast farkas_con = normalize_inequality(sum_inequalities(farkas_coeffs,my_cons)); + ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons)); my_cons.push_back(mk_not(farkas_con)); my_coeffs.push_back(make_int("1")); std::vector my_hyps; @@ -976,15 +976,10 @@ public: 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); -#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()); -#endif - res = iproof->make_congruence(conc(prem(proof,0)),con,args[0]); + 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); break; } case PR_REFLEXIVITY: { @@ -1050,6 +1045,9 @@ public: throw unsupported(); } break; + case ArrayTheory: // nothing fancy for this + res = iproof->make_axiom(lits); + break; default: throw unsupported(); } diff --git a/src/interp/iz3translate.h b/src/interp/iz3translate.h index afb9df9bc..64e3bdea6 100755 --- a/src/interp/iz3translate.h +++ b/src/interp/iz3translate.h @@ -53,7 +53,7 @@ public: }; //#define IZ3_TRANSLATE_DIRECT2 -#ifndef _FOCI2 +#ifdef _FOCI2 #define IZ3_TRANSLATE_DIRECT #else #define IZ3_TRANSLATE_FULL diff --git a/src/math/euclid/README b/src/math/euclid/README index 7235cd76f..17d408fc9 100644 --- a/src/math/euclid/README +++ b/src/math/euclid/README @@ -1,2 +1,2 @@ Basic Euclidean solver for linear integer equations. -This solver generates "explanations". \ No newline at end of file +This solver generates "explanations". diff --git a/src/muz_qe/heap_trie.h b/src/math/hilbert/heap_trie.h similarity index 98% rename from src/muz_qe/heap_trie.h rename to src/math/hilbert/heap_trie.h index a99861359..c2a1c52d1 100644 --- a/src/muz_qe/heap_trie.h +++ b/src/math/hilbert/heap_trie.h @@ -123,9 +123,9 @@ class heap_trie { } // push nodes whose keys are <= key into vector. - void find_le(Key key, ptr_vector& nodes) { + void find_le(KeyLE& le, Key key, ptr_vector& nodes) { for (unsigned i = 0; i < m_nodes.size(); ++i) { - if (KeyLE::le(m_nodes[i].first, key)) { + if (le.le(m_nodes[i].first, key)) { node* n = m_nodes[i].second; if (n->ref_count() > 0){ nodes.push_back(n); @@ -179,6 +179,7 @@ class heap_trie { }; small_object_allocator m_alloc; + KeyLE& m_le; unsigned m_num_keys; unsigned_vector m_keys; unsigned m_do_reshuffle; @@ -189,8 +190,9 @@ class heap_trie { public: - heap_trie(): + heap_trie(KeyLE& le): m_alloc("heap_trie"), + m_le(le), m_num_keys(0), m_do_reshuffle(4), m_root(0), @@ -255,7 +257,7 @@ public: for (unsigned i = 0; i < num_keys(); ++i) { for (unsigned j = 0; j < todo[index].size(); ++j) { ++m_stats.m_num_find_le_nodes; - to_trie(todo[index][j])->find_le(get_key(keys, i), todo[!index]); + to_trie(todo[index][j])->find_le(m_le, get_key(keys, i), todo[!index]); } todo[index].reset(); index = !index; @@ -577,7 +579,7 @@ private: verbose_stream() << " "; } verbose_stream() << nodes[i].first << " <=? " << key << " rc:" << m->ref_count() << "\n";); - if (m->ref_count() > 0 && KeyLE::le(nodes[i].first, key) && find_le(m, index+1, keys, check)) { + if (m->ref_count() > 0 && m_le.le(nodes[i].first, key) && find_le(m, index+1, keys, check)) { if (i > 0) { std::swap(nodes[i], nodes[0]); } diff --git a/src/muz_qe/hilbert_basis.cpp b/src/math/hilbert/hilbert_basis.cpp similarity index 92% rename from src/muz_qe/hilbert_basis.cpp rename to src/math/hilbert/hilbert_basis.cpp index e7c7928a3..a2a3f4654 100644 --- a/src/muz_qe/hilbert_basis.cpp +++ b/src/math/hilbert/hilbert_basis.cpp @@ -58,14 +58,13 @@ public: m_table.reset(); } - bool find(offset_t idx, values const& vs, offset_t& found_idx) { + bool find(offset_t idx, values const& vs) { // display_profile(idx, std::cout); int_table::iterator it = m_table.begin(), end = m_table.end(); for (; it != end; ++it) { offset_t offs(*it); ++m_stats.m_num_comparisons; if (*it != static_cast(idx.m_offset) && hb.is_subsumed(idx, offs)) { - found_idx = offs; ++m_stats.m_num_hit; return true; } @@ -163,20 +162,21 @@ private: class hilbert_basis::value_index2 { struct key_le { - static bool le(numeral const& n1, numeral const& n2) { - return hilbert_basis::is_abs_geq(n2, n1); + hilbert_basis& hb; + key_le(hilbert_basis& hb): hb(hb) {} + bool le(numeral const& n1, numeral const& n2) const { + return hb.is_abs_geq(n2, n1); } }; typedef heap_trie ht; + struct checker : public ht::check_value { hilbert_basis* hb; offset_t m_value; - offset_t* m_found; - checker(): hb(0), m_found(0) {} - virtual bool operator()(unsigned const& v) { - if (m_value.m_offset != v && hb->is_subsumed(m_value, offset_t(v))) { - *m_found = offset_t(v); + checker(): hb(0) {} + virtual bool operator()(unsigned const& v) { + if (m_value.m_offset != v) { // && hb->is_subsumed(m_value, offset_t(v))) { return true; } else { @@ -185,23 +185,25 @@ class hilbert_basis::value_index2 { } }; hilbert_basis& hb; + key_le m_le; ht m_trie; - vector m_found; - bool m_init; checker m_checker; - vector m_keys; + unsigned m_offset; numeral const* get_keys(values const& vs) { - return vs()-1; + return vs()-m_offset; } public: - value_index2(hilbert_basis& hb): hb(hb), m_init(false) { + value_index2(hilbert_basis& hb): + hb(hb), + m_le(hb), + m_trie(m_le), + m_offset(1) { m_checker.hb = &hb; } void insert(offset_t idx, values const& vs) { - init(); m_trie.insert(get_keys(vs), idx.m_offset); } @@ -209,15 +211,13 @@ public: m_trie.remove(get_keys(vs)); } - void reset() { - m_trie.reset(hb.get_num_vars()+1); - m_keys.resize(hb.get_num_vars()+1); + void reset(unsigned offset) { + m_offset = offset; + m_trie.reset(hb.get_num_vars()+m_offset); } - bool find(offset_t idx, values const& vs, offset_t& found_idx) { - init(); + bool find(offset_t idx, values const& vs) { m_checker.m_value = idx; - m_checker.m_found = &found_idx; return m_trie.find_le(get_keys(vs), m_checker); } @@ -236,14 +236,6 @@ public: void display(std::ostream& out) const { // m_trie.display(out); } - -private: - void init() { - if (!m_init) { - reset(); - m_init = true; - } - } }; @@ -254,6 +246,7 @@ class hilbert_basis::index { // typedef value_index1 value_index; typedef value_index2 value_index; + // typedef value_index3 value_index; struct stats { unsigned m_num_find; @@ -271,9 +264,10 @@ class hilbert_basis::index { value_index m_pos; value_index m_zero; stats m_stats; + unsigned m_num_ineqs; public: - index(hilbert_basis& hb): hb(hb), m_pos(hb), m_zero(hb) {} + index(hilbert_basis& hb): hb(hb), m_pos(hb), m_zero(hb), m_num_ineqs(0) {} void insert(offset_t idx, values const& vs) { ++m_stats.m_num_insert; @@ -287,6 +281,7 @@ public: value_index* map = 0; if (!m_neg.find(vs.weight(), map)) { map = alloc(value_index, hb); + map->reset(m_num_ineqs); m_neg.insert(vs.weight(), map); } map->insert(idx, vs); @@ -305,29 +300,30 @@ public: } } - bool find(offset_t idx, values const& vs, offset_t& found_idx) { + bool find(offset_t idx, values const& vs) { ++m_stats.m_num_find; if (vs.weight().is_pos()) { - return m_pos.find(idx, vs, found_idx); + return m_pos.find(idx, vs); } else if (vs.weight().is_zero()) { - return m_zero.find(idx, vs, found_idx); + return m_zero.find(idx, vs); } else { value_index* map; return m_neg.find(vs.weight(), map) && - map->find(idx, vs, found_idx); + map->find(idx, vs); } } - void reset() { + void reset(unsigned num_ineqs) { value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { - it->m_value->reset(); + dealloc(it->m_value); } - m_pos.reset(); - m_zero.reset(); + m_pos.reset(num_ineqs); + m_zero.reset(num_ineqs); + m_num_ineqs = num_ineqs; m_neg.reset(); } @@ -678,15 +674,25 @@ bool hilbert_basis::is_invalid_offset(offset_t offs) { void hilbert_basis::reset() { m_ineqs.reset(); - m_basis.reset(); + m_iseq.reset(); m_store.reset(); + m_basis.reset(); m_free_list.reset(); - m_active.reset(); - m_passive->reset(); - m_passive2->reset(); + m_sos.reset(); m_zero.reset(); - m_index->reset(); - m_cancel = false; + m_active.reset(); + if (m_passive) { + m_passive->reset(); + } + if (m_passive2) { + m_passive2->reset(); + } + if (m_index) { + m_index->reset(1); + } + m_ints.reset(); + m_current_ineq = 0; + } void hilbert_basis::collect_statistics(statistics& st) const { @@ -702,42 +708,46 @@ void hilbert_basis::reset_statistics() { m_index->reset_statistics(); } -void hilbert_basis::add_ge(num_vector const& v, numeral const& b) { +void hilbert_basis::add_ge(rational_vector const& v, rational const& b) { SASSERT(m_ineqs.empty() || v.size() + 1 == m_ineqs.back().size()); num_vector w; - w.push_back(-b); - w.append(v); + w.push_back(to_numeral(-b)); + for (unsigned i = 0; i < v.size(); ++i) { + w.push_back(to_numeral(v[i])); + } m_ineqs.push_back(w); m_iseq.push_back(false); } -void hilbert_basis::add_le(num_vector const& v, numeral const& b) { - num_vector w(v); +void hilbert_basis::add_le(rational_vector const& v, rational const& b) { + rational_vector w(v); for (unsigned i = 0; i < w.size(); ++i) { w[i].neg(); } add_ge(w, -b); } -void hilbert_basis::add_eq(num_vector const& v, numeral const& b) { +void hilbert_basis::add_eq(rational_vector const& v, rational const& b) { SASSERT(m_ineqs.empty() || v.size() + 1 == m_ineqs.back().size()); num_vector w; - w.push_back(-b); - w.append(v); + w.push_back(to_numeral(-b)); + for (unsigned i = 0; i < v.size(); ++i) { + w.push_back(to_numeral(v[i])); + } m_ineqs.push_back(w); m_iseq.push_back(true); } -void hilbert_basis::add_ge(num_vector const& v) { - add_ge(v, numeral(0)); +void hilbert_basis::add_ge(rational_vector const& v) { + add_ge(v, rational(0)); } -void hilbert_basis::add_le(num_vector const& v) { - add_le(v, numeral(0)); +void hilbert_basis::add_le(rational_vector const& v) { + add_le(v, rational(0)); } -void hilbert_basis::add_eq(num_vector const& v) { - add_eq(v, numeral(0)); +void hilbert_basis::add_eq(rational_vector const& v) { + add_eq(v, rational(0)); } void hilbert_basis::set_is_int(unsigned var_index) { @@ -799,7 +809,7 @@ lbool hilbert_basis::saturate() { stopwatch sw; sw.start(); lbool r = saturate(m_ineqs[m_current_ineq], m_iseq[m_current_ineq]); - IF_VERBOSE(1, + IF_VERBOSE(3, { statistics st; collect_statistics(st); st.display(verbose_stream()); @@ -823,7 +833,7 @@ lbool hilbert_basis::saturate_orig(num_vector const& ineq, bool is_eq) { m_active.reset(); m_passive->reset(); m_zero.reset(); - m_index->reset(); + m_index->reset(m_current_ineq+1); int_table support; TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq);); iterator it = begin(); @@ -895,7 +905,7 @@ bool hilbert_basis::vector_lt(offset_t idx1, offset_t idx2) const { lbool hilbert_basis::saturate(num_vector const& ineq, bool is_eq) { m_zero.reset(); - m_index->reset(); + m_index->reset(m_current_ineq+1); m_passive2->reset(); m_sos.reset(); TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq);); @@ -974,19 +984,21 @@ lbool hilbert_basis::saturate(num_vector const& ineq, bool is_eq) { return m_basis.empty()?l_false:l_true; } -void hilbert_basis::get_basis_solution(unsigned i, num_vector& v, bool& is_initial) { +void hilbert_basis::get_basis_solution(unsigned i, rational_vector& v, bool& is_initial) { offset_t offs = m_basis[i]; v.reset(); for (unsigned i = 1; i < get_num_vars(); ++i) { - v.push_back(vec(offs)[i]); + v.push_back(to_rational(vec(offs)[i])); } is_initial = !vec(offs)[0].is_zero(); } -void hilbert_basis::get_ge(unsigned i, num_vector& v, numeral& b, bool& is_eq) { +void hilbert_basis::get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq) { v.reset(); - v.append(m_ineqs[i].size() - 1, m_ineqs[i].c_ptr() + 1); - b = -m_ineqs[i][0]; + for (unsigned j = 1; j < m_ineqs[i].size(); ++j) { + v.push_back(to_rational(m_ineqs[i][j])); + } + b = to_rational(-m_ineqs[i][0]); is_eq = m_iseq[i]; } @@ -996,11 +1008,9 @@ void hilbert_basis::select_inequality() { unsigned best = m_current_ineq; unsigned non_zeros = get_num_nonzeros(m_ineqs[best]); unsigned prod = get_ineq_product(m_ineqs[best]); - //numeral diff = get_ineq_diff(m_ineqs[best]); for (unsigned j = best+1; prod != 0 && j < m_ineqs.size(); ++j) { unsigned non_zeros2 = get_num_nonzeros(m_ineqs[j]); unsigned prod2 = get_ineq_product(m_ineqs[j]); - //numeral diff2 = get_ineq_diff(m_ineqs[j]); if (prod2 == 0) { prod = prod2; non_zeros = non_zeros2; @@ -1009,7 +1019,6 @@ void hilbert_basis::select_inequality() { } if (non_zeros2 < non_zeros || (non_zeros2 == non_zeros && prod2 < prod)) { prod = prod2; - // diff = diff2; non_zeros = non_zeros2; best = j; } @@ -1121,8 +1130,7 @@ bool hilbert_basis::add_goal(offset_t idx) { bool hilbert_basis::is_subsumed(offset_t idx) { - offset_t found_idx; - if (m_index->find(idx, vec(idx), found_idx)) { + if (m_index->find(idx, vec(idx))) { ++m_stats.m_num_subsumptions; return true; } @@ -1316,7 +1324,7 @@ bool hilbert_basis::is_geq(values const& v, values const& w) const { return true; } -bool hilbert_basis::is_abs_geq(numeral const& v, numeral const& w) { +bool hilbert_basis::is_abs_geq(numeral const& v, numeral const& w) const { if (w.is_neg()) { return v <= w; } diff --git a/src/muz_qe/hilbert_basis.h b/src/math/hilbert/hilbert_basis.h similarity index 82% rename from src/muz_qe/hilbert_basis.h rename to src/math/hilbert/hilbert_basis.h index bad4b1fbd..733b3a2f0 100644 --- a/src/muz_qe/hilbert_basis.h +++ b/src/math/hilbert/hilbert_basis.h @@ -18,6 +18,11 @@ Author: Revision History: + Hilbert basis can be templatized + based on traits that define numeral: + as rational, mpz, checked_int64 + (checked or unchecked). + --*/ #ifndef _HILBERT_BASIS_H_ @@ -26,14 +31,28 @@ Revision History: #include "rational.h" #include "lbool.h" #include "statistics.h" +#include "checked_int64.h" + +typedef vector rational_vector; class hilbert_basis { -public: - typedef rational numeral; + + static const bool check = true; + typedef checked_int64 numeral; typedef vector num_vector; -private: + static checked_int64 to_numeral(rational const& r) { + if (!r.is_int64()) { + throw checked_int64::overflow_exception(); + } + return checked_int64(r.get_int64()); + } + static rational to_rational(checked_int64 const& i) { + return rational(i.get_int64(), rational::i64()); + } + class value_index1; class value_index2; + class value_index3; class index; class passive; class passive2; @@ -112,7 +131,7 @@ private: unsigned get_num_vars() const; numeral get_weight(values const & val, num_vector const& ineq) const; bool is_geq(values const& v, values const& w) const; - static bool is_abs_geq(numeral const& v, numeral const& w); + bool is_abs_geq(numeral const& v, numeral const& w) const; bool is_subsumed(offset_t idx); bool is_subsumed(offset_t i, offset_t j) const; void recycle(offset_t idx); @@ -147,16 +166,16 @@ public: // add inequality v*x >= 0 // add inequality v*x <= 0 // add equality v*x = 0 - void add_ge(num_vector const& v); - void add_le(num_vector const& v); - void add_eq(num_vector const& v); + void add_ge(rational_vector const& v); + void add_le(rational_vector const& v); + void add_eq(rational_vector const& v); // add inequality v*x >= b // add inequality v*x <= b // add equality v*x = b - void add_ge(num_vector const& v, numeral const& b); - void add_le(num_vector const& v, numeral const& b); - void add_eq(num_vector const& v, numeral const& b); + void add_ge(rational_vector const& v, rational const& b); + void add_le(rational_vector const& v, rational const& b); + void add_eq(rational_vector const& v, rational const& b); void set_is_int(unsigned var_index); bool get_is_int(unsigned var_index) const; @@ -164,10 +183,10 @@ public: lbool saturate(); unsigned get_basis_size() const { return m_basis.size(); } - void get_basis_solution(unsigned i, num_vector& v, bool& is_initial); + void get_basis_solution(unsigned i, rational_vector& v, bool& is_initial); unsigned get_num_ineqs() const { return m_ineqs.size(); } - void get_ge(unsigned i, num_vector& v, numeral& b, bool& is_eq); + void get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq); void set_cancel(bool f) { m_cancel = f; } diff --git a/src/math/interval/README b/src/math/interval/README index 75aa2e9c6..06ca1ea7a 100644 --- a/src/math/interval/README +++ b/src/math/interval/README @@ -1,2 +1,2 @@ Template for interval arithmetic. The template can be instantiated using different numeral (integers/mpz, rationals/mpq, floating-point/mpf, etc) packages. -The class im_default_config defines a default configuration for the template that uses rationals. It also shows what is the expected signature used by the template. \ No newline at end of file +The class im_default_config defines a default configuration for the template that uses rationals. It also shows what is the expected signature used by the template. diff --git a/src/math/polynomial/README b/src/math/polynomial/README index 5d079eea0..2d2f9f0a0 100644 --- a/src/math/polynomial/README +++ b/src/math/polynomial/README @@ -1,3 +1,3 @@ Polynomial manipulation package. It contains support for univariate (upolynomial.*) and multivariate polynomials (polynomial.*). -Multivariate polynomial factorization does not work yet (polynomial_factorization.*), and it is disabled. \ No newline at end of file +Multivariate polynomial factorization does not work yet (polynomial_factorization.*), and it is disabled. diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 87b1ef8a4..84ba606fd 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -660,8 +660,7 @@ namespace realclosure { return; // interval was already saved. to_restore.push_back(v); inc_ref(v); - void * mem = allocator().allocate(sizeof(mpbqi)); - v->m_old_interval = new (mem) mpbqi(); + v->m_old_interval = new (allocator()) mpbqi(); set_interval(*(v->m_old_interval), v->m_interval); } void save_interval(value * v) { @@ -1237,8 +1236,7 @@ namespace realclosure { } sign_condition * mk_sign_condition(unsigned qidx, int sign, sign_condition * prev_sc) { - void * mem = allocator().allocate(sizeof(sign_condition)); - return new (mem) sign_condition(qidx, sign, prev_sc); + return new (allocator()) sign_condition(qidx, sign, prev_sc); } /** @@ -1246,7 +1244,7 @@ namespace realclosure { This method does not set the interval. It remains (-oo, oo) */ rational_function_value * mk_rational_function_value_core(extension * ext, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { - rational_function_value * r = alloc(rational_function_value, ext); + rational_function_value * r = new (allocator()) rational_function_value(ext); inc_ref(ext); set_p(r->num(), num_sz, num); if (ext->is_algebraic()) { @@ -1283,7 +1281,7 @@ namespace realclosure { */ void mk_infinitesimal(symbol const & n, symbol const & pp_n, numeral & r) { unsigned idx = next_infinitesimal_idx(); - infinitesimal * eps = alloc(infinitesimal, idx, n, pp_n); + infinitesimal * eps = new (allocator()) infinitesimal(idx, n, pp_n); m_extensions[extension::INFINITESIMAL].push_back(eps); set_lower(eps->interval(), mpbq(0)); @@ -1335,7 +1333,7 @@ namespace realclosure { void mk_transcendental(symbol const & n, symbol const & pp_n, mk_interval & proc, numeral & r) { unsigned idx = next_transcendental_idx(); - transcendental * t = alloc(transcendental, idx, n, pp_n, proc); + transcendental * t = new (allocator()) transcendental(idx, n, pp_n, proc); m_extensions[extension::TRANSCENDENTAL].push_back(t); while (contains_zero(t->interval())) { @@ -1798,8 +1796,7 @@ namespace realclosure { M and scs will be empty after this operation. */ sign_det * mk_sign_det(mpz_matrix & M_s, scoped_polynomial_seq const & prs, int_buffer const & taqrs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) { - void * mem = allocator().allocate(sizeof(sign_det)); - sign_det * r = new (mem) sign_det(); + sign_det * r = new (allocator()) sign_det(); r->M_s.swap(M_s); set_array_p(r->m_prs, prs); r->m_taqrs.set(allocator(), taqrs.size(), taqrs.c_ptr()); @@ -1814,8 +1811,7 @@ namespace realclosure { */ algebraic * mk_algebraic(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, sign_det * sd, unsigned sc_idx) { unsigned idx = next_algebraic_idx(); - void * mem = allocator().allocate(sizeof(algebraic)); - algebraic * r = new (mem) algebraic(idx); + algebraic * r = new (allocator()) algebraic(idx); m_extensions[extension::ALGEBRAIC].push_back(r); set_p(r->m_p, p_sz, p); @@ -2561,8 +2557,7 @@ namespace realclosure { } rational_value * mk_rational() { - void * mem = allocator().allocate(sizeof(rational_value)); - return new (mem) rational_value(); + return new (allocator()) rational_value(); } /** diff --git a/src/math/subpaving/subpaving.cpp b/src/math/subpaving/subpaving.cpp index 0bbabc683..ad0819ad8 100644 --- a/src/math/subpaving/subpaving.cpp +++ b/src/math/subpaving/subpaving.cpp @@ -85,7 +85,6 @@ namespace subpaving { }; class context_mpf_wrapper : public context_wrapper { - f2n & m_fm; unsynch_mpq_manager & m_qm; scoped_mpf m_c; scoped_mpf_vector m_as; @@ -103,7 +102,6 @@ namespace subpaving { public: context_mpf_wrapper(f2n & fm, params_ref const & p, small_object_allocator * a): context_wrapper(fm, p, a), - m_fm(fm), m_qm(fm.m().mpq_manager()), m_c(fm.m()), m_as(fm.m()), @@ -145,7 +143,6 @@ namespace subpaving { }; class context_hwf_wrapper : public context_wrapper { - f2n & m_fm; unsynch_mpq_manager & m_qm; hwf m_c; svector m_as; @@ -166,7 +163,6 @@ namespace subpaving { public: context_hwf_wrapper(f2n & fm, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): context_wrapper(fm, p, a), - m_fm(fm), m_qm(qm) { } diff --git a/src/muz_qe/model2expr.cpp b/src/model/model2expr.cpp similarity index 100% rename from src/muz_qe/model2expr.cpp rename to src/model/model2expr.cpp diff --git a/src/muz_qe/model2expr.h b/src/model/model2expr.h similarity index 100% rename from src/muz_qe/model2expr.h rename to src/model/model2expr.h diff --git a/src/model/model_v2_pp.cpp b/src/model/model_v2_pp.cpp index 9073ddece..4600ccc9e 100644 --- a/src/model/model_v2_pp.cpp +++ b/src/model/model_v2_pp.cpp @@ -80,3 +80,8 @@ void model_v2_pp(std::ostream & out, model_core const & md, bool partial) { display_constants(out, md); display_functions(out, md, partial); } + +// debugging support +void pp(model_core const & md) { + model_v2_pp(std::cout, md, false); +} diff --git a/src/muz/README b/src/muz/README new file mode 100644 index 000000000..c7d5a9665 --- /dev/null +++ b/src/muz/README @@ -0,0 +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 diff --git a/src/muz/base/dl_boogie_proof.cpp b/src/muz/base/dl_boogie_proof.cpp new file mode 100644 index 000000000..d11e4a932 --- /dev/null +++ b/src/muz/base/dl_boogie_proof.cpp @@ -0,0 +1,297 @@ +/** + +Example from Boogie: + +(derivation +(step s!4 (main_loop_LoopHead true true) + rule!3 (subst + (= assertsPassed@@1 true) + (= assertsPassed@2@@0 true) + (= main_loop_LoopHead_assertsPassed true) + ) + (labels @950 +895 +668 +670 +893 +899 ) + (ref true )) +(step s!3 (main true false) + rule!1 (subst + (= assertsPassed true) + (= assertsPassed@0 true) + (= assertsPassed@2 false) + (= main_assertsPassed false) + ) + (labels @839 +763 +547 +546 +761 +544 +545 +si_fcall_805 +681 +768 ) + (ref s!4 )) +(step s!2 (main_SeqInstr true false) + rule!2 (subst + (= assertsPassed@@0 true) + (= assertsPassed@0@@0 false) + (= main_SeqInstr_assertsPassed false) + ) + (labels @890 +851 +572 +si_fcall_883 +853 ) + (ref s!3 )) +(step s!1 (@Fail!0) + rule!4 (subst + (= assertsPassed@@2 true) + (= main_SeqInstr_assertsPassed@@0 false) + ) + (labels ) + (ref s!2 )) +) +(model +"tickleBool -> { + true -> true + false -> true + else -> true +} +") +*/ + +#include "dl_boogie_proof.h" +#include "model_pp.h" +#include "proof_utils.h" +#include "ast_pp.h" +#include "qe_util.h" + +namespace datalog { + + /** + \brief push hyper-resolution steps upwards such that every use of + hyper-resolution uses a premise that is not derived from hyper-resolution. + + perform the following rewrite: + + hr(hr(p1,p2,p3,..),p4,p5) => hr(p1,hr(p2,p4),p3,..,p5) + + */ + + void mk_input_resolution(proof_ref& pr) { + ast_manager& m = pr.get_manager(); + proof_ref pr1(m); + proof_ref_vector premises1(m), premises2(m), premises(m); + expr_ref conclusion1(m), conclusion2(m), conclusion(m); + svector > positions1, positions2, positions; + vector substs1, substs2, substs; + + if (m.is_hyper_resolve(pr, premises1, conclusion1, positions1, substs1) && + m.is_hyper_resolve(premises1[0].get(), premises, conclusion2, positions, substs2)) { + for (unsigned i = 1; i < premises1.size(); ++i) { + pr1 = premises1[i].get(); + mk_input_resolution(pr1); + premises1[i] = pr1; + } + for (unsigned i = 0; i < premises.size(); ++i) { + pr1 = premises[i].get(); + mk_input_resolution(pr1); + premises[i] = pr1; + } + unsigned sz = premises.size(); + for (unsigned i = 1; i < sz; ++i) { + proof* premise = premises[i].get(); + expr_ref_vector literals(m); + expr* l1, *l2; + if (!m.is_implies(premise, l1, l2)) { + continue; + } + qe::flatten_and(l1, literals); + positions2.reset(); + premises2.reset(); + premises2.push_back(premise); + substs2.reset(); + for (unsigned j = 0; j < literals.size(); ++j) { + expr* lit = literals[j].get(); + for (unsigned k = 1; k < premises1.size(); ++k) { + if (m.get_fact(premises1[k].get()) == lit) { + premises2.push_back(premises1[k].get()); + positions2.push_back(std::make_pair(j+1,0)); + substs2.push_back(expr_ref_vector(m)); + break; + } + } + } + premises[i] = m.mk_hyper_resolve(premises2.size(), premises2.c_ptr(), l2, positions2, substs2); + } + conclusion = conclusion1; + pr = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); + } + } + + void boogie_proof::set_proof(proof* p) { + std::cout << "set proof\n"; + m_proof = p; + proof_utils::push_instantiations_up(m_proof); + mk_input_resolution(m_proof); + std::cout << "proof set\n"; + } + + void boogie_proof::set_model(model* m) { + m_model = m; + } + + void boogie_proof::pp(std::ostream& out) { + if (m_proof) { + pp_proof(out); + } + if (m_model) { + model_pp(out, *m_model); + } + } + + void boogie_proof::pp_proof(std::ostream& out) { + vector steps; + ptr_vector rules; + rules.push_back(m_proof); + steps.push_back(step()); + obj_map index; + index.insert(m_proof, 0); + + for (unsigned j = 0; j < rules.size(); ++j) { + proof* p = rules[j]; + proof_ref_vector premises(m); + expr_ref conclusion(m); + svector > positions; + vector substs; + + expr* tmp; + steps[j].m_fact = m.get_fact(p); + m.is_implies(steps[j].m_fact, tmp, steps[j].m_fact); + get_subst(p, steps[j].m_subst); + get_labels(p, steps[j].m_labels); + + if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { + for (unsigned i = 1; i < premises.size(); ++i) { + proof* premise = premises[i].get(); + unsigned position = 0; + if (!index.find(premise, position)) { + position = rules.size(); + rules.push_back(premise); + steps.push_back(step()); + index.insert(premise, position); + } + steps[j].m_refs.push_back(position); + } + get_rule_name(premises[0].get(), steps[j].m_rule_name); + } + } + for (unsigned j = steps.size(); j > 0; ) { + --j; + step &s = steps[j]; + + // TBD + // s.m_labels; + + // set references, compensate for reverse ordering. + for (unsigned i = 0; i < s.m_refs.size(); ++i) { + s.m_refs[i] = rules.size()-1-s.m_refs[i]; + } + } + steps.reverse(); + pp_steps(out, steps); + } + + /** + \brief extract the instantiation by searching for the first occurrence of a hyper-resolution + rule that produces an instance. + */ + void boogie_proof::get_subst(proof* p, subst& s) { + ptr_vector todo; + todo.push_back(p); + ast_mark visited; + std::cout << "get_subst\n" << mk_pp(p, m) << "\n"; + while (!todo.empty()) { + proof* p = todo.back(); + todo.pop_back(); + if (visited.is_marked(p)) { + continue; + } + visited.mark(p, true); + proof_ref_vector premises(m); + expr_ref conclusion(m); + svector > positions; + vector substs; + if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { + expr_ref_vector const& sub = substs[0]; + if (!sub.empty()) { + quantifier* q = to_quantifier(m.get_fact(premises[0].get())); + unsigned sz = sub.size(); + SASSERT(sz == q->get_num_decls()); + for (unsigned i = 0; i < sz; ++i) { + s.push_back(std::make_pair(q->get_decl_name(sz-1-i), sub[i])); + } + return; + } + } + unsigned sz = m.get_num_parents(p); + for (unsigned i = 0; i < sz; ++i) { + todo.push_back(m.get_parent(p, i)); + } + } + } + + void boogie_proof::get_rule_name(proof* p, symbol& n) { + + } + + void boogie_proof::get_labels(proof* p, labels& lbls) { + + } + + void boogie_proof::pp_steps(std::ostream& out, vector& steps) { + out << "(derivation\n"; + for (unsigned i = 0; i < steps.size(); ++i) { + pp_step(out, i, steps[i]); + } + out << ")\n"; + + } + + // step :: "(" "step" step-name fact rule-name subst labels premises ")" + void boogie_proof::pp_step(std::ostream& out, unsigned id, step& s) { + out << "(step\n"; + out << " s!" << id << " "; + pp_fact(out, s.m_fact); + out << " " << s.m_rule_name << "\n"; + pp_subst(out << " ", s.m_subst); + pp_labels(out << " ", s.m_labels); + pp_premises(out << " ", s.m_refs); + out << ")\n"; + } + + // fact :: "(" predicate theory-term* ")" + void boogie_proof::pp_fact(std::ostream& out, expr* fact) { + out << mk_pp(fact, m) << "\n"; + } + + // subst :: "(" "subst" assignment* ")" + void boogie_proof::pp_subst(std::ostream& out, subst& s) { + out << "(subst"; + for (unsigned i = 0; i < s.size(); ++i) { + pp_assignment(out, s[i].first, s[i].second); + } + out << ")\n"; + } + + // assignment :: "(" "=" variable theory-term ")" + void boogie_proof::pp_assignment(std::ostream& out, symbol const& v, expr* t) { + out << "\n (= " << v << " " << mk_pp(t, m) << ")"; + } + + + // labels :: "(" "labels" label* ")" + void boogie_proof::pp_labels(std::ostream& out, labels& lbls) { + out << "(labels"; + for (unsigned i = 0; i < lbls.size(); ++i) { + out << " " << lbls[i]; + } + out << ")\n"; + } + + // premises "(" "ref" step-name* ")" + void boogie_proof::pp_premises(std::ostream& out, refs& refs) { + out << "(ref"; + for (unsigned i = 0; i < refs.size(); ++i) { + out << " s!" << refs[i]; + } + out << ")\n"; + } + + +} diff --git a/src/muz/base/dl_boogie_proof.h b/src/muz/base/dl_boogie_proof.h new file mode 100644 index 000000000..6c8fbbae3 --- /dev/null +++ b/src/muz/base/dl_boogie_proof.h @@ -0,0 +1,87 @@ +/** + +output :: derivation model + +derivation :: "(" "derivation" step* ")" + +step :: "(" "step" step-name fact rule-name subst labels premises ")" + +step-name :: identifier + +rule-name :: identifier + +fact :: "(" predicate theory-term* ")" + +subst :: "(" "subst" assignment* ")" + +assignment :: "(" "=" variable theory-term ")" + +labels :: "(" "labels" label* ")" + +premises :: "(" "ref" step-name* ")" + +model :: "(" "model" smtlib2-model ")" + +In each step the "fact" is derivable by hyper-resolution from the named +premises and the named rule, under the given substitution for the +universally quantified variables in the rule. The premises of each +step must have occurred previously in the step sequence. The last fact +is a nullary placeholder predicate representing satisfaction of the query +(its name is arbitrary). + +The labels list consists of all the positively labeled sub-formulas whose +truth is used in the proof, and all the negatively labeled formulas whose +negation is used. A theory-term is a ground term using only interpreted +constants of the background theories. + +The smtlib2-model gives an interpretation of the uninterpreted constants +in the background theory under which the derivation is valid. Currently +it is a quoted string in the old z3 model format, for compatibility with +Boogie, however, this should be changed to the new model format (using +define-fun) when Boogie supports this. + +*/ + +#include "ast.h" +#include "model.h" + +namespace datalog { + class boogie_proof { + typedef vector > subst; + typedef svector labels; + typedef unsigned_vector refs; + struct step { + symbol m_rule_name; + expr* m_fact; + subst m_subst; + labels m_labels; + refs m_refs; + }; + + ast_manager& m; + proof_ref m_proof; + model_ref m_model; + + void pp_proof(std::ostream& out); + void pp_steps(std::ostream& out, vector& steps); + void pp_step(std::ostream& out, unsigned i, step& s); + void pp_fact(std::ostream& out, expr* fact); + void pp_subst(std::ostream& out, subst& s); + void pp_assignment(std::ostream& out, symbol const& v, expr* t); + void pp_labels(std::ostream& out, labels& lbls); + void pp_premises(std::ostream& out, refs& refs); + + void get_subst(proof* p, subst& sub); + void get_rule_name(proof* p, symbol&); + void get_labels(proof* p, labels&); + + public: + boogie_proof(ast_manager& m): m(m), m_proof(m), m_model(0) {} + + void set_proof(proof* p); + + void set_model(model* m); + + void pp(std::ostream& out); + }; +} diff --git a/src/muz_qe/dl_context.cpp b/src/muz/base/dl_context.cpp similarity index 68% rename from src/muz_qe/dl_context.cpp rename to src/muz/base/dl_context.cpp index 70610f04c..89fb19569 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -19,34 +19,15 @@ 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_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_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"expr_functors.h" -#include"dl_mk_partial_equiv.h" -#include"dl_mk_bit_blast.h" -#include"dl_mk_array_blast.h" #include"datatype_decl_plugin.h" -#include"expr_abstract.h" +#include"scoped_proof.h" +#include"fixedpoint_params.hpp" namespace datalog { @@ -215,43 +196,57 @@ 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), + m_params(alloc(fixedpoint_params, m_params_ref)), m_decl_util(m), m_rewriter(m), m_var_subst(m), m_rule_manager(*this), + m_elim_unused_vars(m), + m_abstractor(m), + m_contains_p(*this), + m_check_pred(m_contains_p, m), + m_transf(*this), m_trail(*this), m_pinned(m), m_vars(m), m_rule_set(*this), + m_transformed_rule_set(*this), + m_rule_fmls_head(0), m_rule_fmls(m), m_background(m), + m_mc(0), + m_rel(0), + m_engine(0), m_closed(false), m_saturation_was_run(false), + m_last_status(OK), m_last_answer(m), - m_engine(LAST_ENGINE), + m_engine_type(LAST_ENGINE), m_cancel(false) { - - //register plugins for builtin tables + re.set_context(this); } context::~context() { reset(); + dealloc(m_params); } void context::reset() { m_trail.reset(); m_rule_set.reset(); + m_rule_fmls_head = 0; + m_rule_fmls.reset(); + m_rule_names.reset(); m_argument_var_names.reset(); m_preds.reset(); m_preds_by_name.reset(); reset_dealloc_values(m_sorts); - m_pdr = 0; - m_bmc = 0; + m_engine = 0; m_rel = 0; } @@ -264,17 +259,45 @@ namespace datalog { } context::sort_domain & context::get_sort_domain(relation_sort s) { - sort_domain * dom; - TRUSTME( m_sorts.find(s, dom) ); - return *dom; + return *m_sorts.find(s); } const context::sort_domain & context::get_sort_domain(relation_sort s) const { - sort_domain * dom; - TRUSTME( m_sorts.find(s, dom) ); - return *dom; + 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(); } + + 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); SASSERT(!m_sorts.contains(s)); @@ -292,18 +315,6 @@ namespace datalog { m_sorts.insert(s, dom); } - bool context::is_predicate(func_decl * pred) const { - return m_preds.contains(pred); - } - - func_decl * context::try_get_predicate_decl(symbol pred_name) const { - func_decl * res; - if (!m_preds_by_name.find(pred_name, res)) { - return 0; - } - return res; - } - void context::register_variable(func_decl* var) { m_vars.push_back(m.mk_const(var)); } @@ -311,18 +322,19 @@ namespace datalog { expr_ref context::bind_variables(expr* fml, bool is_forall) { expr_ref result(m); app_ref_vector const & vars = m_vars; + rule_manager& rm = get_rule_manager(); if (vars.empty()) { result = fml; } else { - ptr_vector sorts; - expr_abstract(m, 0, vars.size(), reinterpret_cast(vars.c_ptr()), fml, result); - get_free_vars(result, sorts); + m_names.reset(); + m_abstractor(0, vars.size(), reinterpret_cast(vars.c_ptr()), fml, result); + rm.collect_vars(result); + ptr_vector& sorts = rm.get_var_sorts(); if (sorts.empty()) { result = fml; } else { - svector names; for (unsigned i = 0; i < sorts.size(); ++i) { if (!sorts[i]) { if (i < vars.size()) { @@ -333,30 +345,36 @@ namespace datalog { } } if (i < vars.size()) { - names.push_back(vars[i]->get_decl()->get_name()); + m_names.push_back(vars[i]->get_decl()->get_name()); } else { - names.push_back(symbol(i)); + m_names.push_back(symbol(i)); } } quantifier_ref q(m); sorts.reverse(); - q = m.mk_quantifier(is_forall, sorts.size(), sorts.c_ptr(), names.c_ptr(), result); - elim_unused_vars(m, q, result); + q = m.mk_quantifier(is_forall, sorts.size(), sorts.c_ptr(), m_names.c_ptr(), result); + m_elim_unused_vars(q, result); } } return result; } void context::register_predicate(func_decl * decl, bool named) { - if (m_preds.contains(decl)) { - return; + if (!is_predicate(decl)) { + m_pinned.push_back(decl); + m_preds.insert(decl); + if (named) { + m_preds_by_name.insert(decl->get_name(), decl); + } } - m_pinned.push_back(decl); - m_preds.insert(decl); - if (named) { - SASSERT(!m_preds_by_name.contains(decl->get_name())); - m_preds_by_name.insert(decl->get_name(), decl); + } + + void context::restrict_predicates(func_decl_set const& preds) { + m_preds.reset(); + func_decl_set::iterator it = preds.begin(), end = preds.end(); + for (; it != end; ++it) { + m_preds.insert(*it); } } @@ -432,7 +450,9 @@ namespace datalog { void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { if (relation_name_cnt > 0) { - ensure_rel(); + ensure_engine(); + } + if (relation_name_cnt > 0 && m_rel) { m_rel->set_predicate_representation(pred, relation_name_cnt, relation_names); } } @@ -442,29 +462,14 @@ namespace datalog { func_decl* new_pred = m.mk_fresh_func_decl(prefix, suffix, arity, domain, m.mk_bool_sort()); - register_predicate(new_pred); + register_predicate(new_pred, true); - if (m_rel.get()) { + if (m_rel) { m_rel->inherit_predicate_kind(new_pred, orig_pred); } return new_pred; } - void context::set_output_predicate(func_decl * pred) { - ensure_rel(); - m_rel->set_output_predicate(pred); - } - - bool context::is_output_predicate(func_decl * pred) { - ensure_rel(); - return m_rel->is_output_predicate(pred); - } - - const decl_set & context::get_output_predicates() { - ensure_rel(); - return m_rel->get_output_predicates(); - } - void context::add_rule(expr* rl, symbol const& name) { m_rule_fmls.push_back(rl); m_rule_names.push_back(name); @@ -472,13 +477,19 @@ namespace datalog { void context::flush_add_rules() { datalog::rule_manager& rm = get_rule_manager(); - datalog::rule_ref_vector rules(rm); - for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { - rm.mk_rule(m_rule_fmls[i].get(), rules, m_rule_names[i]); + scoped_proof_mode _scp(m, generate_proof_trace()?PGM_FINE:PGM_DISABLED); + while (m_rule_fmls_head < m_rule_fmls.size()) { + expr* fml = m_rule_fmls[m_rule_fmls_head].get(); + proof* p = generate_proof_trace()?m.mk_asserted(fml):0; + rm.mk_rule(fml, p, m_rule_set, m_rule_names[m_rule_fmls_head]); + ++m_rule_fmls_head; + } + 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); } - add_rules(rules); - m_rule_fmls.reset(); - m_rule_names.reset(); } // @@ -487,21 +498,28 @@ namespace datalog { // void context::update_rule(expr* rl, symbol const& name) { datalog::rule_manager& rm = get_rule_manager(); - datalog::rule_ref_vector rules(rm); - rm.mk_rule(rl, rules, name); - if (rules.size() != 1) { + proof* p = 0; + if (generate_proof_trace()) { + p = m.mk_asserted(rl); + } + unsigned size_before = m_rule_set.get_num_rules(); + rm.mk_rule(rl, p, m_rule_set, name); + unsigned size_after = m_rule_set.get_num_rules(); + if (size_before + 1 != size_after) { std::stringstream strm; strm << "Rule " << name << " has a non-trivial body. It cannot be modified"; throw default_exception(strm.str()); } - rule_ref r(rules[0].get(), rm); + // The new rule is inserted last: + rule_ref r(m_rule_set.get_rule(size_before), rm); rule_ref_vector const& rls = m_rule_set.get_rules(); rule* old_rule = 0; - for (unsigned i = 0; i < rls.size(); ++i) { + for (unsigned i = 0; i < size_before; ++i) { if (rls[i]->name() == name) { if (old_rule) { std::stringstream strm; strm << "Rule " << name << " occurs twice. It cannot be modified"; + m_rule_set.del_rule(r); throw default_exception(strm.str()); } old_rule = rls[i]; @@ -514,11 +532,11 @@ namespace datalog { old_rule->display(*this, strm); strm << "does not subsume new rule "; r->display(*this, strm); + m_rule_set.del_rule(r); throw default_exception(strm.str()); } m_rule_set.del_rule(old_rule); } - m_rule_set.add_rule(r); } bool context::check_subsumes(rule const& stronger_rule, rule const& weaker_rule) { @@ -543,58 +561,18 @@ namespace datalog { } unsigned context::get_num_levels(func_decl* pred) { - switch(get_engine()) { - case DATALOG_ENGINE: - throw default_exception("get_num_levels is not supported for datalog engine"); - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - return m_pdr->get_num_levels(pred); - case BMC_ENGINE: - case QBMC_ENGINE: - throw default_exception("get_num_levels is not supported for bmc"); - case TAB_ENGINE: - throw default_exception("get_num_levels is not supported for tab"); - default: - throw default_exception("unknown engine"); - } + ensure_engine(); + return m_engine->get_num_levels(pred); } expr_ref context::get_cover_delta(int level, func_decl* pred) { - switch(get_engine()) { - case DATALOG_ENGINE: - throw default_exception("operation is not supported for datalog engine"); - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - return m_pdr->get_cover_delta(level, pred); - case BMC_ENGINE: - case QBMC_ENGINE: - throw default_exception("operation is not supported for BMC engine"); - case TAB_ENGINE: - throw default_exception("operation is not supported for TAB engine"); - default: - throw default_exception("unknown engine"); - } + ensure_engine(); + return m_engine->get_cover_delta(level, pred); } void context::add_cover(int level, func_decl* pred, expr* property) { - switch(get_engine()) { - case DATALOG_ENGINE: - throw default_exception("operation is not supported for datalog engine"); - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - m_pdr->add_cover(level, pred, property); - break; - case BMC_ENGINE: - case QBMC_ENGINE: - throw default_exception("operation is not supported for BMC engine"); - case TAB_ENGINE: - throw default_exception("operation is not supported for TAB engine"); - default: - throw default_exception("unknown engine"); - } + ensure_engine(); + m_engine->add_cover(level, pred, property); } void context::check_uninterpreted_free(rule_ref& r) { @@ -618,28 +596,16 @@ namespace datalog { } } - class context::contains_pred : public i_expr_pred { - rule_manager const& m; - public: - contains_pred(rule_manager const& m): m(m) {} - virtual ~contains_pred() {} - - virtual bool operator()(expr* e) { - return is_app(e) && m.is_predicate(to_app(e)); - } - }; void context::check_existential_tail(rule_ref& r) { unsigned ut_size = r->get_uninterpreted_tail_size(); unsigned t_size = r->get_tail_size(); - contains_pred contains_p(get_rule_manager()); - check_pred check_pred(contains_p, get_manager()); TRACE("dl", r->display_smt2(get_manager(), tout); tout << "\n";); for (unsigned i = ut_size; i < t_size; ++i) { app* t = r->get_tail(i); TRACE("dl", tout << "checking: " << mk_ismt2_pp(t, get_manager()) << "\n";); - if (check_pred(t)) { + if (m_check_pred(t)) { std::ostringstream out; out << "interpreted body " << mk_ismt2_pp(t, get_manager()) << " contains recursive predicate"; throw default_exception(out.str()); @@ -658,8 +624,7 @@ namespace datalog { } } ast_manager& m = get_manager(); - datalog::rule_manager& rm = get_rule_manager(); - contains_pred contains_p(rm); + contains_pred contains_p(*this); check_pred check_pred(contains_p, get_manager()); for (unsigned i = ut_size; i < t_size; ++i) { @@ -672,7 +637,7 @@ namespace datalog { continue; } visited.mark(e, true); - if (rm.is_predicate(e)) { + if (is_predicate(e)) { } else if (m.is_and(e) || m.is_or(e)) { todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); @@ -682,7 +647,7 @@ namespace datalog { todo.push_back(e2); } else if (is_quantifier(e)) { - todo.append(to_quantifier(e)->get_expr()); + todo.push_back(to_quantifier(e)->get_expr()); } else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && m.is_true(e1)) { @@ -701,6 +666,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()); } @@ -717,9 +683,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); @@ -736,27 +704,27 @@ namespace datalog { check_existential_tail(r); check_positive_predicates(r); break; + case CLP_ENGINE: + check_existential_tail(r); + check_positive_predicates(r); + break; + case LAST_ENGINE: default: UNREACHABLE(); break; } + if (generate_proof_trace() && !r->get_proof()) { + m_rule_manager.mk_rule_asserted_proof(*r.get()); + } } void context::add_rule(rule_ref& r) { m_rule_set.add_rule(r); } - void context::add_rules(rule_ref_vector& rules) { - for (unsigned i = 0; i < rules.size(); ++i) { - rule_ref rule(rules[i].get(), rules.get_manager()); - add_rule(rule); - } - } - - void context::add_fact(func_decl * pred, const relation_fact & fact) { if (get_engine() == DATALOG_ENGINE) { - ensure_rel(); + ensure_engine(); m_rel->add_fact(pred, fact); } else { @@ -768,18 +736,21 @@ namespace datalog { void context::add_fact(app * head) { SASSERT(is_fact(head)); - relation_fact fact(get_manager()); - unsigned n=head->get_num_args(); - for (unsigned i=0; iget_num_args(); + for (unsigned i = 0; i < n; i++) { fact.push_back(to_app(head->get_arg(i))); } add_fact(head->get_decl(), fact); } + bool context::has_facts(func_decl * pred) const { + return m_rel && m_rel->has_facts(pred); + } + void context::add_table_fact(func_decl * pred, const table_fact & fact) { if (get_engine() == DATALOG_ENGINE) { - ensure_rel(); + ensure_engine(); m_rel->add_fact(pred, fact); } else { @@ -829,28 +800,16 @@ namespace datalog { m_closed = false; } - void context::transform_rules(model_converter_ref& mc, proof_converter_ref& pc) { - rule_transformer transf(*this); - transf.register_plugin(alloc(mk_filter_rules,*this)); - transf.register_plugin(alloc(mk_simple_joins,*this)); - - if (unbound_compressor()) { - transf.register_plugin(alloc(mk_unbound_compressor,*this)); - } - - if (similarity_compressor()) { - transf.register_plugin(alloc(mk_similarity_compressor, *this, - similarity_compressor_threshold())); - } - transf.register_plugin(alloc(datalog::mk_partial_equivalence_transformer, *this)); - - transform_rules(transf, mc, pc); + void context::transform_rules(rule_transformer::plugin* plugin) { + rule_transformer transformer(*this); + transformer.register_plugin(plugin); + transform_rules(transformer); } - - void context::transform_rules(rule_transformer& transf, model_converter_ref& mc, proof_converter_ref& pc) { + + void context::transform_rules(rule_transformer& transf) { SASSERT(m_closed); //we must finish adding rules before we start transforming them TRACE("dl", display_rules(tout);); - if (transf(m_rule_set, mc, pc)) { + if (transf(m_rule_set)) { //we have already ensured the negation is stratified and transformations //should not break the stratification m_rule_set.ensure_closed(); @@ -859,40 +818,19 @@ namespace datalog { } } - void context::replace_rules(rule_set & rs) { + void context::replace_rules(rule_set const & rs) { SASSERT(!m_closed); - m_rule_set.reset(); - m_rule_set.add_rules(rs); + m_rule_set.replace_rules(rs); + if (m_rel) { + m_rel->restrict_predicates(get_predicates()); + } } - void context::apply_default_transformation(model_converter_ref& mc, proof_converter_ref& pc) { - ensure_closed(); - datalog::rule_transformer transf(*this); - transf.register_plugin(alloc(datalog::mk_coi_filter, *this)); - transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this)); + void context::record_transformed_rules() { + m_transformed_rule_set.replace_rules(m_rule_set); + } - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 35005)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 35000)); - transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34990)); - transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34980)); - - //and another round of inlining - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34975)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34970)); - transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34960)); - transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34950)); - - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34940)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34930)); - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34920)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34910)); - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34900)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34890)); - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34880)); - - transf.register_plugin(alloc(datalog::mk_bit_blast, *this, 35000)); - transf.register_plugin(alloc(datalog::mk_array_blast, *this, 36000)); - transform_rules(transf, mc, pc); + void context::apply_default_transformation() { } void context::collect_params(param_descrs& p) { @@ -902,17 +840,7 @@ namespace datalog { void context::updt_params(params_ref const& p) { m_params_ref.copy(p); - if (m_pdr.get()) m_pdr->updt_params(); - } - - void context::collect_predicates(decl_set& res) { - ensure_rel(); - m_rel->collect_predicates(res); - } - - void context::restrict_predicates(decl_set const& res) { - ensure_rel(); - m_rel->restrict_predicates(res); + if (m_engine.get()) m_engine->updt_params(); } expr_ref context::get_background_assertion() { @@ -933,88 +861,100 @@ namespace datalog { void context::cancel() { m_cancel = true; - if (m_pdr.get()) m_pdr->cancel(); - 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(); + m_last_status = CANCELED; + m_transf.cancel(); + if (m_engine) m_engine->cancel(); } void context::cleanup() { m_cancel = false; - if (m_pdr.get()) m_pdr->cleanup(); - if (m_bmc.get()) m_bmc->cleanup(); - if (m_rel.get()) m_rel->cleanup(); - if (m_tab.get()) m_tab->cleanup(); + m_last_status = OK; + if (m_engine) m_engine->cleanup(); } class context::engine_type_proc { ast_manager& m; arith_util a; datatype_util dt; - DL_ENGINE m_engine; + DL_ENGINE m_engine_type; public: - engine_type_proc(ast_manager& m): m(m), a(m), dt(m), m_engine(DATALOG_ENGINE) {} + engine_type_proc(ast_manager& m): m(m), a(m), dt(m), m_engine_type(DATALOG_ENGINE) {} + + DL_ENGINE get_engine() const { return m_engine_type; } - DL_ENGINE get_engine() const { return m_engine; } void operator()(expr* e) { if (is_quantifier(e)) { - m_engine = QPDR_ENGINE; + m_engine_type = QPDR_ENGINE; } - else if (a.is_int_real(e) && m_engine != QPDR_ENGINE) { - m_engine = PDR_ENGINE; - } - else if (is_var(e) && m.is_bool(e)) { - m_engine = PDR_ENGINE; - } - else if (dt.is_datatype(m.get_sort(e))) { - m_engine = PDR_ENGINE; + else if (m_engine_type != QPDR_ENGINE) { + if (a.is_int_real(e)) { + m_engine_type = PDR_ENGINE; + } + else if (is_var(e) && m.is_bool(e)) { + m_engine_type = PDR_ENGINE; + } + else if (dt.is_datatype(m.get_sort(e))) { + m_engine_type = PDR_ENGINE; + } } } }; void context::configure_engine() { - symbol e = m_params.engine(); + symbol e = m_params->engine(); if (e == symbol("datalog")) { - m_engine = DATALOG_ENGINE; + m_engine_type = DATALOG_ENGINE; } else if (e == symbol("pdr")) { - m_engine = PDR_ENGINE; + m_engine_type = PDR_ENGINE; } else if (e == symbol("qpdr")) { - m_engine = QPDR_ENGINE; + m_engine_type = QPDR_ENGINE; } else if (e == symbol("bmc")) { - m_engine = BMC_ENGINE; + m_engine_type = BMC_ENGINE; } else if (e == symbol("qbmc")) { - m_engine = QBMC_ENGINE; + m_engine_type = QBMC_ENGINE; } else if (e == symbol("tab")) { - m_engine = TAB_ENGINE; + m_engine_type = TAB_ENGINE; + } + else if (e == symbol("clp")) { + m_engine_type = CLP_ENGINE; } else if (e == symbol("duality")) { - m_engine = DUALITY_ENGINE; + m_engine_type = DUALITY_ENGINE; } - if (m_engine == LAST_ENGINE) { + if (m_engine_type == LAST_ENGINE) { expr_fast_mark1 mark; engine_type_proc proc(m); - m_engine = DATALOG_ENGINE; - for (unsigned i = 0; m_engine == DATALOG_ENGINE && i < m_rule_set.get_num_rules(); ++i) { + m_engine_type = DATALOG_ENGINE; + for (unsigned i = 0; m_engine_type == DATALOG_ENGINE && i < m_rule_set.get_num_rules(); ++i) { rule * r = m_rule_set.get_rule(i); quick_for_each_expr(proc, mark, r->get_head()); for (unsigned j = 0; j < r->get_tail_size(); ++j) { quick_for_each_expr(proc, mark, r->get_tail(j)); } - m_engine = proc.get_engine(); + m_engine_type = proc.get_engine(); + } + for (unsigned i = m_rule_fmls_head; m_engine_type == DATALOG_ENGINE && i < m_rule_fmls.size(); ++i) { + expr* fml = m_rule_fmls[i].get(); + while (is_quantifier(fml)) { + fml = to_quantifier(fml)->get_expr(); + } + quick_for_each_expr(proc, mark, fml); + m_engine_type = proc.get_engine(); } } } lbool context::query(expr* query) { +#if 0 + // TODO: what? if(get_engine() != DUALITY_ENGINE) { new_query(); rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); @@ -1024,201 +964,97 @@ namespace datalog { check_rule(r); } } - switch(get_engine()) { - case DATALOG_ENGINE: - return rel_query(query); - case PDR_ENGINE: - case QPDR_ENGINE: - return pdr_query(query); - case BMC_ENGINE: - case QBMC_ENGINE: - return bmc_query(query); - case TAB_ENGINE: - return tab_query(query); - case DUALITY_ENGINE: - return duality_query(query); - default: - UNREACHABLE(); - return rel_query(query); - } - } - - void context::new_query() { - flush_add_rules(); +#endif + m_mc = mk_skip_model_converter(); m_last_status = OK; m_last_answer = 0; + switch (get_engine()) { + case DATALOG_ENGINE: + case PDR_ENGINE: + case QPDR_ENGINE: + case BMC_ENGINE: + case QBMC_ENGINE: + case TAB_ENGINE: + case CLP_ENGINE: + flush_add_rules(); + break; + case DUALITY_ENGINE: + break; + default: + UNREACHABLE(); + } + ensure_engine(); + return m_engine->query(query); } model_ref context::get_model() { - switch(get_engine()) { - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - return m_pdr->get_model(); - case DUALITY_ENGINE: - ensure_duality(); - return m_duality->get_model(); - default: - return model_ref(alloc(model, m)); - } + ensure_engine(); + return m_engine->get_model(); } proof_ref context::get_proof() { - switch(get_engine()) { - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - return m_pdr->get_proof(); - case DUALITY_ENGINE: - ensure_duality(); - return m_duality->get_proof(); - default: - return proof_ref(m.mk_asserted(m.mk_true()), m); - } + ensure_engine(); + return m_engine->get_proof(); } - void context::ensure_pdr() { - if (!m_pdr.get()) { - m_pdr = alloc(pdr::dl_interface, *this); - } + void context::ensure_engine() { + if (!m_engine.get()) { + m_engine = m_register_engine.mk_engine(get_engine()); + + // break abstraction. + if (get_engine() == DATALOG_ENGINE) { + m_rel = dynamic_cast(m_engine.get()); + } + + } } - void context::ensure_duality() { - if (!m_duality.get()) { - m_duality = alloc(Duality::dl_interface, *this); - } + lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { + ensure_engine(); + return m_engine->query(num_rels, rels); } - - 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); - } - } - - lbool context::bmc_query(expr* query) { - ensure_bmc(); - return m_bmc->query(query); - } - - void context::ensure_tab() { - if (!m_tab.get()) { - m_tab = alloc(tab, *this); - } - } - - lbool context::tab_query(expr* query) { - ensure_tab(); - return m_tab->query(query); - } - - void context::ensure_rel() { - if (!m_rel.get()) { - m_rel = alloc(rel_context, *this); - } - } - - lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { - ensure_rel(); - return m_rel->query(num_rels, rels); - } - - lbool context::rel_query(expr* query) { - ensure_rel(); - return m_rel->query(query); - } - - + expr* context::get_answer_as_formula() { if (m_last_answer) { return m_last_answer.get(); } - switch(get_engine()) { - case PDR_ENGINE: - case QPDR_ENGINE: - ensure_pdr(); - m_last_answer = m_pdr->get_answer(); - return m_last_answer.get(); - case BMC_ENGINE: - case QBMC_ENGINE: - ensure_bmc(); - m_last_answer = m_bmc->get_answer(); - return m_last_answer.get(); - case DATALOG_ENGINE: - ensure_rel(); - m_last_answer = m_rel->get_last_answer(); - return m_last_answer.get(); - case TAB_ENGINE: - ensure_tab(); - m_last_answer = m_tab->get_answer(); - return m_last_answer.get(); - case DUALITY_ENGINE: - ensure_duality(); - m_last_answer = m_duality->get_answer(); - return m_last_answer.get(); - default: - UNREACHABLE(); - } - m_last_answer = m.mk_false(); + ensure_engine(); + m_last_answer = m_engine->get_answer(); return m_last_answer.get(); } - bool context::display_certificate(std::ostream& out) { - switch(get_engine()) { - case DATALOG_ENGINE: - return false; - case QPDR_ENGINE: - ensure_pdr(); - m_pdr->display_certificate(out); - return true; - case BMC_ENGINE: - case QBMC_ENGINE: - ensure_bmc(); - m_bmc->display_certificate(out); - return true; - case TAB_ENGINE: - ensure_tab(); - m_tab->display_certificate(out); - return true; - case DUALITY_ENGINE: - ensure_duality(); - m_duality->display_certificate(out); - return true; - default: - return false; - } + void context::display_certificate(std::ostream& out) { + ensure_engine(); + m_engine->display_certificate(out); + } + + void context::display(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"; + display_rules(out); + out << "\n---------------\n"; + out << "Transformed rules\n"; + m_transformed_rule_set.display(out); + + if (m_rel) { + m_rel->display_profile(out); + } } void context::reset_statistics() { - if (m_pdr) { - m_pdr->reset_statistics(); - } - if (m_bmc) { - m_bmc->reset_statistics(); - } - if (m_tab) { - m_tab->reset_statistics(); + if (m_engine) { + m_engine->reset_statistics(); } } void context::collect_statistics(statistics& st) const { - if (m_pdr) { - m_pdr->collect_statistics(st); - } - if (m_bmc) { - m_bmc->collect_statistics(st); - } - if (m_tab) { - m_tab->collect_statistics(st); + if (m_engine) { + m_engine->collect_statistics(st); } } @@ -1226,11 +1062,10 @@ namespace datalog { execution_result context::get_status() { return m_last_status; } bool context::result_contains_fact(relation_fact const& f) { - ensure_rel(); - return m_rel->result_contains_fact(f); + return m_rel && m_rel->result_contains_fact(f); } - // TBD: algebraic data-types declarations will not be printed. + // NB: algebraic data-types declarations will not be printed. class free_func_visitor { ast_manager& m; func_decl_set m_funcs; @@ -1275,14 +1110,13 @@ namespace datalog { void context::get_rules_as_formulas(expr_ref_vector& rules, svector& names) { expr_ref fml(m); datalog::rule_manager& rm = get_rule_manager(); - datalog::rule_ref_vector rule_refs(rm); // ensure that rules are all using bound variables. - for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { + for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { ptr_vector sorts; get_free_vars(m_rule_fmls[i].get(), sorts); if (!sorts.empty()) { - rm.mk_rule(m_rule_fmls[i].get(), rule_refs, m_rule_names[i]); + rm.mk_rule(m_rule_fmls[i].get(), 0, m_rule_set, m_rule_names[i]); m_rule_fmls[i] = m_rule_fmls.back(); m_rule_names[i] = m_rule_names.back(); m_rule_fmls.pop_back(); @@ -1290,16 +1124,15 @@ namespace datalog { --i; } } - add_rules(rule_refs); rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); for (; it != end; ++it) { (*it)->to_formula(fml); rules.push_back(fml); names.push_back((*it)->name()); } - for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { + for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { rules.push_back(m_rule_fmls[i].get()); - names.push_back(m_rule_names[i]); + names.push_back(m_rule_names[i]); } } @@ -1316,9 +1149,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_qe/dl_context.h b/src/muz/base/dl_context.h similarity index 60% rename from src/muz_qe/dl_context.h rename to src/muz/base/dl_context.h index 16295498b..51b54bc01 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz/base/dl_context.h @@ -28,34 +28,110 @@ 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"duality_dl_interface.h" -#include"rel_context.h" #include"lbool.h" #include"statistics.h" #include"params.h" #include"trail.h" #include"model_converter.h" -#include"proof_converter.h" #include"model2expr.h" #include"smt_params.h" +#include"dl_rule_transformer.h" +#include"expr_abstract.h" +#include"expr_functors.h" +#include"dl_engine_base.h" + +struct fixedpoint_params; namespace datalog { - class rule_transformer; - enum execution_result { OK, TIMEOUT, MEMOUT, - INPUT_ERROR + INPUT_ERROR, + APPROX, + 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 { @@ -78,40 +154,61 @@ namespace datalog { typedef obj_map > pred2syms; typedef obj_map sort_domain_map; + class contains_pred : public i_expr_pred { + context const& ctx; + public: + contains_pred(context& ctx): ctx(ctx) {} + virtual ~contains_pred() {} + + virtual bool operator()(expr* e) { + return ctx.is_predicate(e); + } + }; + + ast_manager & m; + 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; rule_manager m_rule_manager; - + unused_vars_eliminator m_elim_unused_vars; + expr_abstractor m_abstractor; + contains_pred m_contains_p; + check_pred m_check_pred; + rule_transformer m_transf; trail_stack m_trail; ast_ref_vector m_pinned; app_ref_vector m_vars; + svector m_names; sort_domain_map m_sorts; func_decl_set m_preds; sym2decl m_preds_by_name; pred2syms m_argument_var_names; rule_set m_rule_set; + rule_set m_transformed_rule_set; + unsigned m_rule_fmls_head; expr_ref_vector m_rule_fmls; svector m_rule_names; expr_ref_vector m_background; + model_converter_ref m_mc; + proof_converter_ref m_pc; - scoped_ptr m_pdr; - scoped_ptr m_bmc; - scoped_ptr m_rel; - scoped_ptr m_tab; - scoped_ptr m_duality; + rel_context_base* m_rel; + scoped_ptr m_engine; bool m_closed; bool m_saturation_was_run; execution_result m_last_status; expr_ref m_last_answer; - DL_ENGINE m_engine; + DL_ENGINE m_engine_type; volatile bool m_cancel; + + bool is_fact(app * head) const; bool has_sort_domain(relation_sort s) const; sort_domain & get_sort_domain(relation_sort s); @@ -121,7 +218,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(); @@ -136,31 +233,41 @@ 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; } - DL_ENGINE get_engine() { configure_engine(); return m_engine; } + 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 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; + 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); @@ -180,18 +287,32 @@ namespace datalog { retrieved by the try_get_predicate_decl() function. Auxiliary predicates introduced e.g. by rule transformations do not need to be named. */ - void register_predicate(func_decl * pred, bool named = true); + void register_predicate(func_decl * pred, bool named); + + /** + Restrict reltaions to set of predicates. + */ + void restrict_predicates(func_decl_set const& preds); + + /** + \brief Retrieve predicates + */ + func_decl_set const& get_predicates() const { return m_preds; } + bool is_predicate(func_decl* pred) const { return m_preds.contains(pred); } + bool is_predicate(expr * e) const { return is_app(e) && is_predicate(to_app(e)->get_decl()); } - bool is_predicate(func_decl * pred) const; - /** \brief If a predicate name has a \c func_decl object assigned, return pointer to it; otherwise return 0. - + Not all \c func_decl object used as relation identifiers need to be assigned to their names. Generally, the names coming from the parses are registered here. - */ - func_decl * try_get_predicate_decl(symbol pred_name) const; + */ + func_decl * try_get_predicate_decl(symbol const& pred_name) const { + func_decl * res = 0; + m_preds_by_name.find(pred_name, res); + return res; + } /** \brief Create a fresh head predicate declaration. @@ -231,11 +352,9 @@ namespace datalog { void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names); - void set_output_predicate(func_decl * pred); - bool is_output_predicate(func_decl * pred); - const decl_set & get_output_predicates(); + void set_output_predicate(func_decl * pred) { m_rule_set.set_output_predicate(pred); } - rule_set const & get_rules() { flush_add_rules(); return m_rule_set; } + rule_set & get_rules() { flush_add_rules(); return m_rule_set; } void get_rules_as_formulas(expr_ref_vector& fmls, svector& names); void get_raw_rule_formulas(expr_ref_vector& fmls, svector& names); @@ -243,9 +362,9 @@ namespace datalog { void add_fact(app * head); void add_fact(func_decl * pred, const relation_fact & fact); + bool has_facts(func_decl * pred) const; void add_rule(rule_ref& r); - void add_rules(rule_ref_vector& rs); void assert_expr(expr* e); expr_ref get_background_assertion(); @@ -316,35 +435,32 @@ namespace datalog { void reopen(); void ensure_opened(); - void transform_rules(model_converter_ref& mc, proof_converter_ref& pc); - void transform_rules(rule_transformer& trans, model_converter_ref& mc, proof_converter_ref& pc); - void replace_rules(rule_set & rs); + model_converter_ref& get_model_converter() { return m_mc; } + void add_model_converter(model_converter* mc) { m_mc = concat(m_mc.get(), mc); } + proof_converter_ref& get_proof_converter() { return m_pc; } + void add_proof_converter(proof_converter* pc) { m_pc = concat(m_pc.get(), pc); } - void apply_default_transformation(model_converter_ref& mc, proof_converter_ref& pc); + void transform_rules(rule_transformer& transf); + void transform_rules(rule_transformer::plugin* plugin); + void replace_rules(rule_set const& rs); + void record_transformed_rules(); + + void apply_default_transformation(); void collect_params(param_descrs& r); void updt_params(params_ref const& p); - void collect_predicates(decl_set & res); - /** - \brief Restrict the set of used predicates to \c res. - - The function deallocates unsused relations, it does not deal with rules. - */ - void restrict_predicates(const decl_set & res); - void display_rules(std::ostream & out) const { 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); + void display_profile(std::ostream& out) const; + // ----------------------------------- // // basic usage methods @@ -352,6 +468,7 @@ namespace datalog { // ----------------------------------- void cancel(); + bool canceled() const { return m_cancel; } void cleanup(); void reset_cancel() { cleanup(); } @@ -418,14 +535,14 @@ namespace datalog { /** \brief Display a certificate for reachability and/or unreachability. */ - bool display_certificate(std::ostream& out); + void display_certificate(std::ostream& out); /** \brief query result if it contains fact. */ bool result_contains_fact(relation_fact const& f); - rel_context& get_rel_context() { ensure_rel(); return *m_rel; } + rel_context_base* get_rel_context() { ensure_engine(); return m_rel; } private: @@ -437,27 +554,7 @@ namespace datalog { void flush_add_rules(); - void ensure_pdr(); - - void ensure_bmc(); - - void ensure_tab(); - - void ensure_duality(); - - void ensure_rel(); - - void new_query(); - - lbool rel_query(expr* query); - - lbool pdr_query(expr* query); - - lbool bmc_query(expr* query); - - lbool tab_query(expr* query); - - lbool duality_query(expr* query); + void ensure_engine(); void check_quantifier_free(rule_ref& r); void check_uninterpreted_free(rule_ref& r); diff --git a/src/muz_qe/dl_costs.cpp b/src/muz/base/dl_costs.cpp similarity index 100% rename from src/muz_qe/dl_costs.cpp rename to src/muz/base/dl_costs.cpp diff --git a/src/muz_qe/dl_costs.h b/src/muz/base/dl_costs.h similarity index 100% rename from src/muz_qe/dl_costs.h rename to src/muz/base/dl_costs.h diff --git a/src/muz/base/dl_engine_base.h b/src/muz/base/dl_engine_base.h new file mode 100644 index 000000000..eaeebe979 --- /dev/null +++ b/src/muz/base/dl_engine_base.h @@ -0,0 +1,83 @@ +/*++ +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, + DUALITY_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_qe/dl_rule.cpp b/src/muz/base/dl_rule.cpp similarity index 72% rename from src/muz_qe/dl_rule.cpp rename to src/muz/base/dl_rule.cpp index d34f8a67f..96e2b163a 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -40,158 +40,142 @@ Revision History: #include"quant_hoist.h" #include"expr_replacer.h" #include"bool_rewriter.h" -#include"qe_lite.h" #include"expr_safe_replace.h" +#include"filter_model_converter.h" +#include"scoped_proof.h" namespace datalog { rule_manager::rule_manager(context& ctx) : m(ctx.get_manager()), - m_ctx(ctx), - m_refs(ctx.get_manager()) {} - - bool rule_manager::is_predicate(func_decl * f) const { - return m_ctx.is_predicate(f); - } + m_ctx(ctx), + m_body(m), + m_head(m), + m_args(m), + m_hnf(m), + m_qe(m), + m_cfg(m), + m_rwr(m, false, m_cfg) {} void rule_manager::inc_ref(rule * r) { if (r) { - SASSERT(r->m_ref_cnt!=UINT_MAX); + SASSERT(r->m_ref_cnt != UINT_MAX); r->m_ref_cnt++; } } void rule_manager::dec_ref(rule * r) { if (r) { - SASSERT(r->m_ref_cnt>0); + SASSERT(r->m_ref_cnt > 0); r->m_ref_cnt--; - if (r->m_ref_cnt==0) { + if (r->m_ref_cnt == 0) { r->deallocate(m); } } } - class remove_label_cfg : public default_rewriter_cfg { - family_id m_label_fid; - public: - remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {} - virtual ~remove_label_cfg() {} + rule_manager::remove_label_cfg::~remove_label_cfg() {} - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, - proof_ref & result_pr) - { - if (is_decl_of(f, m_label_fid, OP_LABEL)) { - SASSERT(num == 1); - result = args[0]; - return BR_DONE; - } - return BR_FAILED; + br_status rule_manager::remove_label_cfg::reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, + proof_ref & result_pr) + { + if (is_decl_of(f, m_label_fid, OP_LABEL)) { + SASSERT(num == 1); + result = args[0]; + return BR_DONE; } - }; + return BR_FAILED; + } - void rule_manager::remove_labels(expr_ref& fml) { + + void rule_manager::remove_labels(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); - remove_label_cfg r_cfg(m); - rewriter_tpl rwr(m, false, r_cfg); - rwr(fml, tmp); + m_rwr(fml, tmp); + if (pr && fml != tmp) { + + pr = m.mk_modus_ponens(pr, m.mk_rewrite(fml, tmp)); + } fml = tmp; } - - void rule_manager::mk_rule(expr* fml, rule_ref_vector& rules, symbol const& name) { - expr_ref fml1(m); - m_memoize_disj.reset(); - m_refs.reset(); - bind_variables(fml, true, fml1); - remove_labels(fml1); - mk_rule_core(fml1, rules, name); + var_idx_set& rule_manager::collect_vars(expr* e) { + return collect_vars(e, 0); } - // - // Hoist quantifier from rule (universal) or query (existential) - // - unsigned rule_manager::hoist_quantifier(bool is_forall, expr_ref& fml, svector* names) { + var_idx_set& rule_manager::collect_vars(expr* e1, expr* e2) { + reset_collect_vars(); + if (e1) accumulate_vars(e1); + if (e2) accumulate_vars(e2); + return finalize_collect_vars(); + } - unsigned index = var_counter().get_next_var(fml); - while (is_quantifier(fml) && (is_forall == to_quantifier(fml)->is_forall())) { - quantifier* q = to_quantifier(fml); - index += q->get_num_decls(); - if (names) { - names->append(q->get_num_decls(), q->get_decl_names()); + void rule_manager::reset_collect_vars() { + m_vars.reset(); + m_var_idx.reset(); + m_todo.reset(); + m_mark.reset(); + } + + var_idx_set& rule_manager::finalize_collect_vars() { + unsigned sz = m_vars.size(); + for (unsigned i=0; iget_tail_size(); + for (unsigned i=0;iget_tail(i)); + } + return finalize_collect_vars(); + } + + var_idx_set& rule_manager::collect_rule_vars_ex(rule * r, app* t) { + reset_collect_vars(); + unsigned n = r->get_tail_size(); + accumulate_vars(r->get_head()); + for (unsigned i=0;iget_tail(i) != t) { + accumulate_vars(r->get_tail(i)); } - fml = q->get_expr(); } - if (!has_quantifiers(fml)) { - return index; - } - app_ref_vector vars(m); - quantifier_hoister qh(m); - qh.pull_quantifier(is_forall, fml, vars); - if (vars.empty()) { - return index; - } - // replace vars by de-bruijn indices - expr_safe_replace rep(m); - for (unsigned i = 0; i < vars.size(); ++i) { - app* v = vars[i].get(); - if (names) { - names->push_back(v->get_decl()->get_name()); - } - rep.insert(v, m.mk_var(index++,m.get_sort(v))); - } - rep(fml); - return index; + return finalize_collect_vars(); } - void rule_manager::mk_rule_core(expr* _fml, rule_ref_vector& rules, symbol const& name) { - app_ref_vector body(m); - app_ref head(m); - expr_ref e(m), fml(_fml, m); - svector is_negated; - TRACE("dl_rule", tout << mk_pp(fml, m) << "\n";); - unsigned index = hoist_quantifier(true, fml, 0); - check_app(fml); - head = to_app(fml); - - while (m.is_implies(head)) { - e = head->get_arg(0); - th_rewriter th_rwr(m); - th_rwr(e); - body.push_back(ensure_app(e)); - e = to_app(head)->get_arg(1); - check_app(e); - head = to_app(e.get()); - } - symbol head_name = (name == symbol::null)?head->get_decl()->get_name():name; - flatten_body(body); - if (body.size() == 1 && m.is_or(body[0].get()) && contains_predicate(body[0].get())) { - app* _or = to_app(body[0].get()); - for (unsigned i = 0; i < _or->get_num_args(); ++i) { - e = m.mk_implies(_or->get_arg(i), head); - mk_rule_core(e, rules, head_name); - } - return; + var_idx_set& rule_manager::collect_rule_vars(rule * r) { + reset_collect_vars(); + unsigned n = r->get_tail_size(); + accumulate_vars(r->get_head()); + for (unsigned i=0;iget_tail(i)); } + return finalize_collect_vars(); + } - eliminate_disjunctions(body, rules, head_name); - eliminate_quantifier_body(body, rules, head_name); - hoist_compound(index, head, body); - unsigned sz = body.size(); - for (unsigned i = 0; i < sz; ++i) { - app_ref b(body[i].get(), m); - hoist_compound(index, b, body); - body[i] = b; - } - TRACE("dl_rule", - tout << mk_pp(head, m) << " :- "; - for (unsigned i = 0; i < body.size(); ++i) { - tout << mk_pp(body[i].get(), m) << " "; - } - tout << "\n";); + void rule_manager::accumulate_vars(expr* e) { + ::get_free_vars(m_mark, m_todo, e, m_vars); + } + + void rule_manager::mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { + scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); + proof_ref pr(p, m); + expr_ref fml1(m); + bind_variables(fml, true, fml1); + if (fml1 != fml && pr) { + pr = m.mk_asserted(fml1); + } + remove_labels(fml1, pr); + mk_rule_core(fml1, pr, rules, name); + } + + void rule_manager::mk_negations(app_ref_vector& body, svector& is_negated) { for (unsigned i = 0; i < body.size(); ++i) { expr* e = body[i].get(), *e1; - if (m.is_not(e, e1) && is_predicate(e1)) { + if (m.is_not(e, e1) && m_ctx.is_predicate(e1)) { check_app(e1); body[i] = to_app(e1); is_negated.push_back(true); @@ -199,24 +183,110 @@ namespace datalog { else { is_negated.push_back(false); } - } - check_valid_rule(head, body.size(), body.c_ptr()); + } + } - rules.push_back(mk(head.get(), body.size(), body.c_ptr(), is_negated.c_ptr(), name)); + 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); - if (m_ctx.fix_unbound_vars()) { - unsigned rule_cnt = rules.size(); - for (unsigned i=0; i sorts; + ::get_free_vars(fmls[i].get(), sorts); ); + mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name); } } - void rule_manager::mk_query(expr* query, func_decl_ref& qpred, rule_ref_vector& query_rules, rule_ref& query_rule) { + void rule_manager::mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { + + m_body.reset(); + m_neg.reset(); + unsigned index = extract_horn(fml, m_body, m_head); + hoist_compound_predicates(index, m_head, m_body); + TRACE("dl_rule", + tout << mk_pp(m_head, m) << " :- "; + for (unsigned i = 0; i < m_body.size(); ++i) { + tout << mk_pp(m_body[i].get(), m) << " "; + } + tout << "\n";); + + + mk_negations(m_body, m_neg); + check_valid_rule(m_head, m_body.size(), m_body.c_ptr()); + + rule_ref r(*this); + r = mk(m_head.get(), m_body.size(), m_body.c_ptr(), m_neg.c_ptr(), name); + + expr_ref fml1(m); + if (p) { + r->to_formula(fml1); + if (fml1 == fml) { + // no-op. + } + else if (is_quantifier(fml1)) { + p = m.mk_modus_ponens(p, m.mk_symmetry(m.mk_der(to_quantifier(fml1), fml))); + } + else { + p = m.mk_modus_ponens(p, m.mk_rewrite(fml, fml1)); + } + } + + if (m_ctx.fix_unbound_vars()) { + fix_unbound_vars(r, true); + } + + if (p) { + expr_ref fml2(m); + r->to_formula(fml2); + if (fml1 != fml2) { + p = m.mk_modus_ponens(p, m.mk_rewrite(fml1, fml2)); + } + r->set_proof(m, p); + } + rules.add_rule(r); + } + + unsigned rule_manager::extract_horn(expr* fml, app_ref_vector& body, app_ref& head) { + expr* e1, *e2; + unsigned index = m_counter.get_next_var(fml); + if (::is_forall(fml)) { + index += to_quantifier(fml)->get_num_decls(); + fml = to_quantifier(fml)->get_expr(); + } + if (m.is_implies(fml, e1, e2)) { + expr_ref_vector es(m); + head = ensure_app(e2); + qe::flatten_and(e1, es); + for (unsigned i = 0; i < es.size(); ++i) { + body.push_back(ensure_app(es[i].get())); + } + } + else { + head = ensure_app(fml); + } + return index; + } + + void rule_manager::hoist_compound_predicates(unsigned index, app_ref& head, app_ref_vector& body) { + unsigned sz = body.size(); + hoist_compound(index, head, body); + for (unsigned i = 0; i < sz; ++i) { + app_ref b(body[i].get(), m); + hoist_compound(index, b, body); + body[i] = b; + } + } + + + func_decl* rule_manager::mk_query(expr* query, rule_set& rules) { ptr_vector vars; svector names; app_ref_vector body(m); @@ -225,7 +295,8 @@ namespace datalog { // Add implicit variables. // Remove existential prefix. bind_variables(query, false, q); - hoist_quantifier(false, q, &names); + quantifier_hoister qh(m); + qh.pull_quantifier(false, q, 0, &names); // retrieve free variables. get_free_vars(q, vars); if (vars.contains(static_cast(0))) { @@ -270,7 +341,15 @@ namespace datalog { } vars.reverse(); names.reverse(); - qpred = m_ctx.mk_fresh_head_predicate(symbol("query"), symbol(), vars.size(), vars.c_ptr(), body_pred); + 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); + 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++) { @@ -283,9 +362,13 @@ namespace datalog { rule_expr = m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), impl); } - mk_rule(rule_expr, query_rules); - SASSERT(query_rules.size() >= 1); - query_rule = query_rules.back(); + scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); + proof_ref pr(m); + if (m_ctx.generate_proof_trace()) { + pr = m.mk_asserted(rule_expr); + } + mk_rule(rule_expr, pr, rules); + return qpred; } void rule_manager::bind_variables(expr* fml, bool is_forall, expr_ref& result) { @@ -298,7 +381,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())); @@ -315,45 +398,45 @@ namespace datalog { fml = m.mk_not(fml); return; } - expr_ref_vector args(m); - if (!is_predicate(fml)) { + if (!m_ctx.is_predicate(fml)) { return; } + m_args.reset(); for (unsigned i = 0; i < fml->get_num_args(); ++i) { e = fml->get_arg(i); if (!is_app(e)) { - args.push_back(e); + m_args.push_back(e); continue; } app* b = to_app(e); if (m.is_value(b)) { - args.push_back(e); + m_args.push_back(e); } else { var* v = m.mk_var(num_bound++, m.get_sort(b)); - args.push_back(v); + m_args.push_back(v); body.push_back(m.mk_eq(v, b)); } } - fml = m.mk_app(fml->get_decl(), args.size(), args.c_ptr()); + fml = m.mk_app(fml->get_decl(), m_args.size(), m_args.c_ptr()); TRACE("dl_rule", tout << mk_pp(fml.get(), m) << "\n";); } class contains_predicate_proc { - rule_manager const& m; + context const& ctx; public: struct found {}; - contains_predicate_proc(rule_manager const& m): m(m) {} + contains_predicate_proc(context const& ctx): ctx(ctx) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app* n) { - if (m.is_predicate(n)) throw found(); + if (ctx.is_predicate(n)) throw found(); } }; bool rule_manager::contains_predicate(expr* fml) const { - contains_predicate_proc proc(*this); + contains_predicate_proc proc(m_ctx); try { quick_for_each_expr(proc, fml); } @@ -363,73 +446,6 @@ namespace datalog { return false; } - void rule_manager::eliminate_disjunctions(app_ref_vector::element_ref& body, rule_ref_vector& rules, symbol const& name) { - - app* b = body.get(); - expr* e1; - bool negate_args = false; - bool is_disj = false; - unsigned num_disj = 0; - expr* const* disjs = 0; - if (!contains_predicate(b)) { - return; - } - TRACE("dl_rule", tout << mk_ismt2_pp(b, m) << "\n";); - if (m.is_or(b)) { - is_disj = true; - negate_args = false; - num_disj = b->get_num_args(); - disjs = b->get_args(); - } - if (m.is_not(b, e1) && m.is_and(e1)) { - is_disj = true; - negate_args = true; - num_disj = to_app(e1)->get_num_args(); - disjs = to_app(e1)->get_args(); - } - if (is_disj) { - ptr_vector sorts0, sorts1; - get_free_vars(b, sorts0); - expr_ref_vector args(m); - for (unsigned i = 0; i < sorts0.size(); ++i) { - if (sorts0[i]) { - args.push_back(m.mk_var(i,sorts0[i])); - sorts1.push_back(sorts0[i]); - } - } - app* old_head = 0; - if (m_memoize_disj.find(b, old_head)) { - body = old_head; - } - else { - app_ref head(m); - func_decl_ref f(m); - f = m.mk_fresh_func_decl(name.str().c_str(),"", sorts1.size(), sorts1.c_ptr(), m.mk_bool_sort()); - m_ctx.register_predicate(f); - head = m.mk_app(f, args.size(), args.c_ptr()); - - for (unsigned i = 0; i < num_disj; ++i) { - expr_ref fml(m); - expr* e = disjs[i]; - if (negate_args) e = m.mk_not(e); - fml = m.mk_implies(e,head); - mk_rule_core(fml, rules, name); - } - m_memoize_disj.insert(b, head); - m_refs.push_back(b); - m_refs.push_back(head); - // update the body to be the newly introduced head relation - body = head; - } - } - } - - void rule_manager::eliminate_disjunctions(app_ref_vector& body, rule_ref_vector& rules, symbol const& name) { - for (unsigned i = 0; i < body.size(); ++i) { - app_ref_vector::element_ref t = body[i]; - eliminate_disjunctions(t, rules, name); - } - } bool rule_manager::is_forall(ast_manager& m, expr* e, quantifier*& q) { expr* e1, *e2; @@ -448,40 +464,6 @@ namespace datalog { return false; } - void rule_manager::eliminate_quantifier_body(app_ref_vector::element_ref& body, rule_ref_vector& rules, symbol const& name) { - quantifier* q; - if (is_forall(m, body.get(), q) && contains_predicate(q)) { - expr* e = q->get_expr(); - if (!is_predicate(e)) { - ptr_vector sorts0, sorts1; - get_free_vars(e, sorts0); - expr_ref_vector args(m); - for (unsigned i = 0; i < sorts0.size(); ++i) { - if (sorts0[i]) { - args.push_back(m.mk_var(i,sorts0[i])); - sorts1.push_back(sorts0[i]); - } - } - app_ref head(m), fml(m); - func_decl_ref f(m); - f = m.mk_fresh_func_decl(name.str().c_str(),"", sorts1.size(), sorts1.c_ptr(), m.mk_bool_sort()); - m_ctx.register_predicate(f); - head = m.mk_app(f, args.size(), args.c_ptr()); - fml = m.mk_implies(e, head); - mk_rule_core(fml, rules, name); - // update the body to be the newly introduced head relation - body = m.mk_eq(m.mk_true(), m.update_quantifier(q, head)); - } - } - } - - void rule_manager::eliminate_quantifier_body(app_ref_vector& body, rule_ref_vector& rules, symbol const& name) { - for (unsigned i = 0; i < body.size(); ++i) { - app_ref_vector::element_ref t = body[i]; - eliminate_quantifier_body(t, rules, name); - } - } - app_ref rule_manager::ensure_app(expr* e) { SASSERT(m.is_bool(e)); @@ -509,18 +491,25 @@ namespace datalog { r->m_head = head; r->m_name = name; r->m_tail_size = n; + r->m_proof = 0; m.inc_ref(r->m_head); 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++) { bool is_neg = (is_negated != 0 && is_negated[i]); app * curr = tail[i]; - if (is_neg && !is_predicate(curr)) { + if (is_neg && !m_ctx.is_predicate(curr)) { curr = m.mk_not(curr); is_neg = false; } @@ -528,7 +517,7 @@ namespace datalog { has_neg = true; } app * tail_entry = TAG(app *, curr, is_neg); - if (is_predicate(curr)) { + if (m_ctx.is_predicate(curr)) { *uninterp_tail=tail_entry; uninterp_tail++; } @@ -566,6 +555,11 @@ namespace datalog { if (normalize) { r->norm_vars(*this); } + DEBUG_CODE(ptr_vector sorts; + ::get_free_vars(head, sorts); + for (unsigned i = 0; i < n; ++i) { + ::get_free_vars(tail[i], sorts); + }); return r; } @@ -583,6 +577,7 @@ namespace datalog { r->m_tail_size = n; r->m_positive_cnt = source->m_positive_cnt; r->m_uninterp_cnt = source->m_uninterp_cnt; + r->m_proof = 0; m.inc_ref(r->m_head); for (unsigned i = 0; i < n; i++) { r->m_tail[i] = source->m_tail[i]; @@ -594,29 +589,22 @@ namespace datalog { void rule_manager::reduce_unbound_vars(rule_ref& r) { unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned t_len = r->get_tail_size(); - ptr_vector vars; - uint_set index_set; - qe_lite qe(m); expr_ref_vector conjs(m); if (ut_len == t_len) { return; } - get_free_vars(r->get_head(), vars); + reset_collect_vars(); + accumulate_vars(r->get_head()); for (unsigned i = 0; i < ut_len; ++i) { - get_free_vars(r->get_tail(i), vars); + accumulate_vars(r->get_tail(i)); } + var_idx_set& index_set = finalize_collect_vars(); for (unsigned i = ut_len; i < t_len; ++i) { conjs.push_back(r->get_tail(i)); } - - for (unsigned i = 0; i < vars.size(); ++i) { - if (vars[i]) { - index_set.insert(i); - } - } - qe(index_set, false, conjs); + m_qe(index_set, false, conjs); bool change = conjs.size() != t_len - ut_len; for (unsigned i = 0; !change && i < conjs.size(); ++i) { change = r->get_tail(ut_len+i) != conjs[i].get(); @@ -653,15 +641,14 @@ namespace datalog { return; } - ptr_vector free_rule_vars; var_counter vctr; app_ref_vector tail(m); svector tail_neg; app_ref head(r->get_head(), m); - get_free_vars(r, free_rule_vars); + collect_rule_vars(r); vctr.count_vars(m, head); - + ptr_vector& free_rule_vars = m_vars; for (unsigned i = 0; i < ut_len; i++) { app * t = r->get_tail(i); @@ -705,7 +692,7 @@ namespace datalog { bool_rewriter(m).mk_and(tails_with_unbound.size(), tails_with_unbound.c_ptr(), unbound_tail); unsigned q_var_cnt = unbound_vars.num_elems(); - unsigned max_var = m_var_counter.get_max_var(*r); + unsigned max_var = m_counter.get_max_rule_var(*r); expr_ref_vector subst(m); @@ -787,6 +774,27 @@ namespace datalog { r->set_accounting_parent_object(m_ctx, old_r); } + void rule_manager::mk_rule_rewrite_proof(rule& old_rule, rule& new_rule) { + if (&old_rule != &new_rule && + !new_rule.get_proof() && + old_rule.get_proof()) { + expr_ref fml(m); + new_rule.to_formula(fml); + scoped_proof _sc(m); + proof* p = m.mk_rewrite(m.get_fact(old_rule.get_proof()), fml); + new_rule.set_proof(m, m.mk_modus_ponens(old_rule.get_proof(), p)); + } + } + + void rule_manager::mk_rule_asserted_proof(rule& r) { + if (m_ctx.generate_proof_trace()) { + scoped_proof _scp(m); + expr_ref fml(m); + r.to_formula(fml); + r.set_proof(m, m.mk_asserted(fml)); + } + } + void rule_manager::substitute(rule_ref& r, unsigned sz, expr*const* es) { expr_ref tmp(m); app_ref new_head(m); @@ -814,7 +822,7 @@ namespace datalog { void rule_manager::check_valid_head(expr * head) const { SASSERT(head); - if (!is_predicate(head)) { + if (!m_ctx.is_predicate(head)) { std::ostringstream out; out << "Illegal head. The head predicate needs to be uninterpreted and registered (as recursive) " << mk_pp(head, m); throw default_exception(out.str()); @@ -845,10 +853,23 @@ namespace datalog { for (unsigned i = 0; i < n; i++) { m.dec_ref(get_tail(i)); } + if (m_proof) { + m.dec_ref(m_proof); + } this->~rule(); m.get_allocator().deallocate(get_obj_size(n), this); } + void rule::set_proof(ast_manager& m, proof* p) { + if (p) { + m.inc_ref(p); + } + if (m_proof) { + m.dec_ref(m_proof); + } + m_proof = p; + } + bool rule::is_in_tail(const func_decl * p, bool only_positive) const { unsigned len = only_positive ? get_positive_tail_size() : get_uninterpreted_tail_size(); for (unsigned i = 0; i < len; i++) { @@ -927,6 +948,15 @@ namespace datalog { return exist || univ; } + bool rule::has_negation() const { + for (unsigned i = 0; i < get_uninterpreted_tail_size(); ++i) { + if (is_neg_tail(i)) { + return true; + } + } + return false; + } + void rule::get_used_vars(used_vars& used) const { used.process(get_head()); unsigned sz = get_tail_size(); @@ -946,7 +976,7 @@ namespace datalog { } void rule::norm_vars(rule_manager & rm) { - used_vars used; + used_vars& used = rm.reset_used(); get_used_vars(used); unsigned first_unsused = used.get_max_found_var_idx_plus_1(); @@ -1003,7 +1033,7 @@ namespace datalog { if (is_neg_tail(i)) out << "not "; app * t = get_tail(i); - if (ctx.get_rule_manager().is_predicate(t)) { + if (ctx.is_predicate(t)) { output_predicate(ctx, t, out); } else { @@ -1017,6 +1047,9 @@ namespace datalog { out << '}'; } out << '\n'; + if (m_proof) { + out << mk_pp(m_proof, m) << '\n'; + } } void rule::to_formula(expr_ref& fml) const { @@ -1041,16 +1074,14 @@ namespace datalog { } svector names; used_symbols<> us; - - us(fml); - sorts.reverse(); - for (unsigned i = 0; i < sorts.size(); ++i) { if (!sorts[i]) { sorts[i] = m.mk_bool_sort(); } } - + + us(fml); + sorts.reverse(); for (unsigned j = 0, i = 0; i < sorts.size(); ++j) { for (char c = 'A'; i < sorts.size() && c <= 'Z'; ++c) { func_decl_ref f(m); @@ -1104,5 +1135,8 @@ namespace datalog { } - + }; + +template class rewriter_tpl; + diff --git a/src/muz_qe/dl_rule.h b/src/muz/base/dl_rule.h similarity index 69% rename from src/muz_qe/dl_rule.h rename to src/muz/base/dl_rule.h index a9e360344..77bf9ac74 100644 --- a/src/muz_qe/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -24,11 +24,18 @@ Revision History: #include"dl_costs.h" #include"dl_util.h" #include"used_vars.h" +#include"proof_converter.h" +#include"model_converter.h" +#include"ast_counter.h" +#include"rewriter.h" +#include"hnf.h" +#include"qe_lite.h" namespace datalog { class rule; class rule_manager; + class rule_set; class table; class context; @@ -43,11 +50,33 @@ namespace datalog { */ class rule_manager { - ast_manager& m; - context& m_ctx; - var_counter m_var_counter; - obj_map m_memoize_disj; - expr_ref_vector m_refs; + class remove_label_cfg : public default_rewriter_cfg { + family_id m_label_fid; + public: + remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {} + virtual ~remove_label_cfg(); + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, + proof_ref & result_pr); + }; + + ast_manager& m; + context& m_ctx; + rule_counter m_counter; + used_vars m_used; + ptr_vector m_vars; + var_idx_set m_var_idx; + ptr_vector m_todo; + ast_mark m_mark; + app_ref_vector m_body; + app_ref m_head; + expr_ref_vector m_args; + svector m_neg; + hnf m_hnf; + qe_lite m_qe; + remove_label_cfg m_cfg; + rewriter_tpl m_rwr; + // only the context can create a rule_manager friend class context; @@ -57,19 +86,13 @@ namespace datalog { /** \brief Move functions from predicate tails into the interpreted tail by introducing new variables. */ + void hoist_compound_predicates(unsigned num_bound, app_ref& head, app_ref_vector& body); + void hoist_compound(unsigned& num_bound, app_ref& fml, app_ref_vector& body); void flatten_body(app_ref_vector& body); - void remove_labels(expr_ref& fml); - - void eliminate_disjunctions(app_ref_vector::element_ref& body, rule_ref_vector& rules, symbol const& name); - - void eliminate_disjunctions(app_ref_vector& body, rule_ref_vector& rules, symbol const& name); - - void eliminate_quantifier_body(app_ref_vector::element_ref& body, rule_ref_vector& rules, symbol const& name); - - void eliminate_quantifier_body(app_ref_vector& body, rule_ref_vector& rules, symbol const& name); + void remove_labels(expr_ref& fml, proof_ref& pr); app_ref ensure_app(expr* e); @@ -79,15 +102,25 @@ namespace datalog { void bind_variables(expr* fml, bool is_forall, expr_ref& result); - void mk_rule_core(expr* fml, rule_ref_vector& rules, symbol const& name); + void mk_negations(app_ref_vector& body, svector& is_negated); - unsigned hoist_quantifier(bool is_forall, expr_ref& fml, svector* names); + void mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name); + + void mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name); + + static expr_ref mk_implies(app_ref_vector const& body, expr* head); + + unsigned extract_horn(expr* fml, app_ref_vector& body, app_ref& head); /** \brief Perform cheap quantifier elimination to reduce the number of variables in the interpreted tail. */ void reduce_unbound_vars(rule_ref& r); + void reset_collect_vars(); + + var_idx_set& finalize_collect_vars(); + public: ast_manager& get_manager() const { return m; } @@ -96,18 +129,36 @@ namespace datalog { void dec_ref(rule * r); + used_vars& reset_used() { m_used.reset(); return m_used; } + + var_idx_set& collect_vars(expr * pred); + + var_idx_set& collect_vars(expr * e1, expr* e2); + + var_idx_set& collect_rule_vars(rule * r); + + var_idx_set& collect_rule_vars_ex(rule * r, app* t); + + var_idx_set& collect_tail_vars(rule * r); + + void accumulate_vars(expr* pred); + + ptr_vector& get_var_sorts() { return m_vars; } + + var_idx_set& get_var_idx() { return m_var_idx; } + /** \brief Create a Datalog rule from a Horn formula. The formula is of the form (forall (...) (forall (...) (=> (and ...) head))) */ - void mk_rule(expr* fml, rule_ref_vector& rules, symbol const& name = symbol::null); + void mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name = symbol::null); /** \brief Create a Datalog query from an expression. The formula is of the form (exists (...) (exists (...) (and ...)) */ - void mk_query(expr* query, func_decl_ref& query_pred, rule_ref_vector& query_rules, rule_ref& query_rule); + func_decl* mk_query(expr* query, rule_set& rules); /** \brief Create a Datalog rule head :- tail[0], ..., tail[n-1]. @@ -131,7 +182,15 @@ namespace datalog { /** make sure there are not non-quantified variables that occur only in interpreted predicates */ void fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination); + /** + \brief add proof that new rule is obtained by rewriting old rule. + */ + void mk_rule_rewrite_proof(rule& old_rule, rule& new_rule); + /** + \brief tag rule as asserted. + */ + void mk_rule_asserted_proof(rule& r); /** \brief apply substitution to variables of rule. @@ -155,14 +214,9 @@ namespace datalog { bool is_fact(app * head) const; - bool is_predicate(func_decl * f) const; - bool is_predicate(expr * e) const { - return is_app(e) && is_predicate(to_app(e)->get_decl()); - } - static bool is_forall(ast_manager& m, expr* e, quantifier*& q); - var_counter& get_var_counter() { return m_var_counter; } + rule_counter& get_counter() { return m_counter; } }; @@ -170,6 +224,7 @@ namespace datalog { friend class rule_manager; app * m_head; + proof* m_proof; unsigned m_tail_size:20; // unsigned m_reserve:12; unsigned m_ref_cnt; @@ -201,6 +256,10 @@ namespace datalog { void get_used_vars(used_vars& uv) const; public: + + proof * get_proof() const { return m_proof; } + + void set_proof(ast_manager& m, proof* p); app * get_head() const { return m_head; } @@ -237,6 +296,7 @@ namespace datalog { bool has_uninterpreted_non_predicates(func_decl*& f) const; void has_quantifiers(bool& existential, bool& universal) const; bool has_quantifiers() const; + bool has_negation() const; /** \brief Store in d the (direct) dependencies of the given rule. diff --git a/src/muz_qe/dl_rule_set.cpp b/src/muz/base/dl_rule_set.cpp similarity index 71% rename from src/muz_qe/dl_rule_set.cpp rename to src/muz/base/dl_rule_set.cpp index caecc76ef..ad3b512a3 100644 --- a/src/muz_qe/dl_rule_set.cpp +++ b/src/muz/base/dl_rule_set.cpp @@ -30,17 +30,17 @@ namespace datalog { rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed): m_context(o.m_context) { - if(reversed) { + if (reversed) { iterator oit = o.begin(); iterator oend = o.end(); - for(; oit!=oend; ++oit) { + for (; oit!=oend; ++oit) { func_decl * pred = oit->m_key; item_set & orig_items = *oit->get_value(); ensure_key(pred); item_set::iterator dit = orig_items.begin(); item_set::iterator dend = orig_items.end(); - for(; dit!=dend; ++dit) { + for (; dit!=dend; ++dit) { func_decl * master_pred = *dit; insert(master_pred, pred); } @@ -49,7 +49,7 @@ namespace datalog { else { iterator oit = o.begin(); iterator oend = o.end(); - for(; oit!=oend; ++oit) { + for (; oit!=oend; ++oit) { func_decl * pred = oit->m_key; item_set & orig_items = *oit->get_value(); m_data.insert(pred, alloc(item_set, orig_items)); @@ -64,8 +64,7 @@ namespace datalog { reset_dealloc_values(m_data); } - void rule_dependencies::remove_m_data_entry(func_decl * key) - { + void rule_dependencies::remove_m_data_entry(func_decl * key) { item_set * itm_set = m_data.find(key); dealloc(itm_set); m_data.remove(key); @@ -73,7 +72,7 @@ namespace datalog { rule_dependencies::item_set & rule_dependencies::ensure_key(func_decl * pred) { deps_type::obj_map_entry * e = m_data.insert_if_not_there2(pred, 0); - if(!e->get_data().m_value) { + if (!e->get_data().m_value) { e->get_data().m_value = alloc(item_set); } return *e->get_data().m_value; @@ -101,14 +100,15 @@ namespace datalog { void rule_dependencies::populate(unsigned n, rule * const * rules) { SASSERT(m_data.empty()); - for(unsigned i=0; iget_decl()->get_name() << "\n";); m_visited.reset(); - func_decl * d = r->get_head()->get_decl(); + func_decl * d = r->get_decl(); func_decl_set & s = ensure_key(d); for (unsigned i = 0; i < r->get_tail_size(); ++i) { @@ -141,7 +141,7 @@ namespace datalog { const rule_dependencies::item_set & rule_dependencies::get_deps(func_decl * f) const { deps_type::obj_map_entry * e = m_data.find_core(f); - if(!e) { + if (!e) { return m_empty_item_set; } SASSERT(e->get_data().get_value()); @@ -152,9 +152,9 @@ namespace datalog { ptr_vector to_remove; iterator pit = begin(); iterator pend = end(); - for(; pit!=pend; ++pit) { + for (; pit!=pend; ++pit) { func_decl * pred = pit->m_key; - if(!allowed.contains(pred)) { + if (!allowed.contains(pred)) { to_remove.insert(pred); continue; } @@ -163,7 +163,7 @@ namespace datalog { } ptr_vector::iterator rit = to_remove.begin(); ptr_vector::iterator rend = to_remove.end(); - for(; rit!=rend; ++rit) { + for (; rit != rend; ++rit) { remove_m_data_entry(*rit); } } @@ -172,7 +172,7 @@ namespace datalog { remove_m_data_entry(itm); iterator pit = begin(); iterator pend = end(); - for(; pit!=pend; ++pit) { + for (; pit != pend; ++pit) { item_set & itms = *pit->get_value(); itms.remove(itm); } @@ -181,12 +181,12 @@ namespace datalog { void rule_dependencies::remove(const item_set & to_remove) { item_set::iterator rit = to_remove.begin(); item_set::iterator rend = to_remove.end(); - for(; rit!=rend; ++rit) { + for (; rit!=rend; ++rit) { remove_m_data_entry(*rit); } iterator pit = begin(); iterator pend = end(); - for(; pit!=pend; ++pit) { + for (; pit!=pend; ++pit) { item_set * itms = pit->get_value(); set_difference(*itms, to_remove); } @@ -196,9 +196,9 @@ namespace datalog { unsigned res = 0; iterator pit = begin(); iterator pend = end(); - for(; pit!=pend; ++pit) { + for (; pit!=pend; ++pit) { item_set & itms = *pit->get_value(); - if(itms.contains(f)) { + if (itms.contains(f)) { res++; } } @@ -214,10 +214,10 @@ namespace datalog { iterator pit = begin(); iterator pend = end(); - for(; pit!=pend; ++pit) { + for (; pit!=pend; ++pit) { func_decl * pred = pit->m_key; unsigned deg = in_degree(pred); - if(deg==0) { + if (deg==0) { res.push_back(pred); } else { @@ -225,25 +225,25 @@ namespace datalog { } } - while(curr_indexget_data().m_value; SASSERT(child_deg>0); child_deg--; - if(child_deg==0) { + if (child_deg==0) { res.push_back(child); } } curr_index++; } - if(res.size()m_key; const item_set & deps = *pit->m_value; item_set::iterator dit=deps.begin(); item_set::iterator dend=deps.end(); - if(dit==dend) { + if (dit == dend) { out<get_name()<<" - \n"; } - for(; dit!=dend; ++dit) { + for (; dit != dend; ++dit) { func_decl * dep = *dit; - out<get_name()<<" -> "<get_name()<<"\n"; + out << pred->get_name() << " -> " << dep->get_name() << "\n"; } } } @@ -282,18 +282,20 @@ namespace datalog { m_rule_manager(ctx.get_rule_manager()), m_rules(m_rule_manager), m_deps(ctx), - m_stratifier(0) { + m_stratifier(0), + m_refs(ctx.get_manager()) { } - rule_set::rule_set(const rule_set & rs) - : m_context(rs.m_context), - m_rule_manager(rs.m_rule_manager), - m_rules(m_rule_manager), - m_deps(rs.m_context), - m_stratifier(0) { - add_rules(rs); - if(rs.m_stratifier) { - TRUSTME(close()); + rule_set::rule_set(const rule_set & other) + : m_context(other.m_context), + m_rule_manager(other.m_rule_manager), + m_rules(m_rule_manager), + m_deps(other.m_context), + m_stratifier(0), + m_refs(m_context.get_manager()) { + add_rules(other); + if (other.m_stratifier) { + VERIFY(close()); } } @@ -302,18 +304,62 @@ namespace datalog { } void rule_set::reset() { - if(m_stratifier) { - m_stratifier = 0; - } + m_rules.reset(); reset_dealloc_values(m_head2rules); m_deps.reset(); - m_rules.reset(); + m_stratifier = 0; + m_output_preds.reset(); + m_orig2pred.reset(); + m_pred2orig.reset(); + m_refs.reset(); } ast_manager & rule_set::get_manager() const { return m_context.get_manager(); } + func_decl* rule_set::get_orig(func_decl* pred) const { + func_decl* orig = pred; + m_pred2orig.find(pred, orig); + return orig; + } + + func_decl* rule_set::get_pred(func_decl* orig) const { + func_decl* pred = orig; + m_orig2pred.find(orig, pred); + return pred; + } + + void rule_set::inherit_predicates(rule_set const& other) { + m_refs.append(other.m_refs); + set_union(m_output_preds, other.m_output_preds); + { + obj_map::iterator it = other.m_orig2pred.begin(); + obj_map::iterator end = other.m_orig2pred.end(); + for (; it != end; ++it) { + m_orig2pred.insert(it->m_key, it->m_value); + } + } + { + obj_map::iterator it = other.m_pred2orig.begin(); + obj_map::iterator end = other.m_pred2orig.end(); + for (; it != end; ++it) { + m_pred2orig.insert(it->m_key, it->m_value); + } + } + } + + void rule_set::inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred) { + if (other.is_output_predicate(orig)) { + set_output_predicate(pred); + } + orig = other.get_orig(orig); + m_refs.push_back(pred); + m_refs.push_back(orig); + m_orig2pred.insert(orig, pred); + m_pred2orig.insert(pred, orig); + } + void rule_set::add_rule(rule * r) { TRACE("dl_verbose", r->display(m_context, tout << "add:");); SASSERT(!is_closed()); @@ -328,7 +374,7 @@ namespace datalog { void rule_set::del_rule(rule * r) { TRACE("dl", r->display(m_context, tout << "del:");); - func_decl* d = r->get_head()->get_decl(); + func_decl* d = r->get_decl(); rule_vector* rules = m_head2rules.find(d); #define DEL_VECTOR(_v) \ for (unsigned i = (_v).size(); i > 0; ) { \ @@ -344,33 +390,29 @@ namespace datalog { DEL_VECTOR(m_rules); } - void rule_set::ensure_closed() - { - if(!is_closed()) { - TRUSTME(close()); + void rule_set::ensure_closed() { + if (!is_closed()) { + VERIFY(close()); } } bool rule_set::close() { - SASSERT(!is_closed()); //the rule_set is not already closed - + SASSERT(!is_closed()); //the rule_set is not already closed m_deps.populate(*this); m_stratifier = alloc(rule_stratifier, m_deps); - - if(!stratified_negation()) { + if (!stratified_negation()) { m_stratifier = 0; m_deps.reset(); return false; } - return true; } void rule_set::reopen() { - SASSERT(is_closed()); - - m_stratifier = 0; - m_deps.reset(); + if (is_closed()) { + m_stratifier = 0; + m_deps.reset(); + } } /** @@ -391,7 +433,7 @@ namespace datalog { unsigned head_strat = get_predicate_strat(head_decl); SASSERT(head_strat>=neg_strat); //head strat can never be lower than that of a tail - if(head_strat==neg_strat) { + if (head_strat == neg_strat) { return false; } } @@ -399,23 +441,25 @@ namespace datalog { return true; } - void rule_set::add_rules(const rule_set & src) { - SASSERT(!is_closed()); - unsigned n = src.get_num_rules(); - for (unsigned i=0; iget_data().m_value; @@ -431,10 +475,47 @@ namespace datalog { return m_stratifier->get_predicate_strat(pred); } + void rule_set::split_founded_rules(func_decl_set& founded, func_decl_set& non_founded) { + founded.reset(); + non_founded.reset(); + { + decl2rules::iterator it = begin_grouped_rules(), end = end_grouped_rules(); + for (; it != end; ++it) { + non_founded.insert(it->m_key); + } + } + bool change = true; + while (change) { + change = false; + func_decl_set::iterator it = non_founded.begin(), end = non_founded.end(); + for (; it != end; ++it) { + rule_vector const& rv = get_predicate_rules(*it); + bool found = false; + for (unsigned i = 0; !found && i < rv.size(); ++i) { + rule const& r = *rv[i]; + bool is_founded = true; + for (unsigned j = 0; is_founded && j < r.get_uninterpreted_tail_size(); ++j) { + is_founded = founded.contains(r.get_decl(j)); + } + if (is_founded) { + founded.insert(*it); + non_founded.remove(*it); + change = true; + found = true; + } + } + } + } + } void rule_set::display(std::ostream & out) const { out << "; rule count: " << get_num_rules() << "\n"; out << "; predicate count: " << m_head2rules.size() << "\n"; + func_decl_set::iterator pit = m_output_preds.begin(); + func_decl_set::iterator pend = m_output_preds.end(); + for (; pit != pend; ++pit) { + out << "; output: " << (*pit)->get_name() << '\n'; + } decl2rules::iterator it = m_head2rules.begin(); decl2rules::iterator end = m_head2rules.end(); for (; it != end; ++it) { @@ -443,23 +524,12 @@ namespace datalog { ptr_vector::iterator end2 = rules->end(); for (; it2 != end2; ++it2) { rule * r = *it2; - if(!r->passes_output_thresholds(m_context)) { + if (!r->passes_output_thresholds(m_context)) { continue; } r->display(m_context, out); } } - -#if 0 //print dependencies - out<<"##\n"; - out<get_name()<<" -> "<get_name()<<"\n"; } } - if(non_empty && sit!=send) { + if (non_empty && sit!=send) { out << "\n"; } } @@ -499,7 +569,7 @@ namespace datalog { rule_stratifier::~rule_stratifier() { comp_vector::iterator it = m_strats.begin(); comp_vector::iterator end = m_strats.end(); - for(; it!=end; ++it) { + for (; it!=end; ++it) { SASSERT(*it); dealloc(*it); } @@ -507,7 +577,7 @@ namespace datalog { unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const { unsigned num; - if(!m_pred_strat_nums.find(pred, num)) { + if (!m_pred_strat_nums.find(pred, num)) { //the number of the predicate is not stored, therefore it did not appear //in the algorithm and therefore it does not depend on anything and nothing //depends on it. So it is safe to assign zero strate to it, although it is @@ -520,19 +590,19 @@ namespace datalog { void rule_stratifier::traverse(T* el) { unsigned p_num; - if(m_preorder_nums.find(el, p_num)) { - if(p_numinsert(s_el); m_component_nums.insert(s_el, comp_num); - } while(s_el!=el); + } while (s_el!=el); m_stack_P.pop_back(); } } } void rule_stratifier::process() { - if(m_deps.empty()) { + if (m_deps.empty()) { return; } //detect strong components rule_dependencies::iterator it = m_deps.begin(); rule_dependencies::iterator end = m_deps.end(); - for(; it!=end; ++it) { + for (; it!=end; ++it) { T * el = it->m_key; //we take a note of the preorder number with which this sweep started m_first_preorder = m_next_preorder; @@ -593,32 +663,32 @@ namespace datalog { //init in_degrees it = m_deps.begin(); end = m_deps.end(); - for(; it!=end; ++it) { + for (; it != end; ++it) { T * el = it->m_key; item_set * out_edges = it->m_value; unsigned el_comp; - TRUSTME( m_component_nums.find(el, el_comp) ); + VERIFY( m_component_nums.find(el, el_comp) ); - item_set::iterator eit=out_edges->begin(); - item_set::iterator eend=out_edges->end(); - for(; eit!=eend; ++eit) { + item_set::iterator eit = out_edges->begin(); + item_set::iterator eend = out_edges->end(); + for (; eit!=eend; ++eit) { T * tgt = *eit; unsigned tgt_comp = m_component_nums.find(tgt); - if(el_comp!=tgt_comp) { + if (el_comp != tgt_comp) { in_degrees[tgt_comp]++; } } } - //We put components whose indegree is zero to m_strats and assign its - //m_components entry to zero. + // We put components whose indegree is zero to m_strats and assign its + // m_components entry to zero. unsigned comp_cnt = m_components.size(); - for(unsigned i=0; ibegin(); item_set::iterator cend=comp->end(); - for(; cit!=cend; ++cit) { + for (; cit!=cend; ++cit) { T * el = *cit; const item_set & deps = m_deps.get_deps(el); item_set::iterator eit=deps.begin(); item_set::iterator eend=deps.end(); - for(; eit!=eend; ++eit) { + for (; eit!=eend; ++eit) { T * tgt = *eit; unsigned tgt_comp; - TRUSTME( m_component_nums.find(tgt, tgt_comp) ); + VERIFY( m_component_nums.find(tgt, tgt_comp) ); //m_components[tgt_comp]==0 means the edge is intra-component. //Otherwise it would go to another component, but it is not possible, since //as m_components[tgt_comp]==0, its indegree has already reached zero. - if(m_components[tgt_comp]) { + if (m_components[tgt_comp]) { SASSERT(in_degrees[tgt_comp]>0); in_degrees[tgt_comp]--; - if(in_degrees[tgt_comp]==0) { + if (in_degrees[tgt_comp]==0) { m_strats.push_back(m_components[tgt_comp]); m_components[tgt_comp] = 0; } } - - traverse(*cit); } } @@ -669,13 +737,12 @@ namespace datalog { SASSERT(m_pred_strat_nums.empty()); unsigned strat_cnt = m_strats.size(); - for(unsigned strat_index=0; strat_indexbegin(); item_set::iterator cend=comp->end(); - for(; cit!=cend; ++cit) { + for (; cit != cend; ++cit) { T * el = *cit; - m_pred_strat_nums.insert(el, strat_index); } } @@ -686,6 +753,21 @@ namespace datalog { m_stack_P.finalize(); m_component_nums.finalize(); m_components.finalize(); + + } + + void rule_stratifier::display(std::ostream& out) const { + m_deps.display(out << "dependencies\n"); + out << "strata\n"; + for (unsigned i = 0; i < m_strats.size(); ++i) { + item_set::iterator it = m_strats[i]->begin(); + item_set::iterator end = m_strats[i]->end(); + for (; it != end; ++it) { + out << (*it)->get_name() << " "; + } + out << "\n"; + } + } }; diff --git a/src/muz_qe/dl_rule_set.h b/src/muz/base/dl_rule_set.h similarity index 80% rename from src/muz_qe/dl_rule_set.h rename to src/muz/base/dl_rule_set.h index fdbbf7626..c1fc7ea3f 100644 --- a/src/muz_qe/dl_rule_set.h +++ b/src/muz/base/dl_rule_set.h @@ -20,7 +20,6 @@ Revision History: #define _DL_RULE_SET_H_ #include"obj_hashtable.h" - #include"dl_rule.h" namespace datalog { @@ -46,7 +45,7 @@ namespace datalog { ast_mark m_visited; - //we need to take care with removing to aviod memory leaks + //we need to take care with removing to avoid memory leaks void remove_m_data_entry(func_decl * key); //sometimes we need to return reference to an empty set, @@ -146,6 +145,8 @@ namespace datalog { const comp_vector & get_strats() const { return m_strats; } unsigned get_predicate_strat(func_decl * pred) const; + + void display( std::ostream & out ) const; }; /** @@ -161,10 +162,14 @@ namespace datalog { context & m_context; rule_manager & m_rule_manager; - rule_ref_vector m_rules; //!< all rules - decl2rules m_head2rules; //!< mapping from head symbol to rules. - rule_dependencies m_deps; //!< dependencies - scoped_ptr m_stratifier; //!< contains stratifier object iff the rule_set is closed + rule_ref_vector m_rules; //!< all rules + decl2rules m_head2rules; //!< mapping from head symbol to rules. + rule_dependencies m_deps; //!< dependencies + scoped_ptr m_stratifier; //!< contains stratifier object iff the rule_set is closed + func_decl_set m_output_preds; //!< output predicates + obj_map m_orig2pred; + obj_map m_pred2orig; + func_decl_ref_vector m_refs; //sometimes we need to return reference to an empty rule_vector, @@ -183,6 +188,12 @@ namespace datalog { rule_manager & get_rule_manager() const { return const_cast(m_rule_manager); } context& get_context() const { return m_context; } + + void inherit_predicates(rule_set const& other); + void inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred); + func_decl* get_orig(func_decl* pred) const; + func_decl* get_pred(func_decl* orig) const; + /** \brief Add rule \c r to the rule set. */ @@ -197,7 +208,7 @@ namespace datalog { \brief Add all rules from a different rule_set. */ void add_rules(const rule_set& src); - void add_rules(unsigned sz, rule * const * rules); + void replace_rules(const rule_set& other); /** \brief This method should be invoked after all rules are added to the rule set. @@ -215,11 +226,14 @@ namespace datalog { bool is_closed() const { return m_stratifier != 0; } unsigned get_num_rules() const { return m_rules.size(); } + bool empty() const { return m_rules.size() == 0; } rule * get_rule(unsigned i) const { return m_rules[i]; } + rule * last() const { return m_rules[m_rules.size()-1]; } rule_ref_vector const& get_rules() const { return m_rules; } const rule_vector & get_predicate_rules(func_decl * pred) const; + bool contains(func_decl* pred) const { return m_head2rules.contains(pred); } const rule_stratifier & get_stratifier() const { SASSERT(m_stratifier); @@ -229,9 +243,17 @@ namespace datalog { unsigned get_predicate_strat(func_decl * pred) const; const rule_dependencies & get_dependencies() const { SASSERT(is_closed()); return m_deps; } + // split predicats into founded and non-founded. + void split_founded_rules(func_decl_set& founded, func_decl_set& non_founded); void reset(); + void set_output_predicate(func_decl * pred) { m_refs.push_back(pred); m_output_preds.insert(pred); } + bool is_output_predicate(func_decl * pred) const { return m_output_preds.contains(pred); } + const func_decl_set & get_output_predicates() const { return m_output_preds; } + func_decl* get_output_predicate() const { SASSERT(m_output_preds.size() == 1); return *m_output_preds.begin(); } + + void display(std::ostream & out) const; /** diff --git a/src/muz_qe/dl_rule_subsumption_index.cpp b/src/muz/base/dl_rule_subsumption_index.cpp similarity index 100% rename from src/muz_qe/dl_rule_subsumption_index.cpp rename to src/muz/base/dl_rule_subsumption_index.cpp diff --git a/src/muz_qe/dl_rule_subsumption_index.h b/src/muz/base/dl_rule_subsumption_index.h similarity index 100% rename from src/muz_qe/dl_rule_subsumption_index.h rename to src/muz/base/dl_rule_subsumption_index.h diff --git a/src/muz_qe/dl_rule_transformer.cpp b/src/muz/base/dl_rule_transformer.cpp similarity index 51% rename from src/muz_qe/dl_rule_transformer.cpp rename to src/muz/base/dl_rule_transformer.cpp index 5f52e0a39..2df8b9453 100644 --- a/src/muz_qe/dl_rule_transformer.cpp +++ b/src/muz/base/dl_rule_transformer.cpp @@ -16,12 +16,13 @@ Author: Revision History: --*/ + #include #include #include"dl_context.h" - #include"dl_rule_transformer.h" +#include"stopwatch.h" namespace datalog { @@ -31,25 +32,38 @@ namespace datalog { rule_transformer::~rule_transformer() { - plugin_vector::iterator it=m_plugins.begin(); - plugin_vector::iterator end=m_plugins.end(); - for(; it!=end; ++it) { + reset(); + } + + void rule_transformer::reset() { + plugin_vector::iterator it = m_plugins.begin(); + plugin_vector::iterator end = m_plugins.end(); + for(; it!=end; ++it) { dealloc(*it); } + m_plugins.reset(); + m_dirty = false; + } + + void rule_transformer::cancel() { + plugin_vector::iterator it = m_plugins.begin(); + plugin_vector::iterator end = m_plugins.end(); + for(; it!=end; ++it) { + (*it)->cancel(); + } } struct rule_transformer::plugin_comparator { bool operator()(rule_transformer::plugin * p1, rule_transformer::plugin * p2) { - return p1->get_priority()>p2->get_priority(); + return p1->get_priority() > p2->get_priority(); } }; void rule_transformer::ensure_ordered() { - if (!m_dirty) { - return; + if (m_dirty) { + std::sort(m_plugins.begin(), m_plugins.end(), plugin_comparator()); + m_dirty = false; } - std::sort(m_plugins.begin(), m_plugins.end(), plugin_comparator()); - m_dirty=false; } void rule_transformer::register_plugin(plugin * p) { @@ -58,7 +72,7 @@ namespace datalog { m_dirty=true; } - bool rule_transformer::operator()(rule_set & rules, model_converter_ref& mc, proof_converter_ref& pc) { + bool rule_transformer::operator()(rule_set & rules) { ensure_ordered(); bool modified = false; @@ -67,36 +81,48 @@ namespace datalog { tout<<"init:\n"; rules.display(tout); ); - plugin_vector::iterator it=m_plugins.begin(); - plugin_vector::iterator end=m_plugins.end(); - for(; it!=end; ++it) { + rule_set* new_rules = alloc(rule_set, rules); + plugin_vector::iterator it = m_plugins.begin(); + plugin_vector::iterator end = m_plugins.end(); + for(; it!=end && !m_context.canceled(); ++it) { plugin & p = **it; - rule_set * new_rules = p(rules, mc, pc); - if (!new_rules) { + + 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()) { - if (!new_rules->is_closed()) { - if (!new_rules->close()) { - warning_msg("a rule transformation skipped because it destratified negation"); - dealloc(new_rules); - continue; - } - } + if (p.can_destratify_negation() && + !new_rules1->is_closed() && + !new_rules1->close()) { + 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; - rules.reset(); - rules.add_rules(*new_rules); dealloc(new_rules); - rules.ensure_closed(); + 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"; - rules.display(tout); + new_rules->display(tout); ); - } + if (modified) { + rules.replace_rules(*new_rules); + } + dealloc(new_rules); return modified; } diff --git a/src/muz_qe/dl_rule_transformer.h b/src/muz/base/dl_rule_transformer.h similarity index 83% rename from src/muz_qe/dl_rule_transformer.h rename to src/muz/base/dl_rule_transformer.h index 1cc41a146..3f60e9aea 100644 --- a/src/muz_qe/dl_rule_transformer.h +++ b/src/muz/base/dl_rule_transformer.h @@ -23,11 +23,11 @@ Revision History: #include"vector.h" #include"dl_rule.h" #include"dl_rule_set.h" -#include"model_converter.h" -#include"proof_converter.h" namespace datalog { + class context; + class rule_transformer { public: class plugin; @@ -37,9 +37,9 @@ namespace datalog { typedef svector plugin_vector; struct plugin_comparator; - context & m_context; - rule_manager & m_rule_manager; - bool m_dirty; + context & m_context; + rule_manager & m_rule_manager; + bool m_dirty; svector m_plugins; void ensure_ordered(); @@ -47,6 +47,13 @@ namespace datalog { rule_transformer(context & ctx); ~rule_transformer(); + + /** + \brief Reset all registered transformers. + */ + void reset(); + + void cancel(); /** \brief Add a plugin for rule transformation. @@ -59,7 +66,7 @@ namespace datalog { \brief Transform the rule set using the registered transformation plugins. If the rule set has changed, return true; otherwise return false. */ - bool operator()(rule_set & rules, model_converter_ref& mc, proof_converter_ref& pc); + bool operator()(rule_set & rules); }; class rule_transformer::plugin { @@ -72,6 +79,7 @@ namespace datalog { void attach(rule_transformer & transformer) { m_transformer = &transformer; } protected: + /** \brief Create a plugin object for rule_transformer. @@ -80,24 +88,25 @@ namespace datalog { */ plugin(unsigned priority, bool can_destratify_negation = false) : m_priority(priority), m_can_destratify_negation(can_destratify_negation), m_transformer(0) {} + public: virtual ~plugin() {} unsigned get_priority() { return m_priority; } bool can_destratify_negation() const { return m_can_destratify_negation; } + virtual void cancel() {} + /** \brief Return \c rule_set object with containing transformed rules or 0 if no transformation was done. The caller takes ownership of the returned \c rule_set object. */ - virtual rule_set * operator()(rule_set const & source, - model_converter_ref& mc, - proof_converter_ref& pc) = 0; + virtual rule_set * operator()(rule_set const & source) = 0; /** - Removes duplicate tails. + Removes duplicate tails. */ static void remove_duplicate_tails(app_ref_vector& tail, svector& tail_neg); diff --git a/src/muz_qe/dl_util.cpp b/src/muz/base/dl_util.cpp similarity index 61% rename from src/muz_qe/dl_util.cpp rename to src/muz/base/dl_util.cpp index 4a406578c..218f9906a 100644 --- a/src/muz_qe/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -25,161 +25,34 @@ 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" +#include"stopwatch.h" namespace datalog { - void universal_delete(relation_base* ptr) { - ptr->deallocate(); + 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();); } - void universal_delete(table_base* ptr) { - 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; - } - } + 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); } - void flatten_or(expr* fml, expr_ref_vector& result) { - SASSERT(result.get_manager().is_bool(fml)); - result.push_back(fml); - flatten_or(result); - } - - bool push_toplevel_junction_negation_inside(expr_ref& e) - { - ast_manager& m = e.get_manager(); - bool_rewriter brwr(m); - - expr * arg; - if(!m.is_not(e, arg)) { return false; } - bool is_and = m.is_and(arg); - if(!is_and && !m.is_or(arg)) { return false; } - - //now we know we have formula we need to transform - app * junction = to_app(arg); - expr_ref_vector neg_j_args(m); - unsigned num_args = junction->get_num_args(); - for(unsigned i=0; iget_arg(i), neg_j_arg); - neg_j_args.push_back(neg_j_arg); - } - if(is_and) { - brwr.mk_or(neg_j_args.size(), neg_j_args.c_ptr(), e); - } - else { - brwr.mk_and(neg_j_args.size(), neg_j_args.c_ptr(), e); - } - return true; - } bool contains_var(expr * trm, unsigned var_idx) { @@ -187,36 +60,7 @@ namespace datalog { ::get_free_vars(trm, vars); return var_idx < vars.size() && vars[var_idx] != 0; } - - - void collect_vars(ast_manager & m, expr * e, var_idx_set & result) { - ptr_vector vars; - ::get_free_vars(e, vars); - unsigned sz = vars.size(); - for(unsigned i=0; iget_tail_size(); - for(unsigned i=0;iget_tail(i), result); - } - } - - void get_free_tail_vars(rule * r, ptr_vector& sorts) { - unsigned n = r->get_tail_size(); - for(unsigned i=0;iget_tail(i), sorts); - } - } - - void get_free_vars(rule * r, ptr_vector& sorts) { - get_free_vars(r->get_head(), sorts); - get_free_tail_vars(r, sorts); - } - unsigned count_variable_arguments(app * pred) { SASSERT(is_uninterp(pred)); @@ -231,26 +75,6 @@ namespace datalog { return res; } - void collect_non_local_vars(ast_manager & m, rule const * r, app * t, var_idx_set & result) { - collect_vars(m, r->get_head(), result); - unsigned sz = r->get_tail_size(); - for (unsigned i = 0; i < sz; i++) { - app * curr = r->get_tail(i); - if (curr != t) - collect_vars(m, curr, result); - } - } - - void collect_non_local_vars(ast_manager & m, rule const * r, app * t_1, app * t_2, var_idx_set & result) { - collect_vars(m, r->get_head(), result); - unsigned sz = r->get_tail_size(); - for (unsigned i = 0; i < sz; i++) { - app * curr = r->get_tail(i); - if (curr != t_1 && curr != t_2) - collect_vars(m, curr, result); - } - } - void mk_new_rule_tail(ast_manager & m, app * pred, var_idx_set const & non_local_vars, unsigned & next_idx, varidx2var_map & varidx2var, sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args, app_ref & new_pred) { expr_ref_buffer new_args(m); @@ -431,89 +255,9 @@ namespace datalog { } } - void counter::update(unsigned el, int delta) { - int & counter = get(el); - SASSERT(!m_stay_non_negative || counter>=0); - SASSERT(!m_stay_non_negative || static_cast(counter)>=-delta); - counter += delta; - } - - int & counter::get(unsigned el) { - return m_data.insert_if_not_there2(el, 0)->get_data().m_value; - } - - counter & counter::count(unsigned sz, const unsigned * els, int delta) { - for(unsigned i=0; im_value>0 ) { - cnt++; - } - } - return cnt; - } - - void counter::collect_positive(idx_set & acc) const { - iterator eit = begin(); - iterator eend = end(); - for(; eit!=eend; ++eit) { - if(eit->m_value>0) { acc.insert(eit->m_key); } - } - } - - bool counter::get_max_positive(unsigned & res) const { - bool found = false; - iterator eit = begin(); - iterator eend = end(); - for(; eit!=eend; ++eit) { - if( eit->m_value>0 && (!found || eit->m_key>res) ) { - found = true; - res = eit->m_key; - } - } - return found; - } - - unsigned counter::get_max_positive() const { - unsigned max_pos; - VERIFY(get_max_positive(max_pos)); - return max_pos; - } - - int counter::get_max_counter_value() const { - int res = 0; - iterator eit = begin(); - iterator eend = end(); - for (; eit!=eend; ++eit) { - if( eit->m_value>res ) { - res = eit->m_value; - } - } - return res; - } - - void var_counter::count_vars(ast_manager & m, const app * pred, int coef) { - unsigned n = pred->get_num_args(); - for (unsigned i = 0; i < n; i++) { - m_sorts.reset(); - ::get_free_vars(pred->get_arg(i), m_sorts); - for (unsigned j = 0; j < m_sorts.size(); ++j) { - if (m_sorts[j]) { - update(j, coef); - } - } - } - } - - void var_counter::count_vars(ast_manager & m, const rule * r, int coef) { + + void rule_counter::count_rule_vars(ast_manager & m, const rule * r, int coef) { + reset(); count_vars(m, r->get_head(), 1); unsigned n = r->get_tail_size(); for (unsigned i = 0; i < n; i++) { @@ -521,50 +265,7 @@ namespace datalog { } } - unsigned var_counter::get_max_var(bool& has_var) { - has_var = false; - unsigned max_var = 0; - while (!m_todo.empty()) { - expr* e = m_todo.back(); - unsigned scope = m_scopes.back(); - m_todo.pop_back(); - m_scopes.pop_back(); - if (m_visited.is_marked(e)) { - continue; - } - m_visited.mark(e, true); - switch(e->get_kind()) { - case AST_QUANTIFIER: { - quantifier* q = to_quantifier(e); - m_todo.push_back(q->get_expr()); - m_scopes.push_back(scope + q->get_num_decls()); - break; - } - case AST_VAR: { - if (to_var(e)->get_idx() >= scope + max_var) { - has_var = true; - max_var = to_var(e)->get_idx() - scope; - } - break; - } - case AST_APP: { - app* a = to_app(e); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - m_todo.push_back(a->get_arg(i)); - m_scopes.push_back(scope); - } - break; - } - default: - UNREACHABLE(); - break; - } - } - m_visited.reset(); - return max_var; - } - - unsigned var_counter::get_max_var(const rule & r) { + unsigned rule_counter::get_max_rule_var(const rule & r) { m_todo.push_back(r.get_head()); m_scopes.push_back(0); unsigned n = r.get_tail_size(); @@ -576,22 +277,6 @@ namespace datalog { return get_max_var(has_var); } - unsigned var_counter::get_max_var(expr* e) { - bool has_var = false; - m_todo.push_back(e); - m_scopes.push_back(0); - return get_max_var(has_var); - } - - unsigned var_counter::get_next_var(expr* e) { - bool has_var = false; - m_todo.push_back(e); - m_scopes.push_back(0); - unsigned mv = get_max_var(has_var); - if (has_var) mv++; - return mv; - } - void del_rule(horn_subsume_model_converter* mc, rule& r) { if (mc) { ast_manager& m = mc->get_manager(); @@ -614,6 +299,7 @@ namespace datalog { } } + void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res) { if (!pc) return; @@ -651,6 +337,44 @@ namespace datalog { pc->insert(pr); } + void resolve_rule(rule const& r1, rule const& r2, unsigned idx, + expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res) { + if (!r1.get_proof()) { + return; + } + SASSERT(r2.get_proof()); + ast_manager& m = s1.get_manager(); + expr_ref fml(m); + res.to_formula(fml); + vector substs; + svector > positions; + substs.push_back(s1); + substs.push_back(s2); + + scoped_proof _sc(m); + proof_ref pr(m); + proof_ref_vector premises(m); + premises.push_back(r1.get_proof()); + premises.push_back(r2.get_proof()); + positions.push_back(std::make_pair(idx+1, 0)); + + TRACE("dl", + tout << premises[0]->get_id() << " " << mk_pp(premises[0].get(), m) << "\n"; + for (unsigned i = 0; i < s1.size(); ++i) { + tout << mk_pp(s1[i], m) << " "; + } + tout << "\n"; + tout << premises[1]->get_id() << " " << mk_pp(premises[1].get(), m) << "\n"; + for (unsigned i = 0; i < s2.size(); ++i) { + tout << mk_pp(s2[i], m) << " "; + } + tout << "\n"; + ); + + pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml, positions, substs); + res.set_proof(m, pr); + } + class skip_model_converter : public model_converter { public: skip_model_converter() {} @@ -678,10 +402,6 @@ namespace datalog { proof_converter* mk_skip_proof_converter() { return alloc(skip_proof_converter); } - unsigned get_max_var(const rule & r, ast_manager & m) { - var_counter ctr; - return ctr.get_max_var(r); - } void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt) { SASSERT(tgt.empty()); @@ -714,18 +434,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_qe/dl_util.h b/src/muz/base/dl_util.h similarity index 75% rename from src/muz_qe/dl_util.h rename to src/muz/base/dl_util.h index 61f85a691..57f7575ca 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz/base/dl_util.h @@ -26,7 +26,10 @@ 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" +#include"qe_util.h" namespace datalog { @@ -39,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, @@ -46,17 +57,6 @@ namespace datalog { LAST_CACHE_MODE }; - enum DL_ENGINE { - DATALOG_ENGINE, - PDR_ENGINE, - QPDR_ENGINE, - BMC_ENGINE, - QBMC_ENGINE, - TAB_ENGINE, - DUALITY_ENGINE, - LAST_ENGINE - }; - struct std_string_hash_proc { unsigned operator()(const std::string & s) const { return string_hash(s.c_str(), static_cast(s.length()), 17); } @@ -68,59 +68,14 @@ 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); - /** - Transform - ~(a1 | ... | aN) - into - ~a1 | ... | ~aN - and - ~(a1 & ... & aN) - into - ~a1 | ... | ~aN - - Return true if something was done. - */ - bool push_toplevel_junction_negation_inside(expr_ref& e); - - bool contains_var(expr * trm, unsigned var_idx); - /** - \brief Collect the variables in \c pred. - \pre \c pred must be a valid head or tail. - */ - void collect_vars(ast_manager & m, expr * pred, var_idx_set & result); - void collect_tail_vars(ast_manager & m, rule * r, var_idx_set & result); - - void get_free_vars(rule * r, ptr_vector& sorts); - /** \brief Return number of arguments of \c pred that are variables */ unsigned count_variable_arguments(app * pred); - /** - \brief Store in \c result the set of variables used by \c r when ignoring the tail \c t. - */ - void collect_non_local_vars(ast_manager & m, rule const * r, app * t, var_idx_set & result); - - /** - \brief Store in \c result the set of variables used by \c r when ignoring the tail elements \c t_1 and \c t_2. - */ - void collect_non_local_vars(ast_manager & m, rule const * r, app * t_1, app * t_2, var_idx_set & result); template void copy_nonvariables(app * src, T& tgt) @@ -175,44 +130,12 @@ 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 { bool values_match(const expr * v1, const expr * v2); - ast_manager & m_manager; - unsigned_vector m_args1; unsigned_vector m_args2; @@ -222,9 +145,11 @@ namespace datalog { static unsigned expr_cont_get_size(app * a) { return a->get_num_args(); } static expr * expr_cont_get(app * a, unsigned i) { return a->get_arg(i); } static unsigned expr_cont_get_size(const ptr_vector & v) { return v.size(); } + static unsigned expr_cont_get_size(const expr_ref_vector & v) { return v.size(); } static expr * expr_cont_get(const ptr_vector & v, unsigned i) { return v[i]; } + static expr * expr_cont_get(const expr_ref_vector & v, unsigned i) { return v[i]; } public: - variable_intersection(ast_manager & m) : m_manager(m), m_consts(m) {} + variable_intersection(ast_manager & m) : m_consts(m) {} unsigned size() const { return m_args1.size(); @@ -329,6 +254,12 @@ namespace datalog { } container[i-ofs] = container[i]; } + if (r_i != removed_col_cnt) { + for (unsigned i = 0; i < removed_col_cnt; ++i) { + std::cout << removed_cols[i] << " "; + } + std::cout << " container size: " << n << "\n"; + } SASSERT(r_i==removed_col_cnt); container.resize(n-removed_col_cnt); } @@ -412,80 +343,11 @@ namespace datalog { } - class counter { - protected: - typedef u_map map_impl; - map_impl m_data; - const bool m_stay_non_negative; + class rule_counter : public var_counter { public: - typedef map_impl::iterator iterator; - - counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} - - iterator begin() const { return m_data.begin(); } - iterator end() const { return m_data.end(); } - - void update(unsigned el, int delta); - int & get(unsigned el); - /** - \brief Increase values of elements in \c els by \c delta. - - The function returns a reference to \c *this to allow for expressions like - counter().count(sz, arr).get_positive_count() - */ - counter & count(unsigned sz, const unsigned * els, int delta = 1); - counter & count(const unsigned_vector & els, int delta = 1) { - return count(els.size(), els.c_ptr(), delta); - } - - void collect_positive(idx_set & acc) const; - unsigned get_positive_count() const; - bool get_max_positive(unsigned & res) const; - unsigned get_max_positive() const; - /** - Since the default counter value of a counter is zero, the result is never negative. - */ - int get_max_counter_value() const; - }; - - class var_counter : public counter { - ptr_vector m_sorts; - expr_fast_mark1 m_visited; - ptr_vector m_todo; - unsigned_vector m_scopes; - unsigned get_max_var(bool & has_var); - - public: - var_counter(bool stay_non_negative = true) : counter(stay_non_negative) {} - void count_vars(ast_manager & m, const app * t, int coef = 1); - void count_vars(ast_manager & m, const rule * r, int coef = 1); - unsigned get_max_var(const rule& r); - unsigned get_max_var(expr* e); - unsigned get_next_var(expr* e); - }; - - class ast_counter { - typedef obj_map map_impl; - map_impl m_data; - bool m_stay_non_negative; - public: - typedef map_impl::iterator iterator; - - ast_counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} - - iterator begin() const { return m_data.begin(); } - iterator end() const { return m_data.end(); } - - int & get(ast * el) { - return m_data.insert_if_not_there2(el, 0)->get_data().m_value; - } - void update(ast * el, int delta){ - get(el)+=delta; - SASSERT(!m_stay_non_negative || get(el)>=0); - } - - void inc(ast * el) { update(el, 1); } - void dec(ast * el) { update(el, -1); } + rule_counter(bool stay_non_negative = true): var_counter(stay_non_negative) {} + void count_rule_vars(ast_manager & m, const rule * r, int coef = 1); + unsigned get_max_rule_var(const rule& r); }; void del_rule(horn_subsume_model_converter* mc, rule& r); @@ -493,26 +355,16 @@ namespace datalog { void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res); + void resolve_rule(rule const& r1, rule const& r2, unsigned idx, + expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res); + model_converter* mk_skip_model_converter(); proof_converter* mk_skip_proof_converter(); - /** - Return maximal variable number, or zero is there isn't any - */ - // unsigned get_max_var(const rule & r, ast_manager & m); - 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); @@ -665,17 +517,31 @@ namespace datalog { } template - unsigned int_vector_hash(const T & cont) { - return string_hash(reinterpret_cast(cont.c_ptr()), - cont.size()*sizeof(typename T::data), 0); + struct default_obj_chash { + unsigned operator()(T const& cont, unsigned i) const { + return cont[i]->hash(); + } + }; + template + unsigned obj_vector_hash(const T & cont) { + return get_composite_hash(cont, cont.size(),default_kind_hash_proc(), default_obj_chash()); } template - struct int_vector_hash_proc { + struct obj_vector_hash_proc { unsigned operator()(const T & cont) const { - return int_vector_hash(cont); + return obj_vector_hash(cont); } }; + + template + struct svector_hash_proc { + unsigned operator()(const svector & cont) const { + return svector_hash()(cont); + } + }; + + template struct vector_eq_proc { bool operator()(const T & c1, const T & c2) const { return vectors_equal(c1, c2); } @@ -690,8 +556,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. */ @@ -843,42 +707,11 @@ namespace datalog { // // ----------------------------------- - struct uint64_hash { - typedef uint64 data; - unsigned operator()(uint64 x) const { return hash_ull(x); } - }; - template void universal_delete(T* ptr) { dealloc(ptr); } - void universal_delete(relation_base* ptr); - void universal_delete(table_base* 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() { 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_qe/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg similarity index 87% rename from src/muz_qe/fixedpoint_params.pyg rename to src/muz/base/fixedpoint_params.pyg index fcd280f25..e09e1307b 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -10,6 +10,8 @@ def_module_params('fixedpoint', ('bit_blast', BOOL, False, '(PDR) bit-blast bit-vectors'), ('explanations_on_relation_level', BOOL, False, '(DATALOG) if true, explanations are generated as history of each relation, rather than per fact (generate_explanations must be set to true for this option to have any effect)'), ('magic_sets_for_queries', BOOL, False, "(DATALOG) magic set transformation will be used for queries"), + ('magic', BOOL, False, "perform symbolic magic set transformation"), + ('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"), @@ -41,10 +43,15 @@ def_module_params('fixedpoint', ('simplify_formulas_pre', BOOL, False, "PDR: simplify derived formulas before inductive propagation"), ('simplify_formulas_post', BOOL, False, "PDR: simplify derived formulas after inductive propagation"), ('slice', BOOL, True, "PDR: simplify clause set using slicing"), + ('karr', BOOL, False, "Add linear invariants to clauses using Karr's method"), + ('quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"), + ('instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"), ('coalesce_rules', BOOL, False, "BMC: coalesce rules"), ('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_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)"), @@ -56,7 +63,9 @@ def_module_params('fixedpoint', "by putting in half of the table columns, if it would have been empty otherwise"), ('print_answer', BOOL, False, 'print answer instance(s) to query'), ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), + ('print_boogie_certificate', BOOL, False, 'print certificate for reachability or non-reachability using a format understood by Boogie'), ('print_statistics', BOOL, False, 'print statistics'), + ('use_utvpi', BOOL, True, 'PDR: Enable UTVPI strategy'), ('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), ('full_expand', BOOL, False, 'DUALITY: Fully expand derivation trees'), ('no_conj', BOOL, False, 'DUALITY: No forced covering (conjectures)'), @@ -65,6 +74,7 @@ def_module_params('fixedpoint', ('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'), ('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'), ('profile', BOOL, False, 'DUALITY: profile run time'), + ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), )) diff --git a/src/muz/base/hnf.cpp b/src/muz/base/hnf.cpp new file mode 100644 index 000000000..9d6f3c1ab --- /dev/null +++ b/src/muz/base/hnf.cpp @@ -0,0 +1,501 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + hnf.cpp + +Abstract: + + Horn normal form conversion. + +Author: + + Nikolaj Bjorner (nbjorner) 3-20-2013 + +Notes: + + Convert formula + + (forall x f(x)) + + into conjunction + + (f1 xy) (f2 xy) (f3 xy) + + such that + + (forall x f(x)) ~ /\ (forall xy (f_i xy)) + + modulo definitions that are introduced. + + + Convert proof with + asserted (forall xy (f' xy)) + + To: + (forall xy (f' xy)) by mp~ 1, 2 + 1. asserted/def-intro (forall xy (f xy)) + 2. (forall xy (f xy)) ~ (forall xy (f' xy)) by trans, 3, 4 + 3. (forall xy (f xy)) ~ (forall xy (f1 xy)) by pull quantifiers (rewrite) + 4. (forall xy (f1 xy)) ~ (forall xy (f' xy)) by oeq_quant_intro 5 + 5. f1 xy ~ f' xy by sub-proof. + + +--*/ +#include"hnf.h" +#include"warning.h" +#include"used_vars.h" +#include"well_sorted.h" +#include"var_subst.h" +#include"name_exprs.h" +#include"act_cache.h" +#include"cooperate.h" +#include"ast_pp.h" +#include"quant_hoist.h" +#include"dl_util.h" +#include"for_each_ast.h" +#include"for_each_expr.h" + +class hnf::imp { + ast_manager& m; + bool m_produce_proofs; + volatile bool m_cancel; + expr_ref_vector m_todo; + proof_ref_vector m_proofs; + expr_ref_vector m_refs; + symbol m_name; + svector m_names; + ptr_vector m_sorts; + quantifier_hoister m_qh; + obj_map m_memoize_disj; + obj_map m_memoize_proof; + func_decl_ref_vector m_fresh_predicates; + expr_ref_vector m_body; + proof_ref_vector m_defs; + + +public: + imp(ast_manager & m): + m(m), + m_produce_proofs(false), + m_cancel(false), + m_todo(m), + m_proofs(m), + m_refs(m), + m_name("P"), + m_qh(m), + m_fresh_predicates(m), + m_body(m), + m_defs(m) { + } + + void operator()(expr * n, + proof* p, + expr_ref_vector& result, + proof_ref_vector& ps) { + expr_ref fml(m); + proof_ref pr(m); + m_todo.reset(); + m_proofs.reset(); + m_refs.reset(); + m_memoize_disj.reset(); + m_memoize_proof.reset(); + m_fresh_predicates.reset(); + m_todo.push_back(n); + m_proofs.push_back(p); + m_produce_proofs = p != 0; + while (!m_todo.empty() && !m_cancel) { + fml = m_todo.back(); + pr = m_proofs.back(); + m_todo.pop_back(); + m_proofs.pop_back(); + mk_horn(fml, pr); + if (fml) { + result.push_back(fml); + ps.push_back(pr); + } + } + TRACE("hnf", + tout << mk_pp(n, m) << "\n==>\n"; + for (unsigned i = 0; i < result.size(); ++i) { + tout << mk_pp(result[i].get(), m) << "\n"; + }); + } + + void set_cancel(bool f) { + m_cancel = f; + } + + void set_name(symbol const& n) { + if (n == symbol::null) { + m_name = symbol("P"); + } + else { + m_name = n; + } + } + + func_decl_ref_vector const& get_fresh_predicates() { + return m_fresh_predicates; + } + + void reset() { + m_cancel = false; + m_todo.reset(); + m_proofs.reset(); + m_refs.reset(); + m_memoize_disj.reset(); + m_memoize_proof.reset(); + m_fresh_predicates.reset(); + } + + ast_manager& get_manager() { return m; } + +private: + + bool produce_proofs() const { + return m_produce_proofs; + } + + bool is_predicate(expr* p) const { + return is_app(p) && is_predicate(to_app(p)->get_decl()); + } + + bool is_predicate(func_decl* f) const { + return m.is_bool(f->get_range()) && f->get_family_id() == null_family_id; + } + + class contains_predicate_proc { + imp const& m; + public: + struct found {}; + contains_predicate_proc(imp const& m): m(m) {} + void operator()(var * n) {} + void operator()(quantifier * n) {} + void operator()(app* n) { + if (m.is_predicate(n)) throw found(); + } + }; + + bool contains_predicate(expr* fml) const { + contains_predicate_proc proc(*this); + try { + quick_for_each_expr(proc, fml); + } + catch (contains_predicate_proc::found) { + return true; + } + return false; + } + + + void mk_horn(expr_ref& fml, proof_ref& premise) { + SASSERT(!premise || fml == m.get_fact(premise)); + expr* e1, *e2; + expr_ref fml0(m), fml1(m), fml2(m), head(m); + proof_ref p(m); + fml0 = fml; + m_names.reset(); + m_sorts.reset(); + m_body.reset(); + m_defs.reset(); + m_qh.pull_quantifier(true, fml0, &m_sorts, &m_names); + if (premise){ + fml1 = bind_variables(fml0); + if (!m_sorts.empty()) { + proof* p1 = m.mk_pull_quant(fml, to_quantifier(fml1)); + premise = mk_modus_ponens(premise, p1); + fml = fml1; + } + } + head = fml0; + while (m.is_implies(head, e1, e2)) { + m_body.push_back(e1); + head = e2; + } + qe::flatten_and(m_body); + if (premise) { + p = m.mk_rewrite(fml0, mk_implies(m_body, head)); + } + + // + // Case: + // A \/ B -> C + // => + // A -> C + // B -> C + // + if (m_body.size() == 1 && m.is_or(m_body[0].get()) && contains_predicate(m_body[0].get())) { + app* _or = to_app(m_body[0].get()); + unsigned sz = _or->get_num_args(); + expr* const* args = _or->get_args(); + for (unsigned i = 0; i < sz; ++i) { + m_todo.push_back(bind_variables(m.mk_implies(args[i], head))); + m_proofs.push_back(0); + } + + if (premise) { + expr_ref f1 = bind_variables(mk_implies(m_body, head)); + expr* f2 = m.mk_and(sz, m_todo.c_ptr()+m_todo.size()-sz); + proof_ref p2(m), p3(m); + p2 = m.mk_def_axiom(m.mk_iff(f1, f2)); + p3 = mk_quant_intro(fml, f1, p); + p2 = mk_transitivity(p3, p2); + p2 = mk_modus_ponens(premise, p2); + for (unsigned i = 0; i < sz; ++i) { + m_proofs[m_proofs.size()-sz+i] = m.mk_and_elim(p2, i); + } + } + fml = 0; + return; + } + + + eliminate_disjunctions(m_body, m_defs); + p = mk_congruence(p, m_body, head, m_defs); + + eliminate_quantifier_body(m_body, m_defs); + p = mk_congruence(p, m_body, head, m_defs); + + fml2 = mk_implies(m_body, head); + + fml = bind_variables(fml2); + + if (premise) { + SASSERT(p); + p = mk_quant_intro(fml1, fml, p); + premise = mk_modus_ponens(premise, p); + } + } + + proof* mk_quant_intro(expr* e1, expr* e2, proof* p) { + if (m_sorts.empty()) { + return p; + } + quantifier* q1 = to_quantifier(e1); + quantifier* q2 = to_quantifier(e2); + if (m.is_iff(m.get_fact(p))) { + return m.mk_quant_intro(q1, q2, p); + } + if (m.is_oeq(m.get_fact(p))) { + return m.mk_oeq_quant_intro(q1, q2, p); + } + UNREACHABLE(); + return p; + } + + + void eliminate_disjunctions(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) { + expr* b = body.get(); + expr* e1; + bool negate_args = false; + bool is_disj = false; + unsigned num_disj = 0; + expr* const* disjs = 0; + if (!contains_predicate(b)) { + return; + } + TRACE("hnf", tout << mk_pp(b, m) << "\n";); + if (m.is_or(b)) { + is_disj = true; + negate_args = false; + num_disj = to_app(b)->get_num_args(); + disjs = to_app(b)->get_args(); + } + if (m.is_not(b, e1) && m.is_and(e1)) { + is_disj = true; + negate_args = true; + num_disj = to_app(e1)->get_num_args(); + disjs = to_app(e1)->get_args(); + } + if (is_disj) { + app* old_head = 0; + if (m_memoize_disj.find(b, old_head)) { + body = old_head; + } + else { + app_ref head = mk_fresh_head(b); + proof_ref_vector defs(m); + for (unsigned i = 0; i < num_disj; ++i) { + expr* e = disjs[i]; + if (negate_args) { + e = m.mk_not(e); + } + m_todo.push_back(bind_variables(m.mk_implies(e, head))); + m_proofs.push_back(0); + if (produce_proofs()) { + defs.push_back(m.mk_def_intro(m_todo.back())); + m_proofs[m_proofs.size()-1] = defs.back(); + } + } + if (produce_proofs()) { + proof* p = m.mk_apply_defs(body.get(), head, defs.size(), defs.c_ptr()); + m_refs.push_back(p); + m_memoize_proof.insert(b, p); + } + m_memoize_disj.insert(b, head); + m_refs.push_back(b); + m_refs.push_back(head); + // update the body to be the newly introduced head relation + body = head; + } + + if (produce_proofs()) { + proofs.push_back(m_memoize_proof.find(b)); + } + } + } + + app_ref mk_fresh_head(expr* e) { + ptr_vector sorts0, sorts1; + get_free_vars(e, sorts0); + expr_ref_vector args(m); + for (unsigned i = 0; i < sorts0.size(); ++i) { + if (sorts0[i]) { + args.push_back(m.mk_var(i, sorts0[i])); + sorts1.push_back(sorts0[i]); + } + } + func_decl_ref f(m); + f = m.mk_fresh_func_decl(m_name.str().c_str(), "", sorts1.size(), sorts1.c_ptr(), m.mk_bool_sort()); + m_fresh_predicates.push_back(f); + return app_ref(m.mk_app(f, args.size(), args.c_ptr()), m); + } + + void eliminate_disjunctions(expr_ref_vector& body, proof_ref_vector& proofs) { + for (unsigned i = 0; i < body.size(); ++i) { + expr_ref_vector::element_ref r = body[i]; + eliminate_disjunctions(r, proofs); + } + } + + void eliminate_quantifier_body(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) { + if (is_forall(body.get()) && contains_predicate(body.get())) { + quantifier* q = to_quantifier(body.get()); + expr* e = q->get_expr(); + if (!is_predicate(e)) { + app_ref head = mk_fresh_head(e); + m_todo.push_back(bind_variables(m.mk_implies(e, head))); + m_proofs.push_back(0); + body = m.update_quantifier(q, head); + if (produce_proofs()) { + proof* def_intro = m.mk_def_intro(m_todo.back()); + proof* def_proof = m.mk_apply_def(e, head, def_intro); + proofs.push_back(m.mk_nnf_neg(q, body.get(), 1, &def_proof)); + m_proofs[m_proofs.size()-1] = def_intro; + } + } + } + } + + void eliminate_quantifier_body(expr_ref_vector& body, proof_ref_vector& proofs) { + for (unsigned i = 0; i < body.size(); ++i) { + expr_ref_vector::element_ref r = body[i]; + eliminate_quantifier_body(r, proofs); + } + } + + app_ref mk_implies(expr_ref_vector const& body, expr* head) { + switch (body.size()) { + case 0: + return app_ref(to_app(head), m); + case 1: + return app_ref(m.mk_implies(body[0], head), m); + default: + return app_ref(m.mk_implies(m.mk_and(body.size(), body.c_ptr()), head), m); + } + } + + + proof_ref mk_congruence(proof* p1, expr_ref_vector const& body, expr* head, proof_ref_vector& defs) { + if (defs.empty()) { + return proof_ref(p1, m); + } + else { + SASSERT(p1); + proof_ref p2(m), p3(m); + app_ref fml = mk_implies(body, head); + expr* fact = m.get_fact(p1); + if (m.is_iff(fact)) { + p1 = m.mk_iff_oeq(p1); + fact = m.get_fact(p1); + } + VERIFY (m.is_oeq(fact) || m.is_eq(fact)); + app* e2 = to_app(to_app(fact)->get_arg(1)); + p2 = m.mk_oeq_congruence(e2, fml, defs.size(), defs.c_ptr()); + p3 = mk_transitivity(p1, p2); + defs.reset(); + return proof_ref(p3, m); + } + } + + proof_ref mk_modus_ponens(proof* premise, proof* eq) { + proof_ref result(m); + result = m.mk_modus_ponens(premise, eq); + if (m.get_fact(premise) == m.get_fact(result)) { + result = premise; + } + return result; + } + + proof* mk_transitivity(proof* p1, proof* p2) { + if (p1) { + app* f = to_app(m.get_fact(p1)); + if (f->get_arg(0) == f->get_arg(1)) { + return p2; + } + } + if (p2) { + app* f = to_app(m.get_fact(p2)); + if (f->get_arg(0) == f->get_arg(1)) { + return p1; + } + } + return m.mk_transitivity(p1, p2); + } + + expr_ref bind_variables(expr* e) { + SASSERT(m_sorts.size() == m_names.size()); + if (m_sorts.empty()) { + return expr_ref(e, m); + } + return expr_ref(m.mk_forall(m_sorts.size(), m_sorts.c_ptr(), m_names.c_ptr(), e), m); + } + +}; + +hnf::hnf(ast_manager & m) { + m_imp = alloc(imp, m); +} + +hnf::~hnf() { + dealloc(m_imp); +} + +void hnf::operator()(expr * n, proof* p, expr_ref_vector & rs, proof_ref_vector& ps) { + m_imp->operator()(n, p, rs, ps); + TRACE("hnf", + ast_manager& m = rs.get_manager(); + tout << mk_ismt2_pp(n, m) << "\nHNF result:\n"; + for (unsigned i = 0; i < rs.size(); ++i) { + tout << mk_pp(rs[i].get(), m) << "\n"; + } + ); +} + +void hnf::set_cancel(bool f) { + m_imp->set_cancel(f); +} + +void hnf::set_name(symbol const& n) { + m_imp->set_name(n); +} + +void hnf::reset() { + m_imp->reset(); +} + +func_decl_ref_vector const& hnf::get_fresh_predicates() { + return m_imp->get_fresh_predicates(); +} diff --git a/src/muz/base/hnf.h b/src/muz/base/hnf.h new file mode 100644 index 000000000..37339540b --- /dev/null +++ b/src/muz/base/hnf.h @@ -0,0 +1,49 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + hnf.h + +Abstract: + + Horn normal form convertion. +Author: + + +Notes: + + Very similar to NNF. + +--*/ + +#ifndef _HNF_H_ +#define _HNF_H_ + +#include"ast.h" +#include"params.h" +#include"defined_names.h" +#include"proof_converter.h" + +class hnf { + class imp; + imp * m_imp; +public: + hnf(ast_manager & m); + ~hnf(); + + void operator()(expr * n, // [IN] expression that should be put into Horn NF + proof* p, // [IN] proof of n + expr_ref_vector & rs, // [OUT] resultant (conjunction) of expressions + proof_ref_vector& ps // [OUT] proofs of rs + ); + + void cancel() { set_cancel(true); } + void reset_cancel() { set_cancel(false); } + void set_cancel(bool f); + void set_name(symbol const& name); + void reset(); + func_decl_ref_vector const& get_fresh_predicates(); +}; + +#endif /* _HNF_H_ */ diff --git a/src/muz_qe/proof_utils.cpp b/src/muz/base/proof_utils.cpp similarity index 84% rename from src/muz_qe/proof_utils.cpp rename to src/muz/base/proof_utils.cpp index 369c3aae6..87ecf9985 100644 --- a/src/muz_qe/proof_utils.cpp +++ b/src/muz/base/proof_utils.cpp @@ -1,6 +1,7 @@ #include "dl_util.h" #include "proof_utils.h" #include "ast_smt2_pp.h" +#include "var_subst.h" class reduce_hypotheses { typedef obj_hashtable expr_set; @@ -517,3 +518,95 @@ void proof_utils::permute_unit_resolution(proof_ref& pr) { obj_map cache; ::permute_unit_resolution(refs, cache, pr); } + +class push_instantiations_up_cl { + ast_manager& m; +public: + push_instantiations_up_cl(ast_manager& m): m(m) {} + + void operator()(proof_ref& p) { + expr_ref_vector s0(m); + p = push(p, s0); + } + +private: + + proof* push(proof* p, expr_ref_vector const& sub) { + proof_ref_vector premises(m); + expr_ref conclusion(m); + svector > positions; + vector substs; + + if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { + for (unsigned i = 0; i < premises.size(); ++i) { + compose(substs[i], sub); + premises[i] = push(premises[i].get(), substs[i]); + substs[i].reset(); + } + instantiate(sub, conclusion); + return + m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, + positions, + substs); + } + if (sub.empty()) { + return p; + } + if (m.is_modus_ponens(p)) { + SASSERT(m.get_num_parents(p) == 2); + proof* p0 = m.get_parent(p, 0); + proof* p1 = m.get_parent(p, 1); + if (m.get_fact(p0) == m.get_fact(p)) { + return push(p0, sub); + } + expr* e1, *e2; + if (m.is_rewrite(p1, e1, e2) && + is_quantifier(e1) && is_quantifier(e2) && + to_quantifier(e1)->get_num_decls() == to_quantifier(e2)->get_num_decls()) { + expr_ref r1(e1,m), r2(e2,m); + instantiate(sub, r1); + instantiate(sub, r2); + p1 = m.mk_rewrite(r1, r2); + return m.mk_modus_ponens(push(p0, sub), p1); + } + } + premises.push_back(p); + substs.push_back(sub); + conclusion = m.get_fact(p); + instantiate(sub, conclusion); + return m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); + } + + void compose(expr_ref_vector& sub, expr_ref_vector const& s0) { + for (unsigned i = 0; i < sub.size(); ++i) { + expr_ref e(m); + var_subst(m, false)(sub[i].get(), s0.size(), s0.c_ptr(), e); + sub[i] = e; + } + } + + void instantiate(expr_ref_vector const& sub, expr_ref& fml) { + if (sub.empty()) { + return; + } + if (!is_forall(fml)) { + return; + } + quantifier* q = to_quantifier(fml); + if (q->get_num_decls() != sub.size()) { + TRACE("proof_utils", tout << "quantifier has different number of variables than substitution"; + tout << mk_pp(q, m) << "\n"; + tout << sub.size() << "\n";); + return; + } + var_subst(m, false)(q->get_expr(), sub.size(), sub.c_ptr(), fml); + } + +}; + +void proof_utils::push_instantiations_up(proof_ref& pr) { + push_instantiations_up_cl push(pr.get_manager()); + push(pr); +} + + diff --git a/src/muz_qe/proof_utils.h b/src/muz/base/proof_utils.h similarity index 74% rename from src/muz_qe/proof_utils.h rename to src/muz/base/proof_utils.h index 53a38592e..383b5c379 100644 --- a/src/muz_qe/proof_utils.h +++ b/src/muz/base/proof_utils.h @@ -36,6 +36,13 @@ public: */ static void permute_unit_resolution(proof_ref& pr); + /** + \brief Push instantiations created in hyper-resolutions up to leaves. + This produces a "ground" proof where leaves are annotated by instantiations. + */ + static void push_instantiations_up(proof_ref& pr); + + }; #endif diff --git a/src/muz_qe/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp similarity index 96% rename from src/muz_qe/dl_bmc_engine.cpp rename to src/muz/bmc/dl_bmc_engine.cpp index 80fcdea4a..eb4bc72c4 100644 --- a/src/muz_qe/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -23,13 +23,16 @@ 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" +#include "scoped_proof.h" +#include"fixedpoint_params.hpp" namespace datalog { @@ -44,7 +47,6 @@ namespace datalog { public: qlinear(bmc& b): b(b), m(b.m), m_bv(m), m_bit_width(1) {} - lbool check() { setup(); m_bit_width = 4; @@ -298,6 +300,7 @@ namespace datalog { r->to_formula(fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); + proof_ref p(m); if (r0) { VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); @@ -307,7 +310,10 @@ namespace datalog { r1->to_formula(concl); scoped_proof _sp(m); - proof* p = m.mk_asserted(fml); + p = r->get_proof(); + if (!p) { + p = m.mk_asserted(fml); + } proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); @@ -320,13 +326,17 @@ namespace datalog { else { r2->to_formula(concl); scoped_proof _sp(m); - proof* p = m.mk_asserted(fml); + p = r->get_proof(); + if (!p) { + p = m.mk_asserted(fml); + } if (sub.empty()) { pr = p; } else { substs.push_back(sub); - pr = m.mk_hyper_resolve(1, &p, concl, positions, substs); + proof* ps[1] = { p }; + pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } @@ -340,7 +350,7 @@ namespace datalog { pred = r->get_decl(0); } scoped_proof _sp(m); - apply(m, b.m_pc.get(), pr); + apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; return l_true; } @@ -474,6 +484,9 @@ namespace datalog { } proof_ref get_proof(model_ref& md, func_decl* pred, app* prop, unsigned level) { + if (b.m_cancel) { + return proof_ref(0, m); + } TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); expr_ref prop_r(m), prop_v(m), fml(m), prop_body(m), tmp(m), body(m); @@ -497,7 +510,7 @@ namespace datalog { SASSERT(r); r->to_formula(fml); IF_VERBOSE(1, verbose_stream() << mk_pp(fml, m) << "\n";); - prs.push_back(m.mk_asserted(fml)); + prs.push_back(r->get_proof()); unsigned sz = r->get_uninterpreted_tail_size(); ptr_vector rule_vars; @@ -536,8 +549,9 @@ namespace datalog { model_ref md; b.m_solver.get_model(md); IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); - proof_ref pr = get_proof(md, b.m_query_pred, to_app(level_query), level); - apply(m, b.m_pc.get(), pr); + proof_ref pr(m); + pr = get_proof(md, b.m_query_pred, to_app(level_query), level); + apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } @@ -1001,7 +1015,6 @@ namespace datalog { symbol is_name(_name.str().c_str()); std::stringstream _name2; _name2 << "get_succ#" << i; - symbol acc_name(_name2.str().c_str()); ptr_vector accs; type_ref tr(0); accs.push_back(mk_accessor_decl(name, tr)); @@ -1034,7 +1047,7 @@ namespace datalog { var_subst vs(m, false); mk_subst(*rules[i], path, trace, sub); rules[i]->to_formula(fml); - prs.push_back(m.mk_asserted(fml)); + prs.push_back(rules[i]->get_proof()); unsigned sz = trace->get_num_args(); if (sub.empty() && sz == 0) { pr = prs[0].get(); @@ -1112,7 +1125,6 @@ namespace datalog { } void mk_answer(model_ref& md, expr_ref& trace, expr_ref& path) { - proof_ref pr(m); IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); md->eval(trace, trace); md->eval(path, path); @@ -1120,7 +1132,11 @@ namespace datalog { for (unsigned i = 0; i < b.m_solver.size(); ++i) { verbose_stream() << mk_pp(b.m_solver.get_formulas()[i], m) << "\n"; }); - b.m_answer = get_proof(md, to_app(trace), to_app(path)); + scoped_proof _sp(m); + proof_ref pr(m); + pr = get_proof(md, to_app(trace), to_app(path)); + apply(m, b.m_ctx.get_proof_converter().get(), pr); + b.m_answer = pr; } }; @@ -1155,6 +1171,9 @@ namespace datalog { private: void get_model(unsigned level) { + if (b.m_cancel) { + return; + } rule_manager& rm = b.m_ctx.get_rule_manager(); expr_ref level_query = mk_level_predicate(b.m_query_pred, level); model_ref md; @@ -1203,6 +1222,15 @@ namespace datalog { r->to_formula(fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); + proof_ref p(m); + { + scoped_proof _sp(m); + p = r->get_proof(); + if (!p) { + p = m.mk_asserted(fml); + } + } + if (r0) { VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); @@ -1210,9 +1238,8 @@ namespace datalog { apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); r1->to_formula(concl); - scoped_proof _sp(m); - proof* p = m.mk_asserted(fml); + scoped_proof _sp(m); proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); @@ -1225,13 +1252,13 @@ namespace datalog { else { r2->to_formula(concl); scoped_proof _sp(m); - proof* p = m.mk_asserted(fml); if (sub.empty()) { pr = p; } else { substs.push_back(sub); - pr = m.mk_hyper_resolve(1, &p, concl, positions, substs); + proof * ps[1] = { p }; + pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } @@ -1245,7 +1272,7 @@ namespace datalog { pred = r->get_decl(0); } scoped_proof _sp(m); - apply(m, b.m_pc.get(), pr); + apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } @@ -1392,6 +1419,7 @@ namespace datalog { }; bmc::bmc(context& ctx): + engine_base(ctx.get_manager(), "bmc"), m_ctx(ctx), m(ctx.get_manager()), m_solver(m, m_fparams), @@ -1406,31 +1434,22 @@ namespace datalog { lbool bmc::query(expr* query) { m_solver.reset(); m_answer = 0; - m_ctx.ensure_opened(); m_rules.reset(); datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); - datalog::rule_set old_rules(m_ctx.get_rules()); - datalog::rule_ref_vector query_rules(rule_manager); - datalog::rule_ref query_rule(rule_manager); - rule_manager.mk_query(query, m_query_pred, query_rules, query_rule); - m_ctx.add_rules(query_rules); - expr_ref bg_assertion = m_ctx.get_background_assertion(); - - model_converter_ref mc = datalog::mk_skip_model_converter(); - m_pc = datalog::mk_skip_proof_converter(); - m_ctx.set_output_predicate(m_query_pred); - m_ctx.apply_default_transformation(mc, m_pc); + 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(); + apply_default_transformation(m_ctx); if (m_ctx.get_params().slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); - m_ctx.transform_rules(transformer, mc, m_pc); - m_query_pred = slice->get_predicate(m_query_pred.get()); - m_ctx.set_output_predicate(m_query_pred); + m_ctx.transform_rules(transformer); } - m_rules.add_rules(m_ctx.get_rules()); + m_query_pred = m_ctx.get_rules().get_output_predicate(); + m_rules.replace_rules(m_ctx.get_rules()); m_rules.close(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); diff --git a/src/muz_qe/dl_bmc_engine.h b/src/muz/bmc/dl_bmc_engine.h similarity index 96% rename from src/muz_qe/dl_bmc_engine.h rename to src/muz/bmc/dl_bmc_engine.h index 06901a160..e20987002 100644 --- a/src/muz_qe/dl_bmc_engine.h +++ b/src/muz/bmc/dl_bmc_engine.h @@ -30,7 +30,7 @@ Revision History: namespace datalog { class context; - class bmc { + class bmc : public engine_base { context& m_ctx; ast_manager& m; smt_params m_fparams; @@ -39,7 +39,6 @@ namespace datalog { func_decl_ref m_query_pred; expr_ref m_answer; volatile bool m_cancel; - proof_converter_ref m_pc; void checkpoint(); diff --git a/src/muz/clp/clp_context.cpp b/src/muz/clp/clp_context.cpp new file mode 100644 index 000000000..5f3a09e2d --- /dev/null +++ b/src/muz/clp/clp_context.cpp @@ -0,0 +1,242 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + clp_context.cpp + +Abstract: + + Bounded CLP (symbolic simulation using Z3) context. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + +--*/ + +#include "clp_context.h" +#include "dl_context.h" +#include "unifier.h" +#include "var_subst.h" +#include "substitution.h" +#include "smt_kernel.h" +#include "dl_transforms.h" + +namespace datalog { + + class clp::imp { + struct stats { + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + unsigned m_num_unfold; + unsigned m_num_no_unfold; + unsigned m_num_subsumed; + }; + + context& m_ctx; + ast_manager& m; + rule_manager& rm; + smt_params m_fparams; + smt::kernel m_solver; + var_subst m_var_subst; + expr_ref_vector m_ground; + app_ref_vector m_goals; + volatile bool m_cancel; + stats m_stats; + public: + imp(context& ctx): + m_ctx(ctx), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_solver(m, m_fparams), // TBD: can be replaced by efficient BV solver. + m_var_subst(m, false), + m_ground(m), + m_goals(m), + m_cancel(false) + { + // m_fparams.m_relevancy_lvl = 0; + m_fparams.m_mbqi = false; + m_fparams.m_soft_timeout = 1000; + } + + ~imp() {} + + lbool query(expr* query) { + m_ctx.ensure_opened(); + m_solver.reset(); + m_goals.reset(); + rm.mk_query(query, m_ctx.get_rules()); + 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); + 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); + } + + void cancel() { + m_cancel = true; + m_solver.cancel(); + } + + void cleanup() { + m_cancel = false; + m_goals.reset(); + m_solver.reset_cancel(); + } + + void reset_statistics() { + m_stats.reset(); + } + + void collect_statistics(statistics& st) const { + //st.update("tab.num_unfold", m_stats.m_num_unfold); + //st.update("tab.num_unfold_fail", m_stats.m_num_no_unfold); + //st.update("tab.num_subsumed", m_stats.m_num_subsumed); + } + + void display_certificate(std::ostream& out) const { + expr_ref ans = get_answer(); + out << mk_pp(ans, m) << "\n"; + + } + + expr_ref get_answer() const { + return expr_ref(m.mk_true(), m); + } + + private: + + void reset_ground() { + m_ground.reset(); + } + + void ground(expr_ref& e) { + ptr_vector sorts; + get_free_vars(e, sorts); + if (m_ground.size() < sorts.size()) { + m_ground.resize(sorts.size()); + } + for (unsigned i = 0; i < sorts.size(); ++i) { + if (sorts[i] && !m_ground[i].get()) { + m_ground[i] = m.mk_fresh_const("c",sorts[i]); + } + } + m_var_subst(e, m_ground.size(), m_ground.c_ptr(), e); + } + + static bool rule_sort_fn(const rule *r1, const rule *r2) { + return r1->get_uninterpreted_tail_size() < r2->get_uninterpreted_tail_size(); + } + + lbool search(unsigned depth, unsigned index) { + if (index == m_goals.size()) { + return l_true; + } + if (depth == 0) { + return l_undef; + } + IF_VERBOSE(1, verbose_stream() << "search " << depth << " " << index << "\n";); + unsigned num_goals = m_goals.size(); + app* head = m_goals[index].get(); + + rule_vector rules(m_ctx.get_rules().get_predicate_rules(head->get_decl())); + std::stable_sort(rules.begin(), rules.end(), rule_sort_fn); + + lbool status = l_false; + for (unsigned i = 0; i < rules.size(); ++i) { + rule* r = rules[i]; + m_solver.push(); + reset_ground(); + expr_ref tmp(m); + tmp = r->get_head(); + IF_VERBOSE(2, verbose_stream() << index << " " << mk_pp(tmp, m) << "\n";); + ground(tmp); + for (unsigned j = 0; j < head->get_num_args(); ++j) { + expr_ref eq(m); + eq = m.mk_eq(head->get_arg(j), to_app(tmp)->get_arg(j)); + m_solver.assert_expr(eq); + } + for (unsigned j = r->get_uninterpreted_tail_size(); j < r->get_tail_size(); ++j) { + tmp = r->get_tail(j); + ground(tmp); + m_solver.assert_expr(tmp); + } + lbool is_sat = m_solver.check(); + switch (is_sat) { + case l_false: + break; + case l_true: + if (depth == 1 && (index+1 > m_goals.size() || r->get_uninterpreted_tail_size() > 0)) { + status = l_undef; + break; + } + for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { + tmp = r->get_tail(j); + ground(tmp); + m_goals.push_back(to_app(tmp)); + } + switch(search(depth-1, index+1)) { + case l_undef: + status = l_undef; + // fallthrough + case l_false: + m_goals.resize(num_goals); + break; + case l_true: + return l_true; + } + break; + case l_undef: + status = l_undef; + throw default_exception("undef"); + } + m_solver.pop(1); + } + return status; + } + + + proof_ref get_proof() const { + return proof_ref(0, m); + } + }; + + clp::clp(context& ctx): + engine_base(ctx.get_manager(), "clp"), + m_imp(alloc(imp, ctx)) { + } + clp::~clp() { + dealloc(m_imp); + } + lbool clp::query(expr* query) { + return m_imp->query(query); + } + void clp::cancel() { + m_imp->cancel(); + } + void clp::cleanup() { + m_imp->cleanup(); + } + void clp::reset_statistics() { + m_imp->reset_statistics(); + } + void clp::collect_statistics(statistics& st) const { + m_imp->collect_statistics(st); + } + void clp::display_certificate(std::ostream& out) const { + m_imp->display_certificate(out); + } + expr_ref clp::get_answer() { + return m_imp->get_answer(); + } + +}; diff --git a/src/muz/clp/clp_context.h b/src/muz/clp/clp_context.h new file mode 100644 index 000000000..6464ae634 --- /dev/null +++ b/src/muz/clp/clp_context.h @@ -0,0 +1,46 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + clp_context.h + +Abstract: + + Bounded CLP (symbolic simulation using Z3) context. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + +--*/ +#ifndef _CLP_CONTEXT_H_ +#define _CLP_CONTEXT_H_ + +#include "ast.h" +#include "lbool.h" +#include "statistics.h" +#include "dl_engine_base.h" + +namespace datalog { + class context; + + class clp : public datalog::engine_base { + class imp; + imp* m_imp; + public: + clp(context& ctx); + ~clp(); + virtual lbool query(expr* query); + virtual void cancel(); + virtual void cleanup(); + virtual void reset_statistics(); + virtual void collect_statistics(statistics& st) const; + virtual void display_certificate(std::ostream& out) const; + virtual expr_ref get_answer(); + }; +}; + +#endif diff --git a/src/muz_qe/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp similarity index 80% rename from src/muz_qe/duality_dl_interface.cpp rename to src/muz/duality/duality_dl_interface.cpp index 016731e3a..7de8f54a0 100644 --- a/src/muz_qe/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -18,7 +18,6 @@ Revision History: --*/ -#include "dl_cmds.h" #include "dl_context.h" #include "dl_mk_coi_filter.h" #include "dl_mk_interp_tail_simplifier.h" @@ -26,7 +25,6 @@ Revision History: #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" @@ -36,6 +34,7 @@ Revision History: #include "expr_abstract.h" #include "model_smt2_pp.h" #include "model_v2_pp.h" +#include "fixedpoint_params.hpp" // template class symbol_table; @@ -77,7 +76,9 @@ namespace Duality { }; -dl_interface::dl_interface(datalog::context& dl_ctx) : m_ctx(dl_ctx) +dl_interface::dl_interface(datalog::context& dl_ctx) : + engine_base(dl_ctx.get_manager(), "duality"), + m_ctx(dl_ctx) { _d = 0; @@ -325,7 +326,11 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp } -void dl_interface::display_certificate(std::ostream& out) { + void dl_interface::display_certificate(std::ostream& out) const { + ((dl_interface *)this)->display_certificate_non_const(out); + } + +void dl_interface::display_certificate_non_const(std::ostream& out) { if(_d->status == StatusModel){ ast_manager &m = m_ctx.get_manager(); model_ref md = get_model(); @@ -411,7 +416,74 @@ model_ref dl_interface::get_model() { return md; } +static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) { + context &ctx = d->dd()->ctx; + ast_manager &mgr = ctx.m(); + RPFP::Node &node = *cex.root; + RPFP::Edge &edge = *node.Outgoing; + RPFP::Edge *orig_edge = edge.map; + + // first, prove the children (that are actually used) + + proof_ref_vector prems(mgr); + ::vector substs; + int orig_clause = d->dd()->map[orig_edge]; + expr &t = d->dd()->clauses[orig_clause]; + prems.push_back(mgr.mk_asserted(ctx.uncook(t))); + + substs.push_back(expr_ref_vector(mgr)); + 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)); + expr val = cex.tree->Eval(&edge,skolem); + expr_ref thing(ctx.uncook(val),mgr); + substs[0].push_back(thing); + expr local_skolem = cex.tree->Localize(&edge,skolem); + (*local_func_decls).insert(local_skolem.decl()); + } + } + + svector > pos; + for(unsigned i = 0; i < edge.Children.size(); i++){ + if(!cex.tree->Empty(edge.Children[i])){ + pos.push_back(std::pair(i+1,0)); + Solver::Counterexample foo = cex; + foo.root = edge.Children[i]; + proof_ref prem = extract_proof(d,foo); + prems.push_back(prem); + substs.push_back(expr_ref_vector(mgr)); + } + } + + func_decl f = node.Name; + std::vector args; + for(unsigned i = 0; i < edge.F.IndParams.size(); i++) + args.push_back(cex.tree->Eval(&edge,edge.F.IndParams[i])); + expr conc = f(args); + + + ::vector pprems; + for(unsigned i = 0; i < prems.size(); i++) + pprems.push_back(prems[i].get()); + + proof_ref res(mgr.mk_hyper_resolve(pprems.size(),&pprems[0], ctx.uncook(conc), pos, substs),mgr); + return res; + +} + proof_ref dl_interface::get_proof() { - return proof_ref(m_ctx.get_manager()); + if(_d->status == StatusRefutation){ + hash_set locals; + local_func_decls = &locals; + return extract_proof(this,_d->cex); + } + else + return proof_ref(m_ctx.get_manager()); } } diff --git a/src/muz_qe/duality_dl_interface.h b/src/muz/duality/duality_dl_interface.h similarity index 84% rename from src/muz_qe/duality_dl_interface.h rename to src/muz/duality/duality_dl_interface.h index 332c3f12c..19444ca50 100644 --- a/src/muz_qe/duality_dl_interface.h +++ b/src/muz/duality/duality_dl_interface.h @@ -24,6 +24,7 @@ Revision History: #include "lbool.h" #include "dl_rule.h" #include "dl_rule_set.h" +#include "dl_engine_base.h" #include "statistics.h" namespace datalog { @@ -34,7 +35,7 @@ namespace Duality { class duality_data; - class dl_interface { + class dl_interface : public datalog::engine_base { duality_data *_d; datalog::context &m_ctx; @@ -48,7 +49,7 @@ namespace Duality { void cleanup(); - void display_certificate(std::ostream& out); + void display_certificate(std::ostream& out) const; void collect_statistics(statistics& st) const; @@ -69,6 +70,9 @@ namespace Duality { proof_ref get_proof(); duality_data *dd(){return _d;} + + private: + void display_certificate_non_const(std::ostream& out); }; } diff --git a/src/muz_qe/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp similarity index 99% rename from src/muz_qe/datalog_parser.cpp rename to src/muz/fp/datalog_parser.cpp index cfe283410..830f6d078 100644 --- a/src/muz_qe/datalog_parser.cpp +++ b/src/muz/fp/datalog_parser.cpp @@ -441,6 +441,7 @@ protected: unsigned m_sym_idx; std::string m_path; str2sort m_sort_dict; + // true if an error occured during the current call to the parse_stream // function @@ -812,7 +813,8 @@ protected: } f = m_manager.mk_func_decl(s, domain.size(), domain.c_ptr(), m_manager.mk_bool_sort()); - m_context.register_predicate(f); + m_context.register_predicate(f, true); + while (tok == TK_ID) { char const* pred_pragma = m_lexer->get_token_data(); if(strcmp(pred_pragma, "printtuples")==0 || strcmp(pred_pragma, "outputtuples")==0) { @@ -1162,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_qe/datalog_parser.h b/src/muz/fp/datalog_parser.h similarity index 100% rename from src/muz_qe/datalog_parser.h rename to src/muz/fp/datalog_parser.h diff --git a/src/muz_qe/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp similarity index 92% rename from src/muz_qe/dl_cmds.cpp rename to src/muz/fp/dl_cmds.cpp index ade4b633d..612d319ad 100644 --- a/src/muz_qe/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" @@ -29,6 +30,7 @@ Notes: #include"scoped_ctrl_c.h" #include"scoped_timer.h" #include"trail.h" +#include"fixedpoint_params.hpp" #include @@ -37,6 +39,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 +73,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"); @@ -220,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); @@ -229,12 +233,13 @@ public: status = dlctx.query(m_target); } catch (z3_error & ex) { + ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl; throw ex; } catch (z3_exception& ex) { ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl; + query_exn = true; } - dlctx.cleanup(); } switch (status) { case l_false: @@ -250,6 +255,7 @@ public: ctx.regular_stream() << "unknown\n"; switch(dlctx.get_status()) { case datalog::INPUT_ERROR: + ctx.regular_stream() << "input error\n"; break; case datalog::MEMOUT: @@ -259,14 +265,27 @@ public: case datalog::TIMEOUT: ctx.regular_stream() << "timeout\n"; break; - - case datalog::OK: + + case datalog::APPROX: + ctx.regular_stream() << "approximated relations\n"; break; + + case datalog::OK: + SASSERT(query_exn); + break; + + case datalog::CANCELED: + ctx.regular_stream() << "canceled\n"; + dlctx.display_profile(ctx.regular_stream()); + break; + default: UNREACHABLE(); + break; } break; } + dlctx.cleanup(); print_statistics(ctx); m_target = 0; } @@ -318,9 +337,7 @@ private: void print_certificate(cmd_context& ctx) { if (m_dl_ctx->get_params().print_certificate()) { datalog::context& dlctx = m_dl_ctx->dlctx(); - if (!dlctx.display_certificate(ctx.regular_stream())) { - throw cmd_exception("certificates are not supported for the selected engine"); - } + dlctx.display_certificate(ctx.regular_stream()); ctx.regular_stream() << "\n"; } } @@ -435,40 +452,8 @@ public: ctx.insert(var); m_dl_ctx->dlctx().register_variable(var); } - }; -class dl_push_cmd : public cmd { - ref m_ctx; -public: - dl_push_cmd(dl_context* ctx): - cmd("fixedpoint-push"), - m_ctx(ctx) - {} - - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "push context on the fixedpoint engine"; } - - virtual void execute(cmd_context& ctx) { - m_ctx->push(); - } -}; - -class dl_pop_cmd : public cmd { - ref m_ctx; -public: - dl_pop_cmd(dl_context* ctx): - cmd("fixedpoint-pop"), - m_ctx(ctx) - {} - - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "pop context on the fixedpoint engine"; } - - virtual void execute(cmd_context& ctx) { - m_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); @@ -477,8 +462,11 @@ static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_c ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_var_cmd, dl_ctx)); // #ifndef _EXTERNAL_RELEASE + // TODO: we need these! +#if 0 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 } diff --git a/src/muz_qe/dl_cmds.h b/src/muz/fp/dl_cmds.h similarity index 100% rename from src/muz_qe/dl_cmds.h rename to src/muz/fp/dl_cmds.h diff --git a/src/muz/fp/dl_register_engine.cpp b/src/muz/fp/dl_register_engine.cpp new file mode 100644 index 000000000..57413b7cb --- /dev/null +++ b/src/muz/fp/dl_register_engine.cpp @@ -0,0 +1,54 @@ +/*++ +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" +#include "duality_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 DUALITY_ENGINE: + return alloc(Duality::dl_interface, *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_qe/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp similarity index 84% rename from src/muz_qe/horn_tactic.cpp rename to src/muz/fp/horn_tactic.cpp index c22474a7c..0c094c277 100644 --- a/src/muz_qe/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -21,21 +21,26 @@ 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" +#include"fixedpoint_params.hpp" 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); } @@ -92,7 +97,7 @@ class horn_tactic : public tactic { void register_predicate(expr* a) { SASSERT(is_predicate(a)); - m_ctx.register_predicate(to_app(a)->get_decl(), true); + m_ctx.register_predicate(to_app(a)->get_decl(), false); } void check_predicate(ast_mark& mark, expr* a) { @@ -113,8 +118,8 @@ class horn_tactic : public tactic { todo.append(to_app(a)->get_num_args(), to_app(a)->get_args()); } else if (m.is_ite(a)) { - todo.append(to_app(a)->get_arg(1)); - todo.append(to_app(a)->get_arg(2)); + todo.push_back(to_app(a)->get_arg(1)); + todo.push_back(to_app(a)->get_arg(2)); } else if (is_predicate(a)) { register_predicate(a); @@ -124,13 +129,23 @@ class horn_tactic : public tactic { enum formula_kind { IS_RULE, IS_QUERY, IS_NONE }; + bool is_implication(expr* f) { + expr* e1; + while (is_forall(f)) { + f = to_quantifier(f)->get_expr(); + } + while (m.is_implies(f, e1, f)) ; + return is_predicate(f); + } + formula_kind get_formula_kind(expr_ref& f) { - normalize(f); + expr_ref tmp(f); + normalize(tmp); ast_mark mark; expr_ref_vector args(m), body(m); expr_ref head(m); expr* a = 0, *a1 = 0; - datalog::flatten_or(f, args); + qe::flatten_or(tmp, args); for (unsigned i = 0; i < args.size(); ++i) { a = args[i].get(); check_predicate(mark, a); @@ -147,12 +162,15 @@ class horn_tactic : public tactic { body.push_back(m.mk_not(a)); } } - f = m.mk_and(body.size(), body.c_ptr()); if (head) { - f = m.mk_implies(f, head); + if (!is_implication(f)) { + f = m.mk_and(body.size(), body.c_ptr()); + f = m.mk_implies(f, head); + } return IS_RULE; } else { + f = m.mk_and(body.size(), body.c_ptr()); return IS_QUERY; } } @@ -169,10 +187,9 @@ class horn_tactic : public tactic { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("horn", *g); - bool produce_models = g->models_enabled(); bool produce_proofs = g->proofs_enabled(); - if (produce_proofs) { + if (produce_proofs) { if (!m_ctx.get_params().generate_proof_trace()) { params_ref params = m_ctx.get_params().p; params.set_bool("generate_proof_trace", true); @@ -200,6 +217,7 @@ class horn_tactic : public tactic { break; default: msg << "formula is not in Horn fragment: " << mk_pp(g->form(i), m) << "\n"; + TRACE("horn", tout << msg.str();); throw tactic_exception(msg.str().c_str()); } } @@ -213,6 +231,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(); @@ -230,7 +251,15 @@ class horn_tactic : public tactic { model_converter_ref & mc, proof_converter_ref & pc) { - lbool is_reachable = m_ctx.query(q); + lbool is_reachable = l_undef; + + try { + is_reachable = m_ctx.query(q); + } + catch (default_exception& ex) { + IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";); + throw ex; + } g->inc_depth(); bool produce_models = g->models_enabled(); @@ -240,10 +269,13 @@ class horn_tactic : public tactic { switch (is_reachable) { case l_true: { // goal is unsat - g->assert_expr(m.mk_false()); if (produce_proofs) { proof_ref proof = m_ctx.get_proof(); pc = proof2proof_converter(m, proof); + g->assert_expr(m.mk_false(), proof, 0); + } + else { + g->assert_expr(m.mk_false()); } break; } @@ -252,7 +284,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; } @@ -271,26 +309,18 @@ class horn_tactic : public tactic { proof_converter_ref & pc) { expr_ref fml(m); - bool produce_models = g->models_enabled(); - bool produce_proofs = g->proofs_enabled(); - if (produce_models) { - mc = datalog::mk_skip_model_converter(); - } - if (produce_proofs) { - pc = datalog::mk_skip_proof_converter(); - } 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(mc, pc); + apply_default_transformation(m_ctx); if (m_ctx.get_params().slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); - m_ctx.transform_rules(transformer, mc, pc); + m_ctx.transform_rules(transformer); } expr_substitution sub(m); diff --git a/src/muz_qe/horn_tactic.h b/src/muz/fp/horn_tactic.h similarity index 100% rename from src/muz_qe/horn_tactic.h rename to src/muz/fp/horn_tactic.h diff --git a/src/muz/pdr/pdr_closure.cpp b/src/muz/pdr/pdr_closure.cpp new file mode 100644 index 000000000..86af8b2f9 --- /dev/null +++ b/src/muz/pdr/pdr_closure.cpp @@ -0,0 +1,176 @@ +/*++ +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";); + result = m.mk_true(); + } + 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 qe::mk_and(fmls); + } + + 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_qe/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp similarity index 92% rename from src/muz_qe/pdr_context.cpp rename to src/muz/pdr/pdr_context.cpp index 7df4a2b64..aab7b1388 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -43,6 +43,10 @@ Notes: #include "ast_ll_pp.h" #include "proof_checker.h" #include "smt_value_sort.h" +#include "proof_utils.h" +#include "dl_boogie_proof.h" +#include "qe_util.h" +#include "scoped_proof.h" namespace pdr { @@ -73,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(); @@ -275,7 +279,7 @@ namespace pdr { src.pop_back(); } else if (is_invariant(tgt_level, curr, false, assumes_level)) { - + add_property(curr, assumes_level?tgt_level:infty_level); TRACE("pdr", tout << "is invariant: "<< pp_level(tgt_level) << " " << mk_pp(curr, m) << "\n";); src[i] = src.back(); @@ -340,7 +344,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)) { @@ -585,7 +589,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); @@ -596,7 +600,7 @@ namespace pdr { expr_ref fml = pm.mk_and(conj); th_rewriter rw(m); rw(fml); - if (ctx.is_dl()) { + if (ctx.is_dl() || ctx.is_utvpi()) { hoist_non_bool_if(fml); } TRACE("pdr", tout << mk_pp(fml, m) << "\n";); @@ -763,6 +767,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)); @@ -775,7 +780,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)) { @@ -950,13 +955,48 @@ namespace pdr { return out; } + /** + \brief Ensure that all nodes in the tree have associated models. + get_trace and get_proof_trace rely on models to extract rules. + */ + 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()); + } + + todo.push_back(m_root); + 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()); + } + } + /** Extract trace comprising of constraints and predicates that are satisfied from facts to the query. The resulting trace */ - - expr_ref model_search::get_trace(context const& ctx) const { + expr_ref model_search::get_trace(context const& ctx) { pred_transformer& pt = get_root().pt(); ast_manager& m = pt.get_manager(); manager& pm = pt.get_pdr_manager(); @@ -968,22 +1008,24 @@ namespace pdr { unsigned deltas[2]; datalog::rule_ref rule(rm), r0(rm); model_node* n = m_root; - datalog::var_counter& vc = rm.get_var_counter(); + datalog::rule_counter& vc = rm.get_counter(); substitution subst(m); unifier unif(m); rule = n->get_rule(); - unsigned max_var = vc.get_max_var(*rule); + unsigned max_var = vc.get_max_rule_var(*rule); predicates.push_back(rule->get_head()); - children.append(n); + children.push_back(n); bool first = true; + update_models(); while (!children.empty()) { SASSERT(children.size() == predicates.size()); expr_ref_vector binding(m); n = children.back(); children.pop_back(); + TRACE("pdr", n->display(tout, 0);); n->mk_instantiate(r0, rule, binding); - max_var = std::max(max_var, vc.get_max_var(*rule)); + max_var = std::max(max_var, vc.get_max_rule_var(*rule)); subst.reset(); subst.reserve(2, max_var+1); deltas[0] = 0; @@ -1004,10 +1046,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); @@ -1018,20 +1056,33 @@ 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); } - proof_ref model_search::get_proof_trace(context const& ctx) const { + proof_ref model_search::get_proof_trace(context const& ctx) { pred_transformer& pt = get_root().pt(); ast_manager& m = pt.get_manager(); datalog::context& dctx = ctx.get_context(); datalog::rule_manager& rm = dctx.get_rule_manager(); - datalog::rule_unifier unifier(dctx); + datalog::rule_unifier unif(dctx); datalog::dl_decl_util util(m); datalog::rule_ref r0(rm), r1(rm); obj_map cache; @@ -1040,10 +1091,12 @@ namespace pdr { proof_ref_vector trail(m); datalog::rule_ref_vector rules_trail(rm); proof* pr = 0; - unifier.set_normalize(false); + unif.set_normalize(true); todo.push_back(m_root); + update_models(); while (!todo.empty()) { model_node* n = todo.back(); + TRACE("pdr", n->display(tout, 0);); if (cache.find(n->state(), pr)) { todo.pop_back(); continue; @@ -1066,12 +1119,14 @@ namespace pdr { continue; } proof_ref rl(m); - expr_ref fml0(m); expr_ref_vector binding(m); n->mk_instantiate(r0, r1, binding); - r0->to_formula(fml0); proof_ref p1(m), p2(m); - p1 = m.mk_asserted(fml0); + p1 = r0->get_proof(); + if (!p1) { + r0->display(dctx, std::cout); + } + SASSERT(p1); pfs[0] = p1; rls[0] = r1; TRACE("pdr", @@ -1098,14 +1153,14 @@ namespace pdr { substs.push_back(binding); // TODO base substitution. for (unsigned i = 1; i < rls.size(); ++i) { datalog::rule& src = *rls[i]; - bool unified = unifier.unify_rules(*reduced_rule, 0, src); + bool unified = unif.unify_rules(*reduced_rule, 0, src); if (!unified) { IF_VERBOSE(0, verbose_stream() << "Could not unify rules: "; reduced_rule->display(dctx, verbose_stream()); src.display(dctx, verbose_stream());); } - expr_ref_vector sub1 = unifier.get_rule_subst(*reduced_rule, true); + expr_ref_vector sub1 = unif.get_rule_subst(*reduced_rule, true); TRACE("pdr", for (unsigned k = 0; k < sub1.size(); ++k) { tout << mk_pp(sub1[k].get(), m) << " "; @@ -1124,8 +1179,8 @@ namespace pdr { } positions.push_back(std::make_pair(i,0)); - substs.push_back(unifier.get_rule_subst(src, false)); - VERIFY(unifier.apply(*reduced_rule.get(), 0, src, r3)); + substs.push_back(unif.get_rule_subst(src, false)); + VERIFY(unif.apply(*reduced_rule.get(), 0, src, r3)); reduced_rule = r3; } @@ -1184,12 +1239,13 @@ 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()), m_last_result(l_undef), m_inductive_lvl(0), + m_expanded_lvl(0), m_cancel(false) { } @@ -1250,7 +1306,7 @@ namespace pdr { obj_hashtable::iterator itf = deps.begin(), endf = deps.end(); for (; itf != endf; ++itf) { TRACE("pdr", tout << mk_pp(pred, m) << " " << mk_pp(*itf, m) << "\n";); - VERIFY (rels.find(*itf, pt_user)); + pt_user = rels.find(*itf); pt_user->add_use(pt); } } @@ -1322,9 +1378,10 @@ namespace pdr { bool m_is_bool_arith; bool m_has_arith; bool m_is_dl; + bool m_is_utvpi; public: classifier_proc(ast_manager& m, datalog::rule_set& rules): - m(m), a(m), m_is_bool(true), m_is_bool_arith(true), m_has_arith(false), m_is_dl(false) { + m(m), a(m), m_is_bool(true), m_is_bool_arith(true), m_has_arith(false), m_is_dl(false), m_is_utvpi(false) { classify(rules); } void operator()(expr* e) { @@ -1370,6 +1427,7 @@ namespace pdr { bool is_dl() const { return m_is_dl; } + bool is_utvpi() const { return m_is_utvpi; } private: @@ -1390,6 +1448,7 @@ namespace pdr { mark.reset(); m_is_dl = false; + m_is_utvpi = false; if (m_has_arith) { ptr_vector forms; for (it = rules.begin(); it != end; ++it) { @@ -1401,6 +1460,7 @@ namespace pdr { } } m_is_dl = is_difference_logic(m, forms.size(), forms.c_ptr()); + m_is_utvpi = m_is_dl || is_utvpi_logic(m, forms.size(), forms.c_ptr()); } } @@ -1425,6 +1485,7 @@ namespace pdr { bool ok = checker.check(pr, side_conditions); if (!ok) { msg << "proof validation failed"; + IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); throw default_exception(msg.str()); } for (unsigned i = 0; i < side_conditions.size(); ++i) { @@ -1437,6 +1498,7 @@ namespace pdr { lbool res = solver.check(); if (res != l_false) { msg << "rule validation failed when checking: " << mk_pp(cond, m); + IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); throw default_exception(msg.str()); } } @@ -1488,6 +1550,7 @@ namespace pdr { lbool res = solver.check(); if (res != l_false) { msg << "rule validation failed when checking: " << mk_pp(tmp, m); + IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); throw default_exception(msg.str()); } } @@ -1511,17 +1574,32 @@ namespace pdr { if (use_mc) { m_core_generalizers.push_back(alloc(core_multi_generalizer, *this, 0)); } - if (m_params.use_farkas() && !classify.is_bool()) { + if (!classify.is_bool()) { m.toggle_proof_mode(PGM_FINE); m_fparams.m_arith_bound_prop = BP_NONE; m_fparams.m_arith_auto_config_simplex = true; m_fparams.m_arith_propagate_eqs = false; m_fparams.m_arith_eager_eq_axioms = false; - if (classify.is_dl()) { - m_fparams.m_arith_mode = AS_DIFF_LOGIC; - m_fparams.m_arith_expand_eqs = true; + 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; + } + else if (classify.is_utvpi()) { + IF_VERBOSE(1, verbose_stream() << "UTVPI\n";); + m_fparams.m_arith_mode = AS_UTVPI; + m_fparams.m_arith_expand_eqs = true; + } } } + if (m_params.use_convex_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)); } @@ -1567,6 +1645,12 @@ namespace pdr { IF_VERBOSE(1, verbose_stream() << "\n"; m_search.display(verbose_stream());); m_last_result = l_true; validate(); + + IF_VERBOSE(1, + if (m_params.print_boogie_certificate()) { + display_certificate(verbose_stream()); + }); + return l_true; } catch (inductive_exception) { @@ -1595,6 +1679,7 @@ namespace pdr { catch (unknown_exception) { return l_undef; } + UNREACHABLE(); return l_undef; } @@ -1635,12 +1720,15 @@ 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); TRACE("pdr", tout << "PDR trace: " << mk_pp(proof, m) << "\n";); apply(m, m_pc.get(), proof); + TRACE("pdr", tout << "PDR trace: " << mk_pp(proof, m) << "\n";); + // proof_utils::push_instantiations_up(proof); + // TRACE("pdr", tout << "PDR up: " << mk_pp(proof, m) << "\n";); return proof; } @@ -1672,6 +1760,7 @@ namespace pdr { bool reachable; while (true) { checkpoint(); + m_expanded_lvl = lvl; reachable = check_reachability(lvl); if (reachable) { throw model_exception(); @@ -1730,7 +1819,12 @@ namespace pdr { void context::expand_node(model_node& n) { SASSERT(n.is_open()); expr_ref_vector cube(m); + + if (n.level() < m_expanded_lvl) { + m_expanded_lvl = n.level(); + } + 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); @@ -1789,6 +1883,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); } @@ -1796,7 +1895,7 @@ namespace pdr { if (m_params.simplify_formulas_pre()) { simplify_formulas(); } - for (unsigned lvl = 0; lvl <= max_prop_lvl; lvl++) { + for (unsigned lvl = m_expanded_lvl; lvl <= max_prop_lvl; lvl++) { checkpoint(); bool all_propagated = true; decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); @@ -1861,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(); @@ -1882,7 +1981,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)); @@ -1938,7 +2037,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)); @@ -2067,7 +2166,15 @@ namespace pdr { break; } case l_true: { - strm << mk_pp(mk_sat_answer(), m); + if (m_params.print_boogie_certificate()) { + datalog::boogie_proof bp(m); + bp.set_proof(get_proof()); + bp.set_model(0); + bp.pp(strm); + } + else { + strm << mk_pp(mk_sat_answer(), m); + } break; } case l_undef: { diff --git a/src/muz_qe/pdr_context.h b/src/muz/pdr/pdr_context.h similarity index 94% rename from src/muz_qe/pdr_context.h rename to src/muz/pdr/pdr_context.h index 32f13cdd4..8a4f3e438 100644 --- a/src/muz_qe/pdr_context.h +++ b/src/muz/pdr/pdr_context.h @@ -26,9 +26,9 @@ Revision History: #endif #include #include "pdr_manager.h" -#include "dl_base.h" #include "pdr_prop_solver.h" #include "pdr_reachable_cache.h" +#include "fixedpoint_params.hpp" namespace datalog { @@ -165,6 +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); } + }; }; @@ -240,6 +253,7 @@ namespace pdr { void erase_leaf(model_node& n); void remove_node(model_node& n); void enqueue_leaf(model_node& n); // add leaf to priority queue. + void update_models(); public: model_search(bool bfs): m_bfs(bfs), m_root(0) {} ~model_search(); @@ -253,8 +267,8 @@ namespace pdr { void set_root(model_node* n); model_node& get_root() const { return *m_root; } std::ostream& display(std::ostream& out) const; - expr_ref get_trace(context const& ctx) const; - proof_ref get_proof_trace(context const& ctx) const; + expr_ref get_trace(context const& ctx); + proof_ref get_proof_trace(context const& ctx); void backtrack_level(bool uses_level, model_node& n); }; @@ -299,9 +313,10 @@ namespace pdr { decl2rel m_rels; // Map from relation predicate to fp-operator. func_decl_ref m_query_pred; pred_transformer* m_query; - model_search m_search; + mutable model_search m_search; lbool m_last_result; unsigned m_inductive_lvl; + unsigned m_expanded_lvl; ptr_vector m_core_generalizers; stats m_stats; volatile bool m_cancel; @@ -365,7 +380,7 @@ namespace pdr { expr_ref get_answer(); bool is_dl() const { return m_fparams.m_arith_mode == AS_DIFF_LOGIC; } - + bool is_utvpi() const { return m_fparams.m_arith_mode == AS_UTVPI; } void collect_statistics(statistics& st) const; void reset_statistics(); diff --git a/src/muz_qe/pdr_dl_interface.cpp b/src/muz/pdr/pdr_dl_interface.cpp similarity index 65% rename from src/muz_qe/pdr_dl_interface.cpp rename to src/muz/pdr/pdr_dl_interface.cpp index 54a40f8b8..45872fe99 100644 --- a/src/muz_qe/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" @@ -25,7 +24,6 @@ Revision History: #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 "pdr_context.h" #include "pdr_dl_interface.h" @@ -33,11 +31,14 @@ Revision History: #include "dl_mk_slice.h" #include "dl_mk_unfold.h" #include "dl_mk_coalesce.h" -#include "pdr_quantifiers.h" +#include "dl_transforms.h" +#include "scoped_proof.h" +#include "model_smt2_pp.h" using namespace pdr; dl_interface::dl_interface(datalog::context& ctx) : + engine_base(ctx.get_manager(), "pdr"), m_ctx(ctx), m_pdr_rules(ctx), m_old_rules(ctx), @@ -57,44 +58,40 @@ dl_interface::~dl_interface() { // re-use existing context. // void dl_interface::check_reset() { - datalog::rule_ref_vector const& new_rules = m_ctx.get_rules().get_rules(); + datalog::rule_set const& new_rules = m_ctx.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) { + for (unsigned i = 0; is_subsumed && i < new_rules.get_num_rules(); ++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])) { + if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) { is_subsumed = true; } } if (!is_subsumed) { - TRACE("pdr", new_rules[i]->display(m_ctx, tout << "Fresh rule ");); + TRACE("pdr", new_rules.get_rule(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()); + m_old_rules.replace_rules(new_rules); } lbool dl_interface::query(expr * query) { //we restore the initial state in the datalog context m_ctx.ensure_opened(); - m_pdr_rules.reset(); m_refs.reset(); m_pred2slice.reset(); ast_manager& m = m_ctx.get_manager(); - datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); + datalog::rule_manager& rm = m_ctx.get_rule_manager(); + datalog::rule_set old_rules(m_ctx.get_rules()); func_decl_ref query_pred(m); - datalog::rule_ref_vector query_rules(rule_manager); - datalog::rule_ref query_rule(rule_manager); - rule_manager.mk_query(query, query_pred, query_rules, query_rule); - m_ctx.add_rules(query_rules); + rm.mk_query(query, m_ctx.get_rules()); expr_ref bg_assertion = m_ctx.get_background_assertion(); check_reset(); - + TRACE("pdr", if (!m.is_true(bg_assertion)) { tout << "axioms:\n"; @@ -105,21 +102,14 @@ lbool dl_interface::query(expr * query) { m_ctx.display_rules(tout); ); - model_converter_ref mc = datalog::mk_skip_model_converter(); - proof_converter_ref pc; - if (m_ctx.get_params().generate_proof_trace()) { - pc = datalog::mk_skip_proof_converter(); - } - m_ctx.set_output_predicate(query_pred); - m_ctx.apply_default_transformation(mc, pc); + + apply_default_transformation(m_ctx); if (m_ctx.get_params().slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); - m_ctx.transform_rules(transformer, mc, pc); - query_pred = slice->get_predicate(query_pred.get()); - m_ctx.set_output_predicate(query_pred); + m_ctx.transform_rules(transformer); // track sliced predicates. obj_map const& preds = slice->get_predicates(); @@ -134,59 +124,47 @@ lbool dl_interface::query(expr * query) { if (m_ctx.get_params().unfold_rules() > 0) { unsigned num_unfolds = m_ctx.get_params().unfold_rules(); - datalog::rule_transformer transformer1(m_ctx), transformer2(m_ctx); + datalog::rule_transformer transf1(m_ctx), transf2(m_ctx); + transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); + transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); if (m_ctx.get_params().coalesce_rules()) { - transformer1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); - m_ctx.transform_rules(transformer1, mc, pc); + m_ctx.transform_rules(transf1); } - transformer2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); while (num_unfolds > 0) { - m_ctx.transform_rules(transformer2, mc, pc); + m_ctx.transform_rules(transf2); --num_unfolds; } } - // remove universal quantifiers from body. - datalog::mk_extract_quantifiers* extract_quantifiers = alloc(datalog::mk_extract_quantifiers, m_ctx); - datalog::rule_transformer extract_q_tr(m_ctx); - extract_q_tr.register_plugin(extract_quantifiers); - m_ctx.transform_rules(extract_q_tr, mc, pc); - + + if (m_ctx.get_rules().get_output_predicates().empty()) { + m_context->set_unsat(); + return l_false; + } + + query_pred = m_ctx.get_rules().get_output_predicate(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); - m_pdr_rules.add_rules(m_ctx.get_rules()); + m_pdr_rules.replace_rules(m_ctx.get_rules()); m_pdr_rules.close(); + m_ctx.record_transformed_rules(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); - - quantifier_model_checker quantifier_mc(*m_context, m, extract_quantifiers->quantifiers(), m_pdr_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(pc); - m_context->set_model_converter(mc); + m_context->set_proof_converter(m_ctx.get_proof_converter()); + m_context->set_model_converter(m_ctx.get_model_converter()); m_context->set_query(query_pred); m_context->set_axioms(bg_assertion); m_context->update_rules(m_pdr_rules); if (m_pdr_rules.get_rules().empty()) { m_context->set_unsat(); + IF_VERBOSE(1, model_smt2_pp(verbose_stream(), m, *m_context->get_model(),0);); return l_false; } - lbool result; - while (true) { - result = m_context->solve(); - if (result == l_true && extract_quantifiers->has_quantifiers()) { - result = quantifier_mc.check(); - if (result != l_false) { - return result; - } - // else continue - } - else { - return result; - } - } + return m_context->solve(); } diff --git a/src/muz_qe/pdr_dl_interface.h b/src/muz/pdr/pdr_dl_interface.h similarity index 53% rename from src/muz_qe/pdr_dl_interface.h rename to src/muz/pdr/pdr_dl_interface.h index c4844f892..610e7fe06 100644 --- a/src/muz_qe/pdr_dl_interface.h +++ b/src/muz/pdr/pdr_dl_interface.h @@ -23,6 +23,8 @@ Revision History: #include "lbool.h" #include "dl_rule.h" #include "dl_rule_set.h" +#include "dl_util.h" +#include "dl_engine_base.h" #include "statistics.h" namespace datalog { @@ -33,7 +35,7 @@ namespace pdr { class context; - class dl_interface { + class dl_interface : public datalog::engine_base { datalog::context& m_ctx; datalog::rule_set m_pdr_rules; datalog::rule_set m_old_rules; @@ -47,31 +49,31 @@ namespace pdr { dl_interface(datalog::context& ctx); ~dl_interface(); - lbool query(expr* query); + virtual lbool query(expr* query); - void cancel(); + virtual void cancel(); - void cleanup(); + virtual void cleanup(); - void display_certificate(std::ostream& out) const; + virtual void display_certificate(std::ostream& out) const; - void collect_statistics(statistics& st) const; + virtual void collect_statistics(statistics& st) const; - void reset_statistics(); + virtual void reset_statistics(); - expr_ref get_answer(); + virtual expr_ref get_answer(); - unsigned get_num_levels(func_decl* pred); + virtual unsigned get_num_levels(func_decl* pred); - expr_ref get_cover_delta(int level, func_decl* pred); + virtual expr_ref get_cover_delta(int level, func_decl* pred); - void add_cover(int level, func_decl* pred, expr* property); + virtual void add_cover(int level, func_decl* pred, expr* property); - void updt_params(); + virtual void updt_params(); - model_ref get_model(); + virtual model_ref get_model(); - proof_ref get_proof(); + virtual proof_ref get_proof(); }; } diff --git a/src/muz_qe/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp similarity index 83% rename from src/muz_qe/pdr_farkas_learner.cpp rename to src/muz/pdr/pdr_farkas_learner.cpp index 489b2437e..7c38bf86f 100644 --- a/src/muz_qe/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,12 @@ 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) {} + + 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) { @@ -180,12 +188,130 @@ 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_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); @@ -216,13 +342,16 @@ namespace pdr { } res = m.mk_not(res); th_rewriter rw(m); + params_ref params; + 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) { @@ -246,6 +375,8 @@ 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) { @@ -253,6 +384,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; @@ -316,7 +451,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()); } @@ -409,20 +544,29 @@ 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) { + if (!m_constr) { + m_constr = alloc(constr, m); + } + 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); + rw.mk_or(n, (expr*const*)(lits), res); + res = m.mk_not(res); } - res_c.get(res); } class farkas_learner::constant_replacer_cfg : public default_rewriter_cfg { - ast_manager& m; const obj_map& m_translation; public: - constant_replacer_cfg(ast_manager& m, const obj_map& translation) - : m(m), m_translation(translation) + constant_replacer_cfg(const obj_map& translation) + : m_translation(translation) { } bool get_subst(expr * s, expr * & t, proof * & t_pr) { @@ -692,7 +836,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. @@ -751,6 +895,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/pdr/pdr_farkas_learner.h similarity index 79% rename from src/muz_qe/pdr_farkas_learner.h rename to src/muz/pdr/pdr_farkas_learner.h index eb38455ab..546759a33 100644 --- a/src/muz_qe/pdr_farkas_learner.h +++ b/src/muz/pdr/pdr_farkas_learner.h @@ -42,6 +42,13 @@ 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. + // 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); @@ -74,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) @@ -92,6 +101,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/pdr/pdr_generalizers.cpp similarity index 73% rename from src/muz_qe/pdr_generalizers.cpp rename to src/muz/pdr/pdr_generalizers.cpp index 4cdfb186e..7c2557260 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -23,6 +23,8 @@ Revision History: #include "pdr_generalizers.h" #include "expr_abstract.h" #include "var_subst.h" +#include "expr_safe_replace.h" +#include "model_smt2_pp.h" namespace pdr { @@ -114,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; @@ -128,16 +129,16 @@ 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(); - datalog::flatten_and(C, core); + qe::flatten_and(C, core); uses_level = true; } } @@ -147,6 +148,183 @@ namespace pdr { } + core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx, bool is_closure): + core_generalizer(ctx), + m(ctx.get_manager()), + m_is_closure(is_closure) { + } + + 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); + } + + void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { + UNREACHABLE(); + } + + // 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 const& core, bool uses_level, cores& new_cores) { + 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; + } + 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) { + 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(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)); + 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";); + IF_VERBOSE(0, + verbose_stream() << mk_pp(state, m) << "\n"; + verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";); + } + } + if (!m_is_closure || !change) { + new_cores.push_back(std::make_pair(core, uses_level)); + } + } + + /* + 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"; + }); + bool uses_level1; + expr_ref_vector core1(m); + core1.append(core); + 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"; + }); + + expr_ref tmp(m); + + // 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; + } + + 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(); + 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; + A = cl(Hs); + 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(); + --j; + } + else { + Hs.pop_back(); + } + } + if (Hs.size() > 1) { + As[i] = convA; + } + } + return sz > As.size(); + } + + + 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); + 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(); + } + + // --------------------------------- // core_arith_inductive_generalizer // NB. this is trying out some ideas for generalization in @@ -228,11 +406,13 @@ namespace pdr { is_lower = !is_lower; } + vector bound; + bound.push_back(std::make_pair(x, i)); if (is_lower) { - m_lb.insert(abs(r), std::make_pair(x, i)); + m_lb.insert(abs(r), bound); } else { - m_ub.insert(abs(r), std::make_pair(x, i)); + m_ub.insert(abs(r), bound); } } @@ -414,7 +594,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); @@ -507,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; } @@ -563,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; } @@ -596,3 +776,4 @@ namespace pdr { } } }; + diff --git a/src/muz_qe/pdr_generalizers.h b/src/muz/pdr/pdr_generalizers.h similarity index 78% rename from src/muz_qe/pdr_generalizers.h rename to src/muz/pdr/pdr_generalizers.h index 03bd89c4d..be04ec646 100644 --- a/src/muz_qe/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,6 +74,22 @@ namespace pdr { virtual void collect_statistics(statistics& st) const; }; + + class core_convex_hull_generalizer : public core_generalizer { + ast_manager& m; + obj_map m_models; + bool m_is_closure; + void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); + 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); + public: + 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); + }; + class core_multi_generalizer : public core_generalizer { core_bool_inductive_generalizer m_gen; public: diff --git a/src/muz_qe/pdr_manager.cpp b/src/muz/pdr/pdr_manager.cpp similarity index 96% rename from src/muz_qe/pdr_manager.cpp rename to src/muz/pdr/pdr_manager.cpp index 04facc776..bda54dbd7 100644 --- a/src/muz_qe/pdr_manager.cpp +++ b/src/muz/pdr/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()); } @@ -132,7 +132,7 @@ namespace pdr { for_each_expr(collect_decls, m_relation_info[i].m_body); } for (unsigned i = 0; i < rules.size(); ++i) { - bound_decls.insert(rules[i]->get_head()->get_decl()); + bound_decls.insert(rules[i]->get_decl()); } for (unsigned i = 0; i < rules.size(); ++i) { unsigned u_sz = rules[i]->get_uninterpreted_tail_size(); @@ -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) { } @@ -238,7 +237,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/pdr_manager.h similarity index 97% rename from src/muz_qe/pdr_manager.h rename to src/muz/pdr/pdr_manager.h index cb2c9b253..0e8e890e8 100644 --- a/src/muz_qe/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_qe/pdr_prop_solver.cpp b/src/muz/pdr/pdr_prop_solver.cpp similarity index 88% rename from src/muz_qe/pdr_prop_solver.cpp rename to src/muz/pdr/pdr_prop_solver.cpp index daa52992f..8fe8c0e0e 100644 --- a/src/muz_qe/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 @@ -76,7 +77,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(); @@ -145,7 +146,8 @@ namespace pdr { rational two(2); for (unsigned j = 0; j < bv_size; ++j) { parameter p(j); - expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c); + //expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c); + expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c)); if ((r % two).is_zero()) { e = m.mk_not(e); } @@ -224,19 +226,23 @@ 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), m_proxies(m), m_core(0), + m_model(0), + m_consequences(0), m_subset_based_core(false), - m_in_level(false) + m_use_farkas(false), + m_in_level(false), + m_current_level(0) { m_ctx->assert_expr(m_pm.get_background()); } @@ -327,7 +333,11 @@ namespace pdr { } } - if (result == l_false && m_core && m.proofs_enabled() && !m_subset_based_core) { + if (result == l_false && + m_core && + m.proofs_enabled() && + m_use_farkas && + !m_subset_based_core) { extract_theory_core(safe); } else if (result == l_false && m_core) { @@ -382,26 +392,36 @@ namespace pdr { fl.get_lemmas(pr, bs, lemmas); safe.elim_proxies(lemmas); fl.simplify_lemmas(lemmas); // redundant? - if (m_fparams.m_arith_mode == AS_DIFF_LOGIC && - !is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) { - IF_VERBOSE(1, - verbose_stream() << "not diff\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; - }); - extract_subset_core(safe); - return; + + bool outside_of_logic = + (m_fparams.m_arith_mode == AS_DIFF_LOGIC && + !is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) || + (m_fparams.m_arith_mode == AS_UTVPI && + !is_utvpi_logic(m, lemmas.size(), lemmas.c_ptr())); + + if (outside_of_logic) { + IF_VERBOSE(2, + verbose_stream() << "not diff\n"; + for (unsigned i = 0; i < lemmas.size(); ++i) { + verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; + }); + extract_subset_core(safe); + } + else { + + IF_VERBOSE(2, + verbose_stream() << "Lemmas\n"; + for (unsigned i = 0; i < lemmas.size(); ++i) { + verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; + }); + + m_core->reset(); + m_core->append(lemmas); + + if (m_consequences) { + fl.get_consequences(pr, bs, *m_consequences); + } } - - - IF_VERBOSE(2, - verbose_stream() << "Lemmas\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; - }); - - m_core->reset(); - m_core->append(lemmas); } lbool prop_solver::check_assumptions(const expr_ref_vector & atoms) { diff --git a/src/muz_qe/pdr_prop_solver.h b/src/muz/pdr/pdr_prop_solver.h similarity index 90% rename from src/muz_qe/pdr_prop_solver.h rename to src/muz/pdr/pdr_prop_solver.h index 165a37845..a63ec2bf4 100644 --- a/src/muz_qe/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 { @@ -48,8 +50,10 @@ 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; func_decl_set m_aux_symbols; bool m_in_level; unsigned m_current_level; // set when m_in_level @@ -71,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 { @@ -83,6 +87,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(); @@ -97,6 +103,9 @@ namespace pdr { ~scoped_level() { m_lev = false; } }; + 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); diff --git a/src/muz_qe/pdr_reachable_cache.cpp b/src/muz/pdr/pdr_reachable_cache.cpp similarity index 95% rename from src/muz_qe/pdr_reachable_cache.cpp rename to src/muz/pdr/pdr_reachable_cache.cpp index 4f4f620de..85100c19f 100644 --- a/src/muz_qe/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_qe/pdr_reachable_cache.h b/src/muz/pdr/pdr_reachable_cache.h similarity index 95% rename from src/muz_qe/pdr_reachable_cache.h rename to src/muz/pdr/pdr_reachable_cache.h index 48caa22a5..aef6f7a4f 100644 --- a/src/muz_qe/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_qe/pdr_smt_context_manager.cpp b/src/muz/pdr/pdr_smt_context_manager.cpp similarity index 96% rename from src/muz_qe/pdr_smt_context_manager.cpp rename to src/muz/pdr/pdr_smt_context_manager.cpp index 49ae35423..b6aa8411d 100644 --- a/src/muz_qe/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_qe/pdr_smt_context_manager.h b/src/muz/pdr/pdr_smt_context_manager.h similarity index 97% rename from src/muz_qe/pdr_smt_context_manager.h rename to src/muz/pdr/pdr_smt_context_manager.h index 7d6eebfbd..4775dc58f 100644 --- a/src/muz_qe/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_qe/pdr_sym_mux.cpp b/src/muz/pdr/pdr_sym_mux.cpp similarity index 100% rename from src/muz_qe/pdr_sym_mux.cpp rename to src/muz/pdr/pdr_sym_mux.cpp diff --git a/src/muz_qe/pdr_sym_mux.h b/src/muz/pdr/pdr_sym_mux.h similarity index 100% rename from src/muz_qe/pdr_sym_mux.h rename to src/muz/pdr/pdr_sym_mux.h diff --git a/src/muz_qe/pdr_util.cpp b/src/muz/pdr/pdr_util.cpp similarity index 81% rename from src/muz_qe/pdr_util.cpp rename to src/muz/pdr/pdr_util.cpp index 1ea705e6e..d021d5fb1 100644 --- a/src/muz_qe/pdr_util.cpp +++ b/src/muz/pdr/pdr_util.cpp @@ -32,16 +32,20 @@ Notes: #include "for_each_expr.h" #include "smt_params.h" #include "model.h" -#include "model_v2_pp.h" #include "ref_vector.h" #include "rewriter.h" #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" +#include "model_smt2_pp.h" +#include "poly_rewriter.h" +#include "poly_rewriter_def.h" +#include "arith_rewriter.h" +#include "scoped_proof.h" + namespace pdr { @@ -510,13 +514,24 @@ namespace pdr { set_x(e); } } + + void model_evaluator::eval_exprs(expr_ref_vector& es) { + model_ref mr(m_model); + for (unsigned j = 0; j < es.size(); ++j) { + if (m_array.is_as_array(es[j].get())) { + es[j] = eval(mr, es[j].get()); + } + } + } bool model_evaluator::extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case) { SASSERT(m_array.is_array(a)); + TRACE("pdr", tout << mk_pp(a, m) << "\n";); while (m_array.is_store(a)) { expr_ref_vector store(m); store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1); + eval_exprs(store); stores.push_back(store); a = to_app(a)->get_arg(0); } @@ -526,7 +541,7 @@ namespace pdr { return true; } - if (m_array.is_as_array(a)) { + while (m_array.is_as_array(a)) { func_decl* f = m_array.get_as_array_func_decl(to_app(a)); func_interp* g = m_model->get_func_interp(f); unsigned sz = g->num_entries(); @@ -538,20 +553,30 @@ namespace pdr { store.push_back(fe->get_result()); for (unsigned j = 0; j < store.size(); ++j) { if (!is_ground(store[j].get())) { + TRACE("pdr", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";); return false; } } + eval_exprs(store); stores.push_back(store); } else_case = g->get_else(); if (!else_case) { + TRACE("pdr", tout << "no else case " << mk_pp(a, m) << "\n";); return false; } if (!is_ground(else_case)) { + TRACE("pdr", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";); return false; } + if (m_array.is_as_array(else_case)) { + model_ref mr(m_model); + else_case = eval(mr, else_case); + } + TRACE("pdr", tout << "else case: " << mk_pp(else_case, m) << "\n";); return true; } + TRACE("pdr", tout << "no translation: " << mk_pp(a, m) << "\n";); return false; } @@ -570,7 +595,8 @@ namespace pdr { } sort* s = m.get_sort(arg1); sort* r = get_array_range(s); - if (!r->is_infinite() && !r->is_very_big()) { + // give up evaluating finite domain/range arrays + if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); return; @@ -591,6 +617,9 @@ namespace pdr { << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";); set_false(e); } + else if (m_array.is_array(else1)) { + eval_array_eq(e, else1, else2); + } else { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); @@ -614,18 +643,23 @@ namespace pdr { if (w1 == w2) { continue; } - else if (m.is_value(w1) && m.is_value(w2)) { + if (m.is_value(w1) && m.is_value(w2)) { TRACE("pdr", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n"; tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";); set_false(e); - return; + } + else if (m_array.is_array(w1)) { + eval_array_eq(e, w1, w2); + if (is_true(e)) { + continue; + } } else { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); - return; } + return; } set_true(e); } @@ -869,6 +903,7 @@ namespace pdr { } if (is_x(form)) { IF_VERBOSE(0, verbose_stream() << "formula undetermined in model: " << mk_pp(form, m) << "\n";); + TRACE("pdr", model_smt2_pp(tout, m, *m_model, 0);); has_x = true; } } @@ -919,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) { @@ -1037,7 +1072,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); @@ -1050,6 +1085,7 @@ namespace pdr { arith_util a; bv_util bv; bool m_is_dl; + bool m_test_for_utvpi; bool is_numeric(expr* e) const { if (a.is_numeral(e)) { @@ -1084,6 +1120,16 @@ namespace pdr { } return false; } + if (m_test_for_utvpi) { + if (a.is_mul(e, e1, e2)) { + if (is_minus_one(e1)) { + return is_offset(e2); + } + if (is_minus_one(e2)) { + return is_offset(e1); + } + } + } return !is_arith_expr(e); } @@ -1109,6 +1155,9 @@ namespace pdr { if (!a.is_add(lhs, arg1, arg2)) return false; // x + if (m_test_for_utvpi) { + return is_offset(arg1) && is_offset(arg2); + } if (is_arith_expr(arg1)) std::swap(arg1, arg2); if (is_arith_expr(arg1)) @@ -1178,8 +1227,10 @@ namespace pdr { } public: - test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true) {} - + test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} + + void test_for_utvpi() { m_test_for_utvpi = true; } + void operator()(expr* e) { if (!m_is_dl) { return; @@ -1201,7 +1252,11 @@ namespace pdr { } if (!m_is_dl) { - IF_VERBOSE(1, verbose_stream() << "non-diff: " << mk_pp(e, m) << "\n";); + char const* msg = "non-diff: "; + if (m_test_for_utvpi) { + msg = "non-utvpi: "; + } + IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); } } @@ -1217,9 +1272,164 @@ namespace pdr { return test.is_dl(); } + bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { + test_diff_logic test(m); + test.test_for_utvpi(); + expr_fast_mark1 mark; + for (unsigned i = 0; i < num_fmls; ++i) { + quick_for_each_expr(test, mark, fmls[i]); + } + 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(); + 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; - +template class rewriter_tpl; diff --git a/src/muz_qe/pdr_util.h b/src/muz/pdr/pdr_util.h similarity index 93% rename from src/muz_qe/pdr_util.h rename to src/muz/pdr/pdr_util.h index 220e56b3c..446bde8aa 100644 --- a/src/muz_qe/pdr_util.h +++ b/src/muz/pdr/pdr_util.h @@ -104,6 +104,8 @@ namespace pdr { bool check_model(ptr_vector const & formulas); bool extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case); + + void eval_exprs(expr_ref_vector& es); public: model_evaluator(ast_manager& m) : m(m), m_arith(m), m_array(m), m_refs(m) {} @@ -140,15 +142,26 @@ namespace pdr { Assumption: the model satisfies the conjunctions. */ void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml); - /** \brief hoist non-boolean if expressions. */ 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); + } #endif diff --git a/src/muz/rel/aig_exporter.cpp b/src/muz/rel/aig_exporter.cpp new file mode 100644 index 000000000..ca0030fc3 --- /dev/null +++ b/src/muz/rel/aig_exporter.cpp @@ -0,0 +1,328 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + aig_exporter.cpp + +Abstract: + + Export AIG files from horn clauses + +--*/ + +#include "aig_exporter.h" +#include "dl_context.h" +#include + +namespace datalog { + + aig_exporter::aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts) : + m_rules(rules), m_facts(facts), m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), + m_aigm(m), m_next_decl_id(1), m_next_aig_expr_id(2), m_num_and_gates(0), + m_latch_vars(m), m_latch_varsp(m), m_ruleid_var_set(m), m_ruleid_varp_set(m) + { + std::set predicates; + for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), + E = m_rules.end_grouped_rules(); I != E; ++I) { + predicates.insert(I->m_key); + } + + for (fact_vector::const_iterator I = facts->begin(), E = facts->end(); I != E; ++I) { + predicates.insert(I->first); + } + + // reserve pred id = 0 for initalization purposes + unsigned num_preds = (unsigned)predicates.size() + 1; + + // poor's man round-up log2 + unsigned preds_bitsize = log2(num_preds); + if ((1U << preds_bitsize) < num_preds) + ++preds_bitsize; + SASSERT((1U << preds_bitsize) >= num_preds); + + for (unsigned i = 0; i < preds_bitsize; ++i) { + m_ruleid_var_set.push_back(m.mk_fresh_const("rule_id", m.mk_bool_sort())); + m_ruleid_varp_set.push_back(m.mk_fresh_const("rule_id_p", m.mk_bool_sort())); + } + } + + void aig_exporter::mk_latch_vars(unsigned n) { + for (unsigned i = m_latch_vars.size(); i <= n; ++i) { + m_latch_vars.push_back(m.mk_fresh_const("latch_var", m.mk_bool_sort())); + m_latch_varsp.push_back(m.mk_fresh_const("latch_varp", m.mk_bool_sort())); + } + SASSERT(m_latch_vars.size() > n); + } + + expr* aig_exporter::get_latch_var(unsigned i, const expr_ref_vector& vars) { + mk_latch_vars(i); + return vars.get(i); + } + + void aig_exporter::assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs) { + unsigned id = 0; + if (decl && !m_decl_id_map.find(decl, id)) { + id = m_next_decl_id++; + SASSERT(id < (1U << vars.size())); + m_decl_id_map.insert(decl, id); + } + + for (unsigned i = 0; i < vars.size(); ++i) { + exprs.push_back((id & (1U << i)) ? vars[i] : m.mk_not(vars[i])); + } + } + + void aig_exporter::collect_var_substs(substitution& subst, const app *h, + const expr_ref_vector& vars, expr_ref_vector& eqs) { + for (unsigned i = 0; i < h->get_num_args(); ++i) { + expr *arg = h->get_arg(i); + expr *latchvar = get_latch_var(i, vars); + + if (is_var(arg)) { + var *v = to_var(arg); + expr_offset othervar; + if (subst.find(v, 0, othervar)) { + eqs.push_back(m.mk_eq(latchvar, othervar.get_expr())); + } else { + subst.insert(v, 0, expr_offset(latchvar, 0)); + } + } else { + eqs.push_back(m.mk_eq(latchvar, arg)); + } + } + } + + void aig_exporter::operator()(std::ostream& out) { + expr_ref_vector transition_function(m), output_preds(m); + var_ref_vector input_vars(m); + + rule_counter& vc = m_rm.get_counter(); + expr_ref_vector exprs(m); + substitution subst(m); + + for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), + E = m_rules.end_grouped_rules(); I != E; ++I) { + for (rule_vector::iterator II = I->get_value()->begin(), + EE = I->get_value()->end(); II != EE; ++II) { + rule *r = *II; + unsigned numqs = r->get_positive_tail_size(); + if (numqs > 1) { + std::cerr << "non-linear clauses not supported\n"; + exit(-1); + } + + if (numqs != r->get_uninterpreted_tail_size()) { + std::cerr << "negation of queries not supported\n"; + exit(-1); + } + + exprs.reset(); + assert_pred_id(numqs ? r->get_tail(0)->get_decl() : 0, m_ruleid_var_set, exprs); + assert_pred_id(r->get_head()->get_decl(), m_ruleid_varp_set, exprs); + + subst.reset(); + subst.reserve(1, vc.get_max_rule_var(*r)+1); + if (numqs) + collect_var_substs(subst, r->get_tail(0), m_latch_vars, exprs); + collect_var_substs(subst, r->get_head(), m_latch_varsp, exprs); + + for (unsigned i = numqs; i < r->get_tail_size(); ++i) { + expr_ref e(m); + subst.apply(r->get_tail(i), e); + exprs.push_back(e); + } + + transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); + } + } + + // collect table facts + if (m_facts) { + for (fact_vector::const_iterator I = m_facts->begin(), E = m_facts->end(); I != E; ++I) { + exprs.reset(); + assert_pred_id(0, m_ruleid_var_set, exprs); + assert_pred_id(I->first, m_ruleid_varp_set, exprs); + + for (unsigned i = 0; i < I->second.size(); ++i) { + exprs.push_back(m.mk_eq(get_latch_var(i, m_latch_varsp), I->second[i])); + } + + transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); + } + } + + expr *tr = m.mk_or(transition_function.size(), transition_function.c_ptr()); + aig_ref aig = m_aigm.mk_aig(tr); + expr_ref aig_expr(m); + m_aigm.to_formula(aig, aig_expr); + +#if 0 + std::cout << mk_pp(tr, m) << "\n\n"; + std::cout << mk_pp(aig_expr, m) << "\n\n"; +#endif + + // make rule_id vars latches + for (unsigned i = 0; i < m_ruleid_var_set.size(); ++i) { + m_latch_vars.push_back(m_ruleid_var_set.get(i)); + m_latch_varsp.push_back(m_ruleid_varp_set.get(i)); + } + + // create vars for latches + for (unsigned i = 0; i < m_latch_vars.size(); ++i) { + mk_var(m_latch_vars.get(i)); + mk_input_var(m_latch_varsp.get(i)); + } + + unsigned tr_id = expr_to_aig(aig_expr); + + // create latch next state variables: (ite tr varp var) + unsigned_vector latch_varp_ids; + for (unsigned i = 0; i < m_latch_vars.size(); ++i) { + unsigned in_val = mk_and(tr_id, get_var(m_latch_varsp.get(i))); + unsigned latch_val = mk_and(neg(tr_id), get_var(m_latch_vars.get(i))); + latch_varp_ids.push_back(mk_or(in_val, latch_val)); + } + m_latch_varsp.reset(); + + // create output variable (true iff an output predicate is derivable) + unsigned output_id = 0; + { + expr_ref_vector output(m); + const func_decl_set& preds = m_rules.get_output_predicates(); + + for (func_decl_set::iterator I = preds.begin(), E = preds.end(); I != E; ++I) { + exprs.reset(); + assert_pred_id(*I, m_ruleid_var_set, exprs); + output.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); + } + + expr *out = m.mk_or(output.size(), output.c_ptr()); + aig = m_aigm.mk_aig(out); + m_aigm.to_formula(aig, aig_expr); + output_id = expr_to_aig(aig_expr); + +#if 0 + std::cout << "output formula\n"; + std::cout << mk_pp(out, m) << "\n\n"; + std::cout << mk_pp(aig_expr, m) << "\n\n"; +#endif + } + + // 1) print header + // aag var_index inputs latches outputs andgates + out << "aag " << (m_next_aig_expr_id-1)/2 << ' ' << m_input_vars.size() + << ' ' << m_latch_vars.size() << " 1 " << m_num_and_gates << '\n'; + + // 2) print inputs + for (unsigned i = 0; i < m_input_vars.size(); ++i) { + out << m_input_vars[i] << '\n'; + } + + // 3) print latches + for (unsigned i = 0; i < m_latch_vars.size(); ++i) { + out << get_var(m_latch_vars.get(i)) << ' ' << latch_varp_ids[i] << '\n'; + } + + // 4) print outputs (just one for now) + out << output_id << '\n'; + + // 5) print formula + out << m_buffer.str(); + } + + unsigned aig_exporter::expr_to_aig(const expr *e) { + unsigned id; + if (m_aig_expr_id_map.find(e, id)) + return id; + + if (is_uninterp_const(e)) + return get_var(e); + + switch (e->get_kind()) { + case AST_APP: { + const app *a = to_app(e); + switch (a->get_decl_kind()) { + case OP_OR: + SASSERT(a->get_num_args() > 0); + id = expr_to_aig(a->get_arg(0)); + for (unsigned i = 1; i < a->get_num_args(); ++i) { + id = mk_or(id, expr_to_aig(a->get_arg(i))); + } + m_aig_expr_id_map.insert(e, id); + return id; + + case OP_NOT: + return neg(expr_to_aig(a->get_arg(0))); + + case OP_FALSE: + return 0; + + case OP_TRUE: + return 1; + } + break;} + + case AST_VAR: + return get_var(e); + default: + UNREACHABLE(); + } + + UNREACHABLE(); + return 0; + } + + unsigned aig_exporter::neg(unsigned id) const { + return (id % 2) ? (id-1) : (id+1); + } + + unsigned aig_exporter::mk_and(unsigned id1, unsigned id2) { + if (id1 > id2) + std::swap(id1, id2); + + std::pair key(id1, id2); + and_gates_map::const_iterator I = m_and_gates_map.find(key); + if (I != m_and_gates_map.end()) + return I->second; + + unsigned id = mk_expr_id(); + m_buffer << id << ' ' << id1 << ' ' << id2 << '\n'; + m_and_gates_map[key] = id; + ++m_num_and_gates; + return id; + } + + unsigned aig_exporter::mk_or(unsigned id1, unsigned id2) { + return neg(mk_and(neg(id1), neg(id2))); + } + + unsigned aig_exporter::get_var(const expr *e) { + unsigned id; + if (m_aig_expr_id_map.find(e, id)) + return id; + return mk_input_var(e); + } + + unsigned aig_exporter::mk_var(const expr *e) { + SASSERT(!m_aig_expr_id_map.contains(e)); + unsigned id = mk_expr_id(); + m_aig_expr_id_map.insert(e, id); + return id; + } + + unsigned aig_exporter::mk_input_var(const expr *e) { + SASSERT(!m_aig_expr_id_map.contains(e)); + unsigned id = mk_expr_id(); + m_input_vars.push_back(id); + if (e) + m_aig_expr_id_map.insert(e, id); + return id; + } + + unsigned aig_exporter::mk_expr_id() { + unsigned id = m_next_aig_expr_id; + m_next_aig_expr_id += 2; + return id; + } +} diff --git a/src/muz/rel/aig_exporter.h b/src/muz/rel/aig_exporter.h new file mode 100644 index 000000000..78ab9fe17 --- /dev/null +++ b/src/muz/rel/aig_exporter.h @@ -0,0 +1,68 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + aig_exporter.h + +Abstract: + + Export AIG files from horn clauses + +--*/ + +#ifndef _AIG_EXPORTER_H_ +#define _AIG_EXPORTER_H_ + +#include "aig.h" +#include "dl_rule_set.h" +#include +#include +#include "rel_context.h" + +namespace datalog { + class aig_exporter { + public: + aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts = 0); + void operator()(std::ostream& out); + + private: + typedef obj_map decl_id_map; + typedef obj_map aig_expr_id_map; + typedef std::map, unsigned> and_gates_map; + + const rule_set& m_rules; + const fact_vector *m_facts; + ast_manager& m; + rule_manager& m_rm; + aig_manager m_aigm; + decl_id_map m_decl_id_map; + unsigned m_next_decl_id; + aig_expr_id_map m_aig_expr_id_map; + unsigned m_next_aig_expr_id; + and_gates_map m_and_gates_map; + unsigned m_num_and_gates; + + expr_ref_vector m_latch_vars, m_latch_varsp; + expr_ref_vector m_ruleid_var_set, m_ruleid_varp_set; + unsigned_vector m_input_vars; + + std::stringstream m_buffer; + + void mk_latch_vars(unsigned n); + expr* get_latch_var(unsigned i, const expr_ref_vector& vars); + void assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs); + void collect_var_substs(substitution& subst, const app *h, + const expr_ref_vector& vars, expr_ref_vector& eqs); + unsigned expr_to_aig(const expr *e); + unsigned neg(unsigned id) const; + unsigned mk_and(unsigned id1, unsigned id2); + unsigned mk_or(unsigned id1, unsigned id2); + unsigned get_var(const expr *e); + unsigned mk_var(const expr *e); + unsigned mk_input_var(const expr *e = 0); + unsigned mk_expr_id(); + }; +} + +#endif diff --git a/src/muz_qe/dl_base.cpp b/src/muz/rel/dl_base.cpp similarity index 88% rename from src/muz_qe/dl_base.cpp rename to src/muz/rel/dl_base.cpp index 89ebc7e4e..dc10b5f8e 100644 --- a/src/muz_qe/dl_base.cpp +++ b/src/muz/rel/dl_base.cpp @@ -24,11 +24,42 @@ Revision History: #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; iget_fact(row); - to_remove.append(row); + to_remove.push_back(row); } remove_facts(to_remove.size(), to_remove.c_ptr()); } @@ -325,9 +356,20 @@ namespace datalog { 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); @@ -335,16 +377,14 @@ namespace datalog { fact.resize(sig.first_functional()); fact.append(sig.functional_columns(), func_columns); - if(sig.first_functional()==0) { - if(empty()) { + if (sig.first_functional() == 0) { + if (empty()) { res->add_fact(fact); } return res; } - if(sig.first_functional()!=1) { //now we support only tables with one non-functional column - NOT_IMPLEMENTED_YET(); - } + VERIFY(sig.first_functional() == 1); uint64 upper_bound = get_signature()[0]; bool empty_table = empty(); @@ -356,51 +396,13 @@ namespace datalog { warning_msg(buffer.str().c_str()); } - for(table_element i=0; iadd_fact(fact); } } return res; -#if 0 - svector var_arg_indexes(arity); - var_arg_indexes.fill(0); - - svector var_arg_domain_sizes = s; - - unsigned var_cnt=var_arg_indexes.size(); - table_fact fact; - fact.resize(arity); - fact.fill(0); - unsigned depth=arity; - - while(true) { - if(depth==arity) { - SASSERT(!res->contains_fact(fact)); - if(empty_table || !contains_fact(fact)) { - res->add_fact(fact); - } - depth--; - } - else if(fact[depth]==s[depth]-1) { - val_indexes[depth]=0; - if(depth==0) { - break; - } - depth--; - } - else { - SASSERT(val_indexes[depth] + 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); + 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 @@ -201,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; /** @@ -233,6 +269,7 @@ namespace datalog { 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); } @@ -295,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; } @@ -328,6 +366,10 @@ namespace datalog { 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; } @@ -336,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 { @@ -451,8 +505,8 @@ namespace datalog { class convenient_join_fn : public join_fn { signature m_result_sig; protected: - const unsigned_vector m_cols1; - const unsigned_vector m_cols2; + 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) @@ -467,8 +521,8 @@ namespace datalog { class convenient_join_project_fn : public join_fn { signature m_result_sig; protected: - const unsigned_vector m_cols1; - const unsigned_vector m_cols2; + 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; @@ -495,7 +549,7 @@ namespace datalog { class convenient_project_fn : public convenient_transformer_fn { protected: - const unsigned_vector m_removed_cols; + 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) { @@ -651,50 +705,9 @@ namespace datalog { 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; @@ -714,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; @@ -736,8 +750,8 @@ namespace datalog { struct hash { unsigned operator()(relation_signature const& s) const { - relation_sort const* sorts = s.c_ptr(); - return string_hash(reinterpret_cast(sorts), sizeof(*sorts)*s.size(), 12); } + return obj_vector_hash(s); + } }; struct eq { @@ -768,7 +782,7 @@ namespace datalog { bool is_finite_product_relation() const { return m_special_type==ST_FINITE_PRODUCT_RELATION; } bool is_product_relation() const { return m_special_type==ST_PRODUCT_RELATION; } bool is_sieve_relation() const { return m_special_type==ST_SIEVE_RELATION; } - + /** \brief If true, the relation can contain only one or zero elements. @@ -797,6 +811,7 @@ namespace datalog { virtual void to_formula(expr_ref& fml) const = 0; bool from_table() const { return get_plugin().from_table(); } + virtual bool is_precise() const { return true; } }; typedef ptr_vector relation_vector; @@ -813,9 +828,9 @@ namespace datalog { 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; @@ -835,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; @@ -878,8 +894,8 @@ namespace datalog { public: struct hash { unsigned operator()(table_signature const& s) const { - table_sort const* sorts = s.c_ptr(); - return string_hash(reinterpret_cast(sorts), sizeof(*sorts)*s.size(), 12); } + return svector_hash()(s); + } }; struct eq { @@ -1243,6 +1259,15 @@ namespace datalog { } }; + /** + \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); + }; diff --git a/src/muz_qe/dl_bound_relation.cpp b/src/muz/rel/dl_bound_relation.cpp similarity index 99% rename from src/muz_qe/dl_bound_relation.cpp rename to src/muz/rel/dl_bound_relation.cpp index 0c76e8e1e..182046c1e 100644 --- a/src/muz_qe/dl_bound_relation.cpp +++ b/src/muz/rel/dl_bound_relation.cpp @@ -677,7 +677,7 @@ namespace datalog { void bound_relation::display_index(unsigned i, uint_set2 const& src, std::ostream & out) const { uint_set::iterator it = src.lt.begin(), end = src.lt.end(); - out << i; + out << "#" << i; if (!src.lt.empty()) { out << " < "; for(; it != end; ++it) { @@ -691,6 +691,9 @@ namespace datalog { out << *it << " "; } } + if (src.lt.empty() && src.le.empty()) { + out << " < oo"; + } out << "\n"; } diff --git a/src/muz_qe/dl_bound_relation.h b/src/muz/rel/dl_bound_relation.h similarity index 98% rename from src/muz_qe/dl_bound_relation.h rename to src/muz/rel/dl_bound_relation.h index 603717bd8..906ba571a 100644 --- a/src/muz_qe/dl_bound_relation.h +++ b/src/muz/rel/dl_bound_relation.h @@ -20,6 +20,8 @@ Revision History: #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" @@ -69,6 +71,7 @@ namespace datalog { unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } + #if 0 virtual intersection_filter_fn * mk_filter_by_intersection_fn( const relation_base & t, @@ -138,6 +141,7 @@ namespace datalog { bool is_lt(unsigned i, unsigned j) const; + virtual bool is_precise() const { return false; } private: typedef uint_set2 T; diff --git a/src/muz_qe/dl_check_table.cpp b/src/muz/rel/dl_check_table.cpp similarity index 76% rename from src/muz_qe/dl_check_table.cpp rename to src/muz/rel/dl_check_table.cpp index d7dfbe1ae..ea4003e5f 100644 --- a/src/muz_qe/dl_check_table.cpp +++ b/src/muz/rel/dl_check_table.cpp @@ -82,6 +82,34 @@ namespace datalog { 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; @@ -120,7 +148,6 @@ namespace datalog { } 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(), tchecker->get_signature(), ttocheck, tchecker); @@ -135,6 +162,31 @@ namespace datalog { 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; @@ -233,6 +285,33 @@ namespace datalog { 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; @@ -287,9 +366,6 @@ namespace datalog { bool check_table::well_formed() const { get_plugin().m_count++; - if (get_plugin().m_count == 497) { - std::cout << "here\n"; - } iterator it = m_tocheck->begin(), end = m_tocheck->end(); for (; it != end; ++it) { table_fact fact; @@ -354,8 +430,8 @@ namespace datalog { return result; } - table_base * check_table::complement(func_decl* p) const { - check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->complement(p), m_checker->complement(p)); + 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_qe/dl_check_table.h b/src/muz/rel/dl_check_table.h similarity index 84% rename from src/muz_qe/dl_check_table.h rename to src/muz/rel/dl_check_table.h index 6f098f8bc..e4f439590 100644 --- a/src/muz_qe/dl_check_table.h +++ b/src/muz/rel/dl_check_table.h @@ -35,13 +35,16 @@ namespace datalog { 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: @@ -54,10 +57,15 @@ namespace datalog { 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, @@ -65,6 +73,8 @@ namespace datalog { 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, @@ -89,15 +99,6 @@ namespace datalog { class check_table : public table_base { friend class check_table_plugin; - friend class check_table_plugin::join_fn; - friend class check_table_plugin::union_fn; - friend class check_table_plugin::transformer_fn; - friend class check_table_plugin::rename_fn; - friend class check_table_plugin::project_fn; - friend class check_table_plugin::filter_equal_fn; - friend class check_table_plugin::filter_identical_fn; - friend class check_table_plugin::filter_interpreted_fn; - friend class check_table_plugin::filter_by_negation_fn; table_base* m_checker; table_base* m_tocheck; @@ -119,7 +120,7 @@ namespace datalog { 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; + 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(); } diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz/rel/dl_compiler.cpp similarity index 76% rename from src/muz_qe/dl_compiler.cpp rename to src/muz/rel/dl_compiler.cpp index 44a449779..276e7b836 100644 --- a/src/muz_qe/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" @@ -36,13 +37,13 @@ namespace datalog { } void compiler::ensure_predicate_loaded(func_decl * pred, instruction_block & acc) { - pred2idx::entry * e = m_pred_regs.insert_if_not_there2(pred, UINT_MAX); + pred2idx::obj_map_entry * e = m_pred_regs.insert_if_not_there2(pred, UINT_MAX); if(e->get_data().m_value!=UINT_MAX) { //predicate is already loaded return; } relation_signature sig; - m_context.get_rel_context().get_rmanager().from_predicate(pred, sig); + m_context.get_rel_context()->get_rmanager().from_predicate(pred, sig); reg_idx reg = get_fresh_register(sig); e->get_data().m_value=reg; @@ -61,17 +62,30 @@ namespace datalog { void compiler::make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc) { relation_signature aux_sig; - relation_signature::from_join(m_reg_signatures[t1], m_reg_signatures[t2], vars.size(), - vars.get_cols1(), vars.get_cols2(), aux_sig); + relation_signature sig1 = m_reg_signatures[t1]; + relation_signature sig2 = m_reg_signatures[t2]; + relation_signature::from_join(sig1, sig2, vars.size(), vars.get_cols1(), vars.get_cols2(), aux_sig); relation_signature res_sig; relation_signature::from_project(aux_sig, removed_cols.size(), removed_cols.c_ptr(), res_sig); - result = get_fresh_register(res_sig); + acc.push_back(instruction::mk_join_project(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), removed_cols.size(), removed_cols.c_ptr(), result)); } + void compiler::make_filter_interpreted_and_project(reg_idx src, app_ref & cond, + const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc) { + SASSERT(!removed_cols.empty()); + relation_signature res_sig; + relation_signature::from_project(m_reg_signatures[src], removed_cols.size(), + removed_cols.c_ptr(), res_sig); + result = get_fresh_register(res_sig); + + acc.push_back(instruction::mk_filter_interpreted_and_project(src, cond, + removed_cols.size(), removed_cols.c_ptr(), result)); + } + void compiler::make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col, reg_idx & result, instruction_block & acc) { relation_signature res_sig; @@ -145,7 +159,7 @@ namespace datalog { } void compiler::make_add_constant_column(func_decl* head_pred, reg_idx src, const relation_sort & s, const relation_element & val, - reg_idx & result, instruction_block & acc) { + reg_idx & result, bool & dealloc, instruction_block & acc) { reg_idx singleton_table; if(!m_constant_registers.find(s, val, singleton_table)) { singleton_table = get_single_column_register(s); @@ -154,16 +168,18 @@ namespace datalog { m_constant_registers.insert(s, val, singleton_table); } if(src==execution_context::void_register) { - make_clone(singleton_table, result, acc); + result = singleton_table; + dealloc = false; } else { variable_intersection empty_vars(m_context.get_manager()); make_join(src, singleton_table, empty_vars, result, acc); + dealloc = true; } } void compiler::make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort & s, reg_idx & result, - instruction_block & acc) { + bool & dealloc, instruction_block & acc) { TRACE("dl", tout << "Adding unbound column " << mk_pp(pred, m_context.get_manager()) << "\n";); IF_VERBOSE(3, { @@ -172,25 +188,35 @@ namespace datalog { verbose_stream() << "Compiling unsafe rule column " << col_idx << "\n" << mk_ismt2_pp(e, m_context.get_manager()) << "\n"; }); - reg_idx total_table = get_single_column_register(s); - relation_signature sig; - sig.push_back(s); - acc.push_back(instruction::mk_total(sig, pred, total_table)); + reg_idx total_table; + if (!m_total_registers.find(s, pred, total_table)) { + total_table = get_single_column_register(s); + relation_signature sig; + sig.push_back(s); + m_top_level_code.push_back(instruction::mk_total(sig, pred, total_table)); + m_total_registers.insert(s, pred, total_table); + } if(src == execution_context::void_register) { result = total_table; + dealloc = false; } else { variable_intersection empty_vars(m_context.get_manager()); make_join(src, total_table, empty_vars, result, acc); - make_dealloc_non_void(total_table, acc); + dealloc = true; } } void compiler::make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc) { + SASSERT(sig.empty()); TRACE("dl", tout << "Adding unbound column " << mk_pp(pred, m_context.get_manager()) << "\n";); + if (m_empty_tables_registers.find(pred, result)) + return; + result = get_fresh_register(sig); - acc.push_back(instruction::mk_total(sig, pred, result)); + m_top_level_code.push_back(instruction::mk_total(sig, pred, result)); + m_empty_tables_registers.insert(pred, result); } @@ -232,6 +258,7 @@ namespace datalog { reg_idx src, const svector & acis0, reg_idx & result, + bool & dealloc, instruction_block & acc) { TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); @@ -276,7 +303,9 @@ namespace datalog { if(!src_cols_to_remove.empty()) { reg_idx new_curr; make_projection(curr, src_cols_to_remove.size(), src_cols_to_remove.c_ptr(), new_curr, acc); - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = true; curr=new_curr; curr_sig = & m_reg_signatures[curr]; @@ -297,16 +326,19 @@ namespace datalog { unsigned bound_column_index; if(acis[i].kind!=ACK_UNBOUND_VAR || !handled_unbound.find(acis[i].var_index,bound_column_index)) { reg_idx new_curr; + bool new_dealloc; bound_column_index=curr_sig->size(); if(acis[i].kind==ACK_CONSTANT) { - make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, new_curr, acc); + make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, new_curr, new_dealloc, acc); } else { SASSERT(acis[i].kind==ACK_UNBOUND_VAR); - make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, new_curr, acc); + make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, new_curr, new_dealloc, acc); handled_unbound.insert(acis[i].var_index,bound_column_index); } - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = new_dealloc; curr=new_curr; curr_sig = & m_reg_signatures[curr]; SASSERT(bound_column_index==curr_sig->size()-1); @@ -327,7 +359,9 @@ namespace datalog { } reg_idx new_curr; make_duplicate_column(curr, col, new_curr, acc); - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = true; curr=new_curr; curr_sig = & m_reg_signatures[curr]; unsigned bound_column_index=curr_sig->size()-1; @@ -355,7 +389,9 @@ namespace datalog { reg_idx new_curr; make_rename(curr, permutation.size(), permutation.c_ptr(), new_curr, acc); - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = true; curr=new_curr; curr_sig = & m_reg_signatures[curr]; } @@ -364,6 +400,7 @@ namespace datalog { SASSERT(src==execution_context::void_register); SASSERT(acis0.size()==0); make_full_relation(head_pred, empty_signature, curr, acc); + dealloc = false; } result=curr; @@ -384,8 +421,8 @@ namespace datalog { void compiler::get_local_indexes_for_projection(rule * r, unsigned_vector & res) { SASSERT(r->get_positive_tail_size()==2); ast_manager & m = m_context.get_manager(); - var_counter counter; - counter.count_vars(m, r); + rule_counter counter; + counter.count_rule_vars(m, r); app * t1 = r->get_tail(0); app * t2 = r->get_tail(1); counter.count_vars(m, t1, -1); @@ -397,6 +434,7 @@ namespace datalog { void compiler::compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs, reg_idx delta_reg, bool use_widening, instruction_block & acc) { + ast_manager & m = m_context.get_manager(); m_instruction_observer.start_rule(r); const app * h = r->get_head(); @@ -409,12 +447,15 @@ namespace datalog { SASSERT(pt_len<=2); //we require rules to be processed by the mk_simple_joins rule transformer plugin reg_idx single_res; - ptr_vector single_res_expr; + expr_ref_vector single_res_expr(m); //used to save on filter_identical instructions where the check is already done //by the join operation unsigned second_tail_arg_ofs; + // whether to dealloc the previous result + bool dealloc = true; + if(pt_len == 2) { reg_idx t1_reg=tail_regs[0]; reg_idx t2_reg=tail_regs[1]; @@ -471,8 +512,7 @@ namespace datalog { expr * arg = a->get_arg(i); if(is_app(arg)) { app * c = to_app(arg); //argument is a constant - SASSERT(c->get_num_args()==0); - SASSERT(m_context.get_decl_util().is_numeral_ext(arg)); + SASSERT(m.is_value(c)); reg_idx new_reg; make_select_equal_and_project(single_res, c, single_res_expr.size(), new_reg, acc); if(single_res!=t_reg) { @@ -487,8 +527,7 @@ namespace datalog { } } if(single_res==t_reg) { - //we may be modifying the register later, so we need a local copy - make_clone(t_reg, single_res, acc); + dealloc = false; } } @@ -499,7 +538,7 @@ namespace datalog { single_res=execution_context::void_register; } - add_unbound_columns_for_negation(r, head_pred, single_res, single_res_expr, acc); + add_unbound_columns_for_negation(r, head_pred, single_res, single_res_expr, dealloc, acc); int2ints var_indexes; @@ -510,11 +549,14 @@ namespace datalog { unsigned srlen=single_res_expr.size(); SASSERT((single_res==execution_context::void_register) ? (srlen==0) : (srlen==m_reg_signatures[single_res].size())); for(unsigned i=0; iget_rmanager().from_predicate(neg_pred, i, arg_sort); reg_idx new_reg; - make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), new_reg, acc); + bool new_dealloc; + make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), new_reg, new_dealloc, acc); - make_dealloc_non_void(filtered_res, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; filtered_res = new_reg; // here filtered_res value gets changed !! t_cols.push_back(single_res_expr.size()); @@ -576,18 +624,130 @@ namespace datalog { SASSERT(t_cols.size()==neg_cols.size()); reg_idx neg_reg = m_pred_regs.find(neg_pred); + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_by_negation(filtered_res, neg_reg, t_cols.size(), t_cols.c_ptr(), neg_cols.c_ptr())); + dealloc = true; } - //enforce interpreted tail predicates + // enforce interpreted tail predicates + unsigned ft_len = r->get_tail_size(); // full tail + ptr_vector tail; + for (unsigned tail_index = ut_len; tail_index < ft_len; ++tail_index) { + tail.push_back(r->get_tail(tail_index)); + } + + if (!tail.empty()) { + app_ref filter_cond(tail.size() == 1 ? to_app(tail.back()) : m.mk_and(tail.size(), tail.c_ptr()), m); + ptr_vector filter_vars; + get_free_vars(filter_cond, filter_vars); + + // create binding + expr_ref_vector binding(m); + binding.resize(filter_vars.size()+1); + + for (unsigned v = 0; v < filter_vars.size(); ++v) { + if (!filter_vars[v]) + continue; + + int2ints::entry * entry = var_indexes.find_core(v); + unsigned src_col; + if (entry) { + src_col = entry->get_data().m_value.back(); + } else { + // we have an unbound variable, so we add an unbound column for it + relation_sort unbound_sort = filter_vars[v]; + + reg_idx new_reg; + bool new_dealloc; + make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; + filtered_res = new_reg; + + src_col = single_res_expr.size(); + single_res_expr.push_back(m.mk_var(v, unbound_sort)); + + entry = var_indexes.insert_if_not_there2(v, unsigned_vector()); + entry->get_data().m_value.push_back(src_col); + } + relation_sort var_sort = m_reg_signatures[filtered_res][src_col]; + binding[filter_vars.size()-v] = m.mk_var(src_col, var_sort); + } + + // check if there are any columns to remove + unsigned_vector remove_columns; + { + unsigned_vector var_idx_to_remove; + ptr_vector vars; + get_free_vars(r->get_head(), vars); + for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); + I != E; ++I) { + unsigned var_idx = I->m_key; + if (!vars.get(var_idx, 0)) { + unsigned_vector & cols = I->m_value; + for (unsigned i = 0; i < cols.size(); ++i) { + remove_columns.push_back(cols[i]); + } + var_idx_to_remove.push_back(var_idx); + } + } + + for (unsigned i = 0; i < var_idx_to_remove.size(); ++i) { + var_indexes.remove(var_idx_to_remove[i]); + } + + // update column idx for after projection state + if (!remove_columns.empty()) { + unsigned_vector offsets; + offsets.resize(single_res_expr.size(), 0); + + for (unsigned i = 0; i < remove_columns.size(); ++i) { + for (unsigned col = remove_columns[i]; col < offsets.size(); ++col) { + ++offsets[col]; + } + } + + for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); + I != E; ++I) { + unsigned_vector & cols = I->m_value; + for (unsigned i = 0; i < cols.size(); ++i) { + cols[i] -= offsets[cols[i]]; + } + } + } + } + + expr_ref renamed(m); + m_context.get_var_subst()(filter_cond, binding.size(), binding.c_ptr(), renamed); + app_ref app_renamed(to_app(renamed), m); + if (remove_columns.empty()) { + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); + acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); + } else { + reg_idx new_reg; + std::sort(remove_columns.begin(), remove_columns.end()); + make_filter_interpreted_and_project(filtered_res, app_renamed, remove_columns, new_reg, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + filtered_res = new_reg; + } + dealloc = true; + } + +#if 0 + // this version is potentially better for non-symbolic tables, + // since it constraints each unbound column at a time (reducing the + // size of intermediate results). unsigned ft_len=r->get_tail_size(); //full tail for(unsigned tail_index=ut_len; tail_indexget_tail(tail_index); - var_idx_set t_vars; - ast_manager & m = m_context.get_manager(); - collect_vars(m, t, t_vars); - + ptr_vector t_vars; + ::get_free_vars(t, t_vars); + if(t_vars.empty()) { expr_ref simplified(m); m_context.get_rewriter()(t, simplified); @@ -601,46 +761,32 @@ namespace datalog { } //determine binding size - unsigned max_var=0; - var_idx_set::iterator vit = t_vars.begin(); - var_idx_set::iterator vend = t_vars.end(); - for(; vit!=vend; ++vit) { - unsigned v = *vit; - if(v>max_var) { max_var = v; } + while (!t_vars.back()) { + t_vars.pop_back(); } + unsigned max_var = t_vars.size(); //create binding expr_ref_vector binding(m); binding.resize(max_var+1); - vit = t_vars.begin(); - for(; vit!=vend; ++vit) { - unsigned v = *vit; + + for(unsigned v = 0; v < t_vars.size(); ++v) { + if (!t_vars[v]) { + continue; + } int2ints::entry * e = var_indexes.find_core(v); if(!e) { //we have an unbound variable, so we add an unbound column for it - relation_sort unbound_sort = 0; - - for(unsigned hindex = 0; hindexget_arg(hindex); - if(!is_var(harg) || to_var(harg)->get_idx()!=v) { - continue; - } - unbound_sort = to_var(harg)->get_sort(); - } - if(!unbound_sort) { - // the variable in the interpreted tail is neither bound in the - // uninterpreted tail nor present in the head - std::stringstream sstm; - sstm << "rule with unbound variable #" << v << " in interpreted tail: "; - r->display(m_context, sstm); - throw default_exception(sstm.str()); - } + relation_sort unbound_sort = t_vars[v]; reg_idx new_reg; TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); - make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, acc); + bool new_dealloc; + make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); - make_dealloc_non_void(filtered_res, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; filtered_res = new_reg; // here filtered_res value gets changed !! unsigned unbound_column_index = single_res_expr.size(); @@ -658,8 +804,12 @@ namespace datalog { expr_ref renamed(m); m_context.get_var_subst()(t, binding.size(), binding.c_ptr(), renamed); app_ref app_renamed(to_app(renamed), m); + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); + dealloc = true; } +#endif { //put together the columns of head relation @@ -703,18 +853,20 @@ namespace datalog { SASSERT(head_acis.size()==head_len); reg_idx new_head_reg; - make_assembling_code(r, head_pred, filtered_res, head_acis, new_head_reg, acc); + make_assembling_code(r, head_pred, filtered_res, head_acis, new_head_reg, dealloc, acc); //update the head relation make_union(new_head_reg, head_reg, delta_reg, use_widening, acc); + if (dealloc) + make_dealloc_non_void(new_head_reg, acc); } - finish: +// finish: m_instruction_observer.finish_rule(); } - void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, ptr_vector& single_res_expr, - instruction_block & acc) { + void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, + bool & dealloc, instruction_block & acc) { uint_set pos_vars; u_map neg_vars; ast_manager& m = m_context.get_manager(); @@ -730,25 +882,32 @@ 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); } } } // populate positive variables: for (unsigned i = 0; i < single_res_expr.size(); ++i) { - expr* e = single_res_expr[i]; + expr* e = single_res_expr[i].get(); if (is_var(e)) { pos_vars.insert(to_var(e)->get_idx()); } } - // add negative variables that are not in positive: + // 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 (!pos_vars.contains(v)) { single_res_expr.push_back(e); - make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), single_res, acc); + reg_idx new_single_res; + bool new_dealloc; + make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), new_single_res, new_dealloc, acc); + if (dealloc) + make_dealloc_non_void(single_res, acc); + dealloc = new_dealloc; + single_res = new_single_res; TRACE("dl", tout << "Adding unbound column: " << mk_pp(e, m) << "\n";); } } @@ -760,7 +919,7 @@ namespace datalog { typedef svector tail_delta_infos; unsigned rule_len = r->get_uninterpreted_tail_size(); - reg_idx head_reg = m_pred_regs.find(r->get_head()->get_decl()); + reg_idx head_reg = m_pred_regs.find(r->get_decl()); svector tail_regs; tail_delta_infos tail_deltas; @@ -803,13 +962,13 @@ namespace datalog { ast_mark m_visited; void traverse(T v) { - SASSERT(!m_stack_content.is_marked(v)); - if(m_visited.is_marked(v) || m_removed.contains(v)) { + SASSERT(!m_stack_content.is_marked(v)); + if(m_visited.is_marked(v) || m_removed.contains(v)) { return; } m_stack.push_back(v); - m_stack_content.mark(v, true); + m_stack_content.mark(v, true); m_visited.mark(v, true); const item_set & deps = m_deps.get_deps(v); @@ -817,7 +976,7 @@ namespace datalog { item_set::iterator end = deps.end(); for(; it!=end; ++it) { T d = *it; - if(m_stack_content.is_marked(d)) { + if(m_stack_content.is_marked(d)) { //TODO: find the best vertex to remove in the cycle m_removed.insert(v); break; @@ -827,8 +986,9 @@ namespace datalog { SASSERT(m_stack.back()==v); m_stack.pop_back(); - m_stack_content.mark(v, false); + m_stack_content.mark(v, false); } + public: cycle_breaker(rule_dependencies & deps, item_set & removed) : m_deps(deps), m_removed(removed) { SASSERT(removed.empty()); } @@ -846,7 +1006,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()); @@ -884,13 +1043,47 @@ namespace datalog { rule_vector::const_iterator rend = pred_rules.end(); for(; rit!=rend; ++rit) { rule * r = *rit; - SASSERT(head_pred==r->get_head()->get_decl()); + SASSERT(head_pred==r->get_decl()); compile_rule_evaluation(r, input_deltas, d_head_reg, widen_predicate_in_loop, acc); } } } + void compiler::compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds, + const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc) { + func_decl_vector::const_iterator hpit = head_preds.begin(); + func_decl_vector::const_iterator hpend = head_preds.end(); + reg_idx void_reg = execution_context::void_register; + for(; hpit!=hpend; ++hpit) { + func_decl * head_pred = *hpit; + const rule_vector & pred_rules = m_rule_set.get_predicate_rules(head_pred); + rule_vector::const_iterator rit = pred_rules.begin(); + rule_vector::const_iterator rend = pred_rules.end(); + unsigned stratum = m_rule_set.get_predicate_strat(head_pred); + + for(; rit != rend; ++rit) { + rule * r = *rit; + SASSERT(head_pred==r->get_decl()); + + for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { + unsigned stratum1 = m_rule_set.get_predicate_strat(r->get_decl(i)); + if (stratum1 >= stratum) { + goto next_loop; + } + } + compile_rule_evaluation(r, input_deltas, void_reg, false, acc); + next_loop: + ; + } + + reg_idx d_head_reg; + if (output_deltas.find(head_pred, d_head_reg)) { + acc.push_back(instruction::mk_clone(m_pred_regs.find(head_pred), d_head_reg)); + } + } + } + void compiler::make_inloop_delta_transition(const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc) { //move global head deltas into tail ones @@ -941,7 +1134,7 @@ namespace datalog { const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc) { - if(!output_deltas.empty()) { + if (!output_deltas.empty()) { func_decl_set::iterator hpit = head_preds.begin(); func_decl_set::iterator hpend = head_preds.end(); for(; hpit!=hpend; ++hpit) { @@ -955,12 +1148,17 @@ namespace datalog { } func_decl_vector preds_vector; - func_decl_set global_deltas; + func_decl_set global_deltas_dummy; - detect_chains(head_preds, preds_vector, global_deltas); + detect_chains(head_preds, preds_vector, global_deltas_dummy); + /* + FIXME: right now we use all preds as global deltas for correctness purposes func_decl_set local_deltas(head_preds); set_difference(local_deltas, global_deltas); + */ + func_decl_set local_deltas; + func_decl_set global_deltas(head_preds); pred2idx d_global_src; //these deltas serve as sources of tuples for rule evaluation inside the loop get_fresh_registers(global_deltas, d_global_src); @@ -978,7 +1176,8 @@ namespace datalog { func_decl_set empty_func_decl_set; //generate code for the initial run - compile_preds(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); + // compile_preds(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); + compile_preds_init(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); if (compile_with_widening()) { compile_loop(preds_vector, global_deltas, d_global_tgt, d_global_src, d_local, acc); @@ -1028,21 +1227,23 @@ namespace datalog { func_decl * head_pred = *preds.begin(); const rule_vector & rules = m_rule_set.get_predicate_rules(head_pred); + + reg_idx output_delta; - if(!output_deltas.find(head_pred, output_delta)) { + if (!output_deltas.find(head_pred, output_delta)) { output_delta = execution_context::void_register; } rule_vector::const_iterator it = rules.begin(); rule_vector::const_iterator end = rules.end(); - for(; it!=end; ++it) { + for (; it != end; ++it) { rule * r = *it; - SASSERT(r->get_head()->get_decl()==head_pred); + SASSERT(r->get_decl()==head_pred); compile_rule_evaluation(r, input_deltas, output_delta, false, acc); } - if(add_saturation_marks) { + if (add_saturation_marks) { //now the predicate is saturated, so we may mark it as such acc.push_back(instruction::mk_mark_saturated(m_context.get_manager(), head_pred)); } @@ -1052,7 +1253,7 @@ namespace datalog { func_decl_set::iterator fdit = preds.begin(); func_decl_set::iterator fdend = preds.end(); for(; fdit!=fdend; ++fdit) { - if(!m_context.get_rel_context().get_rmanager().is_saturated(*fdit)) { + if(!m_context.get_rel_context()->get_rmanager().is_saturated(*fdit)) { return false; } } @@ -1068,7 +1269,7 @@ namespace datalog { for(; sit!=send; ++sit) { func_decl_set & strat_preds = **sit; - if(all_saturated(strat_preds)) { + if (all_saturated(strat_preds)) { //all predicates in stratum are saturated, so no need to compile rules for them continue; } @@ -1084,7 +1285,7 @@ namespace datalog { tout << "\n"; ); - if(is_nonrecursive_stratum(strat_preds)) { + if (is_nonrecursive_stratum(strat_preds)) { //this stratum contains just a single non-recursive rule compile_nonrecursive_stratum(strat_preds, input_deltas, output_deltas, add_saturation_marks, acc); } @@ -1110,7 +1311,7 @@ namespace datalog { //load predicate data for(unsigned i=0;iget_head()->get_decl(), acc); + ensure_predicate_loaded(r->get_decl(), acc); unsigned rule_len = r->get_uninterpreted_tail_size(); for(unsigned j=0;j int_set; typedef u_map int2int; typedef u_map int2ints; - typedef map,ptr_eq > pred2idx; + typedef obj_map pred2idx; typedef unsigned_vector var_vector; typedef ptr_vector func_decl_vector; @@ -114,6 +114,8 @@ namespace datalog { reg_idx m_new_reg; vector m_reg_signatures; obj_pair_map m_constant_registers; + obj_pair_map m_total_registers; + obj_map m_empty_tables_registers; instruction_observer m_instruction_observer; /** @@ -143,6 +145,8 @@ namespace datalog { instruction_block & acc); void make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc); + void make_filter_interpreted_and_project(reg_idx src, app_ref & cond, + const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc); void make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col, reg_idx & result, instruction_block & acc); /** @@ -163,20 +167,20 @@ namespace datalog { with empty signature. */ void make_assembling_code(rule* compiled_rule, func_decl* head_pred, reg_idx src, const svector & acis0, - reg_idx & result, instruction_block & acc); + reg_idx & result, bool & dealloc, instruction_block & acc); void make_dealloc_non_void(reg_idx r, instruction_block & acc); void make_add_constant_column(func_decl* pred, reg_idx src, const relation_sort & s, const relation_element & val, - reg_idx & result, instruction_block & acc); + reg_idx & result, bool & dealloc, instruction_block & acc); void make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort & s, reg_idx & result, - instruction_block & acc); + bool & dealloc, instruction_block & acc); void make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc); - void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, ptr_vector& single_res_expr, - instruction_block& acc); + void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, + bool & dealloc, instruction_block& acc); void make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, instruction_block & acc); @@ -209,6 +213,12 @@ namespace datalog { void compile_preds(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc); + /** + \brief Generate code to evaluate predicates in a stratum based on their non-recursive rules. + */ + void compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds, + const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc); + void make_inloop_delta_transition(const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc); void compile_loop(const func_decl_vector & head_preds, const func_decl_set & widened_preds, diff --git a/src/muz_qe/dl_external_relation.cpp b/src/muz/rel/dl_external_relation.cpp similarity index 100% rename from src/muz_qe/dl_external_relation.cpp rename to src/muz/rel/dl_external_relation.cpp diff --git a/src/muz_qe/dl_external_relation.h b/src/muz/rel/dl_external_relation.h similarity index 100% rename from src/muz_qe/dl_external_relation.h rename to src/muz/rel/dl_external_relation.h diff --git a/src/muz_qe/dl_finite_product_relation.cpp b/src/muz/rel/dl_finite_product_relation.cpp similarity index 99% rename from src/muz_qe/dl_finite_product_relation.cpp rename to src/muz/rel/dl_finite_product_relation.cpp index 3070475ac..86fef433b 100644 --- a/src/muz_qe/dl_finite_product_relation.cpp +++ b/src/muz/rel/dl_finite_product_relation.cpp @@ -129,7 +129,7 @@ namespace datalog { for(unsigned i=0; i()(o.m_table_cols); } }; }; diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz/rel/dl_instruction.cpp similarity index 86% rename from src/muz_qe/dl_instruction.cpp rename to src/muz/rel/dl_instruction.cpp index 503ffec3b..a702c27ce 100644 --- a/src/muz_qe/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" @@ -58,14 +59,18 @@ namespace datalog { reset_timelimit(); } + rel_context& execution_context::get_rel_context() { + return dynamic_cast(*m_context.get_rel_context()); + } + struct compare_size_proc { typedef std::pair pr; bool operator()(pr const& a, pr const& b) const { return a.second > b.second; } - }; - void execution_context::report_big_relations(unsigned threshold, std::ostream & out) { + + void execution_context::report_big_relations(unsigned threshold, std::ostream & out) const { unsigned n = register_count(); svector > sizes; size_t total_bytes = 0; @@ -110,6 +115,7 @@ namespace datalog { bool execution_context::should_terminate() { return + m_context.canceled() || memory::above_high_watermark() || (m_stopwatch && m_timelimit_ms != 0 && @@ -135,8 +141,9 @@ namespace datalog { process_costs(); } - void instruction::display_indented(rel_context & 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 << " {"; @@ -182,7 +189,7 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) { ctx.set_register_annotation(m_reg, m_pred->get_name().bare_str()); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { const char * rel_name = m_pred->get_name().bare_str(); if (m_store) { out << "store " << m_reg << " into " << rel_name; @@ -213,7 +220,7 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) { ctx.set_register_annotation(m_reg, "alloc"); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "dealloc " << m_reg; } }; @@ -248,7 +255,7 @@ namespace datalog { ctx.set_register_annotation(m_src, str); } } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << (m_clone ? "clone " : "move ") << m_src << " into " << m_tgt; } }; @@ -304,11 +311,11 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) { m_body->make_annotations(ctx); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const & ctx, std::ostream & out) const { out << "while"; print_container(m_controls, out); } - virtual void display_body_impl(rel_context & 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+" "); } }; @@ -354,21 +361,7 @@ namespace datalog { r2.get_signature().output(ctx.get_rel_context().get_manager(), tout); tout<<":"<\n";); - try { - ctx.set_reg(m_res, (*fn)(r1, r2)); - } - catch(...) - { - std::string annotation; - unsigned sz; - ctx.get_register_annotation(m_rel1, annotation); - sz = ctx.reg(m_rel1)?ctx.reg(m_rel1)->get_size_estimate_rows():0; - std::cout << m_rel1 << "\t" << sz << "\t" << annotation << "\n"; - ctx.get_register_annotation(m_rel2, annotation); - sz = ctx.reg(m_rel2)?ctx.reg(m_rel2)->get_size_estimate_rows():0; - std::cout << m_rel2 << "\t" << sz << "\t" << annotation << "\n"; - throw; - } + ctx.set_reg(m_res, (*fn)(r1, r2)); TRACE("dl", ctx.reg(m_res)->get_signature().output(ctx.get_rel_context().get_manager(), tout); @@ -385,7 +378,7 @@ namespace datalog { ctx.get_register_annotation(m_rel1, a1); ctx.set_register_annotation(m_res, "join " + a1 + " " + a2); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const & ctx, std::ostream & out) const { out << "join " << m_rel1; print_container(m_cols1, out); out << " and " << m_rel2; @@ -434,7 +427,7 @@ namespace datalog { a << "filter_equal " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); ctx.set_register_annotation(m_reg, a.str()); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "filter_equal " << m_reg << " col: " << m_col << " val: " << ctx.get_rmanager().to_nice_string(m_value); } @@ -447,7 +440,7 @@ namespace datalog { class instr_filter_identical : public instruction { - typedef vector column_vector; + typedef unsigned_vector column_vector; reg_idx m_reg; column_vector m_cols; public: @@ -476,7 +469,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "filter_identical " << m_reg << " "; print_container(m_cols, out); } @@ -503,6 +496,7 @@ namespace datalog { relation_mutator_fn * fn; relation_base & r = *ctx.reg(m_reg); + TRACE("dl_verbose", r.display(tout <<"pre-filter-interpreted:\n");); if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_interpreted_fn(r, m_cond); if (!fn) { @@ -516,10 +510,12 @@ namespace datalog { if (ctx.eager_emptiness_checking() && r.empty()) { ctx.make_empty(m_reg); - } + } + TRACE("dl_verbose", r.display(tout <<"post-filter-interpreted:\n");); + return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "filter_interpreted " << m_reg << " using " << mk_pp(m_cond, m_cond.get_manager()); } @@ -535,6 +531,66 @@ namespace datalog { return alloc(instr_filter_interpreted, reg, condition); } + class instr_filter_interpreted_and_project : public instruction { + reg_idx m_src; + app_ref m_cond; + unsigned_vector m_cols; + reg_idx m_res; + public: + instr_filter_interpreted_and_project(reg_idx src, app_ref & condition, + unsigned col_cnt, const unsigned * removed_cols, reg_idx result) + : m_src(src), m_cond(condition), m_cols(col_cnt, removed_cols), + m_res(result) {} + + virtual bool perform(execution_context & ctx) { + if (!ctx.reg(m_src)) { + ctx.make_empty(m_res); + return true; + } + + relation_transformer_fn * fn; + relation_base & reg = *ctx.reg(m_src); + TRACE("dl_verbose", reg.display(tout <<"pre-filter-interpreted-and-project:\n");); + if (!find_fn(reg, fn)) { + fn = reg.get_manager().mk_filter_interpreted_and_project_fn(reg, m_cond, m_cols.size(), m_cols.c_ptr()); + if (!fn) { + throw default_exception( + "trying to perform unsupported filter_interpreted_and_project operation on a relation of kind %s", + reg.get_plugin().get_name().bare_str()); + } + store_fn(reg, fn); + } + + ctx.set_reg(m_res, (*fn)(reg)); + + if (ctx.eager_emptiness_checking() && ctx.reg(m_res)->empty()) { + ctx.make_empty(m_res); + } + TRACE("dl_verbose", reg.display(tout << "post-filter-interpreted-and-project:\n");); + return true; + } + + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + out << "filter_interpreted_and_project " << m_src << " into " << m_res; + out << " using " << mk_pp(m_cond, m_cond.get_manager()); + out << " deleting columns "; + print_container(m_cols, out); + } + + virtual void make_annotations(execution_context & ctx) { + std::stringstream s; + std::string a = "rel_src"; + ctx.get_register_annotation(m_src, a); + s << "filter_interpreted_and_project " << mk_pp(m_cond, m_cond.get_manager()); + ctx.set_register_annotation(m_res, s.str()); + } + }; + + instruction * instruction::mk_filter_interpreted_and_project(reg_idx reg, app_ref & condition, + unsigned col_cnt, const unsigned * removed_cols, reg_idx result) { + return alloc(instr_filter_interpreted_and_project, reg, condition, col_cnt, removed_cols, result); + } + class instr_union : public instruction { reg_idx m_src; @@ -601,6 +657,7 @@ namespace datalog { } } + SASSERT(r_src.get_signature().size() == r_tgt.get_signature().size()); TRACE("dl_verbose", r_tgt.display(tout <<"pre-union:");); (*fn)(r_tgt, r_src, r_delta); @@ -619,12 +676,16 @@ namespace datalog { return true; } virtual void make_annotations(execution_context & ctx) { - std::string str; - if (ctx.get_register_annotation(m_tgt, str) && m_delta!=execution_context::void_register) { - ctx.set_register_annotation(m_delta, "delta of "+str); + std::string str = "union"; + if (!ctx.get_register_annotation(m_tgt, str)) { + ctx.set_register_annotation(m_tgt, "union"); } + if (m_delta != execution_context::void_register) { + str = "delta of " + str; + } + ctx.set_register_annotation(m_delta, str); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << (m_widen ? "widen " : "union ") << m_src << " into " << m_tgt; if (m_delta!=execution_context::void_register) { out << " with delta " << m_delta; @@ -642,7 +703,7 @@ namespace datalog { class instr_project_rename : public instruction { - typedef vector column_vector; + typedef unsigned_vector column_vector; bool m_projection; reg_idx m_src; column_vector m_cols; @@ -678,7 +739,7 @@ namespace datalog { return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << (m_projection ? "project " : "rename ") << m_src << " into " << m_tgt; out << (m_projection ? " deleting columns " : " with cycle "); print_container(m_cols, out); @@ -714,7 +775,8 @@ namespace datalog { instr_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, reg_idx result) : m_rel1(rel1), m_rel2(rel2), m_cols1(joined_col_cnt, cols1), - m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols), m_res(result) {} + m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols), m_res(result) { + } virtual bool perform(execution_context & ctx) { ctx.make_empty(m_res); if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { @@ -739,7 +801,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "join_project " << m_rel1; print_container(m_cols1, out); out << " and " << m_rel2; @@ -800,7 +862,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "select_equal_and_project " << m_src <<" into " << m_result << " col: " << m_col << " val: " << ctx.get_rmanager().to_nice_string(m_value); } @@ -821,7 +883,7 @@ namespace datalog { class instr_filter_by_negation : public instruction { - typedef vector column_vector; + typedef unsigned_vector column_vector; reg_idx m_tgt; reg_idx m_neg_rel; column_vector m_cols1; @@ -854,7 +916,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "filter_by_negation on " << m_tgt; print_container(m_cols1, out); out << " with " << m_neg_rel; @@ -892,7 +954,7 @@ namespace datalog { ctx.set_reg(m_tgt, rel); return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "mk_unary_singleton into " << m_tgt << " sort:" << ctx.get_rmanager().to_nice_string(m_sig[0]) << " val:" << ctx.get_rmanager().to_nice_string(m_sig[0], m_fact[0]); @@ -922,7 +984,7 @@ namespace datalog { ctx.set_reg(m_tgt, ctx.get_rel_context().get_rmanager().mk_full_relation(m_sig, m_pred)); return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "mk_total into " << m_tgt << " sort:" << ctx.get_rmanager().to_nice_string(m_sig); } @@ -947,7 +1009,7 @@ namespace datalog { ctx.get_rel_context().get_rmanager().mark_saturated(m_pred); return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "mark_saturated " << m_pred->get_name().bare_str(); } virtual void make_annotations(execution_context & ctx) { @@ -970,7 +1032,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "instr_assert_signature of " << m_tgt << " signature:"; print_container(m_sig, out); } @@ -1042,7 +1104,8 @@ namespace datalog { } } - void instruction_block::display_indented(rel_context & 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_qe/dl_instruction.h b/src/muz/rel/dl_instruction.h similarity index 90% rename from src/muz_qe/dl_instruction.h rename to src/muz/rel/dl_instruction.h index 89f95c860..fa705a172 100644 --- a/src/muz_qe/dl_instruction.h +++ b/src/muz/rel/dl_instruction.h @@ -26,11 +26,13 @@ Revision History: #include "vector.h" #include "dl_base.h" #include "dl_costs.h" +#include "dl_context.h" namespace datalog { class execution_context; class instruction_block; + class rel_context; inline void check_overflow(unsigned i) { if (i == UINT_MAX) { @@ -78,7 +80,7 @@ namespace datalog { void reset(); - rel_context & get_rel_context() { return m_context.get_rel_context(); }; + rel_context & get_rel_context(); void set_timelimit(unsigned time_in_ms); void reset_timelimit(); @@ -91,10 +93,9 @@ namespace datalog { If register contains zero, it should be treated as if it contains an empty relation. */ - reg_type reg(reg_idx i) { - if (i>=m_registers.size()) { - check_overflow(i); - m_registers.resize(i+1,0); + reg_type reg(reg_idx i) const { + if (i >= m_registers.size()) { + return 0; } return m_registers[i]; } @@ -102,27 +103,29 @@ namespace datalog { \brief Return value of the register and assign zero into it place. */ reg_type release_reg(reg_idx i) { - SASSERT(i=m_registers.size()) { + if (i >= m_registers.size()) { check_overflow(i); m_registers.resize(i+1,0); } - if(m_registers[i]) { + if (m_registers[i]) { m_registers[i]->deallocate(); } - m_registers[i]=val; + m_registers[i] = val; } + void make_empty(reg_idx i) { - if(reg(i)) { + if (reg(i)) { set_reg(i, 0); } } @@ -130,14 +133,16 @@ namespace datalog { unsigned register_count() const { return m_registers.size(); } + bool get_register_annotation(reg_idx reg, std::string & res) const { return m_reg_annotation.find(reg, res); } + void set_register_annotation(reg_idx reg, std::string str) { m_reg_annotation.insert(reg, str); } - void report_big_relations(unsigned threshold, std::ostream & out); + void report_big_relations(unsigned threshold, std::ostream & out) const; }; @@ -208,7 +213,7 @@ namespace datalog { The newline character at the end should not be printed. */ - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const & ctx, std::ostream & out) const { out << ""; } /** @@ -216,7 +221,7 @@ namespace datalog { Each line must be prepended by \c indentation and ended by a newline character. */ - virtual void display_body_impl(rel_context & ctx, std::ostream & out, std::string indentation) const {} + virtual void display_body_impl(rel_context const & ctx, std::ostream & out, std::string indentation) const {} public: typedef execution_context::reg_type reg_type; typedef execution_context::reg_idx reg_idx; @@ -227,10 +232,10 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) = 0; - void display(rel_context & 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 & 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); /** @@ -256,6 +261,8 @@ namespace datalog { static instruction * mk_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col); static instruction * mk_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols); static instruction * mk_filter_interpreted(reg_idx reg, app_ref & condition); + static instruction * mk_filter_interpreted_and_project(reg_idx src, app_ref & condition, + unsigned col_cnt, const unsigned * removed_cols, reg_idx result); static instruction * mk_union(reg_idx src, reg_idx tgt, reg_idx delta); static instruction * mk_widen(reg_idx src, reg_idx tgt, reg_idx delta); static instruction * mk_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, @@ -329,10 +336,10 @@ namespace datalog { void make_annotations(execution_context & ctx); - void display(rel_context & 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 & 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_qe/dl_interval_relation.cpp b/src/muz/rel/dl_interval_relation.cpp similarity index 96% rename from src/muz_qe/dl_interval_relation.cpp rename to src/muz/rel/dl_interval_relation.cpp index 150ba1c78..c04269b02 100644 --- a/src/muz_qe/dl_interval_relation.cpp +++ b/src/muz/rel/dl_interval_relation.cpp @@ -21,6 +21,8 @@ Revision History: #include "optional.h" #include "ast_pp.h" #include "dl_interval_relation.h" +#include "dl_relation_manager.h" +#include "bool_rewriter.h" namespace datalog { @@ -30,8 +32,7 @@ namespace datalog { 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()), - m_bsimp(get_ast_manager()) { + m_arith(get_ast_manager()) { } bool interval_relation_plugin::can_handle_signature(const relation_signature & sig) { @@ -48,7 +49,7 @@ namespace datalog { return alloc(interval_relation, *this, s, true); } - relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { return alloc(interval_relation, *this, s, false); } @@ -99,11 +100,9 @@ namespace datalog { } class interval_relation_plugin::rename_fn : public convenient_relation_rename_fn { - interval_relation_plugin& m_plugin; public: - rename_fn(interval_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) - : convenient_relation_rename_fn(orig_sig, cycle_len, cycle), - m_plugin(p){ + 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) { @@ -120,7 +119,7 @@ namespace datalog { if(!check_kind(r)) { return 0; } - return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle); + return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); } interval interval_relation_plugin::unite(interval const& src1, interval const& src2) { @@ -194,11 +193,9 @@ namespace datalog { } class interval_relation_plugin::union_fn : public relation_union_fn { - interval_relation_plugin& m_plugin; bool m_is_widen; public: - union_fn(interval_relation_plugin& p, bool is_widen) : - m_plugin(p), + union_fn(bool is_widen) : m_is_widen(is_widen) { } @@ -223,7 +220,7 @@ namespace datalog { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } - return alloc(union_fn, *this, false); + return alloc(union_fn, false); } relation_union_fn * interval_relation_plugin::mk_widen_fn( @@ -232,7 +229,7 @@ namespace datalog { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } - return alloc(union_fn, *this, true); + return alloc(union_fn, true); } class interval_relation_plugin::filter_identical_fn : public relation_mutator_fn { @@ -321,6 +318,7 @@ namespace datalog { interval_relation::interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty): vector_relation(p, s, is_empty, interval(p.dep())) { + TRACE("interval_relation", tout << s.size() << "\n";); } void interval_relation::add_fact(const relation_fact & f) { @@ -374,7 +372,6 @@ namespace datalog { void interval_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) { @@ -405,7 +402,8 @@ namespace datalog { } } } - bsimp.mk_and(conjs.size(), conjs.c_ptr(), fml); + bool_rewriter br(m); + br.mk_and(conjs.size(), conjs.c_ptr(), fml); } diff --git a/src/muz_qe/dl_interval_relation.h b/src/muz/rel/dl_interval_relation.h similarity index 97% rename from src/muz_qe/dl_interval_relation.h rename to src/muz/rel/dl_interval_relation.h index 0ff05719e..703cb43c5 100644 --- a/src/muz_qe/dl_interval_relation.h +++ b/src/muz/rel/dl_interval_relation.h @@ -21,6 +21,8 @@ Revision History: #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" @@ -34,7 +36,6 @@ namespace datalog { v_dependency_manager m_dep; interval m_empty; arith_util m_arith; - basic_simplifier_plugin m_bsimp; class join_fn; class project_fn; @@ -105,6 +106,7 @@ namespace datalog { interval_relation_plugin& get_plugin() const; void filter_interpreted(app* cond); + virtual bool is_precise() const { return false; } private: diff --git a/src/muz/rel/dl_lazy_table.cpp b/src/muz/rel/dl_lazy_table.cpp new file mode 100644 index 000000000..ec97a4bf5 --- /dev/null +++ b/src/muz/rel/dl_lazy_table.cpp @@ -0,0 +1,467 @@ +/*++ +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_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..3c0b47367 --- /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* get_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.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; } + 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.get_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.get_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.get_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.get_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.get_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.get_ref()), + m_src(src.get_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_qe/dl_mk_explanations.cpp b/src/muz/rel/dl_mk_explanations.cpp similarity index 85% rename from src/muz_qe/dl_mk_explanations.cpp rename to src/muz/rel/dl_mk_explanations.cpp index b4683bdbe..60a10190a 100644 --- a/src/muz_qe/dl_mk_explanations.cpp +++ b/src/muz/rel/dl_mk_explanations.cpp @@ -20,11 +20,10 @@ Revision History: #include #include"ast_pp.h" -#include "ast_smt_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 { @@ -80,8 +79,8 @@ namespace datalog { virtual bool can_handle_signature(const relation_signature & s) { unsigned n=s.size(); - for(unsigned i=0; i(get_plugin().mk_empty(get_signature())); - if(empty()) { + if (empty()) { res->set_undefined(); } return res; } void display_explanation(app * expl, std::ostream & out) const { - if(expl) { + if (expl) { //TODO: some nice explanation output ast_smt_pp pp(get_plugin().get_ast_manager()); pp.display_expr_smt2(out, expl); @@ -251,13 +248,13 @@ namespace datalog { } virtual void display(std::ostream & out) const { - if(empty()) { + if (empty()) { out << "\n"; return; } unsigned sz = get_signature().size(); - for(unsigned i=0; i(plugin.mk_empty(get_result_signature())); - if(!r1.empty() && !r2.empty()) { + if (!r1.empty() && !r2.empty()) { res->m_empty = false; SASSERT(res->m_data.empty()); res->m_data.append(r1.m_data); @@ -319,10 +316,10 @@ namespace datalog { 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) { + if (&r1.get_plugin()!=this || &r2.get_plugin()!=this) { return 0; } - if(col_cnt!=0) { + if (col_cnt!=0) { return 0; } return alloc(join_fn, r1.get_signature(), r2.get_signature()); @@ -339,7 +336,7 @@ namespace datalog { explanation_relation_plugin & plugin = r.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); - if(!r.empty()) { + if (!r.empty()) { relation_fact proj_data = r.m_data; project_out_vector_columns(proj_data, m_removed_cols); res->assign_data(proj_data); @@ -350,7 +347,7 @@ namespace datalog { 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) { + if (&r.get_plugin()!=this) { return 0; } return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); @@ -367,7 +364,7 @@ namespace datalog { explanation_relation_plugin & plugin = r.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); - if(!r.empty()) { + if (!r.empty()) { relation_fact permutated_data = r.m_data; permutate_by_cycle(permutated_data, m_cycle); res->assign_data(permutated_data); @@ -391,16 +388,16 @@ namespace datalog { 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())) { + if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { UNREACHABLE(); } - if(src.empty()) { + if (src.empty()) { return; } - if(plugin.m_relation_level_explanations) { + if (plugin.m_relation_level_explanations) { tgt.unite_with_data(src.m_data); - if(delta) { - if(!m_delta_union_fun) { + if (delta) { + if (!m_delta_union_fun) { m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src); SASSERT(m_delta_union_fun); } @@ -408,9 +405,9 @@ namespace datalog { } } else { - if(tgt.empty()) { + if (tgt.empty()) { tgt.assign_data(src.m_data); - if(delta && delta->empty()) { + if (delta && delta->empty()) { delta->assign_data(src.m_data); } } @@ -425,11 +422,11 @@ namespace datalog { explanation_relation & tgt = static_cast(tgt0); explanation_relation * delta = delta0 ? static_cast(delta0) : 0; - if(src.empty()) { + if (src.empty()) { return; } tgt.set_undefined(); - if(delta) { + if (delta) { delta->set_undefined(); } } @@ -437,10 +434,10 @@ namespace datalog { 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))) { + if (!check_kind(tgt) || (delta && !check_kind(*delta))) { return 0; } - if(!check_kind(src)) { + if (!check_kind(src)) { //this is to handle the product relation return alloc(foreign_union_fn); } @@ -462,7 +459,7 @@ namespace datalog { virtual void operator()(relation_base & r0) { explanation_relation & r = static_cast(r0); - if(!r.is_undefined(m_col_idx)) { + if (!r.is_undefined(m_col_idx)) { UNREACHABLE(); } @@ -470,7 +467,7 @@ namespace datalog { 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)) { + if (is_var(arg2)) { std::swap(arg1, arg2); } - if(!is_var(arg1) || !is_app(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())) { + if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { return 0; } unsigned col_idx = col_var->get_idx(); @@ -513,7 +510,7 @@ namespace datalog { 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()) { + if (!neg.empty()) { r.reset(); } } @@ -522,43 +519,42 @@ namespace datalog { 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) { + 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 { - explanation_relation_plugin & m_plugin; func_decl_ref m_union_decl; public: intersection_filter_fn(explanation_relation_plugin & plugin) - : m_plugin(plugin), m_union_decl(plugin.m_union_decl) {} + : 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()) { + if (src.empty()) { tgt.reset(); return; } - if(tgt.empty()) { + 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) { + if (curr_tgt->get_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; } @@ -572,18 +568,18 @@ namespace datalog { 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) { + 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() + 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)) { + 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); @@ -597,23 +593,23 @@ namespace datalog { // ----------------------------------- - mk_explanations::mk_explanations(context & ctx, bool relation_level) - : plugin(50000), - m_manager(ctx.get_manager()), - m_context(ctx), - m_decl_util(ctx.get_decl_util()), - m_relation_level(relation_level), - m_pinned(m_manager) { + 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(relation_level); + 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, relation_level, rmgr); + 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) { + if (!m_relation_level) { DEBUG_CODE( finite_product_relation_plugin * dummy; SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); @@ -622,7 +618,7 @@ namespace datalog { } } DEBUG_CODE( - if(!m_relation_level) { + if (!m_relation_level) { finite_product_relation_plugin * dummy; SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); } @@ -640,7 +636,7 @@ namespace datalog { void mk_explanations::assign_rel_level_kind(func_decl * e_decl, func_decl * orig) { SASSERT(m_relation_level); - relation_manager & rmgr = m_context.get_rel_context().get_rmanager(); + relation_manager & rmgr = m_context.get_rel_context()->get_rmanager(); unsigned sz = e_decl->get_arity(); relation_signature sig; rmgr.from_predicate(e_decl, sig); @@ -670,7 +666,7 @@ namespace datalog { 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) { + 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); @@ -679,7 +675,7 @@ namespace datalog { m_pinned.push_back(new_decl); e->get_data().m_value = new_decl; - if(m_relation_level) { + if (m_relation_level) { assign_rel_level_kind(new_decl, orig_decl); } } @@ -708,8 +704,8 @@ namespace datalog { } rule * mk_explanations::get_e_rule(rule * r) { - var_counter ctr; - ctr.count_vars(m_manager, 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++; @@ -718,13 +714,13 @@ namespace datalog { 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)); } @@ -732,9 +728,9 @@ namespace datalog { 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)); } @@ -756,37 +752,32 @@ namespace datalog { 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 & orig, rule_set & tgt) { - rule_set::iterator rit = orig.begin(); - rule_set::iterator rend = orig.end(); - for(; rit!=rend; ++rit) { + 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); - tgt.add_rule(e_rule); + 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 = m_original_preds.begin(); - decl_set::iterator pend = m_original_preds.end(); - for(; pit!=pend; ++pit) { + 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; - if(!m_context.is_output_predicate(orig_decl)) { - continue; - } - 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() }; - tgt.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0)); + 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, @@ -801,7 +792,7 @@ namespace datalog { sieve_relation * srels[] = { static_cast(&prod_rel[0]), static_cast(&prod_rel[1]) }; - if(&srels[0]->get_inner().get_plugin()==m_er_plugin) { + 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()); @@ -823,10 +814,9 @@ namespace datalog { } } - void mk_explanations::transform_facts(relation_manager & rmgr) { + void mk_explanations::transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst) { - - if(!m_e_fact_relation) { + if (!m_e_fact_relation) { relation_signature expl_singleton_sig; expl_singleton_sig.push_back(m_e_sort); @@ -838,29 +828,26 @@ namespace datalog { SASSERT(&expl_singleton->get_plugin()==m_er_plugin); m_e_fact_relation = static_cast(expl_singleton); } - - - - decl_set::iterator it = m_original_preds.begin(); - decl_set::iterator end = m_original_preds.end(); - for(; it!=end; ++it) { + 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(m_context.is_output_predicate(orig_decl)) { - m_context.set_output_predicate(e_decl); - } - - if(!rmgr.try_get_relation(orig_decl)) { - //there are no facts for this predicate + 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) { + + if (m_relation_level) { translate_rel_level_relation(rmgr, orig_rel, e_rel); } else { @@ -871,20 +858,19 @@ namespace datalog { SASSERT(union_fun); (*union_fun)(e_rel, *aux_extended_rel); } - } } - rule_set * mk_explanations::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - SASSERT(!mc && !pc); - if(source.get_num_rules()==0) { + rule_set * mk_explanations::operator()(rule_set const & source) { + + if (source.empty()) { + return 0; + } + if (!m_context.generate_explanations()) { return 0; } - - m_context.collect_predicates(m_original_preds); - rule_set * res = alloc(rule_set, m_context); - transform_facts(m_context.get_rel_context().get_rmanager()); + transform_facts(m_context.get_rel_context()->get_rmanager(), source, *res); transform_rules(source, *res); return res; } diff --git a/src/muz_qe/dl_mk_explanations.h b/src/muz/rel/dl_mk_explanations.h similarity index 81% rename from src/muz_qe/dl_mk_explanations.h rename to src/muz/rel/dl_mk_explanations.h index 36fb1a3a1..9e4d705c3 100644 --- a/src/muz_qe/dl_mk_explanations.h +++ b/src/muz/rel/dl_mk_explanations.h @@ -32,19 +32,13 @@ namespace datalog { typedef obj_map decl_map; - ast_manager & m_manager; - context & m_context; + ast_manager & m_manager; + context & m_context; dl_decl_util & m_decl_util; - - bool m_relation_level; - - decl_set m_original_preds; - + bool m_relation_level; ast_ref_vector m_pinned; - explanation_relation_plugin * m_er_plugin; - - sort * m_e_sort; + sort * m_e_sort; scoped_rel m_e_fact_relation; decl_map m_e_decl_map; @@ -62,15 +56,15 @@ namespace datalog { 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 & orig, rule_set & tgt); + void transform_rules(const rule_set & src, rule_set & dst); - void transform_facts(relation_manager & rmgr); + 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, bool relation_level); + mk_explanations(context & ctx); /** \brief Return explanation predicate that corresponds to \c orig_decl. @@ -82,7 +76,7 @@ namespace datalog { return get_union_decl(m_context); } - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); static expr* get_explanation(relation_base const& r); }; diff --git a/src/muz_qe/dl_mk_partial_equiv.cpp b/src/muz/rel/dl_mk_partial_equiv.cpp similarity index 94% rename from src/muz_qe/dl_mk_partial_equiv.cpp rename to src/muz/rel/dl_mk_partial_equiv.cpp index 367a15743..d79a46720 100644 --- a/src/muz_qe/dl_mk_partial_equiv.cpp +++ b/src/muz/rel/dl_mk_partial_equiv.cpp @@ -18,12 +18,13 @@ 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_head()->get_decl(); + func_decl* p = r->get_decl(); return p->get_arity() == 2 && p->get_domain(0) == p->get_domain(1) && @@ -38,7 +39,7 @@ namespace datalog { bool mk_partial_equivalence_transformer::is_transitivity(rule const* r) { - func_decl* p = r->get_head()->get_decl(); + func_decl* p = r->get_decl(); if (p->get_arity() != 2 || p->get_domain(0) != p->get_domain(1) || r->get_tail_size() != 2 || @@ -86,8 +87,8 @@ namespace datalog { } - rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source) { + // TODO mc if (source.get_num_rules() == 0) { return 0; @@ -97,7 +98,7 @@ namespace datalog { return 0; } - relation_manager & rm = m_context.get_rel_context().get_rmanager(); + relation_manager & rm = m_context.get_rel_context()->get_rmanager(); rule_set::decl2rules::iterator it = source.begin_grouped_rules(); rule_set::decl2rules::iterator end = source.end_grouped_rules(); @@ -144,6 +145,7 @@ namespace datalog { dealloc(res); return 0; } + res->inherit_predicates(source); return res; } diff --git a/src/muz_qe/dl_mk_partial_equiv.h b/src/muz/rel/dl_mk_partial_equiv.h similarity index 89% rename from src/muz_qe/dl_mk_partial_equiv.h rename to src/muz/rel/dl_mk_partial_equiv.h index 8fef4aea9..54a70b3c0 100644 --- a/src/muz_qe/dl_mk_partial_equiv.h +++ b/src/muz/rel/dl_mk_partial_equiv.h @@ -35,7 +35,7 @@ namespace datalog { m(ctx.get_manager()), m_context(ctx) {} - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); private: diff --git a/src/muz_qe/dl_mk_similarity_compressor.cpp b/src/muz/rel/dl_mk_similarity_compressor.cpp similarity index 73% rename from src/muz_qe/dl_mk_similarity_compressor.cpp rename to src/muz/rel/dl_mk_similarity_compressor.cpp index 42a5ba367..aa2fe8ab9 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.cpp +++ b/src/muz/rel/dl_mk_similarity_compressor.cpp @@ -20,19 +20,21 @@ 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, unsigned threshold_count) : - plugin(5000), - m_context(ctx), - m_manager(ctx.get_manager()), - m_threshold_count(threshold_count), - m_result_rules(ctx.get_rule_manager()), - m_pinned(m_manager) { - SASSERT(threshold_count>1); + 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(); @@ -42,51 +44,57 @@ namespace datalog { /** Allows to traverse head and positive tails in a single for loop starting from -1 */ - app * get_by_tail_index(rule * r, int idx) { - if(idx==-1) { + static app * get_by_tail_index(rule * r, int idx) { + if (idx < 0) { return r->get_head(); } - SASSERT(idx(r->get_positive_tail_size())); + SASSERT(idx < static_cast(r->get_positive_tail_size())); return r->get_tail(idx); } template - int aux_compare(T a, T b) { + static int aux_compare(T a, T b) { return (a>b) ? 1 : ( (a==b) ? 0 : -1); } - int compare_var_args(app* t1, app* t2) { + 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; iget_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)) { + 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; } + if (res != 0) { + return res; + } } } return 0; } - int compare_args(app* t1, app* t2, int & skip_countdown) { + 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)); + for (unsigned i=0; iget_arg(i))) { + SASSERT(t1->get_arg(i) == t2->get_arg(i)); continue; } - if((skip_countdown--)==0) { + if ((skip_countdown--) == 0) { continue; } - res = aux_compare(t1->get_arg(i), t2->get_arg(i)); - if(res!=0) { return res; } + res = aux_compare(t1->get_arg(i)->get_id(), t2->get_arg(i)->get_id()); + if (res!=0) { return res; } } return 0; } @@ -98,28 +106,28 @@ namespace datalog { Two rules are in the same rough similarity class if they differ only in constant arguments of positive uninterpreted predicates. */ - int rough_compare(rule * r1, rule * r2) { + 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; } + if (res!=0) { return res; } res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size()); - if(res!=0) { return res; } + if (res!=0) { return res; } res = aux_compare(r1->get_positive_tail_size(), r2->get_positive_tail_size()); - if(res!=0) { return res; } + if (res!=0) { return res; } int pos_tail_sz = r1->get_positive_tail_size(); - for(int i=-1; iget_decl(), t2->get_decl()); - if(res!=0) { return res; } + res = aux_compare(t1->get_decl()->get_id(), t2->get_decl()->get_id()); + if (res!=0) { return res; } res = compare_var_args(t1, t2); - if(res!=0) { return res; } + if (res!=0) { return res; } } unsigned tail_sz = r1->get_tail_size(); - for(unsigned i=pos_tail_sz; iget_tail(i), r2->get_tail(i)); - if(res!=0) { return res; } + for (unsigned i=pos_tail_sz; iget_tail(i)->get_id(), r2->get_tail(i)->get_id()); + if (res!=0) { return res; } } return 0; @@ -129,12 +137,12 @@ namespace datalog { \c r1 and \c r2 must be equal according to the \c rough_compare function for this function to be called. */ - int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) { + 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; - void collect_const_indexes(app * t, int tail_index, info_vector & res) { + 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))) { + for (unsigned i=0; iget_arg(i))) { continue; } res.push_back(const_info(tail_index, i)); } } - void collect_const_indexes(rule * r, info_vector & res) { + 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 - void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) { + 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; i - void collect_orphan_sorts(rule * r, const info_vector & const_infos, T & tgt) { + 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; i1); unsigned const_cnt = const_infos.size(); @@ -224,25 +232,25 @@ namespace datalog { 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) { + if (vals[i]!=val) { vals[i] = 0; } } } unsigned removed_cnt = 0; - for(unsigned i=0; i 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; } - bool initial_comparator(rule * r1, rule * r2) { + static bool initial_comparator(rule * r1, rule * r2) { int res = rough_compare(r1, r2); - if(res!=0) { return res>0; } + if (res!=0) { return res>0; } return total_compare(r1, r2)>0; } @@ -348,7 +356,7 @@ namespace datalog { ptr_vector aux_domain; collect_orphan_sorts(r, const_infos, aux_domain); - func_decl* head_pred = r->get_head()->get_decl(); + 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()), @@ -357,25 +365,25 @@ namespace datalog { relation_fact val_fact(m_manager, const_cnt); rule_vector::iterator it = first; - for(; it!=after_last; ++it) { + 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); + 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)); } - var_counter var_ctr; - var_ctr.count_vars(m_manager, r); + rule_counter ctr; + ctr.count_rule_vars(m_manager, r); unsigned max_var_idx, new_var_idx_base; - if(var_ctr.get_max_positive(max_var_idx)) { + if (ctr.get_max_positive(max_var_idx)) { new_var_idx_base = max_var_idx+1; } else { @@ -387,15 +395,15 @@ namespace datalog { unsigned aux_column_index = 0; - for(unsigned i=0; i mod_args(mod_tail->get_num_args(), mod_tail->get_args()); - for(; im_threshold_count) { + if (it==after_last || !comparator.eq(*prev, *it)) { + if (grp_size>m_threshold_count) { merge_class(grp_begin, it); //group was processed, so we remove it from the class - if(it==after_last) { + if (it==after_last) { after_last=grp_begin; it=after_last; } @@ -484,9 +492,9 @@ namespace datalog { //TODO: compress also rules with pairs (or tuples) of equal constants #if 1 - if(const_cnt>0) { + 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) { + if (rule_cnt>m_threshold_count) { merge_class(first, after_last); return; } @@ -495,17 +503,17 @@ namespace datalog { //put rules which weren't merged into result rule_vector::iterator it = first; - for(; it!=after_last; ++it) { + for (; it!=after_last; ++it) { m_result_rules.push_back(*it); } } - rule_set * mk_similarity_compressor::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + 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) { + 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_qe/dl_mk_similarity_compressor.h b/src/muz/rel/dl_mk_similarity_compressor.h similarity index 84% rename from src/muz_qe/dl_mk_similarity_compressor.h rename to src/muz/rel/dl_mk_similarity_compressor.h index ad4a5e246..34b76e7e1 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.h +++ b/src/muz/rel/dl_mk_similarity_compressor.h @@ -63,13 +63,13 @@ namespace datalog { ast_ref_vector m_pinned; void merge_class(rule_vector::iterator first, rule_vector::iterator after_last); - void process_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, unsigned threshold_count); + mk_similarity_compressor(context & ctx); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp similarity index 94% rename from src/muz_qe/dl_mk_simple_joins.cpp rename to src/muz/rel/dl_mk_simple_joins.cpp index 363a4f0f7..4b9ce582a 100644 --- a/src/muz_qe/dl_mk_simple_joins.cpp +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -21,6 +21,7 @@ Revision History: #include #include #include"dl_mk_simple_joins.h" +#include"dl_relation_manager.h" #include"ast_pp.h" #include"trace.h" @@ -29,7 +30,8 @@ namespace datalog { mk_simple_joins::mk_simple_joins(context & ctx): plugin(1000), - m_context(ctx) { + m_context(ctx), + rm(ctx.get_rule_manager()) { } class join_planner { @@ -89,7 +91,7 @@ namespace datalog { m_consumers++; } if(m_stratified) { - unsigned head_stratum = pl.get_stratum(r->get_head()->get_decl()); + unsigned head_stratum = pl.get_stratum(r->get_decl()); SASSERT(head_stratum>=m_src_stratum); if(head_stratum==m_src_stratum) { m_stratified = false; @@ -120,6 +122,7 @@ namespace datalog { 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 @@ -130,10 +133,13 @@ namespace datalog { 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()), m_var_subst(ctx.get_var_subst()), + : 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()) @@ -175,9 +181,7 @@ namespace datalog { unsigned max_var_idx = 0; { - var_idx_set orig_var_set; - collect_vars(m, t1, orig_var_set); - collect_vars(m, t2, orig_var_set); + 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) { @@ -310,8 +314,8 @@ namespace datalog { } void register_rule(rule * r) { - var_counter counter; - counter.count_vars(m, r, 1); + 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; @@ -323,14 +327,13 @@ namespace datalog { } for(unsigned i=0; iget_tail(i); - var_idx_set t1_vars; - collect_vars(m, t1, t1_vars); + 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(t1_vars); - collect_vars(m, t2, scope_vars); + 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 @@ -383,7 +386,7 @@ namespace datalog { rule * one_parent = inf.m_rules.back(); - func_decl* parent_head = one_parent->get_head()->get_decl(); + 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) { @@ -472,8 +475,7 @@ namespace datalog { while(!added_tails.empty()) { app * a_tail = added_tails.back(); //added tail - var_idx_set a_tail_vars; - collect_vars(m, a_tail, a_tail_vars); + var_idx_set a_tail_vars = rm.collect_vars(a_tail); counter.count_vars(m, a_tail, -1); //temporarily remove a_tail variables from counter for(unsigned i=0; iget_decl(); unsigned n=pred->get_arity(); - relation_manager& rm = m_context.get_rel_context().get_rmanager(); + 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 = m_context.get_rel_context().get_relation(pred).get_size_estimate_rows(); + unsigned rel_size_int = rel->get_relation(pred).get_size_estimate_rows(); if(rel_size_int!=0) { cost rel_size = static_cast(rel_size_int); cost curr_size = rel_size; @@ -706,21 +712,22 @@ namespace datalog { 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()) { + 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, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_simple_joins::operator()(rule_set const & source) { rule_set rs_aux_copy(m_context); - rs_aux_copy.add_rules(source); + rs_aux_copy.replace_rules(source); if(!rs_aux_copy.is_closed()) { rs_aux_copy.close(); } diff --git a/src/muz_qe/dl_mk_simple_joins.h b/src/muz/rel/dl_mk_simple_joins.h similarity index 89% rename from src/muz_qe/dl_mk_simple_joins.h rename to src/muz/rel/dl_mk_simple_joins.h index 5431c4117..36eb08dd5 100644 --- a/src/muz_qe/dl_mk_simple_joins.h +++ b/src/muz/rel/dl_mk_simple_joins.h @@ -49,11 +49,12 @@ namespace datalog { 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; + context & m_context; + rule_manager & rm; public: mk_simple_joins(context & ctx); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_product_relation.cpp b/src/muz/rel/dl_product_relation.cpp similarity index 98% rename from src/muz_qe/dl_product_relation.cpp rename to src/muz/rel/dl_product_relation.cpp index 66074d463..48cd666e6 100644 --- a/src/muz_qe/dl_product_relation.cpp +++ b/src/muz/rel/dl_product_relation.cpp @@ -174,8 +174,7 @@ namespace datalog { relation_base * product_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { rel_spec spec; - relation_signature sig_empty; - m_spec_store.get_relation_spec(sig_empty, kind, spec); + m_spec_store.get_relation_spec(s, kind, spec); relation_vector inner_rels; unsigned rel_cnt = spec.size(); for(unsigned i=0; i(x); } + }; - rel_spec_store m_spec_store; + rel_spec_store > m_spec_store; family_id get_relation_kind(const product_relation & r); @@ -114,6 +119,7 @@ namespace datalog { friend class product_relation_plugin::filter_interpreted_fn; + typedef product_relation_plugin::rel_spec rel_spec; /** @@ -168,6 +174,15 @@ namespace datalog { 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; + } }; }; diff --git a/src/muz_qe/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp similarity index 91% rename from src/muz_qe/dl_relation_manager.cpp rename to src/muz/rel/dl_relation_manager.cpp index 76d538b08..9421b26df 100644 --- a/src/muz_qe/dl_relation_manager.cpp +++ b/src/muz/rel/dl_relation_manager.cpp @@ -80,12 +80,6 @@ namespace datalog { if(m_pred_kinds.find(pred, res)) { return res; } - //This is commented out as the favourite relation might not be suitable for all - //signatures. In the cases where it is suitable, it will be used anyway if we - //now return null_family_id. - //else if (m_favourite_relation_plugin) { - // return m_favourite_relation_plugin->get_kind(); - //} else { return null_family_id; } @@ -115,7 +109,7 @@ namespace datalog { void relation_manager::store_relation(func_decl * pred, relation_base * rel) { SASSERT(rel); relation_map::entry * e = m_relations.insert_if_not_there2(pred, 0); - if(e->get_data().m_value) { + if (e->get_data().m_value) { e->get_data().m_value->deallocate(); } else { @@ -124,14 +118,6 @@ namespace datalog { e->get_data().m_value = rel; } - void relation_manager::collect_predicates(decl_set & res) const { - relation_map::iterator it = m_relations.begin(); - relation_map::iterator end = m_relations.end(); - for(; it!=end; ++it) { - res.insert(it->m_key); - } - } - 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(); @@ -150,7 +136,7 @@ namespace datalog { relation_map::iterator rend = m_relations.end(); for(; rit!=rend; ++rit) { func_decl * pred = rit->m_key; - if(!preds.contains(pred)) { + if (!preds.contains(pred)) { to_remove.insert(pred); } } @@ -160,7 +146,7 @@ namespace datalog { for(; pit!=pend; ++pit) { func_decl * pred = *pit; relation_base * rel; - TRUSTME( m_relations.find(pred, rel) ); + VERIFY( m_relations.find(pred, rel) ); rel->deallocate(); m_relations.remove(pred); get_context().get_manager().dec_ref(pred); @@ -181,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(); @@ -192,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) { @@ -243,15 +234,14 @@ namespace datalog { relation_plugin & relation_manager::get_appropriate_plugin(const relation_signature & s) { relation_plugin * res = try_get_appropriate_plugin(s); - if(!res) { + if (!res) { throw default_exception("no suitable plugin found for given relation signature"); - throw 0; } 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)) { + 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(); @@ -287,7 +277,7 @@ namespace datalog { SASSERT(kind>=0); SASSERT(kindmk_empty(s); } - if(mk_empty_table_relation(s, res)) { + if (mk_empty_table_relation(s, res)) { return res; } @@ -397,7 +387,7 @@ namespace datalog { void relation_manager::relation_to_table(const relation_sort & sort, const relation_element & from, table_element & to) { SASSERT(from->get_num_args()==0); - TRUSTME(get_context().get_decl_util().is_numeral_ext(from, to)); + VERIFY(get_context().get_decl_util().is_numeral_ext(from, to)); } void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, @@ -469,6 +459,12 @@ namespace datalog { } } + void relation_manager::set_cancel(bool f) { + for (unsigned i = 0; i < m_relation_plugins.size(); ++i) { + m_relation_plugins[i]->set_cancel(f); + } + } + std::string relation_manager::to_nice_string(const relation_element & el) const { uint64 val; std::stringstream stm; @@ -534,8 +530,8 @@ namespace datalog { } } - void relation_manager::display_output_tables(std::ostream & out) const { - const decl_set & output_preds = get_context().get_output_predicates(); + 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) { @@ -586,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); } @@ -617,6 +614,60 @@ namespace datalog { 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; @@ -723,7 +774,6 @@ namespace datalog { 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; @@ -743,7 +793,6 @@ namespace datalog { 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); - TRACE("dl", tout << t.get_plugin().get_name() << " " << value << " " << col << "\n";); if(!res) { relation_mutator_fn * selector = mk_filter_equal_fn(t, value, col); if(selector) { @@ -884,10 +933,10 @@ namespace datalog { 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(); + 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(); @@ -1093,12 +1142,10 @@ namespace datalog { class relation_manager::default_table_rename_fn : public convenient_table_rename_fn, auxiliary_table_transformer_fn { - const unsigned m_cycle_len; 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), - m_cycle_len(permutation_cycle_len) { + : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { SASSERT(permutation_cycle_len>=2); } @@ -1393,6 +1440,45 @@ namespace datalog { } + 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, @@ -1458,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_qe/dl_relation_manager.h b/src/muz/rel/dl_relation_manager.h similarity index 95% rename from src/muz_qe/dl_relation_manager.h rename to src/muz/rel/dl_relation_manager.h index ecba3d598..2c148c5e6 100644 --- a/src/muz_qe/dl_relation_manager.h +++ b/src/muz/rel/dl_relation_manager.h @@ -22,7 +22,6 @@ Revision History: #include"map.h" #include"vector.h" - #include"dl_base.h" namespace datalog { @@ -35,14 +34,15 @@ namespace datalog { class finite_product_relation_plugin; class sieve_relation; class sieve_relation_plugin; + class rule_set; - typedef hashtable, ptr_eq > decl_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; @@ -56,6 +56,7 @@ namespace datalog { 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; @@ -153,7 +154,6 @@ namespace datalog { } } - void collect_predicates(decl_set & res) const; void collect_non_empty_predicates(decl_set & res) const; void restrict_predicates(const decl_set & preds); @@ -223,6 +223,7 @@ namespace datalog { relation_fact & to); + void set_cancel(bool f); // ----------------------------------- @@ -351,6 +352,9 @@ namespace datalog { 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. @@ -523,6 +527,9 @@ namespace datalog { 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. @@ -557,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. @@ -594,7 +614,7 @@ namespace datalog { void display(std::ostream & out) const; void display_relation_sizes(std::ostream & out) const; - void display_output_tables(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, @@ -606,7 +626,7 @@ namespace datalog { /** This is a helper class for relation_plugins whose relations can be of various kinds. */ - template, class Eq=vector_eq_proc > + template > class rel_spec_store { typedef relation_signature::hash r_hash; typedef relation_signature::eq r_eq; @@ -662,17 +682,15 @@ namespace datalog { 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; - VERIFY( m_kind_specs.find(sig, idspecs) ); - VERIFY( idspecs->find(kind, spec) ); + family_id2spec * idspecs = m_kind_specs.find(sig); + spec = idspecs->find(kind); } }; diff --git a/src/muz_qe/dl_sieve_relation.cpp b/src/muz/rel/dl_sieve_relation.cpp similarity index 99% rename from src/muz_qe/dl_sieve_relation.cpp rename to src/muz/rel/dl_sieve_relation.cpp index c3ea5a3d0..9f9419089 100644 --- a/src/muz_qe/dl_sieve_relation.cpp +++ b/src/muz/rel/dl_sieve_relation.cpp @@ -158,7 +158,7 @@ namespace datalog { inner_sig_singleton.push_back(s[i]); inner_columns[i] = inner.can_handle_signature(inner_sig_singleton); } -#if Z3DEBUG +#if Z3DEBUG //we assume that if a relation plugin can handle two sets of columns separetely, //it can also handle them in one relation relation_signature inner_sig; @@ -246,7 +246,8 @@ namespace datalog { relation_base * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { relation_signature empty_sig; - relation_base * inner = get_manager().mk_full_relation(empty_sig, p, null_family_id); + 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); @@ -567,8 +568,7 @@ namespace datalog { const relation_signature sig = r.get_signature(); unsigned sz = sig.size(); - var_idx_set cond_vars; - collect_vars(m, condition, cond_vars); + 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; diff --git a/src/muz_qe/dl_sieve_relation.h b/src/muz/rel/dl_sieve_relation.h similarity index 98% rename from src/muz_qe/dl_sieve_relation.h rename to src/muz/rel/dl_sieve_relation.h index d6df3af55..48402cd6d 100644 --- a/src/muz_qe/dl_sieve_relation.h +++ b/src/muz/rel/dl_sieve_relation.h @@ -21,6 +21,7 @@ Revision History: #define _DL_SIEVE_RELATION_H_ #include "dl_context.h" +#include "dl_relation_manager.h" namespace datalog { @@ -52,7 +53,7 @@ namespace datalog { struct hash { unsigned operator()(const rel_spec & s) const { - return int_vector_hash(s.m_inner_cols)^s.m_inner_kind; + return svector_hash()(s.m_inner_cols)^s.m_inner_kind; } }; }; diff --git a/src/muz_qe/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp similarity index 72% rename from src/muz_qe/dl_sparse_table.cpp rename to src/muz/rel/dl_sparse_table.cpp index 1449b7d3d..3b2dc333a 100644 --- a/src/muz_qe/dl_sparse_table.cpp +++ b/src/muz/rel/dl_sparse_table.cpp @@ -24,6 +24,7 @@ Revision History: namespace datalog { + // ----------------------------------- // // entry_storage @@ -33,7 +34,7 @@ namespace datalog { 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) { + if (m_reserve == entry_ofs) { //entry inserted, so reserve is no longer a reserve m_reserve = NO_RESERVE; } @@ -42,7 +43,7 @@ namespace datalog { 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) { + if (m_reserve == entry_ofs) { //entry inserted, so reserve is no longer a reserve m_reserve = NO_RESERVE; return true; @@ -53,7 +54,7 @@ namespace datalog { bool entry_storage::remove_reserve_content() { SASSERT(has_reserve()); store_offset entry_ofs; - if(!find_reserve_content(entry_ofs)) { + if (!find_reserve_content(entry_ofs)) { //the fact was not in the table return false; } @@ -64,8 +65,8 @@ namespace datalog { 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); + 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); @@ -73,7 +74,7 @@ namespace datalog { memcpy(base+ofs, base+last_ofs, m_entry_size); m_data_indexer.insert(ofs); } - if(has_reserve()) { + 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); } @@ -81,9 +82,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); } // ----------------------------------- @@ -98,20 +99,20 @@ namespace datalog { unsigned length = 0; unsigned dom_size_sm; - if(dom_size>UINT_MAX) { + 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 ) { + 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) { + if (dom_size_sm == 1) { length += 1; //unary domains } - else if(dom_size_sm>0x80000000u) { + else if (dom_size_sm > 0x80000000u) { length += 32; } else { @@ -122,30 +123,30 @@ namespace datalog { sparse_table::column_layout::column_layout(const table_signature & sig) : m_functional_col_cnt(sig.functional_columns()) { - SASSERT(sig.size()>0); + 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)) { + 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; + ofs += length; } make_byte_aligned_end(size()-1); - SASSERT(back().next_ofs()%8==0);//the entries must be aligned to whole bytes + 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); + 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 { @@ -156,9 +157,9 @@ namespace datalog { 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); + unsigned rounded_ofs = (ofs_bit_part == 0) ? ofs : (ofs+8-ofs_bit_part); - if(rounded_ofs!=ofs) { + if (rounded_ofs!=ofs) { SASSERT(rounded_ofs>ofs); int diff = rounded_ofs-ofs; unsigned col_idx = col_index0+1; @@ -168,18 +169,18 @@ namespace datalog { col_idx--; column_info & ci = (*this)[col_idx]; unsigned new_length = ci.m_length; - if(ci.m_length<64) { + if (ci.m_length < 64) { unsigned swallowed = std::min(64-static_cast(ci.m_length), diff); - diff-=swallowed; - new_length+=swallowed; + 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); + SASSERT(rounded_ofs%8 == 0); + SASSERT((*this)[col_index0].next_ofs()%8 == 0); } // ----------------------------------- @@ -218,7 +219,7 @@ namespace datalog { m_layout(t.m_column_layout) {} virtual bool is_finished() const { - return m_ptr==m_end; + return m_ptr == m_end; } virtual row_interface & operator*() { @@ -267,7 +268,7 @@ namespace datalog { 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(); } + bool empty() const { return begin() == end(); } }; key_indexer(unsigned key_len, const unsigned * key_cols) @@ -283,7 +284,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; @@ -299,7 +300,7 @@ namespace datalog { 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) { + if (!e) { TRACE("dl_table_relation", tout << "inserting\n";); e = m_map.insert_if_not_there2(ofs, offset_vector()); } @@ -312,7 +313,7 @@ namespace datalog { m_first_nonindexed(0) {} virtual void update(const sparse_table & t) { - if(m_first_nonindexed==t.m_data.after_last_offset()) { + if (m_first_nonindexed == t.m_data.after_last_offset()) { return; } SASSERT(m_first_nonindexedget_data().m_value; @@ -381,15 +382,15 @@ namespace datalog { 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) { + 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) { + 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); + SASSERT(ctr.get_positive_count() == non_func_cols); return true; } @@ -399,7 +400,7 @@ namespace datalog { SASSERT(can_handle(key_len, key_cols, t)); m_permutation.resize(key_len); - for(unsigned i=0; i(m_table); + sparse_table & t = const_cast(m_table); t.write_into_reserve(m_key_fact.c_ptr()); store_offset res; - if(!t.m_data.find_reserve_content(res)) { + if (!t.m_data.find_reserve_content(res)) { return query_result(); } return query_result(res); @@ -461,19 +462,21 @@ 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. //Maybe we might keep a list of indexes that contain functional columns and on an update reset //only those. - SASSERT(key_len==0 || + 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)) { + if (!key_map_entry->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 { @@ -488,7 +491,7 @@ namespace datalog { 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) { + for (; kmit!=kmend; ++kmit) { dealloc((*kmit).m_value); } m_key_indexes.reset(); @@ -499,13 +502,14 @@ namespace datalog { m_data.ensure_reserve(); char * reserve = m_data.get_reserve_ptr(); unsigned col_cnt = m_column_layout.size(); - for(unsigned i=0; i(*this); t.write_into_reserve(f.c_ptr()); unsigned func_col_cnt = get_signature().functional_columns(); - if(func_col_cnt==0) { + 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)) { + 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)) { + if (!t.m_data.find_reserve_content(ofs)) { return false; } unsigned sz = sig.size(); - for(unsigned i=sig.first_functional(); i(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) { + 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,12 +774,13 @@ namespace datalog { } void sparse_table_plugin::recycle(sparse_table * t) { + verbose_action _va("recycle", 2); 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) { + if (vect == 0) { vect = alloc(sp_table_vector); } IF_VERBOSE(12, verbose_stream() << "Recycle: " << t->get_size_estimate_bytes() << "\n";); @@ -776,7 +792,7 @@ namespace datalog { SASSERT(can_handle_signature(s)); sp_table_vector * vect; - if(!m_pool.find(s, vect) || vect->empty()) { + if (!m_pool.find(s, vect) || vect->empty()) { return alloc(sparse_table, *this, s); } sparse_table * res = vect->back(); @@ -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; } @@ -793,7 +809,7 @@ namespace datalog { 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) { + if (col_cnt == 0) { return false; } return counter().count(col_cnt, cols1).get_max_positive()>=s1.first_functional() @@ -813,18 +829,19 @@ 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 //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()) ) { + 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); } @@ -841,7 +858,7 @@ namespace datalog { 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() + 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; @@ -854,8 +871,8 @@ namespace datalog { 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() + 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. @@ -868,16 +885,16 @@ 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(); const char* after_last=src.m_data.after_last(); - for(; ptradd_fact(ptr); } } @@ -886,7 +903,7 @@ namespace datalog { 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() + 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())) { @@ -913,8 +930,8 @@ namespace datalog { const sparse_table::column_layout & tgt_layout) { unsigned r_idx=0; unsigned tgt_i=0; - for(unsigned i=0; i(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; 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) { + 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(); @@ -952,7 +970,7 @@ namespace datalog { 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()) { + if (col_cnt == t.get_signature().size()) { return 0; } return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); @@ -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; @@ -981,14 +1000,14 @@ namespace datalog { 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()) { + 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) { + 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); @@ -996,8 +1015,8 @@ namespace datalog { char * res_reserve = res->m_data.get_reserve_ptr(); unsigned res_i = 0; - for(unsigned i=0; i=t.get_signature().first_functional()) { + 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 @@ -1022,20 +1041,17 @@ namespace datalog { class sparse_table_plugin::rename_fn : public convenient_table_rename_fn { - const unsigned m_cycle_len; - const unsigned m_col_cnt; 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), - m_cycle_len(permutation_cycle_len), m_col_cnt(orig_sig.size()) { + : 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(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())); - 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"); + } 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(); 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) { + 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) { + for (size_t i = 0; i != res_data_size; i += res_fact_size) { TRUSTME(res->m_data.insert_offset(i)); } @@ -1094,7 +1114,7 @@ namespace datalog { 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()) { + if (t.get_kind()!=get_kind()) { return 0; } return alloc(rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); @@ -1112,17 +1132,18 @@ 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, 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); + m_joining_neg_non_functional = ctr.get_max_counter_value() == 1 + && ctr.get_positive_count() == neg_first_func + && (neg_first_func == 0 || ctr.get_max_positive() == neg_first_func-1); } /** @@ -1133,9 +1154,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(); @@ -1150,51 +1169,57 @@ namespace datalog { 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(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); } } } } - if(!tgt_is_first) { + if (!tgt_is_first) { //in this case \c res now may be in arbitrary order std::sort(res.begin(), res.end()); } } 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()) { + if (m_cols1.size() == 0) { + if (!neg.empty()) { tgt.reset(); } return; @@ -1204,19 +1229,16 @@ namespace datalog { //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()) { + 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()) { + while (!to_remove.empty()) { store_offset removed_ofs = to_remove.back(); to_remove.pop_back(); tgt.m_data.remove_offset(removed_ofs); @@ -1229,7 +1251,7 @@ namespace datalog { 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) + 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; @@ -1237,6 +1259,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_qe/dl_sparse_table.h b/src/muz/rel/dl_sparse_table.h similarity index 94% rename from src/muz_qe/dl_sparse_table.h rename to src/muz/rel/dl_sparse_table.h index 3920836e6..0222aa6e2 100644 --- a/src/muz_qe/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 storage; + typedef svector storage; class offset_hash_proc { storage & m_storage; @@ -130,7 +145,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 +229,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,8 +288,11 @@ 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"); + } m_data.resize(sz + sizeof(uint64)); } @@ -359,7 +377,7 @@ namespace datalog { typedef svector key_spec; //sequence of columns in a key typedef svector key_value; //values of key columns - typedef map, + typedef map, vector_eq_proc > key_index_map; static const store_offset NO_RESERVE = UINT_MAX; diff --git a/src/muz_qe/dl_table.cpp b/src/muz/rel/dl_table.cpp similarity index 99% rename from src/muz_qe/dl_table.cpp rename to src/muz/rel/dl_table.cpp index 99a868bea..0b8fc0388 100644 --- a/src/muz_qe/dl_table.cpp +++ b/src/muz/rel/dl_table.cpp @@ -20,6 +20,7 @@ Revision History: #include"dl_context.h" #include"dl_util.h" #include"dl_table.h" +#include"dl_relation_manager.h" namespace datalog { diff --git a/src/muz_qe/dl_table.h b/src/muz/rel/dl_table.h similarity index 99% rename from src/muz_qe/dl_table.h rename to src/muz/rel/dl_table.h index 8dc0a355b..3a240c337 100644 --- a/src/muz_qe/dl_table.h +++ b/src/muz/rel/dl_table.h @@ -73,7 +73,7 @@ namespace datalog { class our_iterator_core; - typedef hashtable, + typedef hashtable, vector_eq_proc > storage; storage m_data; diff --git a/src/muz_qe/dl_table_plugin.h b/src/muz/rel/dl_table_plugin.h similarity index 100% rename from src/muz_qe/dl_table_plugin.h rename to src/muz/rel/dl_table_plugin.h diff --git a/src/muz_qe/dl_table_relation.cpp b/src/muz/rel/dl_table_relation.cpp similarity index 95% rename from src/muz_qe/dl_table_relation.cpp rename to src/muz/rel/dl_table_relation.cpp index fc661366d..364c29367 100644 --- a/src/muz_qe/dl_table_relation.cpp +++ b/src/muz/rel/dl_table_relation.cpp @@ -39,16 +39,15 @@ namespace datalog { 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); + return + get_manager().relation_signature_to_table(s, tsig) && + 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)) { + if (!get_manager().relation_signature_to_table(s, tsig)) { return 0; } table_base * t = m_table_plugin.mk_empty(tsig); @@ -92,6 +91,7 @@ namespace datalog { TRACE("dl_table_relation", tout << "# join => "; 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. @@ -354,6 +354,21 @@ namespace datalog { 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: diff --git a/src/muz_qe/dl_table_relation.h b/src/muz/rel/dl_table_relation.h similarity index 96% rename from src/muz_qe/dl_table_relation.h rename to src/muz/rel/dl_table_relation.h index 12f0d4eaa..f86143c0f 100644 --- a/src/muz_qe/dl_table_relation.h +++ b/src/muz/rel/dl_table_relation.h @@ -71,6 +71,8 @@ namespace datalog { virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + 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, diff --git a/src/muz_qe/dl_vector_relation.h b/src/muz/rel/dl_vector_relation.h similarity index 100% rename from src/muz_qe/dl_vector_relation.h rename to src/muz/rel/dl_vector_relation.h 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_ */ + diff --git a/src/muz_qe/rel_context.cpp b/src/muz/rel/rel_context.cpp similarity index 61% rename from src/muz_qe/rel_context.cpp rename to src/muz/rel/rel_context.cpp index 1b4042ab9..02b00be39 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz/rel/rel_context.cpp @@ -18,6 +18,8 @@ Revision History: Extracted from dl_context --*/ + + #include"rel_context.h" #include"dl_context.h" #include"dl_compiler.h" @@ -27,38 +29,90 @@ Revision History: #include"dl_product_relation.h" #include"dl_bound_relation.h" #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" -#ifndef _EXTERNAL_RELEASE -#include"dl_skip_table.h" -#endif +#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" +#include"dl_mk_separate_negated_tails.h" +#include"fixedpoint_params.hpp" + namespace datalog { - + + class rel_context::scoped_query { + context& m_ctx; + rule_set m_rules; + decl_set m_preds; + bool m_was_closed; + + public: + + scoped_query(context& ctx): + m_ctx(ctx), + m_rules(ctx.get_rules()), + m_preds(ctx.get_predicates()), + m_was_closed(ctx.is_closed()) + { + if (m_was_closed) { + ctx.reopen(); + } + } + + ~scoped_query() { + m_ctx.reopen(); + m_ctx.restrict_predicates(m_preds); + m_ctx.replace_rules(m_rules); + if (m_was_closed) { + m_ctx.close(); + } + } + + void reset() { + m_ctx.reopen(); + m_ctx.restrict_predicates(m_preds); + m_ctx.replace_rules(m_rules); + m_ctx.close(); + } + }; + rel_context::rel_context(context& ctx) - : m_context(ctx), + : rel_context_base(ctx.get_manager(), "datalog"), + m_context(ctx), m(ctx.get_manager()), m_rmanager(ctx), m_answer(m), - m_cancel(false), - m_last_result_relation(0) { - 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())); + m_last_result_relation(0), + m_ectx(ctx) { -#ifndef _EXTERNAL_RELEASE - get_rmanager().register_plugin(alloc(skip_table_plugin, get_rmanager())); -#endif + // register plugins for builtin tables - //register plugins for builtin relations + relation_manager& rm = get_rmanager(); - get_rmanager().register_plugin(alloc(bound_relation_plugin, get_rmanager())); - get_rmanager().register_plugin(alloc(interval_relation_plugin, 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 + + 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() { if (m_last_result_relation) { @@ -67,51 +121,46 @@ namespace datalog { } } - void rel_context::collect_predicates(decl_set & res) { - unsigned rule_cnt = m_context.get_rules().get_num_rules(); - for (unsigned rindex=0; rindexget_head()->get_decl()); - unsigned tail_len = r->get_uninterpreted_tail_size(); - for (unsigned tindex=0; tindexget_tail(tindex)->get_decl()); - } - } - decl_set::iterator oit = m_output_preds.begin(); - decl_set::iterator oend = m_output_preds.end(); - for (; oit!=oend; ++oit) { - res.insert(*oit); - } - get_rmanager().collect_predicates(res); + lbool rel_context::saturate() { + scoped_query sq(m_context); + return saturate(sq); } - - lbool rel_context::saturate() { - m_context.ensure_closed(); - + 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(); - - rule_set original_rules(m_context.get_rules()); - decl_set original_predicates; - m_context.collect_predicates(original_predicates); - - instruction_block rules_code; + instruction_block termination_code; - execution_context ex_ctx(m_context); lbool result; TRACE("dl", m_context.display(tout);); while (true) { - model_converter_ref mc; // Ignored in Datalog mode - proof_converter_ref pc; // Ignored in Datalog mode - m_context.transform_rules(mc, pc); - compiler::compile(m_context, m_context.get_rules(), rules_code, termination_code); + m_ectx.reset(); + m_code.reset(); + termination_code.reset(); + m_context.ensure_closed(); + transform_rules(); + if (m_context.canceled()) { + result = l_undef; + break; + } + TRACE("dl", m_context.display(tout);); - TRACE("dl", rules_code.display(*this, tout); ); + if (m_context.get_params().dump_aig().size()) { + const char *filename = static_cast(m_context.get_params().dump_aig().c_ptr()); + aig_exporter aig(m_context.get_rules(), get_context(), &m_table_facts); + std::ofstream strm(filename, std::ios_base::binary); + aig(strm); + exit(0); + } + + compiler::compile(m_context, m_context.get_rules(), m_code, termination_code); + + TRACE("dl", m_code.display(*this, tout); ); bool timeout_after_this_round = time_limit && (restart_time==0 || remaining_time_limit<=restart_time); @@ -119,29 +168,32 @@ namespace datalog { unsigned timeout = time_limit ? (restart_time!=0) ? std::min(remaining_time_limit, restart_time) : remaining_time_limit : restart_time; - ex_ctx.set_timelimit(timeout); + m_ectx.set_timelimit(timeout); } - bool early_termination = !rules_code.perform(ex_ctx); - ex_ctx.reset_timelimit(); - VERIFY( termination_code.perform(ex_ctx) ); + bool early_termination = !m_code.perform(m_ectx); + m_ectx.reset_timelimit(); + VERIFY( termination_code.perform(m_ectx) || m_context.canceled()); - rules_code.process_all_costs(); + m_code.process_all_costs(); - IF_VERBOSE(10, ex_ctx.report_big_relations(1000, verbose_stream());); - + IF_VERBOSE(10, m_ectx.report_big_relations(1000, verbose_stream());); + + if (m_context.canceled()) { + result = l_undef; + break; + } if (!early_termination) { m_context.set_status(OK); result = l_true; break; } - if (memory::above_high_watermark()) { m_context.set_status(MEMOUT); result = l_undef; break; } - if (timeout_after_this_round || m_cancel) { + if (timeout_after_this_round) { m_context.set_status(TIMEOUT); result = l_undef; break; @@ -158,68 +210,48 @@ namespace datalog { else { restart_time = static_cast(new_restart_time); } - - rules_code.reset(); - termination_code.reset(); - ex_ctx.reset(); - m_context.reopen(); - restrict_predicates(original_predicates); - m_context.replace_rules(original_rules); - m_context.close(); + sq.reset(); } - m_context.reopen(); - restrict_predicates(original_predicates); - m_context.replace_rules(original_rules); - m_context.close(); - TRACE("dl", ex_ctx.report_big_relations(100, tout);); - m_cancel = false; + m_context.record_transformed_rules(); + TRACE("dl", display_profile(tout);); return result; } - -#define BEGIN_QUERY() \ - rule_set original_rules(m_context.get_rules()); \ - decl_set original_preds; \ - m_context.collect_predicates(original_preds); \ - bool was_closed = m_context.is_closed(); \ - if (was_closed) { \ - m_context.reopen(); \ - } \ - -#define END_QUERY() \ - m_context.reopen(); \ - m_context.replace_rules(original_rules); \ - restrict_predicates(original_preds); \ - \ - if (was_closed) { \ - m_context.close(); \ - } \ lbool rel_context::query(unsigned num_rels, func_decl * const* rels) { get_rmanager().reset_saturated_marks(); - BEGIN_QUERY(); + scoped_query _scoped_query(m_context); for (unsigned i = 0; i < num_rels; ++i) { - set_output_predicate(rels[i]); + m_context.set_output_predicate(rels[i]); } m_context.close(); reset_negated_tables(); - lbool res = saturate(); + lbool res = saturate(_scoped_query); switch(res) { case l_true: { expr_ref_vector ans(m); expr_ref e(m); bool some_non_empty = num_rels == 0; + bool is_approx = false; for (unsigned i = 0; i < num_rels; ++i) { - relation_base& rel = get_relation(rels[i]); + func_decl* q = m_context.get_rules().get_pred(rels[i]); + relation_base& rel = get_relation(q); if (!rel.empty()) { some_non_empty = true; } + if (!rel.is_precise()) { + is_approx = true; + } rel.to_formula(e); ans.push_back(e); } SASSERT(!m_last_result_relation); if (some_non_empty) { m_answer = m.mk_and(ans.size(), ans.c_ptr()); + if (is_approx) { + res = l_undef; + m_context.set_status(APPROX); + } } else { m_answer = m.mk_false(); @@ -233,66 +265,75 @@ namespace datalog { case l_undef: break; } - END_QUERY(); 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)); + 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)); + 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(); - BEGIN_QUERY(); + scoped_query _scoped_query(m_context); rule_manager& rm = m_context.get_rule_manager(); - rule_ref qrule(rm); - rule_ref_vector qrules(rm); func_decl_ref query_pred(m); try { - rm.mk_query(query, query_pred, qrules, qrule); - } - catch(default_exception& exn) { - m_context.close(); - m_context.set_status(INPUT_ERROR); - throw exn; - } - try { - m_context.add_rules(qrules); + query_pred = rm.mk_query(query, m_context.get_rules()); } catch (default_exception& exn) { - m_context.close(); m_context.set_status(INPUT_ERROR); throw exn; } - set_output_predicate(qrule->get_head()->get_decl()); m_context.close(); reset_negated_tables(); if (m_context.generate_explanations()) { - model_converter_ref mc; // ignored in Datalog mode - proof_converter_ref pc; // ignored in Datalog mode - rule_transformer transformer(m_context); - //expl_plugin is deallocated when transformer goes out of scope - mk_explanations * expl_plugin = - alloc(mk_explanations, m_context, m_context.explanations_on_relation_level()); - transformer.register_plugin(expl_plugin); - m_context.transform_rules(transformer, mc, pc); - - //we will retrieve the predicate with explanations instead of the original query predicate - query_pred = expl_plugin->get_e_decl(query_pred); - const rule_vector & query_rules = m_context.get_rules().get_predicate_rules(query_pred); - SASSERT(query_rules.size()==1); - qrule = query_rules.back(); + m_context.transform_rules(alloc(mk_explanations, m_context)); } + query_pred = m_context.get_rules().get_pred(query_pred); + if (m_context.magic_sets_for_queries()) { - model_converter_ref mc; // Ignored in Datalog mode - proof_converter_ref pc; // Ignored in Datalog mode - rule_transformer transformer(m_context); - transformer.register_plugin(alloc(mk_magic_sets, m_context, qrule.get())); - m_context.transform_rules(transformer, mc, pc); + 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(); + lbool res = saturate(_scoped_query); - if (res != l_undef) { + query_pred = m_context.get_rules().get_pred(query_pred); + + if (res != l_undef) { m_last_result_relation = get_relation(query_pred).clone(); if (m_last_result_relation->empty()) { res = l_false; @@ -300,10 +341,13 @@ namespace datalog { } else { m_last_result_relation->to_formula(m_answer); + if (!m_last_result_relation->is_precise()) { + m_context.set_status(APPROX); + res = l_undef; + } } } - END_QUERY(); return res; } @@ -371,20 +415,27 @@ namespace datalog { } } - void rel_context::set_output_predicate(func_decl * pred) { - if (!m_output_preds.contains(pred)) { - m_output_preds.insert(pred); - } - } - - void rel_context::restrict_predicates( const decl_set & res ) { - set_intersection(m_output_preds, res); - get_rmanager().restrict_predicates(res); + void rel_context::restrict_predicates(func_decl_set const& predicates) { + get_rmanager().restrict_predicates(predicates); } 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; } @@ -395,8 +446,15 @@ namespace datalog { void rel_context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { - relation_manager & rmgr = get_rmanager(); + TRACE("dl", + tout << pred->get_name() << ": "; + for (unsigned i = 0; i < relation_name_cnt; ++i) { + tout << relation_names[i] << " "; + } + tout << "\n"; + ); + relation_manager & rmgr = get_rmanager(); family_id target_kind = null_family_id; switch (relation_name_cnt) { case 0: @@ -416,7 +474,7 @@ namespace datalog { } else { relation_signature rel_sig; - //rmgr.from_predicate(pred, rel_sig); + rmgr.from_predicate(pred, rel_sig); product_relation_plugin & prod_plugin = product_relation_plugin::get_plugin(rmgr); rel_kind = prod_plugin.get_relation_kind(rel_sig, rel_kinds); } @@ -429,6 +487,10 @@ namespace datalog { get_rmanager().set_predicate_kind(pred, target_kind); } + void rel_context::set_cancel(bool f) { + get_rmanager().set_cancel(f); + } + relation_plugin & rel_context::get_ordinary_relation_plugin(symbol relation_name) { relation_plugin * plugin = get_rmanager().get_relation_plugin(relation_name); if (!plugin) { @@ -492,6 +554,11 @@ namespace datalog { } } + bool rel_context::has_facts(func_decl * pred) const { + relation_base* r = try_get_relation(pred); + return r && !r->empty(); + } + void rel_context::store_relation(func_decl * pred, relation_base * rel) { get_rmanager().store_relation(pred, rel); } @@ -505,13 +572,28 @@ namespace datalog { } } - void rel_context::display_output_facts(std::ostream & out) const { - get_rmanager().display_output_tables(out); + void rel_context::display_output_facts(rule_set const& rules, std::ostream & out) const { + get_rmanager().display_output_tables(rules, out); } void rel_context::display_facts(std::ostream& out) const { get_rmanager().display(out); } + void rel_context::display_profile(std::ostream& out) { + m_code.make_annotations(m_ectx); + m_code.process_all_costs(); + + out << "\n--------------\n"; + out << "Instructions\n"; + m_code.display(*this, out); + + out << "\n--------------\n"; + out << "Big relations\n"; + m_ectx.report_big_relations(1000, out); + + get_rmanager().display_relation_sizes(out); + } + }; diff --git a/src/muz/rel/rel_context.h b/src/muz/rel/rel_context.h new file mode 100644 index 000000000..68a6fbd90 --- /dev/null +++ b/src/muz/rel/rel_context.h @@ -0,0 +1,130 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + rel_context.h + +Abstract: + + context for relational datalog engine. + +Author: + + Nikolaj Bjorner (nbjorner) 2012-12-3. + +Revision History: + + Extracted from dl_context + +--*/ +#ifndef _REL_CONTEXT_H_ +#define _REL_CONTEXT_H_ +#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 { + + class context; + typedef vector > fact_vector; + + class rel_context : public rel_context_base { + context& m_context; + ast_manager& m; + relation_manager m_rmanager; + expr_ref m_answer; + relation_base * m_last_result_relation; + fact_vector m_table_facts; + execution_context m_ectx; + instruction_block m_code; + + class scoped_query; + + void reset_negated_tables(); + + relation_plugin & get_ordinary_relation_plugin(symbol relation_name); + + void reset_tables(); + + lbool saturate(scoped_query& sq); + + void set_cancel(bool f); + + public: + rel_context(context& ctx); + + virtual ~rel_context(); + + 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; } + 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; } + + virtual bool output_profile() const; + + virtual lbool query(expr* q); + virtual lbool query(unsigned num_rels, func_decl * const* rels); + + virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, + symbol const * relation_names); + + virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred); + + + virtual void cancel() { set_cancel(true); } + virtual void cleanup() { set_cancel(false);} + + /** + \brief Restrict the set of used predicates to \c res. + + The function deallocates unsused relations, it does not deal with rules. + */ + 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. + */ + 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 + */ + 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 + */ + 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. + */ + virtual void store_relation(func_decl * pred, relation_base * rel); + + virtual void display_output_facts(rule_set const& rules, std::ostream & out) const; + virtual void display_facts(std::ostream & out) const; + + virtual void display_profile(std::ostream& out); + + virtual lbool saturate(); + + }; +}; + +#endif /* _REL_CONTEXT_H_ */ diff --git a/src/muz_qe/tab_context.cpp b/src/muz/tab/tab_context.cpp similarity index 98% rename from src/muz_qe/tab_context.cpp rename to src/muz/tab/tab_context.cpp index 72727bea8..83842a68b 100644 --- a/src/muz_qe/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -29,6 +29,8 @@ Revision History: #include "datatype_decl_plugin.h" #include "for_each_expr.h" #include "matcher.h" +#include "scoped_proof.h" +#include "fixedpoint_params.hpp" namespace tb { @@ -191,6 +193,7 @@ namespace tb { unsigned get_index() const { return m_index; } void set_index(unsigned index) { m_index = index; } app* get_head() const { return m_head; } + func_decl* get_decl() const { return m_head->get_decl(); } void set_head(app* h) { m_head = h; } unsigned get_parent_index() const { return m_parent_index; } unsigned get_parent_rule() const { return m_parent_rule; } @@ -207,7 +210,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; } @@ -317,7 +320,7 @@ namespace tb { for (unsigned i = utsz; i < tsz; ++i) { fmls.push_back(r->get_tail(i)); } - m_num_vars = 1 + r.get_manager().get_var_counter().get_max_var(*r); + m_num_vars = 1 + r.get_manager().get_counter().get_max_rule_var(*r); m_head = r->get_head(); m_predicates.reset(); for (unsigned i = 0; i < utsz; ++i) { @@ -336,7 +339,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)) { @@ -447,7 +450,7 @@ namespace tb { void insert(ref& g) { unsigned idx = m_rules.size(); m_rules.push_back(g); - func_decl* f = g->get_head()->get_decl(); + func_decl* f = g->get_decl(); map::obj_map_entry* e = m_index.insert_if_not_there2(f, unsigned_vector()); SASSERT(e); e->get_data().m_value.push_back(idx); @@ -613,7 +616,7 @@ namespace tb { bool match_head(clause const& g) { return - m_head->get_decl() == g.get_head()->get_decl() && + m_head->get_decl() == g.get_decl() && m_matcher(m_head, g.get_head(), m_subst, m_sideconds) && match_predicates(0, g); } @@ -663,7 +666,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)) { @@ -739,7 +742,6 @@ namespace tb { typedef svector double_vector; typedef obj_map score_map; typedef obj_map pred_map; - datalog::context& m_ctx; ast_manager& m; datatype_util dt; score_map m_score_map; @@ -749,19 +751,16 @@ namespace tb { pred_map m_pred_map; expr_ref_vector m_refs; double m_weight_multiply; - unsigned m_num_invocations; unsigned m_update_frequency; unsigned m_next_update; public: selection(datalog::context& ctx): - m_ctx(ctx), m(ctx.get_manager()), dt(m), m_refs(m), m_weight_multiply(1.0), - m_num_invocations(0), m_update_frequency(20), m_next_update(20) { set_strategy(ctx.get_params().tab_selection()); @@ -1080,7 +1079,7 @@ namespace tb { bool unify(clause const& tgt, unsigned idx, clause const& src, bool compute_subst, ref& result) { qe_lite qe(m); reset(); - SASSERT(tgt.get_predicate(idx)->get_decl() == src.get_head()->get_decl()); + SASSERT(tgt.get_predicate(idx)->get_decl() == src.get_decl()); unsigned var_cnt = std::max(tgt.get_num_vars(), src.get_num_vars()); m_S1.reserve(2, var_cnt); if (!m_unifier(tgt.get_predicate(idx), src.get_head(), m_S1)) { @@ -1380,11 +1379,10 @@ namespace datalog { m_displayed_rules.reset(); m_rules.init(m_ctx.get_rules()); m_selection.init(m_rules); - rule_ref_vector query_rules(rm); + rule_set query_rules(m_ctx); rule_ref clause(rm); - func_decl_ref query_pred(m); - rm.mk_query(query, query_pred, query_rules, clause); - + rm.mk_query(query, query_rules); + clause = query_rules.last(); ref g = alloc(tb::clause, m); g->init(clause); g->set_head(m.mk_false()); @@ -1644,9 +1642,7 @@ namespace datalog { void resolve_rule(replace_proof_converter& pc, tb::clause const& r1, tb::clause const& r2, expr_ref_vector const& s1, expr_ref_vector const& s2, tb::clause const& res) const { unsigned idx = r1.get_predicate_index(); - expr_ref fml1 = r1.to_formula(); - expr_ref fml2 = r2.to_formula(); - expr_ref fml3 = res.to_formula(); + expr_ref fml = res.to_formula(); vector substs; svector > positions; substs.push_back(s1); @@ -1654,16 +1650,16 @@ namespace datalog { scoped_proof _sc(m); proof_ref pr(m); proof_ref_vector premises(m); - premises.push_back(m.mk_asserted(fml1)); - premises.push_back(m.mk_asserted(fml2)); + premises.push_back(m.mk_asserted(r1.to_formula())); + premises.push_back(m.mk_asserted(r2.to_formula())); positions.push_back(std::make_pair(idx+1, 0)); - pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml3, positions, substs); + pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml, positions, substs); pc.insert(pr); - } - + } }; tab::tab(context& ctx): + datalog::engine_base(ctx.get_manager(),"tabulation"), m_imp(alloc(imp, ctx)) { } tab::~tab() { diff --git a/src/muz_qe/tab_context.h b/src/muz/tab/tab_context.h similarity index 55% rename from src/muz_qe/tab_context.h rename to src/muz/tab/tab_context.h index f0a2eefed..4689598c0 100644 --- a/src/muz_qe/tab_context.h +++ b/src/muz/tab/tab_context.h @@ -22,23 +22,24 @@ Revision History: #include "ast.h" #include "lbool.h" #include "statistics.h" +#include "dl_engine_base.h" namespace datalog { class context; - class tab { + class tab : public engine_base { class imp; imp* m_imp; public: tab(context& ctx); ~tab(); - lbool query(expr* query); - void cancel(); - void cleanup(); - void reset_statistics(); - void collect_statistics(statistics& st) const; - void display_certificate(std::ostream& out) const; - expr_ref get_answer(); + virtual lbool query(expr* query); + virtual void cancel(); + virtual void cleanup(); + virtual void reset_statistics(); + virtual void collect_statistics(statistics& st) const; + virtual void display_certificate(std::ostream& out) const; + virtual expr_ref get_answer(); }; }; diff --git a/src/muz_qe/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp similarity index 60% rename from src/muz_qe/dl_mk_array_blast.cpp rename to src/muz/transforms/dl_mk_array_blast.cpp index b22fdf7ef..776a2da5b 100644 --- a/src/muz_qe/dl_mk_array_blast.cpp +++ b/src/muz/transforms/dl_mk_array_blast.cpp @@ -18,8 +18,7 @@ Revision History: --*/ #include "dl_mk_array_blast.h" -#include "expr_safe_replace.h" - +#include "qe_util.h" namespace datalog { @@ -30,7 +29,10 @@ namespace datalog { m(ctx.get_manager()), a(m), rm(ctx.get_rule_manager()), - m_rewriter(m, m_params){ + 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); } @@ -49,12 +51,60 @@ 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); + qe::flatten_and(body, conjs); + m_defs.reset(); + m_sub.reset(); + m_next_var = 0; ptr_vector todo; todo.push_back(head); for (unsigned i = 0; i < conjs.size(); ++i) { @@ -65,22 +115,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(); @@ -96,22 +141,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; @@ -120,12 +171,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))); } @@ -144,7 +198,6 @@ namespace datalog { expr_ref_vector conjs(m), new_conjs(m); expr_ref tmp(m); expr_safe_replace sub(m); - uint_set lhs_vars, rhs_vars; bool change = false; bool inserted = false; @@ -154,16 +207,14 @@ 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(); if (is_store_def(e, x, y)) { // enforce topological order consistency: - uint_set lhs; - collect_vars(m, x, lhs_vars); - collect_vars(m, y, rhs_vars); - lhs = lhs_vars; + uint_set lhs = rm.collect_vars(x); + uint_set rhs_vars = rm.collect_vars(y); lhs &= rhs_vars; if (!lhs.empty()) { TRACE("dl", tout << "unusable equality " << mk_pp(e, m) << "\n";); @@ -181,55 +232,46 @@ namespace datalog { } } - rule_ref_vector new_rules(rm); - 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; } fml2 = m.mk_implies(body, head); - rm.mk_rule(fml2, new_rules, r.name()); - SASSERT(new_rules.size() == 1); + proof_ref p(m); + rule_set new_rules(m_ctx); + rm.mk_rule(fml2, p, new_rules, r.name()); - TRACE("dl", new_rules[0]->display(m_ctx, tout << "new rule\n");); - - rules.add_rule(new_rules[0].get()); - if (m_pc) { - new_rules[0]->to_formula(fml2); - m_pc->insert(fml1, fml2); + 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; } - rule_set * mk_array_blast::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - ref epc; - if (pc) { - epc = alloc(equiv_proof_converter, m); - } - m_pc = epc.get(); + rule_set * mk_array_blast::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); + rules->inherit_predicates(source); rule_set::iterator it = source.begin(), end = source.end(); bool change = false; - for (; it != end; ++it) { + for (; !m_ctx.canceled() && it != end; ++it) { change = blast(**it, *rules) || change; } if (!change) { dealloc(rules); rules = 0; } - if (pc) { - pc = concat(pc.get(), epc.get()); - } return rules; } diff --git a/src/muz_qe/dl_mk_array_blast.h b/src/muz/transforms/dl_mk_array_blast.h similarity index 63% rename from src/muz_qe/dl_mk_array_blast.h rename to src/muz/transforms/dl_mk_array_blast.h index 1618e4fa8..f4b685b7a 100644 --- a/src/muz_qe/dl_mk_array_blast.h +++ b/src/muz/transforms/dl_mk_array_blast.h @@ -22,8 +22,10 @@ Revision History: #include"dl_context.h" #include"dl_rule_set.h" #include"dl_rule_transformer.h" +#include"dl_mk_interp_tail_simplifier.h" #include "equiv_proof_converter.h" #include "array_decl_plugin.h" +#include "expr_safe_replace.h" namespace datalog { @@ -31,31 +33,41 @@ 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; rule_manager& rm; params_ref m_params; th_rewriter m_rewriter; - equiv_proof_converter* m_pc; + 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: /** - \brief Create rule transformer that extracts universal quantifiers (over recursive predicates). + \brief Create rule transformer that removes array stores and selects by ackermannization. */ mk_array_blast(context & ctx, unsigned priority); virtual ~mk_array_blast(); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; diff --git a/src/muz/transforms/dl_mk_backwards.cpp b/src/muz/transforms/dl_mk_backwards.cpp new file mode 100644 index 000000000..771de0dc3 --- /dev/null +++ b/src/muz/transforms/dl_mk_backwards.cpp @@ -0,0 +1,78 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_backwards.cpp + +Abstract: + + Create Horn clauses for backwards flow. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-17 + +Revision History: + +--*/ + +#include"dl_mk_backwards.h" +#include"dl_context.h" + +namespace datalog { + + mk_backwards::mk_backwards(context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx) { + } + + mk_backwards::~mk_backwards() { } + + rule_set * mk_backwards::operator()(rule_set const & source) { + 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; + app_ref query(m); + query = m.mk_fresh_const("Q", m.mk_bool_sort()); + result->set_output_predicate(query->get_decl()); + m_ctx.register_predicate(query->get_decl(), false); + for (unsigned i = 0; i < sz; ++i) { + tail.reset(); + neg.reset(); + rule & r = *source.get_rule(i); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + if (!source.is_output_predicate(r.get_decl())) { + tail.push_back(r.get_head()); + neg.push_back(false); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + neg.push_back(false); + } + for (unsigned j = 0; j <= utsz; ++j) { + if (j == utsz && j > 0) { + break; + } + if (j == utsz) { + head = query; + } + else { + head = r.get_tail(j); + } + new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + } + } + TRACE("dl", result->display(tout);); + return result; + } + +}; diff --git a/src/muz/transforms/dl_mk_backwards.h b/src/muz/transforms/dl_mk_backwards.h new file mode 100644 index 000000000..4e546c848 --- /dev/null +++ b/src/muz/transforms/dl_mk_backwards.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_backwards.h + +Abstract: + + Create Horn clauses for backwards flow. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-17 + +Revision History: + +--*/ +#ifndef _DL_MK_BACKWARDS_H_ +#define _DL_MK_BACKWARDS_H_ + +#include"dl_rule_transformer.h" + +namespace datalog { + + class mk_backwards : public rule_transformer::plugin { + ast_manager& m; + context& m_ctx; + public: + mk_backwards(context & ctx, unsigned priority = 33000); + ~mk_backwards(); + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_BACKWARDS_H_ */ + diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp new file mode 100644 index 000000000..91481ed5a --- /dev/null +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -0,0 +1,319 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + dl_mk_bit_blast.cpp + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2012-08-30 + +Revision History: + +--*/ + +#include "dl_mk_bit_blast.h" +#include "bit_blaster_rewriter.h" +#include "rewriter_def.h" +#include "ast_pp.h" +#include "expr_safe_replace.h" +#include "filter_model_converter.h" +#include "dl_mk_interp_tail_simplifier.h" +#include "fixedpoint_params.hpp" + +namespace datalog { + + // + // P(v) :- Q(extract[1:1]v ++ 0), R(1 ++ extract[0:0]v). + // -> + // P(bv(x,y)) :- Q(bv(x,0)), R(bv(1,y)) . + // + // Introduce P_bv: + // P_bv(x,y) :- Q_bv(x,0), R_bv(1,y) + // P(bv(x,y)) :- P_bv(x,y) + // Query + + // this model converter should be composed with a filter converter + // that gets rid of the new functions. + class bit_blast_model_converter : public model_converter { + ast_manager& m; + bv_util m_bv; + func_decl_ref_vector m_old_funcs; + func_decl_ref_vector m_new_funcs; + public: + bit_blast_model_converter(ast_manager& m): + m(m), + m_bv(m), + m_old_funcs(m), + m_new_funcs(m) {} + + void insert(func_decl* old_f, func_decl* new_f) { + m_old_funcs.push_back(old_f); + m_new_funcs.push_back(new_f); + } + + virtual model_converter * translate(ast_translation & translator) { + return alloc(bit_blast_model_converter, m); + } + + virtual void operator()(model_ref & model) { + for (unsigned i = 0; i < m_new_funcs.size(); ++i) { + func_decl* p = m_new_funcs[i].get(); + func_decl* q = m_old_funcs[i].get(); + func_interp* f = model->get_func_interp(p); + expr_ref body(m); + unsigned arity_p = p->get_arity(); + unsigned arity_q = q->get_arity(); + SASSERT(0 < arity_p); + model->register_decl(p, f); + func_interp* g = alloc(func_interp, m, arity_q); + + if (f) { + body = f->get_interp(); + SASSERT(!f->is_partial()); + SASSERT(body); + } + else { + body = m.mk_false(); + } + unsigned idx = 0; + expr_ref arg(m), proj(m); + expr_safe_replace sub(m); + for (unsigned j = 0; j < arity_q; ++j) { + sort* s = q->get_domain(j); + arg = m.mk_var(j, s); + if (m_bv.is_bv_sort(s)) { + expr* args[1] = { arg }; + unsigned sz = m_bv.get_bv_size(s); + for (unsigned k = 0; k < sz; ++k) { + proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, args); + sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj); + } + } + else { + sub.insert(m.mk_var(idx++, s), arg); + } + } + sub(body); + g->set_else(body); + model->register_decl(q, g); + } + } + }; + + class expand_mkbv_cfg : public default_rewriter_cfg { + + context& m_context; + ast_manager& m; + bv_util m_util; + expr_ref_vector m_args, m_f_vars, m_g_vars; + func_decl_ref_vector m_old_funcs; + func_decl_ref_vector m_new_funcs; + rule_set const* m_src; + rule_set* m_dst; + obj_map m_pred2blast; + + + public: + + expand_mkbv_cfg(context& ctx): + m_context(ctx), + m(ctx.get_manager()), + m_util(m), + m_args(m), + m_f_vars(m), + m_g_vars(m), + m_old_funcs(m), + m_new_funcs(m), + m_src(0), + m_dst(0) + {} + + ~expand_mkbv_cfg() {} + + void set_src(rule_set const* src) { m_src = src; } + void set_dst(rule_set* dst) { m_dst = dst; } + func_decl_ref_vector const& old_funcs() const { return m_old_funcs; } + func_decl_ref_vector const& new_funcs() const { return m_new_funcs; } + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + if (num == 0) { + if (m_src->is_output_predicate(f)) + m_dst->set_output_predicate(f); + return BR_FAILED; + } + + for (unsigned i = 0; i < num; ++i) { + if (!m_util.is_mkbv(args[i])) + return BR_FAILED; + } + + // + // f(mk_bv(args),...) + // + m_args.reset(); + m_g_vars.reset(); + m_f_vars.reset(); + expr_ref fml(m); + unsigned idx = 0; + for (unsigned j = 0; j < num; ++j) { + expr* arg = args[j]; + if (m_util.is_mkbv(arg)) { + app* a = to_app(arg); + unsigned sz = a->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + m_args.push_back(a->get_arg(i)); + m_g_vars.push_back(m.mk_var(idx++,m.mk_bool_sort())); + } + m_f_vars.push_back(m_util.mk_bv(sz, m_g_vars.c_ptr()+m_g_vars.size()-sz)); + } + else { + m_args.push_back(arg); + m_f_vars.push_back(m.mk_var(idx++, m.get_sort(arg))); + m_g_vars.push_back(m_f_vars.back()); + } + } + func_decl* g = 0; + + if (!m_pred2blast.find(f, g)) { + + ptr_vector domain; + for (unsigned i = 0; i < m_args.size(); ++i) { + domain.push_back(m.get_sort(m_args[i].get())); + } + g = m_context.mk_fresh_head_predicate(f->get_name(), symbol("bv"), m_args.size(), domain.c_ptr(), f); + m_old_funcs.push_back(f); + m_new_funcs.push_back(g); + m_pred2blast.insert(f, g); + + m_dst->inherit_predicate(*m_src, f, g); + } + result = m.mk_app(g, m_args.size(), m_args.c_ptr()); + result_pr = 0; + return BR_DONE; + } + }; + + struct expand_mkbv : public rewriter_tpl { + expand_mkbv_cfg m_cfg; + expand_mkbv(ast_manager& m, context& ctx): + rewriter_tpl(m, m.proofs_enabled(), m_cfg), + m_cfg(ctx) { + } + }; + + + class mk_bit_blast::impl { + + context & m_context; + ast_manager & m; + params_ref m_params; + mk_interp_tail_simplifier m_simplifier; + bit_blaster_rewriter m_blaster; + expand_mkbv m_rewriter; + + + bool blast(rule *r, expr_ref& fml) { + proof_ref pr(m); + expr_ref fml1(m), fml2(m), fml3(m); + rule_ref r2(m_context.get_rule_manager()); + // We need to simplify rule before bit-blasting. + if (!m_simplifier.transform_rule(r, r2)) { + r2 = r; + } + r2->to_formula(fml1); + m_blaster(fml1, fml2, pr); + m_rewriter(fml2, fml3); + TRACE("dl", tout << mk_pp(fml, m) << " -> " << mk_pp(fml2, m) << " -> " << mk_pp(fml3, m) << "\n";); + if (fml3 != fml) { + fml = fml3; + return true; + } + else { + return false; + } + } + + public: + impl(context& ctx): + m_context(ctx), + m(ctx.get_manager()), + m_params(ctx.get_params().p), + m_simplifier(ctx), + m_blaster(ctx.get_manager(), m_params), + m_rewriter(ctx.get_manager(), ctx) { + m_params.set_bool("blast_full", true); + m_params.set_bool("blast_quant", true); + m_blaster.updt_params(m_params); + } + + rule_set * operator()(rule_set const & source) { + // TODO pc + if (!m_context.bit_blast()) { + return 0; + } + rule_manager& rm = m_context.get_rule_manager(); + unsigned sz = source.get_num_rules(); + expr_ref fml(m); + rule_set * result = alloc(rule_set, m_context); + m_rewriter.m_cfg.set_src(&source); + m_rewriter.m_cfg.set_dst(result); + for (unsigned i = 0; !m_context.canceled() && i < sz; ++i) { + rule * r = source.get_rule(i); + r->to_formula(fml); + if (blast(r, fml)) { + proof_ref pr(m); + if (m_context.generate_proof_trace()) { + pr = m.mk_asserted(fml); // loses original proof of r. + } + // TODO add logic for pc: + // 1. replace fresh predicates by non-bit-blasted predicates + // 2. replace pr by the proof of r. + rm.mk_rule(fml, pr, *result, r->name()); + } + else { + result->add_rule(r); + } + } + + // copy output predicates without any rule (bit-blasting not really needed) + const func_decl_set& decls = source.get_output_predicates(); + for (func_decl_set::iterator I = decls.begin(), E = decls.end(); I != E; ++I) { + if (!source.contains(*I)) + result->set_output_predicate(*I); + } + + if (m_context.get_model_converter()) { + filter_model_converter* fmc = alloc(filter_model_converter, m); + bit_blast_model_converter* bvmc = alloc(bit_blast_model_converter, m); + func_decl_ref_vector const& old_funcs = m_rewriter.m_cfg.old_funcs(); + func_decl_ref_vector const& new_funcs = m_rewriter.m_cfg.new_funcs(); + for (unsigned i = 0; i < old_funcs.size(); ++i) { + fmc->insert(new_funcs[i]); + bvmc->insert(old_funcs[i], new_funcs[i]); + } + m_context.add_model_converter(concat(bvmc, fmc)); + } + + return result; + } + }; + + mk_bit_blast::mk_bit_blast(context & ctx, unsigned priority) : plugin(priority) { + m_impl = alloc(impl, ctx); + } + + mk_bit_blast::~mk_bit_blast() { + dealloc(m_impl); + } + + rule_set * mk_bit_blast::operator()(rule_set const & source) { + return (*m_impl)(source); + } + +}; diff --git a/src/muz_qe/dl_mk_bit_blast.h b/src/muz/transforms/dl_mk_bit_blast.h similarity index 53% rename from src/muz_qe/dl_mk_bit_blast.h rename to src/muz/transforms/dl_mk_bit_blast.h index e16c2058b..da91c5804 100644 --- a/src/muz_qe/dl_mk_bit_blast.h +++ b/src/muz/transforms/dl_mk_bit_blast.h @@ -7,7 +7,7 @@ Module Name: Abstract: - + Functor for bit-blasting a rule set Author: @@ -19,32 +19,17 @@ Revision History: #ifndef _DL_MK_BIT_BLAST_H_ #define _DL_MK_BIT_BLAST_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 bit-blasting a rule set. - */ - class mk_bit_blast : public rule_transformer::plugin { class impl; - - impl* m_impl; - void blast(expr_ref& b); - void reset(); - + impl* m_impl; public: mk_bit_blast(context & ctx, unsigned priority = 35000); - ~mk_bit_blast(); - - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + ~mk_bit_blast(); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_coalesce.cpp b/src/muz/transforms/dl_mk_coalesce.cpp similarity index 91% rename from src/muz_qe/dl_mk_coalesce.cpp rename to src/muz/transforms/dl_mk_coalesce.cpp index 222881bc4..8c7f1d5b4 100644 --- a/src/muz_qe/dl_mk_coalesce.cpp +++ b/src/muz/transforms/dl_mk_coalesce.cpp @@ -133,7 +133,7 @@ namespace datalog { tail.push_back(to_app(fml)); is_neg.push_back(false); res = rm.mk(head, tail.size(), tail.c_ptr(), is_neg.c_ptr(), tgt->name()); - if (m_pc) { + if (m_ctx.generate_proof_trace()) { src.to_formula(fml1); tgt->to_formula(fml2); res->to_formula(fml); @@ -142,12 +142,13 @@ namespace datalog { sort* domain[3] = { ps, ps, m.mk_bool_sort() }; func_decl* merge = m.mk_func_decl(symbol("merge-clauses"), 3, domain, ps); // TBD: ad-hoc proof rule expr* args[3] = { m.mk_asserted(fml1), m.mk_asserted(fml2), fml }; - m_pc->insert(m.mk_app(merge, 3, args)); + // ...m_pc->insert(m.mk_app(merge, 3, args)); #else svector > pos; vector substs; - proof* p = m.mk_asserted(fml1); - m_pc->insert(m.mk_hyper_resolve(1, &p, fml, pos, substs)); + proof* p = src.get_proof(); + p = m.mk_hyper_resolve(1, &p, fml, pos, substs); + res->set_proof(m, p); #endif } tgt = res; @@ -170,14 +171,9 @@ namespace datalog { return true; } - rule_set * mk_coalesce::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - m_pc = 0; - ref rpc; - if (pc) { - rpc = alloc(replace_proof_converter, m); - m_pc = rpc.get(); - } + rule_set * mk_coalesce::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); + rules->inherit_predicates(source); rule_set::decl2rules::iterator it = source.begin_grouped_rules(), end = source.end_grouped_rules(); for (; it != end; ++it) { rule_ref_vector d_rules(rm); @@ -195,9 +191,6 @@ namespace datalog { rules->add_rule(r1.get()); } } - if (pc) { - pc = concat(pc.get(), rpc.get()); - } return rules; } diff --git a/src/muz_qe/dl_mk_coalesce.h b/src/muz/transforms/dl_mk_coalesce.h similarity index 88% rename from src/muz_qe/dl_mk_coalesce.h rename to src/muz/transforms/dl_mk_coalesce.h index 4259d31fe..4a4065174 100644 --- a/src/muz_qe/dl_mk_coalesce.h +++ b/src/muz/transforms/dl_mk_coalesce.h @@ -37,7 +37,6 @@ namespace datalog { rule_manager& rm; expr_ref_vector m_sub1, m_sub2; unsigned m_idx; - replace_proof_converter* m_pc; void mk_pred(app_ref& pred, app* p1, app* p2); @@ -53,7 +52,7 @@ namespace datalog { */ mk_coalesce(context & ctx); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp new file mode 100644 index 000000000..31af7a53f --- /dev/null +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -0,0 +1,212 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_mk_coi_filter.cpp + +Abstract: + + Rule transformer which removes relations which are out of the cone of + influence of output relations + +Author: + + Krystof Hoder (t-khoder) 2011-10-01. + +Revision History: + + Andrey Rybalchenko (rybal) 2013-8-8 + Added bottom_up pruning. + +--*/ + + +#include +#include"ast_pp.h" +#include"dl_mk_coi_filter.h" +#include"extension_model_converter.h" + +namespace datalog { + + // ----------------------------------- + // + // mk_coi_filter + // + // ----------------------------------- + + rule_set * mk_coi_filter::operator()(rule_set const & source) { + if (source.empty()) { + return 0; + } + scoped_ptr result1 = top_down(source); + scoped_ptr result2 = bottom_up(result1?*result1:source); + if (!result2) { + result2 = result1.detach(); + } + return result2.detach(); + } + + rule_set * mk_coi_filter::bottom_up(rule_set const & source) { + func_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); + } + } + } + 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(); + 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()) { + TRACE("dl", tout << "No transformation\n";); + res = 0; + } + else { + res->close(); + } + + // set to false each unreached predicate + if (m_context.get_model_converter()) { + extension_model_converter* mc0 = alloc(extension_model_converter, m); + for (func_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); + } + CTRACE("dl", 0 != res, res->display(tout);); + return res.detach(); + } + + rule_set * mk_coi_filter::top_down(rule_set const & source) { + + func_decl_set interesting_preds; + func_decl_set pruned_preds; + ptr_vector todo; + { + 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); + } + } + + const rule_dependencies& deps = source.get_dependencies(); + + while (!todo.empty()) { + func_decl * curr = todo.back(); + todo.pop_back(); + interesting_preds.insert(curr); + + 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) { + func_decl * dep_pred = *it; + if (!interesting_preds.contains(dep_pred)) { + interesting_preds.insert(dep_pred); + todo.push_back(dep_pred); + } + } + } + + scoped_ptr res = alloc(rule_set, m_context); + res->inherit_predicates(source); + + rule_set::iterator rend = source.end(); + for (rule_set::iterator rit = source.begin(); rit != rend; ++rit) { + rule * r = *rit; + func_decl * pred = r->get_decl(); + if (interesting_preds.contains(pred)) { + res->add_rule(r); + } + else if (m_context.get_model_converter()) { + pruned_preds.insert(pred); + } + } + + if (res->get_num_rules() == source.get_num_rules()) { + TRACE("dl", tout << "No transformation\n";); + res = 0; + } + + if (res && m_context.get_model_converter()) { + 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()); + } + m_context.add_model_converter(mc0); + } + CTRACE("dl", 0 != res, res->display(tout);); + return res.detach(); + } + +}; + diff --git a/src/muz_qe/dl_mk_coi_filter.h b/src/muz/transforms/dl_mk_coi_filter.h similarity index 83% rename from src/muz_qe/dl_mk_coi_filter.h rename to src/muz/transforms/dl_mk_coi_filter.h index b8fb37964..8ec7e80c4 100644 --- a/src/muz_qe/dl_mk_coi_filter.h +++ b/src/muz/transforms/dl_mk_coi_filter.h @@ -32,16 +32,17 @@ 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), m(ctx.get_manager()), m_context(ctx) {} - - rule_set * operator()(rule_set const & source, - model_converter_ref& mc, - proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz/transforms/dl_mk_different.h b/src/muz/transforms/dl_mk_different.h new file mode 100644 index 000000000..2def011f9 --- /dev/null +++ b/src/muz/transforms/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_filter_rules.cpp b/src/muz/transforms/dl_mk_filter_rules.cpp similarity index 81% rename from src/muz_qe/dl_mk_filter_rules.cpp rename to src/muz/transforms/dl_mk_filter_rules.cpp index 4f01a3651..ef89c9ffa 100644 --- a/src/muz_qe/dl_mk_filter_rules.cpp +++ b/src/muz/transforms/dl_mk_filter_rules.cpp @@ -27,10 +27,12 @@ namespace datalog { mk_filter_rules::mk_filter_rules(context & ctx): plugin(2000), m_context(ctx), - m_manager(ctx.get_manager()), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), m_result(0), - m_pinned(m_manager) { + m_pinned(m) { } + mk_filter_rules::~mk_filter_rules() { ptr_vector to_dealloc; filter_cache::iterator it = m_tail2filter.begin(); @@ -50,15 +52,15 @@ namespace datalog { \brief Return true if \c pred is a cadidate for a "filter" rule. */ bool mk_filter_rules::is_candidate(app * pred) { - if (!m_context.get_rule_manager().is_predicate(pred)) { - TRACE("mk_filter_rules", tout << mk_pp(pred, m_manager) << "\nis not a candidate because it is interpreted.\n";); + if (!m_context.is_predicate(pred)) { + TRACE("mk_filter_rules", tout << mk_pp(pred, m) << "\nis not a candidate because it is interpreted.\n";); return false; } var_idx_set used_vars; unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = pred->get_arg(i); - if (m_manager.is_value(arg)) + if (m.is_value(arg)) return true; SASSERT(is_var(arg)); unsigned vidx = to_var(arg)->get_idx(); @@ -73,10 +75,10 @@ namespace datalog { \brief Create a "filter" (if it doesn't exist already) for the given predicate. */ func_decl * mk_filter_rules::mk_filter_decl(app * pred, var_idx_set const & non_local_vars) { - sort_ref_buffer filter_domain(m_manager); + sort_ref_buffer filter_domain(m); - filter_key * key = alloc(filter_key, m_manager); - mk_new_rule_tail(m_manager, pred, non_local_vars, filter_domain, key->filter_args, key->new_pred); + filter_key * key = alloc(filter_key, m); + mk_new_rule_tail(m, pred, non_local_vars, filter_domain, key->filter_args, key->new_pred); func_decl * filter_decl = 0; if (!m_tail2filter.find(key, filter_decl)) { filter_decl = m_context.mk_fresh_head_predicate(pred->get_decl()->get_name(), symbol("filter"), @@ -84,12 +86,13 @@ namespace datalog { m_pinned.push_back(filter_decl); m_tail2filter.insert(key, filter_decl); - app_ref filter_head(m_manager); - filter_head = m_manager.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr()); + app_ref filter_head(m); + filter_head = m.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr()); app * filter_tail = key->new_pred; rule * filter_rule = m_context.get_rule_manager().mk(filter_head, 1, &filter_tail, (const bool *)0); filter_rule->set_accounting_parent_object(m_context, m_current); m_result->add_rule(filter_rule); + m_context.get_rule_manager().mk_rule_asserted_proof(*filter_rule); } else { dealloc(key); @@ -102,16 +105,15 @@ namespace datalog { void mk_filter_rules::process(rule * r) { m_current = r; app * new_head = r->get_head(); - app_ref_vector new_tail(m_manager); + app_ref_vector new_tail(m); svector new_is_negated; unsigned sz = r->get_tail_size(); bool rule_modified = false; for (unsigned i = 0; i < sz; i++) { app * tail = r->get_tail(i); if (is_candidate(tail)) { - TRACE("mk_filter_rules", tout << "is_candidate: " << mk_pp(tail, m_manager) << "\n";); - var_idx_set non_local_vars; - collect_non_local_vars(m_manager, r, tail, non_local_vars); + TRACE("mk_filter_rules", tout << "is_candidate: " << mk_pp(tail, m) << "\n";); + var_idx_set non_local_vars = rm.collect_rule_vars_ex(r, tail); func_decl * filter_decl = mk_filter_decl(tail, non_local_vars); ptr_buffer new_args; var_idx_set used_vars; @@ -127,7 +129,7 @@ namespace datalog { } } SASSERT(new_args.size() == filter_decl->get_arity()); - new_tail.push_back(m_manager.mk_app(filter_decl, new_args.size(), new_args.c_ptr())); + new_tail.push_back(m.mk_app(filter_decl, new_args.size(), new_args.c_ptr())); rule_modified = true; } else { @@ -135,12 +137,13 @@ namespace datalog { } new_is_negated.push_back(r->is_neg_tail(i)); } - if(rule_modified) { + if (rule_modified) { remove_duplicate_tails(new_tail, new_is_negated); SASSERT(new_tail.size() == new_is_negated.size()); rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), new_is_negated.c_ptr()); new_rule->set_accounting_parent_object(m_context, m_current); m_result->add_rule(new_rule); + m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule); m_modified = true; } else { @@ -148,20 +151,19 @@ namespace datalog { } } - rule_set * mk_filter_rules::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_filter_rules::operator()(rule_set const & source) { m_tail2filter.reset(); m_result = alloc(rule_set, m_context); m_modified = false; unsigned num_rules = source.get_num_rules(); for (unsigned i = 0; i < num_rules; i++) { - rule * r = source.get_rule(i); - process(r); + process(source.get_rule(i)); } if(!m_modified) { dealloc(m_result); return static_cast(0); } + m_result->inherit_predicates(source); return m_result; } diff --git a/src/muz_qe/dl_mk_filter_rules.h b/src/muz/transforms/dl_mk_filter_rules.h similarity index 82% rename from src/muz_qe/dl_mk_filter_rules.h rename to src/muz/transforms/dl_mk_filter_rules.h index cd1d10997..b51cb8e24 100644 --- a/src/muz_qe/dl_mk_filter_rules.h +++ b/src/muz/transforms/dl_mk_filter_rules.h @@ -45,17 +45,22 @@ namespace datalog { filter_key(ast_manager & m) : new_pred(m), filter_args(m) {} unsigned hash() const { - return new_pred->hash() ^ int_vector_hash(filter_args); + unsigned r = new_pred->hash(); + for (unsigned i = 0; i < filter_args.size(); ++i) { + r ^= filter_args[i]->hash(); + } + return r; } bool operator==(const filter_key & o) const { return o.new_pred==new_pred && vectors_equal(o.filter_args, filter_args); } }; - typedef map, deref_eq > filter_cache; + typedef obj_map filter_cache; context & m_context; - ast_manager & m_manager; + ast_manager & m; + rule_manager & rm; filter_cache m_tail2filter; rule_set * m_result; rule * m_current; @@ -72,7 +77,7 @@ namespace datalog { /** \brief Return a new rule set where only filter rules contain atoms with repeated variables and/or values. */ - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp similarity index 76% rename from src/muz_qe/dl_mk_interp_tail_simplifier.cpp rename to src/muz/transforms/dl_mk_interp_tail_simplifier.cpp index d028c8751..b01b4326c 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp @@ -35,7 +35,7 @@ namespace datalog { // ----------------------------------- void mk_interp_tail_simplifier::rule_substitution::reset(rule * r) { - unsigned var_cnt = m_context.get_rule_manager().get_var_counter().get_max_var(*r)+1; + unsigned var_cnt = m_context.get_rule_manager().get_counter().get_max_rule_var(*r)+1; m_subst.reset(); m_subst.reserve(1, var_cnt); m_rule = r; @@ -67,24 +67,23 @@ namespace datalog { void mk_interp_tail_simplifier::rule_substitution::get_result(rule_ref & res) { SASSERT(m_rule); - app_ref new_head(m); - apply(m_rule->get_head(), new_head); + apply(m_rule->get_head(), m_head); - app_ref_vector tail(m); - svector tail_neg; + m_tail.reset(); + m_neg.reset(); unsigned tail_len = m_rule->get_tail_size(); for (unsigned i=0; iget_tail(i), new_tail_el); - tail.push_back(new_tail_el); - tail_neg.push_back(m_rule->is_neg_tail(i)); + m_tail.push_back(new_tail_el); + m_neg.push_back(m_rule->is_neg_tail(i)); } - mk_rule_inliner::remove_duplicate_tails(tail, tail_neg); + mk_rule_inliner::remove_duplicate_tails(m_tail, m_neg); - SASSERT(tail.size() == tail_neg.size()); - res = m_context.get_rule_manager().mk(new_head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); + SASSERT(m_tail.size() == m_neg.size()); + res = m_context.get_rule_manager().mk(m_head, m_tail.size(), m_tail.c_ptr(), m_neg.c_ptr()); res->set_accounting_parent_object(m_context, m_rule); res->norm_vars(res.get_manager()); } @@ -296,18 +295,40 @@ namespace datalog { br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - if (m.is_not(f)) { + + if (m.is_not(f) && (m.is_and(args[0]) || m.is_or(args[0]))) { SASSERT(num==1); - if (m.is_and(args[0]) || m.is_or(args[0])) { - expr_ref e(m.mk_not(args[0]),m); - if (push_toplevel_junction_negation_inside(e)) { - result = e; - return BR_REWRITE2; - } + expr_ref tmp(m); + app* a = to_app(args[0]); + m_app_args.reset(); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + m_brwr.mk_not(a->get_arg(i), tmp); + m_app_args.push_back(tmp); } + if (m.is_and(args[0])) { + result = m.mk_or(m_app_args.size(), m_app_args.c_ptr()); + } + else { + result = m.mk_and(m_app_args.size(), m_app_args.c_ptr()); + } + return BR_REWRITE2; + } + if (!m.is_and(f) && !m.is_or(f)) { + return BR_FAILED; + } + if (num == 0) { + if (m.is_and(f)) { + result = m.mk_true(); + } + else { + result = m.mk_false(); + } + return BR_DONE; + } + if (num == 1) { + result = args[0]; + return BR_DONE; } - if (!m.is_and(f) && !m.is_or(f)) { return BR_FAILED; } - if (num<2) { return BR_FAILED; } m_app_args.reset(); m_app_args.append(num, args); @@ -318,30 +339,18 @@ namespace datalog { bool have_rewritten_args = false; - if (m.is_or(f) || m.is_and(f)) { - have_rewritten_args = detect_equivalences(m_app_args, m.is_or(f)); -#if 0 - if (have_rewritten_args) { - std::sort(m_app_args.c_ptr(), m_app_args.c_ptr()+m_app_args.size(), m_expr_cmp); - - app_ref orig(m.mk_app(f, num, args),m); - app_ref res(m.mk_app(f, m_app_args.size(), m_app_args.c_ptr()),m); - std::cout<<"s:"< { + public: + normalizer_rw(ast_manager& m, normalizer_cfg& cfg): rewriter_tpl(m, false, cfg) {} + }; + + + mk_interp_tail_simplifier::mk_interp_tail_simplifier(context & ctx, unsigned priority) + : plugin(priority), + m(ctx.get_manager()), + m_context(ctx), + m_simp(ctx.get_rewriter()), + a(m), + m_rule_subst(ctx), + m_tail(m), + m_itail_members(m), + m_conj(m) { + m_cfg = alloc(normalizer_cfg, m); + m_rw = alloc(normalizer_rw, m, *m_cfg); + } + + mk_interp_tail_simplifier::~mk_interp_tail_simplifier() { + dealloc(m_rw); + dealloc(m_cfg); + } + + void mk_interp_tail_simplifier::simplify_expr(app * a, expr_ref& res) { expr_ref simp1_res(m); m_simp(a, simp1_res); - normalizer_cfg r_cfg(m); - rewriter_tpl rwr(m, false, r_cfg); - expr_ref dl_form_e(m); - rwr(simp1_res.get(), res); + (*m_rw)(simp1_res.get(), res); /*if (simp1_res.get()!=res.get()) { std::cout<<"pre norm:\n"< todo; + m_todo.reset(); + m_leqs.reset(); for (unsigned i = u_len; i < len; i++) { - todo.push_back(r->get_tail(i)); + m_todo.push_back(r->get_tail(i)); SASSERT(!r->is_neg_tail(i)); } m_rule_subst.reset(r); - obj_hashtable leqs; expr_ref_vector trail(m); expr_ref tmp1(m), tmp2(m); bool found_something = false; @@ -391,10 +423,10 @@ namespace datalog { #define TRY_UNIFY(_x,_y) if (m_rule_subst.unify(_x,_y)) { found_something = true; } #define IS_FLEX(_x) (is_var(_x) || m.is_value(_x)) - while (!todo.empty()) { + while (!m_todo.empty()) { expr * arg1, *arg2; - expr * t0 = todo.back(); - todo.pop_back(); + expr * t0 = m_todo.back(); + m_todo.pop_back(); expr* t = t0; bool neg = m.is_not(t, t); if (is_var(t)) { @@ -402,7 +434,7 @@ namespace datalog { } else if (!neg && m.is_and(t)) { app* a = to_app(t); - todo.append(a->get_num_args(), a->get_args()); + m_todo.append(a->get_num_args(), a->get_args()); } else if (!neg && m.is_eq(t, arg1, arg2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); @@ -430,12 +462,12 @@ namespace datalog { else if (!neg && (a.is_le(t, arg1, arg2) || a.is_ge(t, arg2, arg1))) { tmp1 = a.mk_sub(arg1, arg2); tmp2 = a.mk_sub(arg2, arg1); - if (false && leqs.contains(tmp2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { + if (false && m_leqs.contains(tmp2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); } else { trail.push_back(tmp1); - leqs.insert(tmp1); + m_leqs.insert(tmp1); } } } @@ -469,18 +501,18 @@ namespace datalog { start: unsigned u_len = r->get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); - if (u_len==len) { + if (u_len == len) { res = r; return true; } app_ref head(r->get_head(), m); - app_ref_vector tail(m); - svector tail_neg; + m_tail.reset(); + m_tail_neg.reset(); for (unsigned i=0; iget_tail(i)); - tail_neg.push_back(r->is_neg_tail(i)); + m_tail.push_back(r->get_tail(i)); + m_tail_neg.push_back(r->is_neg_tail(i)); } bool modified = false; @@ -492,47 +524,42 @@ namespace datalog { SASSERT(!r->is_neg_tail(u_len)); } else { - expr_ref_vector itail_members(m); + m_itail_members.reset(); for (unsigned i=u_len; iget_tail(i)); + m_itail_members.push_back(r->get_tail(i)); SASSERT(!r->is_neg_tail(i)); } - itail = m.mk_and(itail_members.size(), itail_members.c_ptr()); + itail = m.mk_and(m_itail_members.size(), m_itail_members.c_ptr()); modified = true; } expr_ref simp_res(m); simplify_expr(itail.get(), simp_res); - modified |= itail.get()!=simp_res.get(); - - if (is_app(simp_res.get())) { - itail = to_app(simp_res.get()); - } - else if (m.is_bool(simp_res)) { - itail = m.mk_eq(simp_res, m.mk_true()); - } - else { - throw default_exception("simplification resulted in non-boolean non-function"); - } - - if (m.is_false(itail.get())) { - //the tail member is never true, so we may delete the rule + modified |= itail.get() != simp_res.get(); + + if (m.is_false(simp_res)) { TRACE("dl", r->display(m_context, tout << "rule is infeasible\n");); return false; } - if (!m.is_true(itail.get())) { - //if the simplified tail is not a tautology, we add it to the rule - tail.push_back(itail); - tail_neg.push_back(false); - } - else { - modified = true; - } + SASSERT(m.is_bool(simp_res)); - SASSERT(tail.size() == tail_neg.size()); if (modified) { - res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); + m_conj.reset(); + 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)) { + m_tail.push_back(to_app(e)); + } + else { + m_tail.push_back(m.mk_eq(e, m.mk_true())); + } + m_tail_neg.push_back(false); + } + + SASSERT(m_tail.size() == m_tail_neg.size()); + res = m_context.get_rule_manager().mk(head, m_tail.size(), m_tail.c_ptr(), m_tail_neg.c_ptr()); res->set_accounting_parent_object(m_context, r); } else { @@ -541,8 +568,8 @@ namespace datalog { rule_ref pro_var_eq_result(m_context.get_rule_manager()); if (propagate_variable_equivalences(res, pro_var_eq_result)) { - SASSERT(var_counter().get_max_var(*r.get())==0 || - var_counter().get_max_var(*r.get()) > var_counter().get_max_var(*pro_var_eq_result.get())); + SASSERT(rule_counter().get_max_rule_var(*r.get())==0 || + rule_counter().get_max_rule_var(*r.get()) > rule_counter().get_max_rule_var(*pro_var_eq_result.get())); r = pro_var_eq_result; goto start; } @@ -554,11 +581,13 @@ namespace datalog { bool mk_interp_tail_simplifier::transform_rules(const rule_set & orig, rule_set & tgt) { bool modified = false; + rule_manager& rm = m_context.get_rule_manager(); rule_set::iterator rit = orig.begin(); rule_set::iterator rend = orig.end(); for (; rit!=rend; ++rit) { - rule_ref new_rule(m_context.get_rule_manager()); + rule_ref new_rule(rm); if (transform_rule(*rit, new_rule)) { + rm.mk_rule_rewrite_proof(**rit, *new_rule.get()); bool is_modified = *rit != new_rule; modified |= is_modified; tgt.add_rule(new_rule); @@ -570,14 +599,15 @@ namespace datalog { return modified; } - rule_set * mk_interp_tail_simplifier::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_interp_tail_simplifier::operator()(rule_set const & source) { if (source.get_num_rules() == 0) { return 0; } rule_set * res = alloc(rule_set, m_context); - if (!transform_rules(source, *res)) { + if (transform_rules(source, *res)) { + res->inherit_predicates(source); + } else { dealloc(res); res = 0; } diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.h b/src/muz/transforms/dl_mk_interp_tail_simplifier.h similarity index 77% rename from src/muz_qe/dl_mk_interp_tail_simplifier.h rename to src/muz/transforms/dl_mk_interp_tail_simplifier.h index 5e4fd8fbc..5047e1c6e 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.h +++ b/src/muz/transforms/dl_mk_interp_tail_simplifier.h @@ -34,15 +34,17 @@ namespace datalog { { ast_manager& m; context& m_context; - substitution m_subst; - unifier m_unif; - - rule * m_rule; + substitution m_subst; + unifier m_unif; + app_ref m_head; + app_ref_vector m_tail; + svector m_neg; + rule * m_rule; void apply(app * a, app_ref& res); public: rule_substitution(context & ctx) - : m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_rule(0) {} + : m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_head(m), m_tail(m), m_rule(0) {} /** Reset substitution and get it ready for working with rule r. @@ -61,13 +63,23 @@ namespace datalog { } }; + class normalizer_cfg; + class normalizer_rw; + ast_manager & m; context & m_context; th_rewriter & m_simp; arith_util a; rule_substitution m_rule_subst; + ptr_vector m_todo; + obj_hashtable m_leqs; + app_ref_vector m_tail; + expr_ref_vector m_itail_members; + expr_ref_vector m_conj; + svector m_tail_neg; + normalizer_cfg* m_cfg; + normalizer_rw* m_rw; - class normalizer_cfg; void simplify_expr(app * a, expr_ref& res); @@ -77,13 +89,8 @@ namespace datalog { /** Return true if something was modified */ bool transform_rules(const rule_set & orig, rule_set & tgt); public: - mk_interp_tail_simplifier(context & ctx, unsigned priority=40000) - : plugin(priority), - m(ctx.get_manager()), - m_context(ctx), - m_simp(ctx.get_rewriter()), - a(m), - m_rule_subst(ctx) {} + mk_interp_tail_simplifier(context & ctx, unsigned priority=40000); + virtual ~mk_interp_tail_simplifier(); /**If rule should be retained, assign transformed version to res and return true; if rule can be deleted, return false. @@ -93,7 +100,7 @@ namespace datalog { */ bool transform_rule(rule * r, rule_ref& res); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; 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..ced0d39f4 --- /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.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/transforms/dl_mk_loop_counter.cpp b/src/muz/transforms/dl_mk_loop_counter.cpp new file mode 100644 index 000000000..678bfc5a3 --- /dev/null +++ b/src/muz/transforms/dl_mk_loop_counter.cpp @@ -0,0 +1,158 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_loop_counter.cpp + +Abstract: + + Add loop counter argument to relations. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-31 + +Revision History: + +--*/ + +#include"dl_mk_loop_counter.h" +#include"dl_context.h" + +namespace datalog { + + mk_loop_counter::mk_loop_counter(context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx), + a(m), + m_refs(m) { + } + + mk_loop_counter::~mk_loop_counter() { } + + app_ref mk_loop_counter::add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx) { + expr_ref_vector args(m); + func_decl* new_fn, *old_fn = fn->get_decl(); + args.append(fn->get_num_args(), fn->get_args()); + args.push_back(m.mk_var(idx, a.mk_int())); + + if (!m_old2new.find(old_fn, new_fn)) { + ptr_vector domain; + domain.append(fn->get_num_args(), old_fn->get_domain()); + domain.push_back(a.mk_int()); + new_fn = m.mk_func_decl(old_fn->get_name(), domain.size(), domain.c_ptr(), old_fn->get_range()); + m_old2new.insert(old_fn, new_fn); + m_new2old.insert(new_fn, old_fn); + m_refs.push_back(new_fn); + m_ctx.register_predicate(new_fn, false); + if (src.is_output_predicate(old_fn)) { + dst.set_output_predicate(new_fn); + } + } + return app_ref(m.mk_app(new_fn, args.size(), args.c_ptr()), m); + } + + app_ref mk_loop_counter::del_arg(app* fn) { + expr_ref_vector args(m); + func_decl* old_fn, *new_fn = fn->get_decl(); + SASSERT(fn->get_num_args() > 0); + args.append(fn->get_num_args()-1, fn->get_args()); + VERIFY (m_new2old.find(new_fn, old_fn)); + return app_ref(m.mk_app(old_fn, args.size(), args.c_ptr()), m); + } + + rule_set * mk_loop_counter::operator()(rule_set const & source) { + m_refs.reset(); + m_old2new.reset(); + m_new2old.reset(); + rule_manager& rm = source.get_rule_manager(); + 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; + rule_counter& vc = rm.get_counter(); + for (unsigned i = 0; i < sz; ++i) { + tail.reset(); + neg.reset(); + rule & r = *source.get_rule(i); + unsigned cnt = vc.get_max_rule_var(r)+1; + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + for (unsigned j = 0; j < utsz; ++j, ++cnt) { + tail.push_back(add_arg(source, *result, r.get_tail(j), cnt)); + neg.push_back(r.is_neg_tail(j)); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + neg.push_back(false); + } + head = add_arg(source, *result, r.get_head(), cnt); + // set the loop counter to be an increment of the previous + bool found = false; + unsigned last = head->get_num_args()-1; + for (unsigned j = 0; !found && j < utsz; ++j) { + if (head->get_decl() == tail[j]->get_decl()) { + tail.push_back(m.mk_eq(head->get_arg(last), + a.mk_add(tail[j]->get_arg(last), + a.mk_numeral(rational(1), true)))); + neg.push_back(false); + found = true; + } + } + // initialize loop counter to 0 if none was found. + if (!found) { + expr_ref_vector args(m); + args.append(head->get_num_args(), head->get_args()); + args[last] = a.mk_numeral(rational(0), true); + head = m.mk_app(head->get_decl(), args.size(), args.c_ptr()); + } + + new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + } + + // model converter: remove references to extra argument. + // proof converter: remove references to extra argument as well. + + return result; + } + + rule_set * mk_loop_counter::revert(rule_set const & source) { + context& ctx = source.get_context(); + rule_manager& rm = source.get_rule_manager(); + rule_set * result = alloc(rule_set, ctx); + unsigned sz = source.get_num_rules(); + rule_ref new_rule(rm); + app_ref_vector tail(m); + app_ref head(m); + svector neg; + for (unsigned i = 0; i < sz; ++i) { + tail.reset(); + neg.reset(); + rule & r = *source.get_rule(i); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + for (unsigned j = 0; j < utsz; ++j) { + tail.push_back(del_arg(r.get_tail(j))); + neg.push_back(r.is_neg_tail(j)); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + neg.push_back(false); + } + head = del_arg(r.get_head()); + new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + } + + // model converter: ... + // proof converter: ... + + return result; + + } +}; diff --git a/src/muz/transforms/dl_mk_loop_counter.h b/src/muz/transforms/dl_mk_loop_counter.h new file mode 100644 index 000000000..d67c88e0e --- /dev/null +++ b/src/muz/transforms/dl_mk_loop_counter.h @@ -0,0 +1,51 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_loop_counter.h + +Abstract: + + Add loop counter argument to relations. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-31 + +Revision History: + +--*/ +#ifndef _DL_MK_LOOP_COUNTER_H_ +#define _DL_MK_LOOP_COUNTER_H_ + +#include"dl_rule_transformer.h" +#include"arith_decl_plugin.h" + +namespace datalog { + + class mk_loop_counter : public rule_transformer::plugin { + ast_manager& m; + context& m_ctx; + arith_util a; + func_decl_ref_vector m_refs; + obj_map m_new2old; + obj_map m_old2new; + + app_ref add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx); + app_ref del_arg(app* fn); + public: + mk_loop_counter(context & ctx, unsigned priority = 33000); + ~mk_loop_counter(); + + rule_set * operator()(rule_set const & source); + + func_decl* get_old(func_decl* f) const { return m_new2old.find(f); } + + rule_set * revert(rule_set const& source); + }; + +}; + +#endif /* _DL_MK_LOOP_COUNTER_H_ */ + diff --git a/src/muz_qe/dl_mk_magic_sets.cpp b/src/muz/transforms/dl_mk_magic_sets.cpp similarity index 70% rename from src/muz_qe/dl_mk_magic_sets.cpp rename to src/muz/transforms/dl_mk_magic_sets.cpp index 373a90969..48bd69255 100644 --- a/src/muz_qe/dl_mk_magic_sets.cpp +++ b/src/muz/transforms/dl_mk_magic_sets.cpp @@ -24,13 +24,13 @@ Revision History: namespace datalog { - mk_magic_sets::mk_magic_sets(context & ctx, rule * goal_rule) : + mk_magic_sets::mk_magic_sets(context & ctx, func_decl* goal) : plugin(10000, true), m_context(ctx), - m_manager(ctx.get_manager()), - m_rules(ctx.get_rule_manager()), - m_pinned(m_manager), - m_goal_rule(goal_rule, ctx.get_rule_manager()) { + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_pinned(m), + m_goal(goal, m) { } void mk_magic_sets::reset() { @@ -39,14 +39,13 @@ namespace datalog { m_adorned_preds.reset(); m_adornments.reset(); m_magic_preds.reset(); - m_rules.reset(); m_pinned.reset(); } void mk_magic_sets::adornment::populate(app * lit, const var_idx_set & bound_vars) { SASSERT(empty()); unsigned arity = lit->get_num_args(); - for(unsigned i=0; iget_arg(i); bool bound = !is_var(arg) || bound_vars.contains(to_var(arg)->get_idx()); push_back(bound ? AD_BOUND : AD_FREE); @@ -57,17 +56,8 @@ namespace datalog { std::string res; const_iterator eit = begin(); const_iterator eend = end(); - for(; eit!=eend; ++eit) { - switch(*eit) { - case AD_BOUND: - res+='b'; - break; - case AD_FREE: - res+='f'; - break; - default: - UNREACHABLE(); - } + for (; eit != eend; ++eit) { + res += (*eit == AD_BOUND)?'b':'f'; } return res; } @@ -75,14 +65,13 @@ namespace datalog { unsigned get_bound_arg_count(app * lit, const var_idx_set & bound_vars) { unsigned res = 0; unsigned n = lit->get_num_args(); - for(unsigned i=0; iget_arg(i); - if(is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { - continue; + if (!is_var(arg) || bound_vars.contains(to_var(arg)->get_idx())) { + SASSERT(is_var(arg) || is_app(arg)); + SASSERT(!is_app(arg) || to_app(arg)->get_num_args()==0); + res++; } - SASSERT(is_var(arg) || is_app(arg)); - SASSERT(!is_app(arg) || to_app(arg)->get_num_args()==0); - res++; } return res; } @@ -91,10 +80,10 @@ namespace datalog { func_decl * pred = lit->get_decl(); float res = 1; unsigned n = lit->get_num_args(); - for(unsigned i=0; iget_arg(i); - if(is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { - res*=m_context.get_sort_size_estimate(pred->get_domain(i)); + if (is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { + res *= m_context.get_sort_size_estimate(pred->get_domain(i)); } //res-=1; } @@ -111,22 +100,22 @@ namespace datalog { float best_cost; int candidate_index = -1; unsigned n = cont.size(); - for(unsigned i=0; iget_tail(cont[i]); unsigned bound_cnt = get_bound_arg_count(lit, bound_vars); - if(bound_cnt==0) { + if (bound_cnt==0) { continue; } float cost = get_unbound_cost(lit, bound_vars); - if(candidate_index==-1 || cost(n-1)) { + if (candidate_index != static_cast(n-1)) { std::swap(cont[candidate_index], cont[n-1]); } unsigned res = cont.back(); @@ -137,12 +126,12 @@ namespace datalog { app * mk_magic_sets::adorn_literal(app * lit, const var_idx_set & bound_vars) { SASSERT(!m_extentional.contains(lit->get_decl())); func_decl * old_pred = lit->get_decl(); - SASSERT(m_manager.is_bool(old_pred->get_range())); + SASSERT(m.is_bool(old_pred->get_range())); adornment_desc adn(old_pred); adn.m_adornment.populate(lit, bound_vars); adornment_map::entry * e = m_adorned_preds.insert_if_not_there2(adn, 0); func_decl * new_pred = e->get_data().m_value; - if(new_pred==0) { + if (new_pred==0) { std::string suffix = "ad_"+adn.m_adornment.to_string(); new_pred = m_context.mk_fresh_head_predicate( old_pred->get_name(), symbol(suffix.c_str()), @@ -152,34 +141,34 @@ namespace datalog { m_todo.push_back(adn); m_adornments.insert(new_pred, adn.m_adornment); } - app * res = m_manager.mk_app(new_pred, lit->get_args()); + app * res = m.mk_app(new_pred, lit->get_args()); m_pinned.push_back(res); return res; } app * mk_magic_sets::create_magic_literal(app * l) { func_decl * l_pred = l->get_decl(); - SASSERT(m_manager.is_bool(l_pred->get_range())); + SASSERT(m.is_bool(l_pred->get_range())); pred_adornment_map::obj_map_entry * ae = m_adornments.find_core(l_pred); SASSERT(ae); const adornment & adn = ae->get_data().m_value; unsigned l_arity = l->get_num_args(); ptr_vector bound_args; - for(unsigned i=0; iget_arg(i)); } } pred2pred::obj_map_entry * e = m_magic_preds.insert_if_not_there2(l_pred, 0); func_decl * mag_pred = e->get_data().m_value; - if(mag_pred==0) { + if (mag_pred==0) { unsigned mag_arity = bound_args.size(); ptr_vector mag_domain; - for(unsigned i=0; iget_domain(i)); } } @@ -190,12 +179,12 @@ namespace datalog { e->get_data().m_value = mag_pred; } - app * res = m_manager.mk_app(mag_pred, bound_args.c_ptr()); + app * res = m.mk_app(mag_pred, bound_args.c_ptr()); m_pinned.push_back(res); return res; } - void mk_magic_sets::create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated) { + void mk_magic_sets::create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated, rule_set& result) { //TODO: maybe include relevant interpreted predicates from the original rule ptr_vector new_tail; svector negations; @@ -204,26 +193,26 @@ namespace datalog { negations.push_back(false); negations.append(tail_cnt, negated); - for(unsigned i=0; iget_decl())) { + for (unsigned i=0; iget_decl())) { continue; } app * mag_head = create_magic_literal(tail[i]); rule * r = m_context.get_rule_manager().mk(mag_head, i+1, new_tail.c_ptr(), negations.c_ptr()); TRACE("dl", r->display(m_context,tout); ); - m_rules.push_back(r); + result.add_rule(r); } } - void mk_magic_sets::transform_rule(const adornment & head_adornment, rule * r) { + void mk_magic_sets::transform_rule(const adornment & head_adornment, rule * r, rule_set& result) { app * head = r->get_head(); unsigned head_len = head->get_num_args(); SASSERT(head_len==head_adornment.size()); var_idx_set bound_vars; - for(unsigned i=0; iget_arg(i); - if(head_adornment[i]==AD_BOUND && is_var(arg)) { + if (head_adornment[i]==AD_BOUND && is_var(arg)) { bound_vars.insert(to_var(arg)->get_idx()); } } @@ -231,9 +220,9 @@ namespace datalog { unsigned processed_tail_len = r->get_uninterpreted_tail_size(); unsigned_vector exten_tails; unsigned_vector inten_tails; - for(unsigned i=0; iget_tail(i); - if(m_extentional.contains(t->get_decl())) { + if (m_extentional.contains(t->get_decl())) { exten_tails.push_back(i); } else { @@ -243,17 +232,17 @@ namespace datalog { ptr_vector new_tail; svector negations; - while(new_tail.size()!=processed_tail_len) { + while (new_tail.size()!=processed_tail_len) { bool intentional = false; int curr_index = pop_bound(exten_tails, r, bound_vars); - if(curr_index==-1) { + if (curr_index==-1) { curr_index = pop_bound(inten_tails, r,bound_vars); - if(curr_index!=-1) { + if (curr_index!=-1) { intentional = true; } } - if(curr_index==-1) { - if(!exten_tails.empty()) { + if (curr_index==-1) { + if (!exten_tails.empty()) { curr_index = exten_tails.back(); exten_tails.pop_back(); } @@ -266,24 +255,24 @@ namespace datalog { } SASSERT(curr_index!=-1); app * curr = r->get_tail(curr_index); - if(intentional) { + if (intentional) { curr = adorn_literal(curr, bound_vars); } new_tail.push_back(curr); negations.push_back(r->is_neg_tail(curr_index)); - collect_vars(m_manager, curr, bound_vars); + bound_vars |= rm.collect_vars(curr); } func_decl * new_head_pred; VERIFY( m_adorned_preds.find(adornment_desc(head->get_decl(), head_adornment), new_head_pred) ); - app * new_head = m_manager.mk_app(new_head_pred, head->get_args()); + app * new_head = m.mk_app(new_head_pred, head->get_args()); SASSERT(new_tail.size()==r->get_uninterpreted_tail_size()); - create_magic_rules(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr()); + create_magic_rules(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr(), result); unsigned tail_len = r->get_tail_size(); - for(unsigned i=processed_tail_len; iget_tail(i)); negations.push_back(r->is_neg_tail(i)); } @@ -292,43 +281,51 @@ namespace datalog { negations.push_back(false); rule * nr = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr()); - m_rules.push_back(nr); + result.add_rule(nr); nr->set_accounting_parent_object(m_context, r); } - void mk_magic_sets::create_transfer_rule(const adornment_desc & d) { - func_decl * adn_pred; - TRUSTME( m_adorned_preds.find(d, adn_pred) ); + void mk_magic_sets::create_transfer_rule(const adornment_desc & d, rule_set& result) { + func_decl * adn_pred = m_adorned_preds.find(d); unsigned arity = adn_pred->get_arity(); - SASSERT(arity==d.m_pred->get_arity()); + SASSERT(arity == d.m_pred->get_arity()); ptr_vector args; - for(unsigned i=0; iget_domain(i))); + for (unsigned i=0; iget_domain(i))); } - app * lit = m_manager.mk_app(d.m_pred, args.c_ptr()); - app * adn_lit = m_manager.mk_app(adn_pred, args.c_ptr()); + app * lit = m.mk_app(d.m_pred, args.c_ptr()); + app * adn_lit = m.mk_app(adn_pred, args.c_ptr()); app * mag_lit = create_magic_literal(adn_lit); app * tail[] = {lit, mag_lit}; rule * r = m_context.get_rule_manager().mk(adn_lit, 2, tail, 0); - m_rules.push_back(r); + result.add_rule(r); } - rule_set * mk_magic_sets::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - SASSERT(!mc && !pc); + rule_set * mk_magic_sets::operator()(rule_set const & source) { + + if (!m_context.magic_sets_for_queries()) { + return 0; + } + SASSERT(source.contains(m_goal)); + SASSERT(source.get_predicate_rules(m_goal).size() == 1); + + app * goal_head = source.get_predicate_rules(m_goal)[0]->get_head(); + unsigned init_rule_cnt = source.get_num_rules(); { func_decl_set intentional; - for(unsigned i=0; iget_head()->get_decl()); + for (unsigned i=0; iget_decl(); + intentional.insert(pred); } //now we iterate through all predicates and collect the set of extentional ones const rule_dependencies * deps; rule_dependencies computed_deps(m_context); - if(source.is_closed()) { + if (source.is_closed()) { deps = &source.get_dependencies(); } else { @@ -337,9 +334,9 @@ namespace datalog { } rule_dependencies::iterator it = deps->begin(); rule_dependencies::iterator end = deps->end(); - for(; it!=end; ++it) { + for (; it!=end; ++it) { func_decl * pred = it->m_key; - if(intentional.contains(pred)) { + if (intentional.contains(pred)) { continue; } SASSERT(it->m_value->empty());//extentional predicates have no dependency @@ -347,48 +344,39 @@ namespace datalog { } } - SASSERT(m_rules.empty()); - - app * goal_head = m_goal_rule->get_head(); //adornment goal_adn; //goal_adn.populate(goal_head, ); var_idx_set empty_var_idx_set; adorn_literal(goal_head, empty_var_idx_set); - while(!m_todo.empty()) { + rule_set * result = alloc(rule_set, m_context); + result->inherit_predicates(source); + + while (!m_todo.empty()) { adornment_desc task = m_todo.back(); m_todo.pop_back(); const rule_vector & pred_rules = source.get_predicate_rules(task.m_pred); rule_vector::const_iterator it = pred_rules.begin(); rule_vector::const_iterator end = pred_rules.end(); - for(; it!=end; ++it) { + for (; it != end; ++it) { rule * r = *it; - transform_rule(task.m_adornment, r); + 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); + create_transfer_rule(task, *result); } } app * adn_goal_head = adorn_literal(goal_head, empty_var_idx_set); app * mag_goal_head = create_magic_literal(adn_goal_head); SASSERT(mag_goal_head->is_ground()); - //SASSERT(is_fact(m_manager, mag_goal_head)); - //m_context.add_fact(mag_goal_head); rule * mag_goal_rule = m_context.get_rule_manager().mk(mag_goal_head, 0, 0, 0); - m_rules.push_back(mag_goal_rule); + result->add_rule(mag_goal_rule); rule * back_to_goal_rule = m_context.get_rule_manager().mk(goal_head, 1, &adn_goal_head, 0); - m_rules.push_back(back_to_goal_rule); - - rule_set * result = static_cast(0); - result = alloc(rule_set, m_context); - unsigned fin_rule_cnt = m_rules.size(); - for(unsigned i=0; iadd_rule(m_rules.get(i)); - } + result->add_rule(back_to_goal_rule); return result; } }; diff --git a/src/muz_qe/dl_mk_magic_sets.h b/src/muz/transforms/dl_mk_magic_sets.h similarity index 79% rename from src/muz_qe/dl_mk_magic_sets.h rename to src/muz/transforms/dl_mk_magic_sets.h index f98ad55a3..3496a5967 100644 --- a/src/muz_qe/dl_mk_magic_sets.h +++ b/src/muz/transforms/dl_mk_magic_sets.h @@ -47,6 +47,11 @@ namespace datalog { AD_BOUND }; + struct a_flag_hash { + typedef a_flag data; + unsigned operator()(a_flag x) const { return x; } + }; + struct adornment : public svector { void populate(app * lit, const var_idx_set & bound_vars); @@ -71,7 +76,7 @@ namespace datalog { return m_pred==o.m_pred && m_adornment==o.m_adornment; } unsigned hash() const { - return m_pred->hash()^int_vector_hash(m_adornment); + return m_pred->hash()^svector_hash()(m_adornment); } }; @@ -88,21 +93,21 @@ namespace datalog { typedef obj_map pred_adornment_map; typedef obj_map pred2pred; - context & m_context; - ast_manager & m_manager; - rule_ref_vector m_rules; - ast_ref_vector m_pinned; - rule_ref m_goal_rule; + context & m_context; + ast_manager & m; + rule_manager& rm; + ast_ref_vector m_pinned; /** \brief Predicates from the original set that appear in a head of a rule */ - func_decl_set m_extentional; + func_decl_set m_extentional; //adornment_set m_processed; vector m_todo; - adornment_map m_adorned_preds; - pred_adornment_map m_adornments; - pred2pred m_magic_preds; + adornment_map m_adorned_preds; + pred_adornment_map m_adornments; + pred2pred m_magic_preds; + func_decl_ref m_goal; void reset(); @@ -110,18 +115,18 @@ namespace datalog { int pop_bound(unsigned_vector & cont, rule * r, const var_idx_set & bound_vars); app * create_magic_literal(app * l); - void create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated); + void create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated, rule_set& result); app * adorn_literal(app * lit, const var_idx_set & bound_vars); - void transform_rule(const adornment & head_adornment, rule * r); - void create_transfer_rule(const adornment_desc & d); + void transform_rule(const adornment & head_adornment, rule * r, rule_set& result); + void create_transfer_rule(const adornment_desc & d, rule_set& result); public: /** \brief Create magic sets rule transformer for \c goal_rule. When applying the transformer, the \c goal_rule must be present in the \c rule_set that is being transformed. */ - mk_magic_sets(context & ctx, rule * goal_rule); + mk_magic_sets(context & ctx, func_decl* goal); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz/transforms/dl_mk_magic_symbolic.cpp b/src/muz/transforms/dl_mk_magic_symbolic.cpp new file mode 100644 index 000000000..1d269cff0 --- /dev/null +++ b/src/muz/transforms/dl_mk_magic_symbolic.cpp @@ -0,0 +1,135 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_magic_symbolic.cpp + +Abstract: + + Create Horn clauses for magic symbolic flow. + + Q(x) :- A(y), B(z), phi1(x,y,z). + Q(x) :- C(y), phi2(x,y). + A(x) :- C(y), phi3(x,y). + A(x) :- A(y), phi3(x,y). + B(x) :- C(y), A(z), phi4(x,y,z). + C(x) :- phi5(x). + + Transformed clauses: + + Q_ans(x) :- Q_query(x), A_ans(y), B_ans(z), phi1(x,y,z). + Q_ans(x) :- Q_query(x), C_ans(y), phi2(x,y). + Q_query(x) :- true. + + A_ans(x) :- A_query(x), C_ans(y), phi2(x,y) + A_ans(x) :- A_query(x), A_ans(y), phi3(x,y). + A_query(y) :- Q_query(x), phi1(x,y,z). + A_query(y) :- A_query(x), phi3(x,y). + A_query(z) :- B_query(x), C_ans(y), phi4(x,y,z). + + B_ans(x) :- B_query(x), C_ans(y), A_ans(z), phi4(x,y,z). + B_query(z) :- Q_query(x), A_ans(y), phi1(x,y,z). + + C_ans(x) :- C_query(x), phi5(x). + C_query(y) :- Q_query(x), phi2(x,y). + C_query(y) :- Q_query(x), phi3(x,y). + C_query(y) :- B_query(x), phi4(x,y,z). + +General scheme: + A(x) :- P1(x_1), ..., Pn(x_n), phi(x,x1,..,x_n). + + P(x) :- Prefix(x,y,z), A(z) ... + + A_ans(x) :- A_query(x), P_i_ans(x_i), phi(x,..). + A_query(z) :- P_query(x), Prefix_ans(x,y,z). + +Author: + + Nikolaj Bjorner (nbjorner) 2013-06-19 + +Revision History: + +--*/ + +#include"dl_mk_magic_symbolic.h" +#include"dl_context.h" + +namespace datalog { + + + mk_magic_symbolic::mk_magic_symbolic(context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx) { + } + + mk_magic_symbolic::~mk_magic_symbolic() { } + + rule_set * mk_magic_symbolic::operator()(rule_set const & source) { + if (!m_ctx.magic()) { + return 0; + } + context& ctx = source.get_context(); + rule_manager& rm = source.get_rule_manager(); + rule_set * result = alloc(rule_set, ctx); + unsigned sz = source.get_num_rules(); + rule_ref new_rule(rm); + app_ref_vector tail(m); + app_ref head(m); + svector neg; + for (unsigned i = 0; i < sz; ++i) { + rule & r = *source.get_rule(i); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + tail.reset(); + neg.reset(); + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + neg.push_back(false); + } + tail.push_back(mk_query(r.get_head())); + neg.push_back(false); + for (unsigned j = 0; j < utsz; ++j) { + tail.push_back(mk_ans(r.get_tail(j))); + neg.push_back(false); + } + new_rule = rm.mk(mk_ans(r.get_head()), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + if (source.is_output_predicate(r.get_decl())) { + result->set_output_predicate(new_rule->get_decl()); + new_rule = rm.mk(mk_query(r.get_head()), 0, 0, 0, r.name(), true); + result->add_rule(new_rule); + } + + for (unsigned j = 0; j < utsz; ++j) { + new_rule = rm.mk(mk_query(r.get_tail(j)), tail.size()-utsz+j, tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + } + + } + TRACE("dl", result->display(tout);); + return result; + } + + app_ref mk_magic_symbolic::mk_query(app* q) { + string_buffer<64> name; + func_decl* f = q->get_decl(); + name << f->get_name() << "!query"; + func_decl_ref g(m); + g = m.mk_func_decl(symbol(name.c_str()), f->get_arity(), f->get_domain(), f->get_range()); + m_ctx.register_predicate(g, false); + return app_ref(m.mk_app(g, q->get_num_args(), q->get_args()), m); + } + + app_ref mk_magic_symbolic::mk_ans(app* q) { + string_buffer<64> name; + func_decl* f = q->get_decl(); + func_decl_ref g(m); + name << f->get_name() << "!ans"; + g = m.mk_func_decl(symbol(name.c_str()), f->get_arity(), f->get_domain(), f->get_range()); + m_ctx.register_predicate(g, false); + return app_ref(m.mk_app(g, q->get_num_args(), q->get_args()), m); + } + +}; diff --git a/src/muz/transforms/dl_mk_magic_symbolic.h b/src/muz/transforms/dl_mk_magic_symbolic.h new file mode 100644 index 000000000..ce9ca1145 --- /dev/null +++ b/src/muz/transforms/dl_mk_magic_symbolic.h @@ -0,0 +1,40 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_magic_symbolic.h + +Abstract: + + Create Horn clauses for magic symbolic transformation. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-06-19 + +Revision History: + +--*/ +#ifndef _DL_MK_MAGIC_SYMBOLIC_H_ +#define _DL_MK_MAGIC_SYMBOLIC_H_ + +#include"dl_rule_transformer.h" + +namespace datalog { + + class mk_magic_symbolic : public rule_transformer::plugin { + ast_manager& m; + context& m_ctx; + app_ref mk_ans(app* q); + app_ref mk_query(app* q); + public: + mk_magic_symbolic(context & ctx, unsigned priority = 33037); + ~mk_magic_symbolic(); + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_MAGIC_SYMBOLIC_H_ */ + diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp new file mode 100644 index 000000000..0ad4d61ab --- /dev/null +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp @@ -0,0 +1,368 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_quantifier_abstraction.cpp + +Abstract: + + Create quantified Horn clauses from benchmarks with arrays. + +Author: + + Ken McMillan + Andrey Rybalchenko + Nikolaj Bjorner (nbjorner) 2013-04-02 + +Revision History: + +--*/ + +#include "dl_mk_quantifier_abstraction.h" +#include "dl_context.h" +#include "expr_safe_replace.h" +#include "expr_abstract.h" +#include"fixedpoint_params.hpp" + + +namespace datalog { + + + // model converter: + // Given model for P^(x, y, i, a[i]) + // create model: P(x,y,a) == forall i . P^(x,y,i,a[i]) + // requires substitution and list of bound variables. + + class mk_quantifier_abstraction::qa_model_converter : public model_converter { + ast_manager& m; + func_decl_ref_vector m_old_funcs; + func_decl_ref_vector m_new_funcs; + vector m_subst; + vector m_sorts; + vector > m_bound; + + public: + + qa_model_converter(ast_manager& m): + m(m), m_old_funcs(m), m_new_funcs(m) {} + + virtual ~qa_model_converter() {} + + virtual model_converter * translate(ast_translation & translator) { + return alloc(qa_model_converter, m); + } + + void insert(func_decl* old_p, func_decl* new_p, expr_ref_vector& sub, sort_ref_vector& sorts, svector const& bound) { + m_old_funcs.push_back(old_p); + m_new_funcs.push_back(new_p); + m_subst.push_back(sub); + m_bound.push_back(bound); + m_sorts.push_back(sorts); + } + + virtual void operator()(model_ref & old_model) { + model_ref new_model = alloc(model, m); + for (unsigned i = 0; i < m_new_funcs.size(); ++i) { + func_decl* p = m_new_funcs[i].get(); + func_decl* q = m_old_funcs[i].get(); + expr_ref_vector const& sub = m_subst[i]; + sort_ref_vector const& sorts = m_sorts[i]; + svector const& is_bound = m_bound[i]; + func_interp* f = old_model->get_func_interp(p); + expr_ref body(m); + unsigned arity_p = p->get_arity(); + unsigned arity_q = q->get_arity(); + SASSERT(0 < arity_p); + func_interp* g = alloc(func_interp, m, arity_q); + + if (f) { + body = f->get_interp(); + SASSERT(!f->is_partial()); + SASSERT(body); + } + else { + body = m.mk_false(); + } + // Create quantifier wrapper around body. + + TRACE("dl", tout << mk_pp(body, m) << "\n";); + // 1. replace variables by the compound terms from + // the original predicate. + expr_safe_replace rep(m); + for (unsigned i = 0; i < sub.size(); ++i) { + rep.insert(m.mk_var(i, m.get_sort(sub[i])), sub[i]); + } + rep(body); + rep.reset(); + + TRACE("dl", tout << mk_pp(body, m) << "\n";); + // 2. replace bound variables by constants. + expr_ref_vector consts(m), bound(m), free(m); + svector names; + ptr_vector bound_sorts; + for (unsigned i = 0; i < sorts.size(); ++i) { + sort* s = sorts[i]; + consts.push_back(m.mk_fresh_const("C", s)); + rep.insert(m.mk_var(i, s), consts.back()); + if (is_bound[i]) { + bound.push_back(consts.back()); + names.push_back(symbol(i)); + bound_sorts.push_back(s); + } + else { + free.push_back(consts.back()); + } + } + rep(body); + rep.reset(); + + TRACE("dl", tout << mk_pp(body, m) << "\n";); + // 3. abstract and quantify those variables that should be bound. + expr_abstract(m, 0, bound.size(), bound.c_ptr(), body, body); + body = m.mk_forall(names.size(), bound_sorts.c_ptr(), names.c_ptr(), body); + + TRACE("dl", tout << mk_pp(body, m) << "\n";); + // 4. replace remaining constants by variables. + for (unsigned i = 0; i < free.size(); ++i) { + rep.insert(free[i].get(), m.mk_var(i, m.get_sort(free[i].get()))); + } + rep(body); + g->set_else(body); + TRACE("dl", tout << mk_pp(body, m) << "\n";); + + new_model->register_decl(q, g); + } + old_model = new_model; + } + }; + + mk_quantifier_abstraction::mk_quantifier_abstraction( + context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx), + a(m), + m_refs(m) { + } + + mk_quantifier_abstraction::~mk_quantifier_abstraction() { + } + + func_decl* mk_quantifier_abstraction::declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p) { + + if (rules.is_output_predicate(old_p)) { + dst.inherit_predicate(rules, old_p, old_p); + return 0; + } + + unsigned sz = old_p->get_arity(); + unsigned num_arrays = 0; + for (unsigned i = 0; i < sz; ++i) { + if (a.is_array(old_p->get_domain(i))) { + num_arrays++; + } + } + if (num_arrays == 0) { + return 0; + } + + func_decl* new_p = 0; + if (!m_old2new.find(old_p, new_p)) { + expr_ref_vector sub(m), vars(m); + svector bound; + sort_ref_vector domain(m), sorts(m); + expr_ref arg(m); + for (unsigned i = 0; i < sz; ++i) { + sort* s0 = old_p->get_domain(i); + unsigned lookahead = 0; + sort* s = s0; + while (a.is_array(s)) { + lookahead += get_array_arity(s); + s = get_array_range(s); + } + arg = m.mk_var(bound.size() + lookahead, s0); + s = s0; + while (a.is_array(s)) { + unsigned arity = get_array_arity(s); + expr_ref_vector args(m); + for (unsigned j = 0; j < arity; ++j) { + sort* s1 = get_array_domain(s, j); + domain.push_back(s1); + args.push_back(m.mk_var(bound.size(), s1)); + bound.push_back(true); + sorts.push_back(s1); + } + arg = mk_select(arg, args.size(), args.c_ptr()); + s = get_array_range(s); + } + domain.push_back(s); + bound.push_back(false); + sub.push_back(arg); + sorts.push_back(s0); + } + SASSERT(old_p->get_range() == m.mk_bool_sort()); + new_p = m.mk_func_decl(old_p->get_name(), domain.size(), domain.c_ptr(), old_p->get_range()); + m_refs.push_back(new_p); + m_ctx.register_predicate(new_p, false); + if (m_mc) { + m_mc->insert(old_p, new_p, sub, sorts, bound); + } + m_old2new.insert(old_p, new_p); + } + return new_p; + } + + app_ref mk_quantifier_abstraction::mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx) { + func_decl* new_p = declare_pred(rules, dst, p->get_decl()); + if (!new_p) { + return app_ref(p, m); + } + expr_ref_vector args(m); + expr_ref arg(m); + unsigned sz = p->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + arg = p->get_arg(i); + sort* s = m.get_sort(arg); + while (a.is_array(s)) { + unsigned arity = get_array_arity(s); + for (unsigned j = 0; j < arity; ++j) { + args.push_back(m.mk_var(idx++, get_array_domain(s, j))); + } + arg = mk_select(arg, arity, args.c_ptr()+args.size()-arity); + s = get_array_range(s); + } + args.push_back(arg); + } + TRACE("dl", + tout << mk_pp(new_p, m) << "\n"; + for (unsigned i = 0; i < args.size(); ++i) { + tout << mk_pp(args[i].get(), m) << "\n"; + }); + return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m); + } + + app_ref mk_quantifier_abstraction::mk_tail(rule_set const& rules, rule_set& dst, app* p) { + func_decl* old_p = p->get_decl(); + func_decl* new_p = declare_pred(rules, dst, old_p); + if (!new_p) { + return app_ref(p, m); + } + SASSERT(new_p->get_arity() > old_p->get_arity()); + unsigned num_extra_args = new_p->get_arity() - old_p->get_arity(); + var_shifter shift(m); + expr_ref p_shifted(m); + shift(p, num_extra_args, p_shifted); + app* ps = to_app(p_shifted); + expr_ref_vector args(m); + app_ref_vector pats(m); + sort_ref_vector vars(m); + svector names; + expr_ref arg(m); + unsigned idx = 0; + unsigned sz = p->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + arg = ps->get_arg(i); + sort* s = m.get_sort(arg); + bool is_pattern = false; + while (a.is_array(s)) { + is_pattern = true; + unsigned arity = get_array_arity(s); + for (unsigned j = 0; j < arity; ++j) { + vars.push_back(get_array_domain(s, j)); + names.push_back(symbol(idx)); + args.push_back(m.mk_var(idx++, vars.back())); + } + arg = mk_select(arg, arity, args.c_ptr()+args.size()-arity); + s = get_array_range(s); + } + if (is_pattern) { + pats.push_back(to_app(arg)); + } + args.push_back(arg); + } + expr* pat = 0; + expr_ref pattern(m); + pattern = m.mk_pattern(pats.size(), pats.c_ptr()); + pat = pattern.get(); + app_ref result(m); + symbol qid, skid; + result = m.mk_app(new_p, args.size(), args.c_ptr()); + result = m.mk_eq(m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), result, 1, qid, skid, 1, &pat), m.mk_true()); + return result; + } + + expr * mk_quantifier_abstraction::mk_select(expr* arg, unsigned num_args, expr* const* args) { + ptr_vector args2; + args2.push_back(arg); + args2.append(num_args, args); + return a.mk_select(args2.size(), args2.c_ptr()); + } + + rule_set * mk_quantifier_abstraction::operator()(rule_set const & source) { + if (!m_ctx.quantify_arrays()) { + return 0; + } + unsigned sz = source.get_num_rules(); + for (unsigned i = 0; i < sz; ++i) { + rule& r = *source.get_rule(i); + if (r.has_negation()) { + return 0; + } + } + + m_refs.reset(); + m_old2new.reset(); + m_new2old.reset(); + rule_manager& rm = source.get_rule_manager(); + rule_ref new_rule(rm); + expr_ref_vector tail(m); + app_ref head(m); + expr_ref fml(m); + rule_counter& vc = rm.get_counter(); + + if (m_ctx.get_model_converter()) { + m_mc = alloc(qa_model_converter, m); + } + rule_set * result = alloc(rule_set, m_ctx); + + for (unsigned i = 0; i < sz; ++i) { + tail.reset(); + rule & r = *source.get_rule(i); + TRACE("dl", r.display(m_ctx, tout); ); + unsigned cnt = vc.get_max_rule_var(r)+1; + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + for (unsigned j = 0; j < utsz; ++j) { + tail.push_back(mk_tail(source, *result, r.get_tail(j))); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + } + 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);); + } + + // proof converter: proofs are not necessarily preserved using this transformation. + + if (m_old2new.empty()) { + dealloc(result); + dealloc(m_mc); + result = 0; + } + else { + m_ctx.add_model_converter(m_mc); + } + m_mc = 0; + + return result; + } + + +}; + + diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.h b/src/muz/transforms/dl_mk_quantifier_abstraction.h new file mode 100644 index 000000000..0e65c54f7 --- /dev/null +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.h @@ -0,0 +1,64 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_quantifier_abstraction.h + +Abstract: + + Convert clauses with array arguments to predicates + into Quantified Horn clauses. + +Author: + + Ken McMillan + Andrey Rybalchenko + Nikolaj Bjorner (nbjorner) 2013-04-02 + +Revision History: + + Based on approach suggested in SAS 2013 paper + "On Solving Universally Quantified Horn Clauses" + +--*/ +#ifndef _DL_MK_QUANTIFIER_ABSTRACTION_H_ +#define _DL_MK_QUANTIFIER_ABSTRACTION_H_ + + +#include"dl_rule_transformer.h" +#include"array_decl_plugin.h" + +namespace datalog { + + class context; + + class mk_quantifier_abstraction : public rule_transformer::plugin { + class qa_model_converter; + ast_manager& m; + context& m_ctx; + array_util a; + func_decl_ref_vector m_refs; + obj_map m_new2old; + obj_map m_old2new; + qa_model_converter* m_mc; + + func_decl* declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p); + app_ref mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx); + app_ref mk_tail(rule_set const& rules, rule_set& dst, app* p); + expr* mk_select(expr* a, unsigned num_args, expr* const* args); + + public: + mk_quantifier_abstraction(context & ctx, unsigned priority); + + virtual ~mk_quantifier_abstraction(); + + rule_set * operator()(rule_set const & source); + }; + + + +}; + +#endif /* _DL_MK_QUANTIFIER_ABSTRACTION_H_ */ + diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp new file mode 100644 index 000000000..9c55ca658 --- /dev/null +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -0,0 +1,302 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_quantifier_instantiation.cpp + +Abstract: + + Convert Quantified Horn clauses into non-quantified clauses using + instantiation. + +Author: + + Ken McMillan + Andrey Rybalchenko + Nikolaj Bjorner (nbjorner) 2013-04-02 + +Revision History: + + Based on approach suggested in the SAS 2013 paper + "On Solving Universally Quantified Horn Clauses" + +--*/ + +#include "dl_mk_quantifier_instantiation.h" +#include "dl_context.h" +#include "pattern_inference.h" + + +namespace datalog { + + mk_quantifier_instantiation::mk_quantifier_instantiation( + context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx), + m_var2cnst(m), + m_cnst2var(m) { + } + + mk_quantifier_instantiation::~mk_quantifier_instantiation() { + } + + void mk_quantifier_instantiation::extract_quantifiers(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs) { + conjs.reset(); + qs.reset(); + unsigned tsz = r.get_tail_size(); + for (unsigned j = 0; j < tsz; ++j) { + conjs.push_back(r.get_tail(j)); + } + qe::flatten_and(conjs); + for (unsigned j = 0; j < conjs.size(); ++j) { + expr* e = conjs[j].get(); + quantifier* q; + if (rule_manager::is_forall(m, e, q)) { + qs.push_back(q); + conjs[j] = conjs.back(); + conjs.pop_back(); + --j; + } + } + } + + void mk_quantifier_instantiation::instantiate_quantifier(quantifier* q, expr_ref_vector & conjs) { + expr_ref qe(m); + qe = q; + m_var2cnst(qe); + q = to_quantifier(qe); + if (q->get_num_patterns() == 0) { + proof_ref new_pr(m); + pattern_inference_params params; + pattern_inference infer(m, params); + infer(q, qe, new_pr); + q = to_quantifier(qe); + } + unsigned num_patterns = q->get_num_patterns(); + for (unsigned i = 0; i < num_patterns; ++i) { + expr * pat = q->get_pattern(i); + SASSERT(m.is_pattern(pat)); + instantiate_quantifier(q, to_app(pat), conjs); + } + } + + + void mk_quantifier_instantiation::instantiate_quantifier(quantifier* q, app* pat, expr_ref_vector & conjs) { + m_binding.reset(); + m_binding.resize(q->get_num_decls()); + term_pairs todo; + match(0, pat, 0, todo, q, conjs); + } + + void mk_quantifier_instantiation::match(unsigned i, app* pat, unsigned j, term_pairs& todo, quantifier* q, expr_ref_vector& conjs) { + TRACE("dl", tout << "match" << mk_pp(pat, m) << "\n";); + while (j < todo.size()) { + expr* p = todo[j].first; + expr* t = todo[j].second; + if (is_var(p)) { + unsigned idx = to_var(p)->get_idx(); + if (!m_binding[idx]) { + m_binding[idx] = t; + match(i, pat, j + 1, todo, q, conjs); + m_binding[idx] = 0; + return; + } + ++j; + continue; + } + if (!is_app(p)) { + return; + } + app* a1 = to_app(p); + unsigned id = t->get_id(); + unsigned next_id = id; + unsigned sz = todo.size(); + do { + expr* t2 = m_terms[next_id]; + if (is_app(t2)) { + app* a2 = to_app(t2); + if (a1->get_decl() == a2->get_decl() && + a1->get_num_args() == a2->get_num_args()) { + for (unsigned k = 0; k < a1->get_num_args(); ++k) { + todo.push_back(std::make_pair(a1->get_arg(k), a2->get_arg(k))); + } + match(i, pat, j + 1, todo, q, conjs); + todo.resize(sz); + } + } + next_id = m_uf.next(next_id); + } + while (next_id != id); + return; + } + + if (i == pat->get_num_args()) { + yield_binding(q, conjs); + return; + } + expr* arg = pat->get_arg(i); + ptr_vector* terms = 0; + + if (m_funs.find(to_app(arg)->get_decl(), terms)) { + for (unsigned k = 0; k < terms->size(); ++k) { + todo.push_back(std::make_pair(arg, (*terms)[k])); + match(i + 1, pat, j, todo, q, conjs); + todo.pop_back(); + } + } + } + + void mk_quantifier_instantiation::yield_binding(quantifier* q, expr_ref_vector& conjs) { + DEBUG_CODE( + for (unsigned i = 0; i < m_binding.size(); ++i) { + SASSERT(m_binding[i]); + }); + m_binding.reverse(); + expr_ref res(m); + instantiate(m, q, m_binding.c_ptr(), res); + m_binding.reverse(); + m_cnst2var(res); + conjs.push_back(res); + TRACE("dl", tout << mk_pp(q, m) << "\n==>\n" << mk_pp(res, m) << "\n";); + } + + void mk_quantifier_instantiation::collect_egraph(expr* e) { + expr* e1, *e2; + m_todo.push_back(e); + expr_fast_mark1 visited; + while (!m_todo.empty()) { + e = m_todo.back(); + m_todo.pop_back(); + if (visited.is_marked(e)) { + continue; + } + unsigned n = e->get_id(); + if (n >= m_terms.size()) { + m_terms.resize(n+1); + } + m_terms[n] = e; + visited.mark(e); + if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) { + m_uf.merge(e1->get_id(), e2->get_id()); + } + if (is_app(e)) { + app* ap = to_app(e); + ptr_vector* terms = 0; + if (!m_funs.find(ap->get_decl(), terms)) { + terms = alloc(ptr_vector); + m_funs.insert(ap->get_decl(), terms); + } + terms->push_back(e); + m_todo.append(ap->get_num_args(), ap->get_args()); + } + } + } + + void mk_quantifier_instantiation::instantiate_rule(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs, rule_set& rules) { + rule_manager& rm = m_ctx.get_rule_manager(); + expr_ref fml(m), cnst(m); + var_ref var(m); + ptr_vector sorts; + r.get_vars(sorts); + m_uf.reset(); + m_terms.reset(); + m_var2cnst.reset(); + m_cnst2var.reset(); + fml = m.mk_and(conjs.size(), conjs.c_ptr()); + + for (unsigned i = 0; i < sorts.size(); ++i) { + if (!sorts[i]) { + sorts[i] = m.mk_bool_sort(); + } + var = m.mk_var(i, sorts[i]); + cnst = m.mk_fresh_const("C", sorts[i]); + m_var2cnst.insert(var, cnst); + m_cnst2var.insert(cnst, var); + } + + fml = m.mk_and(conjs.size(), conjs.c_ptr()); + m_var2cnst(fml); + collect_egraph(fml); + + for (unsigned i = 0; i < qs.size(); ++i) { + instantiate_quantifier(qs[i].get(), conjs); + } + obj_map*>::iterator it = m_funs.begin(), end = m_funs.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + m_funs.reset(); + + fml = m.mk_and(conjs.size(), conjs.c_ptr()); + fml = m.mk_implies(fml, r.get_head()); + TRACE("dl", r.display(m_ctx, tout); tout << mk_pp(fml, m) << "\n";); + + rule_set added_rules(m_ctx); + proof_ref pr(m); + rm.mk_rule(fml, pr, added_rules); + if (r.get_proof()) { + // use def-axiom to encode that new rule is a weakening of the original. + proof* p1 = r.get_proof(); + for (unsigned i = 0; i < added_rules.get_num_rules(); ++i) { + rule* r2 = added_rules.get_rule(i); + r2->to_formula(fml); + pr = m.mk_modus_ponens(m.mk_def_axiom(m.mk_implies(m.get_fact(p1), fml)), p1); + r2->set_proof(m, pr); + } + } + rules.add_rules(added_rules); + } + + rule_set * mk_quantifier_instantiation::operator()(rule_set const & source) { + if (!m_ctx.instantiate_quantifiers()) { + return 0; + } + bool has_quantifiers = false; + unsigned sz = source.get_num_rules(); + for (unsigned i = 0; !has_quantifiers && i < sz; ++i) { + rule& r = *source.get_rule(i); + has_quantifiers = has_quantifiers || r.has_quantifiers(); + if (r.has_negation()) { + return 0; + } + } + if (!has_quantifiers) { + return 0; + } + + expr_ref_vector conjs(m); + quantifier_ref_vector qs(m); + rule_set * result = alloc(rule_set, m_ctx); + + bool instantiated = false; + + for (unsigned i = 0; i < sz; ++i) { + rule * r = source.get_rule(i); + extract_quantifiers(*r, conjs, qs); + if (qs.empty()) { + result->add_rule(r); + } + else { + instantiate_rule(*r, conjs, qs, *result); + instantiated = true; + } + } + + // model convertion: identity function. + + if (instantiated) { + result->inherit_predicates(source); + } + else { + dealloc(result); + result = 0; + } + return result; + } + + +}; + + diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.h b/src/muz/transforms/dl_mk_quantifier_instantiation.h new file mode 100644 index 000000000..138d5abee --- /dev/null +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.h @@ -0,0 +1,136 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_quantifier_instantiation.h + +Abstract: + + Convert Quantified Horn clauses into non-quantified clauses using + instantiation. + +Author: + + Ken McMillan + Andrey Rybalchenko + Nikolaj Bjorner (nbjorner) 2013-04-02 + +Revision History: + + Based on approach suggested in the SAS 2013 paper + "On Solving Universally Quantified Horn Clauses" + +--*/ +#ifndef _DL_MK_QUANTIFIER_INSTANTIATION_H_ +#define _DL_MK_QUANTIFIER_INSTANTIATION_H_ + + +#include"dl_rule_transformer.h" +#include"expr_safe_replace.h" + + +namespace datalog { + + class context; + + class mk_quantifier_instantiation : public rule_transformer::plugin { + typedef svector > term_pairs; + + class union_find { + unsigned_vector m_find; + unsigned_vector m_size; + unsigned_vector m_next; + + void ensure_size(unsigned v) { + while (v >= get_num_vars()) { + mk_var(); + } + } + public: + unsigned mk_var() { + unsigned r = m_find.size(); + m_find.push_back(r); + m_size.push_back(1); + m_next.push_back(r); + return r; + } + unsigned get_num_vars() const { return m_find.size(); } + + unsigned find(unsigned v) const { + if (v >= get_num_vars()) { + return v; + } + while (true) { + unsigned new_v = m_find[v]; + if (new_v == v) + return v; + v = new_v; + } + } + + unsigned next(unsigned v) const { + if (v >= get_num_vars()) { + return v; + } + return m_next[v]; + } + + bool is_root(unsigned v) const { + return v >= get_num_vars() || m_find[v] == v; + } + + void merge(unsigned v1, unsigned v2) { + unsigned r1 = find(v1); + unsigned r2 = find(v2); + if (r1 == r2) + return; + ensure_size(v1); + ensure_size(v2); + if (m_size[r1] > m_size[r2]) + std::swap(r1, r2); + m_find[r1] = r2; + m_size[r2] += m_size[r1]; + std::swap(m_next[r1], m_next[r2]); + } + + void reset() { + m_find.reset(); + m_next.reset(); + m_size.reset(); + } + }; + + ast_manager& m; + context& m_ctx; + expr_safe_replace m_var2cnst; + expr_safe_replace m_cnst2var; + union_find m_uf; + ptr_vector m_todo; + ptr_vector m_terms; + ptr_vector m_binding; + obj_map*> m_funs; + + + void extract_quantifiers(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs); + void collect_egraph(expr* e); + void instantiate_rule(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs, rule_set& rules); + void instantiate_quantifier(quantifier* q, expr_ref_vector & conjs); + void instantiate_quantifier(quantifier* q, app* pat, expr_ref_vector & conjs); + void match(unsigned i, app* pat, unsigned j, term_pairs& todo, quantifier* q, expr_ref_vector& conjs); + void yield_binding(quantifier* q, expr_ref_vector& conjs); + + public: + mk_quantifier_instantiation(context & ctx, unsigned priority); + + virtual ~mk_quantifier_instantiation(); + + rule_set * operator()(rule_set const & source); + }; + + + +}; + +#endif /* _DL_MK_QUANTIFIER_INSTANTIATION_H_ */ + diff --git a/src/muz_qe/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp similarity index 92% rename from src/muz_qe/dl_mk_rule_inliner.cpp rename to src/muz/transforms/dl_mk_rule_inliner.cpp index 0919e2ff0..3e933b099 100644 --- a/src/muz_qe/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -49,12 +49,10 @@ 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" +#include "fixedpoint_params.hpp" namespace datalog { @@ -65,8 +63,8 @@ namespace datalog { // ----------------------------------- bool rule_unifier::unify_rules(const rule& tgt, unsigned tgt_idx, const rule& src) { - var_counter& vc = m_rm.get_var_counter(); - unsigned var_cnt = std::max(vc.get_max_var(tgt), vc.get_max_var(src))+1; + rule_counter& vc = m_rm.get_counter(); + unsigned var_cnt = std::max(vc.get_max_rule_var(tgt), vc.get_max_rule_var(src))+1; m_subst.reset(); m_subst.reserve(2, var_cnt); @@ -181,10 +179,10 @@ namespace datalog { } if (m_unifier.apply(tgt, tail_index, src, res)) { - if (m_pc) { + if (m_context.generate_proof_trace()) { expr_ref_vector s1 = m_unifier.get_rule_subst(tgt, true); expr_ref_vector s2 = m_unifier.get_rule_subst(src, false); - datalog::resolve_rule(m_pc, tgt, src, tail_index, s1, s2, *res.get()); + datalog::resolve_rule(tgt, src, tail_index, s1, s2, *res.get()); } return true; } @@ -206,7 +204,10 @@ namespace datalog { void mk_rule_inliner::count_pred_occurrences(rule_set const & orig) { - m_context.get_rel_context().get_rmanager().collect_non_empty_predicates(m_preds_with_facts); + rel_context_base* rel = m_context.get_rel_context(); + if (rel) { + rel->collect_non_empty_predicates(m_preds_with_facts); + } rule_set::iterator rend = orig.end(); for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) { @@ -230,10 +231,10 @@ namespace datalog { } } - bool mk_rule_inliner::inlining_allowed(func_decl * pred) + bool mk_rule_inliner::inlining_allowed(rule_set const& source, func_decl * pred) { if (//these three conditions are important for soundness - m_context.is_output_predicate(pred) || + source.is_output_predicate(pred) || m_preds_with_facts.contains(pred) || m_preds_with_neg_occurrence.contains(pred) || //this condition is used for breaking of cycles among inlined rules @@ -241,8 +242,10 @@ namespace datalog { return false; } - //these conditions are optional, they avoid possible exponential increase - //in the size of the problem + // + // these conditions are optional, they avoid possible exponential increase + // in the size of the problem + // return //m_head_pred_non_empty_tails_ctr.get(pred)<=1 @@ -258,7 +261,7 @@ namespace datalog { unsigned rcnt = orig.get_num_rules(); for (unsigned i=0; iget_decl())) { + if (inlining_allowed(orig, r->get_decl())) { res->add_rule(r); } } @@ -322,7 +325,7 @@ namespace datalog { unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); - if (!inlining_allowed(tail_pred)) { + if (!inlining_allowed(orig, tail_pred)) { continue; } unsigned tail_pred_head_cnt = m_head_pred_ctr.get(tail_pred); @@ -357,7 +360,7 @@ namespace datalog { func_decl * head_pred = r->get_decl(); - if (inlining_allowed(head_pred)) { + if (inlining_allowed(orig, head_pred)) { //we have already processed inlined rules continue; } @@ -366,7 +369,7 @@ namespace datalog { unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); - if (!inlining_allowed(pred)) { + if (!inlining_allowed(orig, pred)) { continue; } if (m_head_pred_ctr.get(pred)<=1) { @@ -415,14 +418,14 @@ namespace datalog { const rule_vector& pred_rules = candidate_inlined_set->get_predicate_rules(pred); rule_vector::const_iterator iend = pred_rules.end(); for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) { - transform_rule(*iit, m_inlined_rules); + transform_rule(orig, *iit, m_inlined_rules); } } TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; ); } - bool mk_rule_inliner::transform_rule(rule * r0, rule_set& tgt) { + bool mk_rule_inliner::transform_rule(rule_set const& orig, rule * r0, rule_set& tgt) { bool modified = false; rule_ref_vector todo(m_rm); todo.push_back(r0); @@ -434,7 +437,7 @@ namespace datalog { unsigned i = 0; - for (; i < pt_len && !inlining_allowed(r->get_decl(i)); ++i) {}; + for (; i < pt_len && !inlining_allowed(orig, r->get_decl(i)); ++i) {}; SASSERT(!has_quantifier(*r.get())); @@ -476,12 +479,12 @@ namespace datalog { // this relation through inlining, // so we don't add its rules to the result - something_done |= !inlining_allowed(pred) && transform_rule(r, tgt); + something_done |= !inlining_allowed(orig, pred) && transform_rule(orig, r, tgt); } if (something_done && m_mc) { for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) { - if (inlining_allowed((*rit)->get_decl())) { + if (inlining_allowed(orig, (*rit)->get_decl())) { datalog::del_rule(m_mc, **rit); } } @@ -503,9 +506,6 @@ namespace datalog { unsigned head_arity = head_pred->get_arity(); - //var_idx_set head_vars; - //var_idx_set same_strat_vars; - //collect_vars(m, r->get_head(), head_vars); unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti=0; tiget_head(), same_strat_vars); if (pred->get_arity()>head_arity || (pred->get_arity()==head_arity && pred->get_id()>=head_pred->get_id()) ) { return false; @@ -665,7 +664,7 @@ namespace datalog { return et->get_data().m_value; } - void mk_rule_inliner::add_rule(rule* r, unsigned i) { + void mk_rule_inliner::add_rule(rule_set const& source, rule* r, unsigned i) { svector& can_remove = m_head_visitor.can_remove(); svector& can_expand = m_head_visitor.can_expand(); app* head = r->get_head(); @@ -674,7 +673,7 @@ namespace datalog { m_head_index.insert(head); m_pinned.push_back(r); - if (m_context.is_output_predicate(headd) || + if (source.is_output_predicate(headd) || m_preds_with_facts.contains(headd)) { can_remove.set(i, false); TRACE("dl", output_predicate(m_context, head, tout << "cannot remove: " << i << " "); tout << "\n";); @@ -690,7 +689,7 @@ namespace datalog { tl_sz == 1 && r->get_positive_tail_size() == 1 && !m_preds_with_facts.contains(r->get_decl(0)) - && !m_context.is_output_predicate(r->get_decl(0)); + && !source.is_output_predicate(r->get_decl(0)); can_expand.set(i, can_exp); } @@ -708,7 +707,6 @@ namespace datalog { #define PRT(_x_) ((_x_)?"T":"F") bool mk_rule_inliner::inline_linear(scoped_ptr& rules) { - scoped_ptr res = alloc(rule_set, m_context); bool done_something = false; unsigned sz = rules->get_num_rules(); @@ -729,11 +727,11 @@ namespace datalog { svector& can_expand = m_head_visitor.can_expand(); for (unsigned i = 0; i < sz; ++i) { - add_rule(acc[i].get(), i); + add_rule(*rules, acc[i].get(), i); } // initialize substitution. - var_counter& vc = m_rm.get_var_counter(); + rule_counter& vc = m_rm.get_counter(); unsigned max_var = 0; for (unsigned i = 0; i < sz; ++i) { rule* r = acc[i].get(); @@ -806,7 +804,7 @@ namespace datalog { TRACE("dl", r->display(m_context, tout); r2->display(m_context, tout); rl_res->display(m_context, tout); ); del_rule(r, i); - add_rule(rl_res.get(), i); + add_rule(*rules, rl_res.get(), i); r = rl_res; @@ -820,28 +818,29 @@ namespace datalog { del_rule(r2, j); } - max_var = std::max(max_var, vc.get_max_var(*r.get())); + max_var = std::max(max_var, vc.get_max_rule_var(*r.get())); m_subst.reserve_vars(max_var+1); } } if (done_something) { - rules = alloc(rule_set, m_context); + scoped_ptr res = alloc(rule_set, m_context); for (unsigned i = 0; i < sz; ++i) { if (valid.get(i)) { - rules->add_rule(acc[i].get()); + res->add_rule(acc[i].get()); } } - TRACE("dl", rules->display(tout);); + res->inherit_predicates(*rules); + TRACE("dl", res->display(tout);); + rules = res.detach(); } return done_something; } - rule_set * mk_rule_inliner::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { + rule_set * mk_rule_inliner::operator()(rule_set const & source) { bool something_done = false; ref hsmc; - ref hpc; if (source.get_num_rules() == 0) { return 0; @@ -855,14 +854,10 @@ namespace datalog { } - if (mc) { + if (m_context.get_model_converter()) { hsmc = alloc(horn_subsume_model_converter, m); } - if (pc) { - hpc = alloc(replace_proof_converter, m); - } m_mc = hsmc.get(); - m_pc = hpc.get(); scoped_ptr res = alloc(rule_set, m_context); @@ -874,8 +869,14 @@ namespace datalog { // try eager inlining if (do_eager_inlining(res)) { something_done = true; - } + } TRACE("dl", res->display(tout << "after eager inlining\n");); + } + if (something_done) { + res->inherit_predicates(source); + } + else { + res = alloc(rule_set, source); } if (m_context.get_params().inline_linear() && inline_linear(res)) { @@ -886,12 +887,7 @@ namespace datalog { res = 0; } else { - if (mc) { - mc = concat(mc.get(), hsmc.get()); - } - if (pc) { - pc = concat(pc.get(), hpc.get()); - } + m_context.add_model_converter(hsmc.get()); } return res.detach(); diff --git a/src/muz_qe/dl_mk_rule_inliner.h b/src/muz/transforms/dl_mk_rule_inliner.h similarity index 93% rename from src/muz_qe/dl_mk_rule_inliner.h rename to src/muz/transforms/dl_mk_rule_inliner.h index 6d5448cd6..ed555492a 100644 --- a/src/muz_qe/dl_mk_rule_inliner.h +++ b/src/muz/transforms/dl_mk_rule_inliner.h @@ -106,15 +106,14 @@ 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; rule_set m_inlined_rules; horn_subsume_model_converter* m_mc; - replace_proof_converter* m_pc; //used in try_to_inline_rule and do_eager_inlining @@ -130,7 +129,7 @@ namespace datalog { bool try_to_inline_rule(rule& tgt, rule& src, unsigned tail_index, rule_ref& res); - bool inlining_allowed(func_decl * pred); + bool inlining_allowed(rule_set const& orig, func_decl * pred); void count_pred_occurrences(rule_set const & orig); @@ -144,7 +143,7 @@ namespace datalog { bool forbid_multiple_multipliers(const rule_set & orig, rule_set const & proposed_inlined_rules); /** Return true if the rule was modified */ - bool transform_rule(rule * r, rule_set& tgt); + bool transform_rule(rule_set const& orig, rule * r, rule_set& tgt); /** Return true if some transformation was performed */ bool transform_rules(const rule_set & orig, rule_set & tgt); @@ -175,7 +174,7 @@ namespace datalog { */ bool inline_linear(scoped_ptr& rules); - void add_rule(rule* r, unsigned i); + void add_rule(rule_set const& rule_set, rule* r, unsigned i); void del_rule(rule* r, unsigned i); public: @@ -188,7 +187,6 @@ namespace datalog { m_pinned(m_rm), m_inlined_rules(m_context), m_mc(0), - m_pc(0), m_unifier(ctx), m_head_index(m), m_tail_index(m), @@ -198,7 +196,7 @@ namespace datalog { {} virtual ~mk_rule_inliner() { } - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz/transforms/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp new file mode 100644 index 000000000..9ada7be20 --- /dev/null +++ b/src/muz/transforms/dl_mk_scale.cpp @@ -0,0 +1,242 @@ +/*++ +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" +#include"fixedpoint_params.hpp" + +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), + m(ctx.get_manager()), + m_ctx(ctx), + a(m), + m_trail(m), + m_eqs(m) { + } + + mk_scale::~mk_scale() { + } + + rule_set * mk_scale::operator()(rule_set const & source) { + if (!m_ctx.scale()) { + return 0; + } + rule_manager& rm = source.get_rule_manager(); + 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(); + unsigned tsz = r.get_tail_size(); + tail.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))); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(mk_constraint(num_vars, r.get_tail(j))); + } + 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.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()); + } + } + TRACE("dl", result->display(tout);); + if (m_mc) { + m_ctx.add_model_converter(m_mc); + } + m_trail.reset(); + m_cache.reset(); + return result; + } + + 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()); + 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); + } + return app_ref(m.mk_app(g, q->get_num_args() + 1, args.c_ptr()), m); + } + + 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 sigma_idx, expr* e) { + expr* r; + if (m_cache.find(e, r)) { + return r; + } + if (!is_app(e)) { + return e; + } + 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(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(sigma_idx, a.mk_real()), e); + } + else { + result = e; + } + m_trail.push_back(result); + m_cache.insert(e, result); + return result; + } + +}; diff --git a/src/muz/transforms/dl_mk_scale.h b/src/muz/transforms/dl_mk_scale.h new file mode 100644 index 000000000..8498c891f --- /dev/null +++ b/src/muz/transforms/dl_mk_scale.h @@ -0,0 +1,53 @@ +/*++ +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 { + + class scale_model_converter; + + ast_manager& m; + 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; + + 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); + virtual ~mk_scale(); + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_SCALE_H_ */ + 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..782e1011d --- /dev/null +++ b/src/muz/transforms/dl_mk_separate_negated_tails.cpp @@ -0,0 +1,137 @@ +/*++ +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" + +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..8cd806f43 --- /dev/null +++ b/src/muz/transforms/dl_mk_separate_negated_tails.h @@ -0,0 +1,58 @@ +/*++ +Copyright (c) 2013 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_qe/dl_mk_slice.cpp b/src/muz/transforms/dl_mk_slice.cpp similarity index 95% rename from src/muz_qe/dl_mk_slice.cpp rename to src/muz/transforms/dl_mk_slice.cpp index 84b732280..0df75324d 100644 --- a/src/muz_qe/dl_mk_slice.cpp +++ b/src/muz/transforms/dl_mk_slice.cpp @@ -120,12 +120,11 @@ namespace datalog { obj_map::iterator end = m_rule2slice.end(); expr_ref fml(m); for (; it != end; ++it) { - TRACE("dl", - it->m_key->display(m_ctx, tout << "orig:\n"); - it->m_value->display(m_ctx, tout << "new:\n");); - it->m_value->to_formula(fml); m_pinned_exprs.push_back(fml); + TRACE("dl", + tout << "orig: " << mk_pp(fml, m) << "\n"; + it->m_value->display(m_ctx, tout << "new:\n");); m_sliceform2rule.insert(fml, it->m_key); } } @@ -164,10 +163,8 @@ namespace datalog { TRACE("dl", tout << "does not have fact\n" << mk_pp(fact, m) << "\n";); return false; } - expr_ref fml(m); proof_ref new_p(m); - r->to_formula(fml); - new_p = m.mk_asserted(fml); + new_p = r->get_proof(); m_pinned_exprs.push_back(new_p); m_todo.pop_back(); m_new_proof.insert(p, new_p); @@ -204,9 +201,10 @@ namespace datalog { proof* p0_new = m_new_proof.find(p0); expr* fact0 = m.get_fact(p0); TRACE("dl", tout << "fact0: " << mk_pp(fact0, m) << "\n";); - rule* orig0 = m_sliceform2rule.find(fact0); - /* rule* slice0 = */ m_rule2slice.find(orig0); - /* unsigned_vector const& renaming0 = m_renaming.find(orig0); */ + rule* orig0; + if (!m_sliceform2rule.find(fact0, orig0)) { + return false; + } premises.push_back(p0_new); rule_ref r1(rm), r2(rm), r3(rm); r1 = orig0; @@ -216,9 +214,10 @@ namespace datalog { proof* p1_new = m_new_proof.find(p1); expr* fact1 = m.get_fact(p1); TRACE("dl", tout << "fact1: " << mk_pp(fact1, m) << "\n";); - rule* orig1 = m_sliceform2rule.find(fact1); - /* rule* slice1 = */ m_rule2slice.find(orig1); - /* unsigned_vector const& renaming1 = m_renaming.find(orig1); TBD */ + rule* orig1 = 0; + if (!m_sliceform2rule.find(fact1, orig1)) { + return false; + } premises.push_back(p1_new); // TODO: work with substitutions. @@ -243,6 +242,9 @@ namespace datalog { proof* new_p = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), concl, positions, substs); m_pinned_exprs.push_back(new_p); m_pinned_rules.push_back(r1.get()); + TRACE("dl", + tout << "orig: " << mk_pp(slice_concl, m) << "\n"; + r1->display(m_ctx, tout << "new:");); m_sliceform2rule.insert(slice_concl, r1.get()); m_rule2slice.insert(r1.get(), 0); m_renaming.insert(r1.get(), unsigned_vector()); @@ -617,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; } @@ -705,9 +707,10 @@ namespace datalog { m_pinned.reset(); } - void mk_slice::declare_predicates() { + void mk_slice::declare_predicates(rule_set const& src, rule_set& dst) { obj_map::iterator it = m_sliceable.begin(), end = m_sliceable.end(); ptr_vector domain; + bool has_output = false; func_decl* f; for (; it != end; ++it) { domain.reset(); @@ -722,10 +725,19 @@ namespace datalog { f = m_ctx.mk_fresh_head_predicate(p->get_name(), symbol("slice"), domain.size(), domain.c_ptr(), p); m_pinned.push_back(f); m_predicates.insert(p, f); + dst.inherit_predicate(src, p, f); if (m_mc) { m_mc->add_predicate(p, f); } } + else if (src.is_output_predicate(p)) { + dst.set_output_predicate(p); + has_output = true; + } + } + // disable slicing if the output predicates don't occur in rules. + if (!has_output) { + m_predicates.reset(); } } @@ -784,6 +796,9 @@ namespace datalog { rm.fix_unbound_vars(new_rule, false); TRACE("dl", r.display(m_ctx, tout << "replacing:\n"); new_rule->display(m_ctx, tout << "by:\n");); + if (m_ctx.generate_proof_trace()) { + rm.mk_rule_asserted_proof(*new_rule.get()); + } } else { new_rule = &r; @@ -801,7 +816,7 @@ namespace datalog { } } - rule_set * mk_slice::operator()(rule_set const & src, model_converter_ref& mc, proof_converter_ref& pc) { + rule_set * mk_slice::operator()(rule_set const & src) { for (unsigned i = 0; i < src.get_num_rules(); ++i) { if (src.get_rule(i)->has_quantifiers()) { return 0; @@ -809,23 +824,24 @@ namespace datalog { } ref spc; ref smc; - if (pc) { - spc = alloc(slice_proof_converter, m_ctx); + if (m_ctx.generate_proof_trace()) { + spc = alloc(slice_proof_converter, m_ctx); } - if (mc) { + if (m_ctx.get_model_converter()) { smc = alloc(slice_model_converter, *this, m); } m_pc = spc.get(); m_mc = smc.get(); reset(); saturate(src); - declare_predicates(); + rule_set* result = alloc(rule_set, m_ctx); + declare_predicates(src, *result); if (m_predicates.empty()) { // nothing could be sliced. + dealloc(result); return 0; } TRACE("dl", display(tout);); - rule_set* result = alloc(rule_set, m_ctx); update_rules(src, *result); TRACE("dl", result->display(tout);); if (m_mc) { @@ -834,8 +850,8 @@ namespace datalog { m_mc->add_sliceable(it->m_key, it->m_value); } } - pc = concat(pc.get(), spc.get()); - mc = concat(mc.get(), smc.get()); + m_ctx.add_proof_converter(spc.get()); + m_ctx.add_model_converter(smc.get()); return result; } diff --git a/src/muz_qe/dl_mk_slice.h b/src/muz/transforms/dl_mk_slice.h similarity index 95% rename from src/muz_qe/dl_mk_slice.h rename to src/muz/transforms/dl_mk_slice.h index 26a8175f2..aedc1feb0 100644 --- a/src/muz_qe/dl_mk_slice.h +++ b/src/muz/transforms/dl_mk_slice.h @@ -83,7 +83,7 @@ namespace datalog { expr_ref_vector get_tail_conjs(rule const& r); - void declare_predicates(); + void declare_predicates(rule_set const& src, rule_set& dst); bool rule_updated(rule const& r); @@ -102,7 +102,7 @@ namespace datalog { virtual ~mk_slice() { } - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); func_decl* get_predicate(func_decl* p) { func_decl* q = p; m_predicates.find(p, q); return q; } diff --git a/src/muz_qe/dl_mk_subsumption_checker.cpp b/src/muz/transforms/dl_mk_subsumption_checker.cpp similarity index 87% rename from src/muz_qe/dl_mk_subsumption_checker.cpp rename to src/muz/transforms/dl_mk_subsumption_checker.cpp index 19c28c036..1967fdc93 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.cpp +++ b/src/muz/transforms/dl_mk_subsumption_checker.cpp @@ -21,13 +21,9 @@ Revision History: #include #include"ast_pp.h" - #include "rewriter.h" #include "rewriter_def.h" - - #include"dl_mk_subsumption_checker.h" -#include"dl_table_relation.h" namespace datalog { @@ -82,7 +78,7 @@ namespace datalog { void mk_subsumption_checker::on_discovered_total_relation(func_decl * pred, rule * r) { //this should be rule marking a new relation as total SASSERT(!m_total_relations.contains(pred)); - SASSERT(!r || pred==r->get_head()->get_decl()); + SASSERT(!r || pred==r->get_decl()); SASSERT(!r || is_total_rule(r)); m_total_relations.insert(pred); @@ -102,7 +98,7 @@ namespace datalog { rule_set::iterator rend = rules.end(); for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) { rule * r = *rit; - func_decl * head_pred = r->get_head()->get_decl(); + func_decl * head_pred = r->get_decl(); if(is_total_rule(r) && !m_total_relations.contains(head_pred)) { on_discovered_total_relation(head_pred, r); new_discovered = true; @@ -166,7 +162,8 @@ namespace datalog { res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); res->set_accounting_parent_object(m_context, r); m_context.get_rule_manager().fix_unbound_vars(res, true); - + m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *res.get()); + return true; } @@ -195,10 +192,10 @@ namespace datalog { for(rule_set::iterator rit = rbegin; rit!=rend; ++rit) { rule * r = *rit; - func_decl * head_pred = r->get_head()->get_decl(); + func_decl * head_pred = r->get_decl(); if(m_total_relations.contains(head_pred)) { - if(!m_context.is_output_predicate(head_pred) || + if(!orig.is_output_predicate(head_pred) || total_relations_with_included_rules.contains(head_pred)) { //We just skip definitions of total non-output relations as //we'll eliminate them from the problem. @@ -208,15 +205,15 @@ namespace datalog { continue; } rule * defining_rule; - TRUSTME(m_total_relation_defining_rules.find(head_pred, defining_rule)); - if(defining_rule) { + VERIFY(m_total_relation_defining_rules.find(head_pred, defining_rule)); + if (defining_rule) { rule_ref totality_rule(m_context.get_rule_manager()); - TRUSTME(transform_rule(defining_rule, subs_index, totality_rule)); + VERIFY(transform_rule(defining_rule, subs_index, totality_rule)); if(defining_rule!=totality_rule) { modified = true; } tgt.add_rule(totality_rule); - SASSERT(totality_rule->get_head()->get_decl()==head_pred); + SASSERT(totality_rule->get_decl()==head_pred); } else { modified = true; @@ -243,30 +240,31 @@ namespace datalog { tgt.add_rule(new_rule); subs_index.add(new_rule); } + tgt.inherit_predicates(orig); TRACE("dl", tout << "original set size: "<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; } + if (arity > 30) { continue; } //for now we only check booleans domains for(unsigned i=0; iget_size_estimate_rows(); obj_hashtable * head_store; if(m_ground_unconditional_rule_heads.find(pred, head_store)) { @@ -306,7 +303,7 @@ namespace datalog { rule_set::iterator rend = rules.end(); for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) { rule * r = *rit; - func_decl * pred = r->get_head()->get_decl(); + func_decl * pred = r->get_decl(); if(r->get_tail_size()!=0) { continue; } @@ -323,27 +320,25 @@ namespace datalog { if(!m_ground_unconditional_rule_heads.contains(pred)) { m_ground_unconditional_rule_heads.insert(pred, alloc(obj_hashtable)); } - obj_hashtable * head_store; - m_ground_unconditional_rule_heads.find(pred, head_store); - head_store->insert(head); + m_ground_unconditional_rule_heads.find(pred)->insert(head); next_rule:; } } - rule_set * mk_subsumption_checker::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_subsumption_checker::operator()(rule_set const & source) { + // TODO mc m_have_new_total_rule = false; collect_ground_unconditional_rule_heads(source); - scan_for_relations_total_due_to_facts(); + scan_for_relations_total_due_to_facts(source); scan_for_total_rules(source); m_have_new_total_rule = false; rule_set * res = alloc(rule_set, m_context); bool modified = transform_rules(source, *res); - if(!m_have_new_total_rule && !modified) { + if (!m_have_new_total_rule && !modified) { dealloc(res); return 0; } @@ -352,7 +347,7 @@ namespace datalog { //During the construction of the new set we may discover new total relations //(by quantifier elimination on the uninterpreted tails). SASSERT(m_new_total_relation_discovery_during_transformation || !m_have_new_total_rule); - while(m_have_new_total_rule) { + while (m_have_new_total_rule) { m_have_new_total_rule = false; rule_set * old = res; diff --git a/src/muz_qe/dl_mk_subsumption_checker.h b/src/muz/transforms/dl_mk_subsumption_checker.h similarity index 91% rename from src/muz_qe/dl_mk_subsumption_checker.h rename to src/muz/transforms/dl_mk_subsumption_checker.h index ce33e7574..8d797a9e2 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.h +++ b/src/muz/transforms/dl_mk_subsumption_checker.h @@ -64,8 +64,8 @@ namespace datalog { /** Function to be called when a new total relation is discovered */ void on_discovered_total_relation(func_decl * pred, rule * r); - void scan_for_total_rules(const rule_set & rules); - void scan_for_relations_total_due_to_facts(); + void scan_for_total_rules(rule_set const& rules); + void scan_for_relations_total_due_to_facts(rule_set const& rules); void collect_ground_unconditional_rule_heads(const rule_set & rules); @@ -84,7 +84,7 @@ namespace datalog { reset_dealloc_values(m_ground_unconditional_rule_heads); } - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + virtual rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_unbound_compressor.cpp b/src/muz/transforms/dl_mk_unbound_compressor.cpp similarity index 82% rename from src/muz_qe/dl_mk_unbound_compressor.cpp rename to src/muz/transforms/dl_mk_unbound_compressor.cpp index e3db82759..68c35e2c9 100644 --- a/src/muz_qe/dl_mk_unbound_compressor.cpp +++ b/src/muz/transforms/dl_mk_unbound_compressor.cpp @@ -26,9 +26,10 @@ namespace datalog { mk_unbound_compressor::mk_unbound_compressor(context & ctx) : plugin(500), m_context(ctx), - m_manager(ctx.get_manager()), - m_rules(ctx.get_rule_manager()), - m_pinned(m_manager) { + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_rules(rm), + m_pinned(m) { } void mk_unbound_compressor::reset() { @@ -47,10 +48,7 @@ namespace datalog { } unsigned var_idx = to_var(head_arg)->get_idx(); - var_idx_set tail_vars; - collect_tail_vars(m_manager, r, tail_vars); - - return tail_vars.contains(var_idx); + return rm.collect_tail_vars(r).contains(var_idx); } void mk_unbound_compressor::add_task(func_decl * pred, unsigned arg_index) { @@ -81,22 +79,21 @@ namespace datalog { m_map.insert(ci, cpred); } - void mk_unbound_compressor::detect_tasks(unsigned rule_index) { + void mk_unbound_compressor::detect_tasks(rule_set const& source, unsigned rule_index) { rule * r = m_rules.get(rule_index); - var_idx_set tail_vars; - collect_tail_vars(m_manager, r, tail_vars); + var_idx_set& tail_vars = rm.collect_tail_vars(r); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); - if (m_context.is_output_predicate(head_pred)) { + if (source.is_output_predicate(head_pred)) { //we don't compress output predicates return; } unsigned n = head_pred->get_arity(); - - var_counter head_var_counter; - head_var_counter.count_vars(m_manager, head, 1); + + rm.get_counter().reset(); + rm.get_counter().count_vars(m, head, 1); for (unsigned i=0; iget_arg(i); @@ -107,7 +104,7 @@ namespace datalog { if (!tail_vars.contains(var_idx)) { //unbound - unsigned occurence_cnt = head_var_counter.get(var_idx); + unsigned occurence_cnt = rm.get_counter().get(var_idx); SASSERT(occurence_cnt>0); if (occurence_cnt == 1) { TRACE("dl", r->display(m_context, tout << "Compress: ");); @@ -118,18 +115,17 @@ namespace datalog { } } - void mk_unbound_compressor::try_compress(unsigned rule_index) { + void mk_unbound_compressor::try_compress(rule_set const& source, unsigned rule_index) { start: rule * r = m_rules.get(rule_index); - var_idx_set tail_vars; - collect_tail_vars(m_manager, r, tail_vars); + var_idx_set& tail_vars = rm.collect_tail_vars(r); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); unsigned head_arity = head_pred->get_arity(); - var_counter head_var_counter; - head_var_counter.count_vars(m_manager, head); + rm.get_counter().reset(); + rm.get_counter().count_vars(m, head); unsigned arg_index; for (arg_index = 0; arg_index < head_arity; arg_index++) { @@ -140,7 +136,7 @@ namespace datalog { unsigned var_idx = to_var(arg)->get_idx(); if (!tail_vars.contains(var_idx)) { //unbound - unsigned occurence_cnt = head_var_counter.get(var_idx); + unsigned occurence_cnt = rm.get_counter().get(var_idx); SASSERT(occurence_cnt>0); if ( occurence_cnt==1 && m_in_progress.contains(c_info(head_pred, arg_index)) ) { //we have found what to compress @@ -163,13 +159,13 @@ namespace datalog { } } - app_ref chead(m_manager.mk_app(cpred, head_arity-1, cargs.c_ptr()), m_manager); + app_ref chead(m.mk_app(cpred, head_arity-1, cargs.c_ptr()), m); if (r->get_tail_size()==0 && m_context.get_rule_manager().is_fact(chead)) { m_non_empty_rels.insert(cpred); m_context.add_fact(chead); //remove the rule that became fact by placing the last rule on its place - m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_head()->get_decl()); + m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_decl()); m_rules.set(rule_index, m_rules.get(m_rules.size()-1)); m_rules.shrink(m_rules.size()-1); //since we moved the last rule to rule_index, we have to try to compress it as well @@ -181,10 +177,10 @@ namespace datalog { rule_ref new_rule(m_context.get_rule_manager().mk(r, chead), m_context.get_rule_manager()); new_rule->set_accounting_parent_object(m_context, r); - m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_head()->get_decl()); + m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_decl()); m_rules.set(rule_index, new_rule); - m_head_occurrence_ctr.inc(m_rules.get(rule_index)->get_head()->get_decl()); - detect_tasks(rule_index); + m_head_occurrence_ctr.inc(m_rules.get(rule_index)->get_decl()); + detect_tasks(source, rule_index); } m_modified = true; @@ -205,10 +201,10 @@ namespace datalog { } } SASSERT(dtail_args.size()==dtail_pred->get_arity()); - app_ref dtail(m_manager.mk_app(dtail_pred, dtail_args.size(), dtail_args.c_ptr()), m_manager); + app_ref dtail(m.mk_app(dtail_pred, dtail_args.size(), dtail_args.c_ptr()), m); svector tails_negated; - app_ref_vector tails(m_manager); + app_ref_vector tails(m); unsigned tail_len = r->get_tail_size(); for (unsigned i=0; iis_neg_tail(i)); @@ -232,16 +228,17 @@ namespace datalog { m_context.get_rule_manager().fix_unbound_vars(res, true); } - void mk_unbound_compressor::add_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index) { + void mk_unbound_compressor::add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index) { rule_ref new_rule(m_context.get_rule_manager()); mk_decompression_rule(r, tail_index, arg_index, new_rule); unsigned new_rule_index = m_rules.size(); m_rules.push_back(new_rule); - m_head_occurrence_ctr.inc(new_rule->get_head()->get_decl()); + m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule.get()); + m_head_occurrence_ctr.inc(new_rule->get_decl()); - detect_tasks(new_rule_index); + detect_tasks(source, new_rule_index); m_modified = true; @@ -257,7 +254,7 @@ namespace datalog { //P:- R1(x), S1(x) } - void mk_unbound_compressor::replace_by_decompression_rule(unsigned rule_index, unsigned tail_index, unsigned arg_index) + void mk_unbound_compressor::replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index) { rule * r = m_rules.get(rule_index); @@ -268,12 +265,12 @@ namespace datalog { //we don't update the m_head_occurrence_ctr because the head predicate doesn't change - detect_tasks(rule_index); + detect_tasks(source, rule_index); m_modified = true; } - void mk_unbound_compressor::add_decompression_rules(unsigned rule_index) { + void mk_unbound_compressor::add_decompression_rules(rule_set const& source, unsigned rule_index) { unsigned_vector compressed_tail_pred_arg_indexes; @@ -305,11 +302,11 @@ namespace datalog { m_head_occurrence_ctr.get(t_pred)==0; if (can_remove_orig_rule || is_negated_predicate) { - replace_by_decompression_rule(rule_index, tail_index, arg_index); + replace_by_decompression_rule(source, rule_index, tail_index, arg_index); orig_rule_replaced = true; } else { - add_decompression_rule(r, tail_index, arg_index); + add_decompression_rule(source, r, tail_index, arg_index); } } if (orig_rule_replaced) { @@ -333,22 +330,24 @@ namespace datalog { } } - rule_set * mk_unbound_compressor::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_unbound_compressor::operator()(rule_set const & source) { + // TODO mc m_modified = false; - m_context.get_rel_context().get_rmanager().collect_non_empty_predicates(m_non_empty_rels); - + rel_context_base* rel = m_context.get_rel_context(); + if (rel) { + rel->collect_non_empty_predicates(m_non_empty_rels); + } unsigned init_rule_cnt = source.get_num_rules(); SASSERT(m_rules.empty()); for (unsigned i=0; iget_head()->get_decl()); + m_head_occurrence_ctr.inc(r->get_decl()); } for (unsigned i=0; iadd_rule(m_rules.get(i)); } + result->inherit_predicates(source); } reset(); return result; diff --git a/src/muz_qe/dl_mk_unbound_compressor.h b/src/muz/transforms/dl_mk_unbound_compressor.h similarity index 75% rename from src/muz_qe/dl_mk_unbound_compressor.h rename to src/muz/transforms/dl_mk_unbound_compressor.h index 0e13bad75..44877e646 100644 --- a/src/muz_qe/dl_mk_unbound_compressor.h +++ b/src/muz/transforms/dl_mk_unbound_compressor.h @@ -50,8 +50,9 @@ namespace datalog { typedef hashtable > in_progress_table; typedef svector todo_stack; - context & m_context; - ast_manager & m_manager; + context & m_context; + ast_manager & m; + rule_manager & rm; rule_ref_vector m_rules; bool m_modified; todo_stack m_todo; @@ -61,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; @@ -71,18 +72,18 @@ namespace datalog { bool is_unbound_argument(rule * r, unsigned head_index); bool has_unbound_head_var(rule * r); - void detect_tasks(unsigned rule_index); + void detect_tasks(rule_set const& source, unsigned rule_index); void add_task(func_decl * pred, unsigned arg_index); - void try_compress(unsigned rule_index); - void add_decompression_rules(unsigned rule_index); + void try_compress(rule_set const& source, unsigned rule_index); + void add_decompression_rules(rule_set const& source, unsigned rule_index); void mk_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index, rule_ref& res); - void add_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index); - void replace_by_decompression_rule(unsigned rule_index, unsigned tail_index, unsigned arg_index); + void add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index); + void replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index); void reset(); public: mk_unbound_compressor(context & ctx); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_unfold.cpp b/src/muz/transforms/dl_mk_unfold.cpp similarity index 78% rename from src/muz_qe/dl_mk_unfold.cpp rename to src/muz/transforms/dl_mk_unfold.cpp index d26a0a290..a9357f88a 100644 --- a/src/muz_qe/dl_mk_unfold.cpp +++ b/src/muz/transforms/dl_mk_unfold.cpp @@ -42,29 +42,21 @@ namespace datalog { if (m_unify.unify_rules(r, tail_idx, r2) && m_unify.apply(r, tail_idx, r2, new_rule)) { expr_ref_vector s1 = m_unify.get_rule_subst(r, true); - expr_ref_vector s2 = m_unify.get_rule_subst(r2, false); - resolve_rule(m_pc, r, r2, tail_idx, s1, s2, *new_rule.get()); + expr_ref_vector s2 = m_unify.get_rule_subst(r2, false); + resolve_rule(r, r2, tail_idx, s1, s2, *new_rule.get()); expand_tail(*new_rule.get(), tail_idx+r2.get_uninterpreted_tail_size(), src, dst); } } } } - rule_set * mk_unfold::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - m_pc = 0; - ref rpc; - if (pc) { - rpc = alloc(replace_proof_converter, m); - m_pc = rpc.get(); - } + rule_set * mk_unfold::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); rule_set::iterator it = source.begin(), end = source.end(); for (; it != end; ++it) { expand_tail(**it, 0, source, *rules); } - if (pc) { - pc = concat(pc.get(), rpc.get()); - } + rules->inherit_predicates(source); return rules; } diff --git a/src/muz_qe/dl_mk_unfold.h b/src/muz/transforms/dl_mk_unfold.h similarity index 86% rename from src/muz_qe/dl_mk_unfold.h rename to src/muz/transforms/dl_mk_unfold.h index 3d20686a7..26f64926d 100644 --- a/src/muz_qe/dl_mk_unfold.h +++ b/src/muz/transforms/dl_mk_unfold.h @@ -35,7 +35,6 @@ namespace datalog { ast_manager& m; rule_manager& rm; rule_unifier m_unify; - replace_proof_converter* m_pc; void expand_tail(rule& r, unsigned tail_idx, rule_set const& src, rule_set& dst); @@ -45,7 +44,7 @@ namespace datalog { */ mk_unfold(context & ctx); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz/transforms/dl_transforms.cpp b/src/muz/transforms/dl_transforms.cpp new file mode 100644 index 000000000..2cf48d46b --- /dev/null +++ b/src/muz/transforms/dl_transforms.cpp @@ -0,0 +1,82 @@ +/*++ +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" +#include"fixedpoint_params.hpp" + +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/muz_qe/README b/src/muz_qe/README deleted file mode 100644 index 5d6f433b8..000000000 --- a/src/muz_qe/README +++ /dev/null @@ -1 +0,0 @@ -muZ and Quantifier Elimination modules \ No newline at end of file diff --git a/src/muz_qe/dl_mk_bit_blast.cpp b/src/muz_qe/dl_mk_bit_blast.cpp deleted file mode 100644 index 1fa93a0ca..000000000 --- a/src/muz_qe/dl_mk_bit_blast.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_bit_blast.cpp - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) 2012-08-30 - -Revision History: - ---*/ - -#include "dl_mk_bit_blast.h" -#include "bit_blaster_rewriter.h" -#include "rewriter_def.h" -#include "ast_pp.h" - - -namespace datalog { - - // - // P(v) :- Q(extract[1:1]v ++ 0), R(1 ++ extract[0:0]v). - // -> - // P(bv(x,y)) :- Q(bv(x,0)), R(bv(1,y)) . - // - // Introduce P_bv: - // P_bv(x,y) :- Q_bv(x,0), R_bv(1,y) - // P(bv(x,y)) :- P_bv(x,y) - // Query - - - class expand_mkbv_cfg : public default_rewriter_cfg { - - context& m_context; - rule_ref_vector& m_rules; - ast_manager& m; - bv_util m_util; - expr_ref_vector m_args, m_f_vars, m_g_vars; - func_decl_ref_vector m_pinned; - obj_map m_pred2blast; - - - public: - - expand_mkbv_cfg(context& ctx, rule_ref_vector& rules): - m_context(ctx), - m_rules(rules), - m(ctx.get_manager()), - m_util(m), - m_args(m), - m_f_vars(m), - m_g_vars(m), - m_pinned(m) - {} - - ~expand_mkbv_cfg() {} - - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - rule_manager& rm = m_context.get_rule_manager(); - bool found = false; - for (unsigned j = 0; !found && j < num; ++j) { - found = m_util.is_mkbv(args[j]); - } - if (!found) { - return BR_FAILED; - } - // - // f(mk_bv(args),...) - // - m_args.reset(); - m_g_vars.reset(); - m_f_vars.reset(); - expr_ref fml(m); - unsigned idx = 0; - for (unsigned j = 0; j < num; ++j) { - expr* arg = args[j]; - if (m_util.is_mkbv(arg)) { - app* a = to_app(arg); - unsigned sz = a->get_num_args(); - for (unsigned i = 0; i < sz; ++i) { - m_args.push_back(a->get_arg(i)); - m_g_vars.push_back(m.mk_var(idx++,m.mk_bool_sort())); - } - m_f_vars.push_back(m_util.mk_bv(sz, m_g_vars.c_ptr()+m_g_vars.size()-sz)); - } - else { - m_args.push_back(arg); - m_f_vars.push_back(m.mk_var(idx++, m.get_sort(arg))); - m_g_vars.push_back(m_f_vars.back()); - } - } - func_decl* g = 0; - - if (!m_pred2blast.find(f, g)) { - - ptr_vector domain; - for (unsigned i = 0; i < m_args.size(); ++i) { - domain.push_back(m.get_sort(m_args[i].get())); - } - g = m_context.mk_fresh_head_predicate(f->get_name(), symbol("bv"), m_args.size(), domain.c_ptr(), f); - m_pinned.push_back(g); - m_pred2blast.insert(f, g); - - // Create rule f(mk_mkbv(args)) :- g(args) - - fml = m.mk_implies(m.mk_app(g, m_g_vars.size(), m_g_vars.c_ptr()), m.mk_app(f, m_f_vars.size(), m_f_vars.c_ptr())); - rm.mk_rule(fml, m_rules, g->get_name()); - } - result = m.mk_app(g, m_args.size(), m_args.c_ptr()); - result_pr = 0; - return BR_DONE; - } - }; - - struct expand_mkbv : public rewriter_tpl { - expand_mkbv_cfg m_cfg; - expand_mkbv(ast_manager& m, context& ctx, rule_ref_vector& rules): - rewriter_tpl(m, m.proofs_enabled(), m_cfg), - m_cfg(ctx, rules) { - } - }; - - - class mk_bit_blast::impl { - - context & m_context; - ast_manager & m; - params_ref m_params; - rule_ref_vector m_rules; - bit_blaster_rewriter m_blaster; - expand_mkbv m_rewriter; - - - bool blast(expr_ref& fml) { - proof_ref pr(m); - expr_ref fml1(m), fml2(m); - m_blaster(fml, fml1, pr); - m_rewriter(fml1, fml2); - TRACE("dl", tout << mk_pp(fml, m) << " -> " << mk_pp(fml1, m) << " -> " << mk_pp(fml2, m) << "\n";); - if (fml2 != fml) { - fml = fml2; - return true; - } - else { - return false; - } - } - - void reset() { - m_rules.reset(); - } - - public: - impl(context& ctx): - m_context(ctx), - m(ctx.get_manager()), - m_params(ctx.get_params().p), - m_rules(ctx.get_rule_manager()), - m_blaster(ctx.get_manager(), m_params), - m_rewriter(ctx.get_manager(), ctx, m_rules) { - m_params.set_bool("blast_full", true); - m_params.set_bool("blast_quant", true); - m_blaster.updt_params(m_params); - } - - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc - if (!m_context.get_params().bit_blast()) { - return 0; - } - if (m_context.get_engine() != PDR_ENGINE) { - return 0; - } - rule_manager& rm = m_context.get_rule_manager(); - unsigned sz = source.get_num_rules(); - expr_ref fml(m); - reset(); - rule_set * result = alloc(rule_set, m_context); - for (unsigned i = 0; i < sz; ++i) { - rule * r = source.get_rule(i); - r->to_formula(fml); - if (blast(fml)) { - rm.mk_rule(fml, m_rules, r->name()); - } - else { - m_rules.push_back(r); - } - } - - for (unsigned i = 0; i < m_rules.size(); ++i) { - result->add_rule(m_rules.get(i)); - } - - return result; - } - }; - - mk_bit_blast::mk_bit_blast(context & ctx, unsigned priority) : plugin(priority) { - m_impl = alloc(impl, ctx); - } - - mk_bit_blast::~mk_bit_blast() { - dealloc(m_impl); - } - - rule_set * mk_bit_blast::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - return (*m_impl)(source, mc, pc); - } - -}; diff --git a/src/muz_qe/dl_mk_coi_filter.cpp b/src/muz_qe/dl_mk_coi_filter.cpp deleted file mode 100644 index 04d654120..000000000 --- a/src/muz_qe/dl_mk_coi_filter.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_mk_coi_filter.cpp - -Abstract: - - Rule transformer which removes relations which are out of the cone of - influence of output relations - -Author: - - Krystof Hoder (t-khoder) 2011-10-01. - -Revision History: - ---*/ - - -#include -#include"ast_pp.h" -#include"dl_mk_coi_filter.h" -#include"extension_model_converter.h" - -namespace datalog { - - // ----------------------------------- - // - // mk_coi_filter - // - // ----------------------------------- - - - rule_set * mk_coi_filter::operator()( - rule_set const & source, - model_converter_ref& mc, - proof_converter_ref& pc) - { - if (source.get_num_rules()==0) { - return 0; - } - - decl_set interesting_preds; - decl_set pruned_preds; - ptr_vector todo; - { - const decl_set& output_preds = m_context.get_output_predicates(); - decl_set::iterator oend = output_preds.end(); - for (decl_set::iterator it = output_preds.begin(); it!=oend; ++it) { - todo.push_back(*it); - interesting_preds.insert(*it); - } - } - - const rule_dependencies& deps = source.get_dependencies(); - - while (!todo.empty()) { - func_decl * curr = todo.back(); - todo.pop_back(); - interesting_preds.insert(curr); - - 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) { - func_decl * dep_pred = *it; - if (!interesting_preds.contains(dep_pred)) { - interesting_preds.insert(dep_pred); - todo.push_back(dep_pred); - } - } - } - - scoped_ptr res = alloc(rule_set, m_context); - - rule_set::iterator rend = source.end(); - for (rule_set::iterator rit = source.begin(); rit!=rend; ++rit) { - rule * r = *rit; - func_decl * pred = r->get_decl(); - if (interesting_preds.contains(pred)) { - res->add_rule(r); - } - else if (mc.get()) { - pruned_preds.insert(pred); - } - } - - if (res->get_num_rules() == source.get_num_rules()) { - res = 0; - } - - if (res && mc) { - decl_set::iterator end = pruned_preds.end(); - 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()); - } - mc = concat(mc.get(), mc0); - } - - return res.detach(); - } - -}; - diff --git a/src/muz_qe/dl_mk_extract_quantifiers.cpp b/src/muz_qe/dl_mk_extract_quantifiers.cpp deleted file mode 100644 index 35f7485d5..000000000 --- a/src/muz_qe/dl_mk_extract_quantifiers.cpp +++ /dev/null @@ -1,371 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_extract_quantifiers.cpp - -Abstract: - - Remove universal quantifiers over interpreted predicates in the body. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-11-21 - -Revision History: - ---*/ - -#include"dl_mk_extract_quantifiers.h" -#include"ast_pp.h" -#include"dl_bmc_engine.h" -#include"smt_quantifier.h" -#include"smt_context.h" -#include"for_each_expr.h" -#include "expr_abstract.h" - - -namespace datalog { - - - mk_extract_quantifiers::mk_extract_quantifiers(context & ctx) : - rule_transformer::plugin(101, false), - m_ctx(ctx), - m(ctx.get_manager()), - rm(ctx.get_rule_manager()), - m_query_pred(m) - {} - - mk_extract_quantifiers::~mk_extract_quantifiers() { - reset(); - } - - void mk_extract_quantifiers::set_query(func_decl* q) { - m_query_pred = q; - } - - void mk_extract_quantifiers::ensure_predicate(expr* e, unsigned& max_var, app_ref_vector& tail) { - SASSERT(is_app(e)); - SASSERT(to_app(e)->get_decl()->get_family_id() == null_family_id); - app* a = to_app(e); - expr_ref_vector args(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - expr* arg = a->get_arg(i); - if (is_var(arg) || m.is_value(arg)) { - args.push_back(arg); - } - else { - expr_ref new_var(m); - new_var = m.mk_var(++max_var, m.get_sort(arg)); - args.push_back(new_var); - tail.push_back(m.mk_eq(new_var, arg)); - } - } - tail.push_back(m.mk_app(a->get_decl(), args.size(), args.c_ptr())); - } - - class mk_extract_quantifiers::collect_insts { - ast_manager& m; - ptr_vector m_binding; - vector m_bindings; - ptr_vector m_quantifiers; - public: - collect_insts(ast_manager& m): m(m) { } - - void operator()(expr* n) { - expr* not_q_or_i, *e1, *e2, *e3; - if (m.is_quant_inst(n, not_q_or_i, m_binding)) { - VERIFY(m.is_or(not_q_or_i, e1, e2)); - VERIFY(m.is_not(e1, e3)); - SASSERT(is_quantifier(e3)); - m_quantifiers.push_back(to_quantifier(e3)); - m_bindings.push_back(expr_ref_vector(m,m_binding.size(), m_binding.c_ptr())); - m_binding.reset(); - } - else if ((m.is_rewrite(n, e1, e2) || - (m.is_rewrite_star(n) && - (e3 = to_app(n)->get_arg(to_app(n)->get_num_args()-1), - e1 = to_app(e3)->get_arg(0), - e2 = to_app(e3)->get_arg(1), - true))) && - is_quantifier(e1) && m.is_false(e2)) { - quantifier* q = to_quantifier(e1); - m_quantifiers.push_back(q); - m_bindings.push_back(expr_ref_vector(m)); - expr_ref_vector& b = m_bindings.back(); - for (unsigned i = 0; i < q->get_num_decls(); ++i) { - b.push_back(m.mk_fresh_const("V", q->get_decl_sort(i))); - } - } - } - - void reset() { - m_quantifiers.reset(); - m_bindings.reset(); - } - - unsigned size() const { return m_quantifiers.size(); } - ptr_vector const& quantifiers() const { return m_quantifiers; } - vector const& bindings() const { return m_bindings; } - }; - - - /* - * forall y . P1(x,y) & - * forall y . P2(x,y) & - * Body[x] & - * ~H[x] - * forall y . y != binding1 => ~ P1(x,y) - * forall y . y != binding2 => ~ P2(x,y) - */ - void mk_extract_quantifiers::extract(rule& r, rule_set& new_rules) { - unsigned utsz = r.get_uninterpreted_tail_size(); - expr_ref_vector conjs(m); - quantifier_ref_vector qs(m); - for (unsigned i = utsz; i < r.get_tail_size(); ++i) { - conjs.push_back(r.get_tail(i)); - } - datalog::flatten_and(conjs); - for (unsigned j = 0; j < conjs.size(); ++j) { - expr* e = conjs[j].get(); - quantifier* q; - if (rule_manager::is_forall(m, e, q)) { - qs.push_back(q); - conjs[j] = conjs.back(); - conjs.pop_back(); - --j; - } - } - if (qs.empty()) { - new_rules.add_rule(&r); - } - else { - expr_ref fml(m); - expr_ref_vector bindings(m); - obj_map insts; - for (unsigned i = 0; i < qs.size(); ++i) { - insts.insert(qs[i].get(), alloc(expr_ref_vector, m)); - } - - unsigned max_inst = 10; // TODO configuration. - - for (unsigned i = 0; i < max_inst; ++i) { - app_ref_vector sub(m); - rule2formula(r, insts, fml, sub); - bool new_binding = find_instantiations_proof_based(fml, sub, insts, bindings); - if (!new_binding) { - break; - } - } - - expr_ref_vector fmls(m); - for (unsigned i = 0; i < utsz; ++i) { - fmls.push_back(r.get_tail(i)); - } - fmls.append(bindings); - fmls.append(conjs); - fml = m.mk_implies(m.mk_and(fmls.size(), fmls.c_ptr()), r.get_head()); - TRACE("dl", tout << "new rule\n" << mk_pp(fml, m) << "\n";); - rule_ref_vector rules(rm); - rm.mk_rule(fml, rules, r.name()); - for (unsigned i = 0; i < rules.size(); ++i) { - new_rules.add_rule(rules[i].get()); - m_quantifiers.insert(rules[i].get(), alloc(quantifier_ref_vector, qs)); - } - obj_map::iterator it = insts.begin(), end = insts.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - } - } - - void mk_extract_quantifiers::rule2formula( - rule& r, - obj_map const& insts, - expr_ref& fml, - app_ref_vector& sub) - { - expr_ref body(m); - expr_ref_vector fmls(m); - ptr_vector sorts; - var_subst vs(m, false); - obj_map::iterator it = insts.begin(), end = insts.end(); - for (; it != end; ++it) { - quantifier* q = it->m_key; - expr_ref_vector& eqs = *it->m_value; - expr_ref_vector disj(m); - disj.append(eqs); - disj.push_back(m.mk_not(q->get_expr())); - body = m.mk_or(disj.size(), disj.c_ptr()); - fml = m.update_quantifier(q, body); - fmls.push_back(fml); - } - fml = m.mk_or(fmls.size(), fmls.c_ptr()); - fmls.reset(); - fmls.push_back(fml); - for (unsigned i = 0; i < r.get_tail_size(); ++i) { - SASSERT(!r.is_neg_tail(i)); - fmls.push_back(r.get_tail(i)); - } - fmls.push_back(m.mk_not(r.get_head())); - fml = m.mk_and(fmls.size(), fmls.c_ptr()); - - get_free_vars(fml, sorts); - for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } - sub.push_back(m.mk_const(symbol(i), sorts[i])); - } - vs(fml, sub.size(), (expr*const*)sub.c_ptr(), fml); - } - - bool mk_extract_quantifiers::find_instantiations_proof_based( - expr* fml, - app_ref_vector const& var_inst, - obj_map& insts, - expr_ref_vector& bindings) - { - datalog::scoped_proof _scp(m); - smt_params fparams; - fparams.m_mbqi = true; // false - fparams.m_soft_timeout = 1000; - smt::kernel solver(m, fparams); - solver.assert_expr(fml); - IF_VERBOSE(1, verbose_stream() << "check\n";); - lbool result = solver.check(); - IF_VERBOSE(1, verbose_stream() << "checked\n";); - TRACE("dl", tout << result << "\n";); - if (result != l_false) { - return false; - } - - map qid_map; - quantifier* q; - - obj_map::iterator it = insts.begin(), end = insts.end(); - for (; it != end; ++it) { - q = it->m_key; - qid_map.insert(q->get_qid(), q); - } - - proof* p = solver.get_proof(); - TRACE("dl", tout << mk_pp(p, m) << "\n";); - collect_insts collector(m); - for_each_expr(collector, p); - ptr_vector const& quants = collector.quantifiers(); - - for (unsigned i = 0; i < collector.size(); ++i) { - symbol qid = quants[i]->get_qid(); - if (!qid_map.find(qid, q)) { - TRACE("dl", tout << "Could not find quantifier " << mk_pp(quants[i], m) << "\n";); - continue; - } - expr_ref_vector const& binding = collector.bindings()[i]; - - TRACE("dl", tout << "Instantiating:\n" << mk_pp(quants[i], m) << "\n"; - for (unsigned j = 0; j < binding.size(); ++j) { - tout << mk_pp(binding[j], m) << " "; - } - tout << "\n";); - - expr_ref_vector instantiation(m); - for (unsigned j = 0; j < binding.size(); ++j) { - instantiation.push_back(binding[j]); - } - add_binding(var_inst, bindings, q, instantiation, insts); - } - - return collector.size() > 0; - } - - void mk_extract_quantifiers::add_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts) - { - if (instantiation.size() == q->get_num_decls()) { - // Full binding. - apply_binding(var_inst, bindings, q, instantiation, insts); - } - } - - void mk_extract_quantifiers::apply_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts) - { - datalog::scoped_no_proof _scp(m); - expr_ref e(m); - expr_ref_vector eqs(m); - var_subst vs(m, false); - inv_var_shifter invsh(m); - vs(q->get_expr(), instantiation.size(), instantiation.c_ptr(), e); - invsh(e, q->get_num_decls(), e); - expr_ref_vector inst(m); - inst.append(var_inst.size(), (expr*const*)var_inst.c_ptr()); - inst.reverse(); - expr_abstract(m, 0, inst.size(), inst.c_ptr(), e, e); - bindings.push_back(e); - for (unsigned i = 0; i < instantiation.size(); ++i) { - e = instantiation[i]; - e = m.mk_eq(m.mk_var(i, q->get_decl_sort(i)), e); - eqs.push_back(e); - } - e = m.mk_and(eqs.size(), eqs.c_ptr()); - insts.find(q)->push_back(e); - - TRACE("dl", tout << mk_pp(q, m) << "\n"; - tout << "instantiation: "; - for (unsigned i = 0; i < instantiation.size(); ++i) { - tout << mk_pp(instantiation[i], m) << " "; - } - tout << "\n"; - tout << "inst: "; - for (unsigned i = 0; i < var_inst.size(); ++i) { - tout << mk_pp(var_inst[i], m) << " "; - } - tout << "\n"; - tout << mk_pp(bindings.back(), m) << "\n"; - tout << "eqs: " << mk_pp(e, m) << "\n"; - ); - } - - - void mk_extract_quantifiers::reset() { - obj_map::iterator it = m_quantifiers.begin(), - end = m_quantifiers.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - m_has_quantifiers = false; - m_quantifiers.reset(); - } - - rule_set * mk_extract_quantifiers::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - reset(); - rule_set::iterator it = source.begin(), end = source.end(); - for (; !m_has_quantifiers && it != end; ++it) { - m_has_quantifiers = (*it)->has_quantifiers(); - } - if (!m_has_quantifiers) { - return 0; - } - - rule_set* rules = alloc(rule_set, m_ctx); - it = source.begin(); - for (; it != end; ++it) { - extract(**it, *rules); - } - - return rules; - } - -}; - - diff --git a/src/muz_qe/dl_mk_extract_quantifiers.h b/src/muz_qe/dl_mk_extract_quantifiers.h deleted file mode 100644 index b32dbc32d..000000000 --- a/src/muz_qe/dl_mk_extract_quantifiers.h +++ /dev/null @@ -1,99 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_extract_quantifiers.h - -Abstract: - - Replace universal quantifiers over interpreted predicates in the body - by instantiations mined using bounded model checking search. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-11-21 - -Revision History: - ---*/ -#ifndef _DL_MK_EXTRACT_QUANTIFIERS_H_ -#define _DL_MK_EXTRACT_QUANTIFIERS_H_ - -#include"dl_context.h" -#include"dl_rule_set.h" -#include"dl_rule_transformer.h" -#include"obj_pair_hashtable.h" - -namespace datalog { - - /** - \brief Extract universal quantifiers from rules. - */ - class mk_extract_quantifiers : public rule_transformer::plugin { - - class collect_insts; - - context& m_ctx; - ast_manager& m; - rule_manager& rm; - func_decl_ref m_query_pred; - bool m_has_quantifiers; - obj_map m_quantifiers; - - void reset(); - - void extract(rule& r, rule_set& new_rules); - - void rule2formula( - rule& r, - obj_map const& insts, - expr_ref& fml, - app_ref_vector& sub); - - - void add_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts); - - void apply_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts); - - - public: - /** - \brief Create rule transformer that extracts universal quantifiers (over recursive predicates). - */ - mk_extract_quantifiers(context & ctx); - - virtual ~mk_extract_quantifiers(); - - void set_query(func_decl* q); - - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); - - bool has_quantifiers() { return m_has_quantifiers; } - - obj_map& quantifiers() { return m_quantifiers; } - - void ensure_predicate(expr* e, unsigned& max_var, app_ref_vector& tail); - - bool find_instantiations_proof_based( - expr* fml, - app_ref_vector const& var_inst, - obj_map& insts, - expr_ref_vector& bindings); - - }; - -}; - -#endif /* _DL_MK_EXTRACT_QUANTIFIERS_H_ */ - diff --git a/src/muz_qe/dl_skip_table.cpp b/src/muz_qe/dl_skip_table.cpp deleted file mode 100644 index 28efaabee..000000000 --- a/src/muz_qe/dl_skip_table.cpp +++ /dev/null @@ -1,622 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_skip_table.h - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) - Leonardo de Moura (leonardo) 2010-10-14 - - -Revision History: - ---*/ - -#ifndef _EXTERNAL_RELEASE - -#include "dl_skip_table.h" -#include "dl_table.h" -#include "dl_context.h" - - -namespace datalog { - - skip_table & skip_table_plugin::get(table_base& r) { - return static_cast(r); - } - - skip_table const & skip_table_plugin::get(table_base const& r) { - return static_cast(r); - } - - table_base * skip_table_plugin::mk_empty(const table_signature & s) { - return alloc(skip_table, *this, s); - } - - skip_table* skip_table_plugin::mk_join( - table_base const& t1, table_base const& t2, table_signature const& result_sig, - unsigned_vector const& cols1, unsigned_vector const& cols2) { - skip_table const& s1 = get(t1); - skip_table const& s2 = get(t2); - imdd_manager& m = s1.get_imdd_manager(); - imdd_ref pr(m); - m.mk_join(s1.get_imdd(), s2.get_imdd(), pr, cols1, cols2); - return alloc(skip_table, s1.get_plugin(), result_sig, pr); - } - - skip_table* skip_table_plugin::mk_join_project( - table_base const& t1, table_base const& t2, table_signature const& result_sig, - unsigned_vector const& cols1, unsigned_vector const& cols2, - unsigned_vector const& proj_cols) { - - skip_table const& s1 = get(t1); - skip_table const& s2 = get(t2); - imdd_manager& m = s1.get_imdd_manager(); - imdd_ref pr(m); - m.mk_join_project(s1.get_imdd(), s2.get_imdd(), pr, cols1, cols2, proj_cols); - return alloc(skip_table, s1.get_plugin(), result_sig, pr); - } - - class skip_table_plugin::join_fn : public convenient_table_join_fn { - public: - join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2): - convenient_table_join_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2) { - } - - virtual table_base* operator()(const table_base & t1, const table_base & t2) { - return skip_table_plugin::mk_join(t1, t2, get_result_signature(), m_cols1, m_cols2); - } - }; - - table_join_fn * skip_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, t2, col_cnt, cols1, cols2); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::join_project_fn : public convenient_table_join_project_fn { - public: - 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): - convenient_table_join_project_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, - removed_col_cnt, removed_cols) { - } - - virtual table_base* operator()(const table_base & t1, const table_base & t2) { - return skip_table_plugin::mk_join_project(t1, t2, get_result_signature(), m_cols1, m_cols2, m_removed_cols); - } - }; - - - - table_join_fn * skip_table_plugin::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) { - if (check_kind(t1) && check_kind(t2)) { - return alloc(join_project_fn, t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::union_fn : public table_union_fn { - public: - virtual void operator()(table_base& tgt, const table_base& src, table_base* delta) { - skip_table& s1 = get(tgt); - skip_table const& s2 = get(src); - imdd_manager& m = s1.get_imdd_manager(); - imdd_ref r(m); - m.mk_union(s1.get_imdd(), s2.get_imdd(), r); - if (delta) { - skip_table& d = get(*delta); - if (m.is_subset(r, s1.get_imdd())) { - d.update(m.mk_empty(s1.get_signature().size())); - } - else { - d.update(r); - } - } - s1.update(r); - } - }; - - table_union_fn * skip_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); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - skip_table* skip_table_plugin::mk_project(table_base const& src, table_signature const& result_sig, unsigned_vector const& cols) { - skip_table const& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - imdd_ref pr(m); - m.mk_project(s.get_imdd(), pr, cols.size(), cols.c_ptr()); - return alloc(skip_table, s.get_plugin(), result_sig, pr); - } - - - class skip_table_plugin::project_fn : public convenient_table_project_fn { - public: - project_fn(table_signature const& orig_sig, unsigned col_cnt, unsigned const* removed_cols): - convenient_table_project_fn(orig_sig, col_cnt, removed_cols) {} - - table_base* operator()(table_base const& src) { - return mk_project(src, get_result_signature(), m_removed_cols); - } - }; - - table_transformer_fn * skip_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); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::rename_fn : public convenient_table_rename_fn { - - void swap2(imdd_ref& n, unsigned col1, unsigned col2) { - imdd_manager& m = n.get_manager(); - imdd_ref tmp(m); - if (col1 == col2) { - return; - } - if (col1 > col2) { - std::swap(col1, col2); - } - for (unsigned i = col1; i < col2; ++i) { - m.mk_swap(n, tmp, i); - n = tmp; - } - for (unsigned i = col2 - 1; i > col1; ) { - --i; - m.mk_swap(n, tmp, i); - n = tmp; - } - } - public: - rename_fn(table_signature const& sig, unsigned cycle_len, unsigned const* cycle): - convenient_rename_fn(sig, cycle_len, cycle) {} - - table_base* operator()(table_base const& src) { - TRACE("skip", - for (unsigned i = 0; i < m_cycle.size(); ++i) { - tout << m_cycle[i] << " "; - } - tout << "\n"; - src.display(tout);); - skip_table const& s = get(src); - imdd_ref n(s.m_imdd, s.get_imdd_manager()); - unsigned cycle_len = m_cycle.size(); - unsigned col1, col2; - // TBD: review this for proper direction - for (unsigned i = 0; i + 1 < cycle_len; ++i) { - col1 = m_cycle[i]; - col2 = m_cycle[i+1]; - swap2(n, col1, col2); - } - if (cycle_len > 2) { - col1 = m_cycle[cycle_len-1]; - col2 = m_cycle[0]; - swap2(n, col1, col2); - } - skip_table* res = alloc(skip_table, s.get_plugin(), get_result_signature(), n); - TRACE("skip",res->display(tout);); - return res; - } - }; - - table_transformer_fn * skip_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) { - if (check_kind(t)) { - return alloc(rename_fn, t.get_signature(), len, cycle); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_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) - {} - - void operator()(table_base & t) { - skip_table& s = get(t); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_identical(s.get_imdd(), s.m_imdd, m_cols.size(), m_cols.c_ptr(), true); - } - }; - - table_mutator_fn * skip_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); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_equal_fn : public table_mutator_fn { - unsigned m_col; - unsigned m_value; - public: - filter_equal_fn(const table_base & t, const table_element & v, unsigned col): - m_col(col), - m_value(static_cast(v)) - { - SASSERT(v <= UINT_MAX); - } - - virtual void operator()(table_base& src) { - skip_table& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_equal(s.get_imdd(), s.m_imdd, m_col, m_value); - } - }; - - table_mutator_fn * skip_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, t, value, col); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_not_equal_fn : public table_mutator_fn { - unsigned m_col; - unsigned m_value; - public: - filter_not_equal_fn(const table_base & t, const table_element & v, unsigned col): - m_col(col), - m_value(static_cast(v)) - { - SASSERT(v <= UINT_MAX); - } - - virtual void operator()(table_base& src) { - skip_table& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_disequal(s.get_imdd(), s.m_imdd, m_col, m_value); - } - }; - - table_mutator_fn * skip_table_plugin::mk_filter_not_equal_fn(const table_base & t, const table_element & value, - unsigned col) { - if (check_kind(t)) { - return alloc(filter_not_equal_fn, t, value, col); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_distinct_fn : public table_mutator_fn { - unsigned m_col1; - unsigned m_col2; - public: - filter_distinct_fn(const table_base & t, unsigned col1, unsigned col2): - m_col1(col1), - m_col2(col2) { - } - - virtual void operator()(table_base& src) { - skip_table& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_distinct(s.get_imdd(), s.m_imdd, m_col1, m_col2); - } - }; - - table_mutator_fn * skip_table_plugin::mk_filter_distinct_fn(const table_base & t, unsigned col1, unsigned col2) { - if (check_kind(t)) { - return alloc(filter_distinct_fn, t, col1, col2); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - // - // The default implementation uses an iterator - // if the condition is a comparison <, <=, then interval table native will be an advantage. - // - table_mutator_fn * skip_table_plugin::mk_filter_interpreted_fn(const table_base & t, app * condition) { - ast_manager& m = get_ast_manager(); - dl_decl_util& util = get_context().get_decl_util(); - uint64 value; - - if (m.is_eq(condition)) { - expr* x = condition->get_arg(0); - expr* y = condition->get_arg(1); - if (is_var(y)) { - std::swap(x,y); - } - if (is_var(x) && is_var(y)) { - unsigned cols[2] = { to_var(x)->get_idx(), to_var(y)->get_idx() }; - return mk_filter_identical_fn(t, 2, cols); - } - if (is_var(x) && util.is_numeral_ext(y, value)) { - return mk_filter_equal_fn(t, value, to_var(x)->get_idx()); - } - } - - if (m.is_not(condition) && is_app(condition->get_arg(0))) { - condition = to_app(condition->get_arg(0)); - if (m.is_eq(condition)) { - expr* x = condition->get_arg(0); - expr* y = condition->get_arg(1); - if (is_var(y)) { - std::swap(x,y); - } - if (is_var(x) && is_var(y)) { - return mk_filter_distinct_fn(t, to_var(x)->get_idx(), to_var(y)->get_idx()); - } - if (is_var(x) && util.is_numeral_ext(y, value)) { - return mk_filter_not_equal_fn(t, value, to_var(x)->get_idx()); - } - } - } - - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_by_negation_fn : public convenient_table_negation_filter_fn { - public: - filter_by_negation_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) { - - } - - // - // Compute - // { (x,y) | t(x,y) & ! exists z . negated_obj(x,z) } - // - // 1. Project z - // 2. Join with result. - // - - virtual void operator()(table_base & tgt0, const table_base & neg0) { - skip_table & tgt = get(tgt0); - const skip_table & neg = get(neg0); - unsigned_vector cols2(m_cols2); - unsigned_vector proj_cols; - table_base* t1 = 0; - if (!m_all_neg_bound) { - unsigned_vector proj_cols, remap; - table_signature sig2; - table_signature const& neg_sig = neg.get_signature(); - for (unsigned i = 0, j = 0; i < m_bound.size(); ++i) { - if (m_bound[i]) { - remap.push_back(j++); - sig2.push_back(neg_sig[i]); - } - else { - proj_cols.push_back(i); - remap.push_back(0); - } - } - for (unsigned i = 0; i < cols2.size(); ++i) { - cols2[i] = remap[cols2[i]]; - } - skip_table* t0 = skip_table_plugin::mk_project(neg, sig2, proj_cols); - t1 = t0->complement(); - t0->deallocate(); - proj_cols.reset(); - } - else { - t1 = neg.complement(); - } - for (unsigned i = 0; i < t1->get_signature().size(); ++i) { - proj_cols.push_back(tgt0.get_signature().size()+i); - } - skip_table* t2 = skip_table_plugin::mk_join_project(tgt0, *t1, tgt0.get_signature(), m_cols1, cols2, proj_cols); - t1->deallocate(); - tgt.update(*t2); - t2->deallocate(); - } - }; - - table_intersection_filter_fn * skip_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, t, negated_obj, joined_col_cnt, t_cols, negated_cols); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - bool skip_table_plugin::can_handle_signature(table_signature const& sig) { - for (unsigned i = 0; i < sig.size(); ++i) { - if (sig[i] >= UINT_MAX) { - return false; - } - } - return true; - } - - // ------------------ - // skip_table - - - skip_table::skip_table(skip_table_plugin & p, const table_signature & sig): - table_base(p, sig), - m_imdd(p.get_imdd_manager().mk_empty(sig.size()), p.get_imdd_manager()) { - SASSERT(well_formed()); - } - - skip_table::skip_table(skip_table_plugin & p, const table_signature & sig, imdd* m): - table_base(p, sig), - m_imdd(m, p.get_imdd_manager()) { - SASSERT(well_formed()); - } - - skip_table::~skip_table() { - } - - - bool skip_table::well_formed() const { - table_signature const& sig = get_signature(); - return - get_plugin().can_handle_signature(sig) && - (get_imdd()->get_arity() == sig.size()); - } - - bool skip_table::empty() const { - return get_imdd()->empty(); - } - - void skip_table::update(imdd* n) { - m_imdd = n; - SASSERT(well_formed()); - } - - void skip_table::add_fact(const table_fact & f) { - imdd_manager& m = get_plugin().get_imdd_manager(); - unsigned const* fact = get_fact(f.c_ptr()); - m.add_fact(get_imdd(), m_imdd, f.size(), fact); - SASSERT(well_formed()); - } - - void skip_table::remove_fact(const table_element* f) { - imdd_manager& m = get_imdd_manager(); - unsigned const* fact = get_fact(f); - m.remove_facts(get_imdd(), m_imdd, get_signature().size(), fact, fact); - } - - bool skip_table::contains_fact(const table_fact & f) const { - imdd_manager& m = get_imdd_manager(); - unsigned const* fact = get_fact(f.c_ptr()); - return m.contains(get_imdd(), f.size(), fact); - } - - table_base * skip_table::clone() const { - return alloc(skip_table, get_plugin(), get_signature(), get_imdd()); - } - - table_base * skip_table::complement() const { - imdd_manager& m = get_plugin().get_imdd_manager(); - table_signature const& sig = get_signature(); - unsigned_vector mins, maxs; - for (unsigned i = 0; i < sig.size(); ++i) { - SASSERT(sig[i] < UINT_MAX); - mins.push_back(0); - maxs.push_back(static_cast(sig[i])); - } - imdd_ref cmpl(m); - m.mk_complement(get_imdd(), cmpl, sig.size(), mins.c_ptr(), maxs.c_ptr()); - return alloc(skip_table, get_plugin(), get_signature(), cmpl); - } - - unsigned const* skip_table::get_fact(table_element const* f) const { - table_signature const& sig = get_signature(); - const_cast(m_fact).reset(); - for (unsigned i = 0; i < sig.size(); ++i) { - const_cast(m_fact).push_back(static_cast(f[i])); - SASSERT(f[i] < UINT_MAX); - } - return m_fact.c_ptr(); - } - - - - class skip_table::our_iterator_core : public table_base::iterator_core { - skip_table const& m_table; - imdd_manager::iterator m_iterator; - - class our_row : public row_interface { - const our_iterator_core & m_parent; - public: - our_row(const our_iterator_core & parent) : row_interface(parent.m_table), m_parent(parent) {} - - virtual void get_fact(table_fact & result) const { - result.reset(); - unsigned arity = m_parent.m_iterator.get_arity(); - unsigned const* values = *(m_parent.m_iterator); - for (unsigned i = 0; i < arity; ++i) { - result.push_back(values[i]); - } - } - virtual table_element operator[](unsigned col) const { - SASSERT(col < m_parent.m_iterator.get_arity()); - unsigned const* values = *(m_parent.m_iterator); - return values[col]; - } - }; - - our_row m_row_obj; - - public: - struct b {}; - struct e {}; - - our_iterator_core(skip_table const& t, b): - m_table(t), - m_iterator(t.m_imdd.get_manager(), t.get_imdd()), - m_row_obj(*this) {} - - our_iterator_core(skip_table const& t, e): - m_table(t), - m_iterator(), - m_row_obj(*this) {} - - virtual bool is_finished() const { - return m_iterator == imdd_manager::iterator(); - } - - virtual row_interface & operator*() { - SASSERT(!is_finished()); - return m_row_obj; - } - - virtual void operator++() { - SASSERT(!is_finished()); - ++m_iterator; - } - }; - - - table_base::iterator skip_table::begin() const { - return mk_iterator(alloc(our_iterator_core, *this, our_iterator_core::b())); - } - - table_base::iterator skip_table::end() const { - return mk_iterator(alloc(our_iterator_core, *this, our_iterator_core::e())); - } - - unsigned skip_table::get_size_estimate_rows() const { - imdd_manager& m = get_plugin().get_imdd_manager(); - size_t sz = m.get_num_rows(get_imdd()); - unsigned sz0 = static_cast(sz); - SASSERT (sz == sz0 && "we need to use size_t or big-ints for row count"); - return sz0; - } - - unsigned skip_table::get_size_estimate_bytes() const { - imdd_manager& m = get_plugin().get_imdd_manager(); - return m.memory(get_imdd()); - } - -}; - -#endif diff --git a/src/muz_qe/dl_skip_table.h b/src/muz_qe/dl_skip_table.h deleted file mode 100644 index 6a9af9636..000000000 --- a/src/muz_qe/dl_skip_table.h +++ /dev/null @@ -1,161 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_skip_table.h - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) - Leonardo de Moura (leonardo) 2010-10-14 - - -Revision History: - ---*/ - -#ifndef _DL_SKIP_TABLE_H_ -#define _DL_SKIP_TABLE_H_ - -#include "dl_base.h" -#include "imdd.h" - -namespace datalog { - class skip_table; - - class skip_table_plugin : public table_plugin { - friend class skip_table; - imdd_manager m_manager; - protected: - class join_fn; - class join_project_fn; - class union_fn; - class transformer_fn; - class rename_fn; - class project_fn; - class filter_equal_fn; - class filter_not_equal_fn; - class filter_identical_fn; - class filter_distinct_fn; - class filter_by_negation_fn; - - imdd_manager& get_imdd_manager() const { return const_cast(m_manager); } - - public: - typedef skip_table table; - - skip_table_plugin(relation_manager & manager) - : table_plugin(symbol("skip"), 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); - virtual 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); - 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); - - virtual bool can_handle_signature(table_signature const& s); - - private: - static skip_table& get(table_base& r); - - static skip_table const & get(table_base const& r); - - static skip_table* mk_join(table_base const& t1, table_base const& t2, table_signature const& s, - unsigned_vector const& cols_t1, unsigned_vector const& cols_t2); - - static skip_table* mk_join_project(table_base const& t1, table_base const& t2, table_signature const& s, - unsigned_vector const& cols_t1, unsigned_vector const& cols_t2, unsigned_vector const& proj_cols); - - static skip_table* mk_project(table_base const& src, table_signature const& result_sig, - unsigned_vector const& cols); - - virtual table_mutator_fn * mk_filter_distinct_fn(const table_base & t, unsigned col1, unsigned col2); - - virtual table_mutator_fn * mk_filter_not_equal_fn(const table_base & t, const table_element & value, - unsigned col); - - }; - - class skip_table : public table_base { - friend class skip_table_plugin; - friend class skip_table_plugin::join_fn; - friend class skip_table_plugin::union_fn; - friend class skip_table_plugin::transformer_fn; - friend class skip_table_plugin::rename_fn; - friend class skip_table_plugin::project_fn; - friend class skip_table_plugin::filter_equal_fn; - friend class skip_table_plugin::filter_not_equal_fn; - friend class skip_table_plugin::filter_identical_fn; - friend class skip_table_plugin::filter_distinct_fn; - - class our_iterator_core; - - imdd_ref m_imdd; - unsigned_vector m_fact; - - imdd* get_imdd() const { return m_imdd.get(); } - - imdd_manager& get_imdd_manager() const { return get_plugin().get_imdd_manager(); } - - unsigned const* get_fact(table_element const* f) const; - - bool well_formed() const; - - void update(imdd* n); - - void update(skip_table& t) { update(t.m_imdd); } - - skip_table(skip_table_plugin & p, const table_signature & sig); - - skip_table(skip_table_plugin & p, const table_signature & sig, imdd*); - - skip_table(const skip_table & t); - - virtual ~skip_table(); - - public: - - skip_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() 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; - }; - - }; - - #endif /* _DL_SKIP_TABLE_H_ */ diff --git a/src/muz_qe/equiv_proof_converter.cpp b/src/muz_qe/equiv_proof_converter.cpp deleted file mode 100644 index 53db01900..000000000 --- a/src/muz_qe/equiv_proof_converter.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - equiv_proof_converter.cpp - -Abstract: - - Proof converter that applies equivalence rule to leaves. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-11-23 - -Revision History: - ---*/ - -#include "equiv_proof_converter.h" -#include "ast_pp.h" -#include "dl_util.h" - -void equiv_proof_converter::insert(expr* fml1, expr* fml2) { - datalog::scoped_proof _sp(m); - proof_ref p1(m), p2(m), p3(m); - p1 = m.mk_asserted(fml1); - p2 = m.mk_rewrite(fml1, fml2); - p3 = m.mk_modus_ponens(p1, p2); - TRACE("proof_converter", tout << mk_pp(p3.get(), m) << "\n";); - SASSERT(m.has_fact(p3)); - m_replace.insert(p3); -} diff --git a/src/muz_qe/imdd.cpp b/src/muz_qe/imdd.cpp deleted file mode 100644 index 1d7c6d2b2..000000000 --- a/src/muz_qe/imdd.cpp +++ /dev/null @@ -1,3688 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - imdd.cpp - -Abstract: - - Interval based Multiple-valued Decision Diagrams. - -Author: - - Leonardo de Moura (leonardo) 2010-10-13. - -Revision History: - ---*/ - -#include"imdd.h" -#include"map.h" - -#define DEFAULT_SIMPLE_MAX_ENTRIES 32 - -inline bool is_cached(imdd2imdd_cache & cache, imdd * d, imdd * & r) { - if (d->is_shared()) { - if (cache.find(d, r) && (r == 0 || !r->is_dead())) - return true; - } - return false; -} - -inline void cache_result(imdd2imdd_cache & cache, imdd * d, imdd * r) { - if (d->is_shared()) - cache.insert(d, r); -} - -inline bool is_cached(imdd_pair2imdd_cache & cache, imdd * d1, imdd * d2, imdd * & r) { - if (d1->is_shared() && d2->is_shared()) { - if (cache.find(d1, d2, r) && (r == 0 || !r->is_dead())) - return true; - } - return false; -} - -inline void cache_result(imdd_pair2imdd_cache & cache, imdd * d1, imdd * d2, imdd * r) { - if (d1->is_shared() && d2->is_shared()) - cache.insert(d1, d2, r); -} - -inline bool destructive_update_at(bool destructive, imdd * d) { - return destructive && !d->is_memoized() && !d->is_shared(); -} - -void sl_imdd_manager::inc_ref_eh(imdd * v) { - m_manager->inc_ref(v); -} - -void sl_imdd_manager::dec_ref_eh(imdd * v) { - m_manager->dec_ref(v); -} - -unsigned imdd::hc_hash() const { - unsigned r = 0; - r = get_arity(); - imdd_children::iterator it = begin_children(); - imdd_children::iterator end = end_children(); - for (; it != end; ++it) { - unsigned b = it->begin_key(); - unsigned e = it->end_key(); - imdd const * child = it->val(); - mix(b, e, r); - if (child) { - SASSERT(child->is_memoized()); - unsigned v = child->get_id(); - mix(v, v, r); - } - } - return r; -} - -bool imdd::hc_equal(imdd const * other) const { - if (m_arity != other->m_arity) - return false; - return m_children.is_equal(other->m_children); -} - -imdd_manager::delay_dealloc::~delay_dealloc() { - SASSERT(m_manager.m_to_delete.size() >= m_to_delete_size); - ptr_vector::iterator it = m_manager.m_to_delete.begin() + m_to_delete_size; - ptr_vector::iterator end = m_manager.m_to_delete.end(); - for (; it != end; ++it) { - SASSERT((*it)->is_dead()); - m_manager.deallocate_imdd(*it); - } - m_manager.m_to_delete.shrink(m_to_delete_size); - m_manager.m_delay_dealloc = m_delay_dealloc_value; -} - -imdd_manager::imdd_manager(): - m_sl_manager(m_alloc), - m_simple_max_entries(DEFAULT_SIMPLE_MAX_ENTRIES), - m_delay_dealloc(false) { - m_sl_manager.m_manager = this; - m_swap_new_child = 0; -} - -imdd * imdd_manager::_mk_empty(unsigned arity) { - SASSERT(arity > 0); - void * mem = m_alloc.allocate(sizeof(imdd)); - return new (mem) imdd(m_sl_manager, m_id_gen.mk(), arity); -} - -imdd * imdd_manager::defrag_core(imdd * d) { - if (d->is_memoized()) - return d; - unsigned h = d->get_arity(); - if (h == 1 && !is_simple_node(d)) - return d; - imdd * new_d = 0; - if (is_cached(m_defrag_cache, d, new_d)) - return new_d; - - if (h == 1) { - SASSERT(is_simple_node(d)); - imdd * new_can_d = memoize(d); - cache_result(m_defrag_cache, d, new_can_d); - return new_can_d; - } - - SASSERT(h > 1); - new_d = _mk_empty(h); - imdd_children::push_back_proc push_back(m_sl_manager, new_d->m_children); - bool has_new = false; - bool children_memoized = true; - - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * child = it->val(); - imdd * new_child = defrag_core(child); - if (child != new_child) - has_new = true; - if (!new_child->is_memoized()) - children_memoized = false; - push_back(it->begin_key(), it->end_key(), new_child); - } - - if (has_new) { - if (children_memoized && is_simple_node(new_d)) { - imdd * new_can_d = memoize(new_d); - if (new_can_d != new_d) { - SASSERT(new_d->get_ref_count() == 0); - delete_imdd(new_d); - } - new_d = new_can_d; - } - } - else { - SASSERT(!has_new); - delete_imdd(new_d); - new_d = d; - if (children_memoized && is_simple_node(new_d)) { - new_d = memoize(new_d); - } - } - - cache_result(m_defrag_cache, d, new_d); - return new_d; -} - -/** - \brief Compress the given IMDD by using hash-consing. -*/ -void imdd_manager::defrag(imdd_ref & d) { - delay_dealloc delay(*this); - m_defrag_cache.reset(); - d = defrag_core(d); -} - -/** - \brief Memoize the given IMDD. - Return an IMDD structurally equivalent to d. - This method assumes the children of d are memoized. - If that is not the case, the user should invoke defrag instead. -*/ -imdd * imdd_manager::memoize(imdd * d) { - if (d->is_memoized()) - return d; - unsigned h = d->get_arity(); - m_tables.reserve(h); - DEBUG_CODE({ - if (h > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - SASSERT(it->val()->is_memoized()); - } - } - }); - imdd * r = m_tables[h-1].insert_if_not_there(d); - if (r == d) { - r->mark_as_memoized(); - } - SASSERT(r->is_memoized()); - return r; -} - -/** - \brief Remove the given IMDD from the hash-consing table -*/ -void imdd_manager::unmemoize(imdd * d) { - SASSERT(d->is_memoized()); - m_tables[d->get_arity()-1].erase(d); - d->mark_as_memoized(false); -} - - -/** - \brief Remove the given IMDD (and its children) from the hash-consing tables. -*/ -void imdd_manager::unmemoize_rec(imdd * d) { - SASSERT(m_worklist.empty()); - m_worklist.push_back(d); - while (!m_worklist.empty()) { - d = m_worklist.back(); - if (d->is_memoized()) { - unmemoize(d); - SASSERT(!d->is_memoized()); - if (d->get_arity() > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) - m_worklist.push_back(it->val()); - } - } - } -} - -bool imdd_manager::is_simple_node(imdd * d) const { - return !d->m_children.has_more_than_k_entries(m_simple_max_entries); -} - -void imdd_manager::mark_as_dead(imdd * d) { - // The references to the children were decremented by delete_imdd. - SASSERT(!d->is_dead()); - d->m_children.deallocate_no_decref(m_sl_manager); - d->mark_as_dead(); - if (m_delay_dealloc) - m_to_delete.push_back(d); - else - deallocate_imdd(d); -} - -void imdd_manager::deallocate_imdd(imdd * d) { - SASSERT(d->is_dead()); - memset(d, 0, sizeof(*d)); - m_alloc.deallocate(sizeof(imdd), d); -} - -void imdd_manager::delete_imdd(imdd * d) { - SASSERT(m_worklist.empty()); - m_worklist.push_back(d); - - while (!m_worklist.empty()) { - d = m_worklist.back(); - m_worklist.pop_back(); - - m_id_gen.recycle(d->get_id()); - - SASSERT(d->get_ref_count() == 0); - if (d->is_memoized()) { - unsigned arity = d->get_arity(); - SASSERT(m_tables[arity-1].contains(d)); - m_tables[arity-1].erase(d); - } - - if (d->get_arity() > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * child = it->val(); - SASSERT(child); - child->dec_ref(); - if (child->get_ref_count() == 0) - m_worklist.push_back(child); - } - } - - mark_as_dead(d); - } -} - -/** - \brief Return a (non-memoized) shallow copy of d. -*/ -imdd * imdd_manager::copy_main(imdd * d) { - imdd * d_copy = _mk_empty(d->get_arity()); - d_copy->m_children.copy(m_sl_manager, d->m_children); - SASSERT(!d_copy->is_memoized()); - SASSERT(d_copy->get_ref_count() == 0); - return d_copy; -} - -/** - \brief Insert the values [b, e] into a IMDD of arity 1. - - If destructive == true, d is not memoized and is not shared, then a - destructive update is performed, and d is returned. - - Otherwise, a fresh IMDD is returned. -*/ -imdd * imdd_manager::insert_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == 1); - if (destructive_update_at(destructive, d)) { - add_child(d, b, e, 0); - return d; - } - else { - imdd * new_d = copy_main(d); - add_child(new_d, b, e, 0); - return memoize_new_imdd_if(memoize_res, new_d); - } -} - - -/** - \brief Remove the values [b, e] from an IMDD of arity 1. - - If destructive == true, d is not memoized and is not shared, then a - destructive update is performed, and d is returned. - - Otherwise, a fresh IMDD is returned. -*/ -imdd * imdd_manager::remove_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == 1); - if (destructive_update_at(destructive, d)) { - remove_child(d, b, e); - return d; - } - else { - imdd * new_d = copy_main(d); - remove_child(new_d, b, e); - return memoize_new_imdd_if(memoize_res, new_d); - } -} - -/** - \brief Auxiliary functor used to implement destructive version of mk_product. -*/ -struct imdd_manager::null2imdd_proc { - imdd * m_d2; - null2imdd_proc(imdd * d2):m_d2(d2) {} - imdd * operator()(imdd * d) { SASSERT(d == 0); return m_d2; } -}; - -/** - \brief Auxiliary functor used to implement destructive version of mk_product. -*/ -struct imdd_manager::mk_product_proc { - imdd_manager & m_manager; - imdd * m_d2; - bool m_memoize; - mk_product_proc(imdd_manager & m, imdd * d2, bool memoize): - m_manager(m), - m_d2(d2), - m_memoize(memoize) { - } - imdd * operator()(imdd * d1) { return m_manager.mk_product_core(d1, m_d2, true, m_memoize); } -}; - -imdd * imdd_manager::mk_product_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - if (destructive && !d1->is_shared()) { - if (d1->is_memoized()) - unmemoize(d1); - - if (d1->get_arity() == 1) { - null2imdd_proc f(d2); - d1->m_children.update_values(m_sl_manager, f); - d1->m_arity += d2->m_arity; - } - else { - mk_product_proc f(*this, d2, memoize_res); - d1->m_children.update_values(m_sl_manager, f); - d1->m_arity += d2->m_arity; - } - return d1; - } - else { - imdd * new_d1 = 0; - if (is_cached(m_mk_product_cache, d1, new_d1)) - return new_d1; - unsigned arity1 = d1->get_arity(); - unsigned arity2 = d2->get_arity(); - new_d1 = _mk_empty(arity1 + arity2); - imdd_children::push_back_proc push_back(m_sl_manager, new_d1->m_children); - imdd_children::iterator it = d1->begin_children(); - imdd_children::iterator end = d1->end_children(); - bool children_memoized = true; - for (; it != end; ++it) { - imdd * new_child; - if (arity1 == 1) - new_child = d2; - else - new_child = mk_product_core(it->val(), d2, false, memoize_res); - if (!new_child->is_memoized()) - children_memoized = false; - push_back(it->begin_key(), it->end_key(), new_child); - } - new_d1 = memoize_new_imdd_if(memoize_res && children_memoized, new_d1); - cache_result(m_mk_product_cache, d1, new_d1); - return new_d1; - } -} - -imdd * imdd_manager::mk_product_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - if (d1->empty() || d2->empty()) { - unsigned a1 = d1->get_arity(); - unsigned a2 = d2->get_arity(); - return _mk_empty(a1 + a2); - } - delay_dealloc delay(*this); - if (d1 == d2) - destructive = false; - m_mk_product_cache.reset(); - return mk_product_core(d1, d2, destructive, memoize_res); -} - -void imdd_manager::init_add_facts_new_children(unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res) { - if (m_add_facts_new_children[num] != 0) { - DEBUG_CODE({ - for (unsigned i = 1; i <= num; i++) { - SASSERT(m_add_facts_new_children[i] != 0); - }}); - return; - } - for (unsigned i = 1; i <= num; i++) { - if (m_add_facts_new_children[i] == 0) { - imdd * new_child = _mk_empty(i); - unsigned b = lowers[num - i]; - unsigned e = uppers[num - i]; - bool prev_memoized = true; - if (i == 1) { - add_child(new_child, b, e, 0); - } - else { - SASSERT(m_add_facts_new_children[i-1] != 0); - prev_memoized = m_add_facts_new_children[i-1]->is_memoized(); - add_child(new_child, b, e, m_add_facts_new_children[i-1]); - } - new_child = memoize_new_imdd_if(memoize_res && prev_memoized, new_child); - m_add_facts_new_children[i] = new_child; - } - } - DEBUG_CODE({ - for (unsigned i = 1; i <= num; i++) { - SASSERT(m_add_facts_new_children[i] != 0); - }}); -} - -imdd * imdd_manager::add_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - imdd_ref new_child(*this); - bool new_children_memoized = true; -#define INIT_NEW_CHILD() { \ - if (new_child == 0) { \ - init_add_facts_new_children(num - 1, lowers + 1, uppers + 1, memoize_res); \ - new_child = m_add_facts_new_children[num-1]; \ - if (!new_child->is_memoized()) new_children_memoized = false; \ - }} - - if (destructive_update_at(destructive, d)) { - if (num == 1) - return insert_main(d, *lowers, *uppers, destructive, memoize_res); - SASSERT(num > 1); - sbuffer to_insert; - new_child = m_add_facts_new_children[num-1]; - - unsigned b = *lowers; - unsigned e = *uppers; - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->end_children(); - for (; it != end && b <= e; ++it) { - imdd_children::entry const & curr_entry = *it; - if (e < curr_entry.begin_key()) - break; - if (b < curr_entry.begin_key()) { - INIT_NEW_CHILD(); - SASSERT(b <= curr_entry.begin_key() - 1); - to_insert.push_back(entry(b, curr_entry.begin_key() - 1, new_child)); - b = curr_entry.begin_key(); - } - imdd * curr_child = curr_entry.val(); - SASSERT(b >= curr_entry.begin_key()); - bool cover = b == curr_entry.begin_key() && e >= curr_entry.end_key(); - // If cover == true, then the curr_child is completely covered by the new facts, and it is not needed anymore. - // So, we can perform a destructive update. - imdd * new_curr_child = add_facts_core(curr_child, num - 1, lowers + 1, uppers + 1, cover, memoize_res); - - if (e >= curr_entry.end_key()) { - SASSERT(b <= curr_entry.end_key()); - to_insert.push_back(entry(b, curr_entry.end_key(), new_curr_child)); - } - else { - SASSERT(e < curr_entry.end_key()); - SASSERT(b <= e); - to_insert.push_back(entry(b, e, new_curr_child)); - } - b = curr_entry.end_key() + 1; - } - // Re-insert entries in m_add_facts_to_insert into d->m_children, - // and if b <= e also insert [b, e] -> new_child - if (b <= e) { - INIT_NEW_CHILD(); - add_child(d, b, e, new_child); - } - - svector::iterator it2 = to_insert.begin(); - svector::iterator end2 = to_insert.end(); - for (; it2 != end2; ++it2) { - imdd_children::entry const & curr_entry = *it2; - add_child(d, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val()); - } - - return d; - } - else { - imdd * new_d = 0; - if (is_cached(m_add_facts_cache, d, new_d)) - return new_d; - - if (num == 1) { - new_d = insert_main(d, *lowers, *uppers, destructive, memoize_res); - } - else { - new_d = copy_main(d); - new_child = m_add_facts_new_children[num-1]; - TRACE("add_facts_bug", tout << "after copying: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n";); - - unsigned b = *lowers; - unsigned e = *uppers; - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->end_children(); - for (; it != end && b <= e; ++it) { - imdd_children::entry const & curr_entry = *it; - if (e < curr_entry.begin_key()) - break; - if (b < curr_entry.begin_key()) { - INIT_NEW_CHILD(); - SASSERT(b <= curr_entry.begin_key() - 1); - add_child(new_d, b, curr_entry.begin_key() - 1, new_child); - TRACE("add_facts_bug", tout << "after inserting new child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n";); - b = curr_entry.begin_key(); - } - imdd * curr_child = curr_entry.val(); - imdd * new_curr_child = add_facts_core(curr_child, num - 1, lowers + 1, uppers + 1, false, memoize_res); - if (!new_curr_child->is_memoized()) - new_children_memoized = false; - if (e >= curr_entry.end_key()) { - SASSERT(b <= curr_entry.end_key()); - add_child(new_d, b, curr_entry.end_key(), new_curr_child); - TRACE("add_facts_bug", tout << "1) after inserting new curr child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n"; - tout << "new_curr_child: " << mk_ll_pp(new_curr_child, *this) << "\n";); - } - else { - SASSERT(e < curr_entry.end_key()); - SASSERT(b <= e); - add_child(new_d, b, e, new_curr_child); - TRACE("add_facts_bug", tout << "2) after inserting new curr child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n"; - tout << "new_curr_child: " << mk_ll_pp(new_curr_child, *this) << "\n";); - } - b = curr_entry.end_key() + 1; - } - if (b <= e) { - INIT_NEW_CHILD(); - add_child(new_d, b, e, new_child); - } - - new_d = memoize_new_imdd_if(memoize_res && d->is_memoized() && new_children_memoized, new_d); - } - - cache_result(m_add_facts_cache, d, new_d); - return new_d; - } -} - -imdd * imdd_manager::add_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - delay_dealloc delay(*this); - m_add_facts_cache.reset(); - m_add_facts_new_children.reset(); - m_add_facts_new_children.resize(num, 0); - return add_facts_core(d, num, lowers, uppers, destructive, memoize_res); -} - -inline void update_memoized_flag(imdd * d, bool & memoized_flag) { - if (d && !d->is_memoized()) - memoized_flag = false; -} - -void imdd_manager::push_back_entries(unsigned head, imdd_children::iterator & it, imdd_children::iterator & end, - imdd_children::push_back_proc & push_back, bool & children_memoized) { - if (it != end) { - push_back(head, it->end_key(), it->val()); - update_memoized_flag(it->val(), children_memoized); - ++it; - for (; it != end; ++it) { - update_memoized_flag(it->val(), children_memoized); - push_back(it->begin_key(), it->end_key(), it->val()); - } - } -} - -/** - \brief Push the entries starting at head upto the given limit (no included). - That is, we are copying the entries in the interval [head, limit). -*/ -void imdd_manager::push_back_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, - unsigned limit, imdd_children::push_back_proc & push_back, bool & children_memoized) { - SASSERT(it != end); - SASSERT(head <= it->end_key()); - SASSERT(head >= it->begin_key()); - SASSERT(head < limit); - while (head < limit && it != end) { - if (it->end_key() < limit) { - update_memoized_flag(it->val(), children_memoized); - push_back(head, it->end_key(), it->val()); - ++it; - if (it != end) - head = it->begin_key(); - } - else { - SASSERT(it->end_key() >= limit); - update_memoized_flag(it->val(), children_memoized); - push_back(head, limit-1, it->val()); - head = limit; - } - } - SASSERT(head == limit || it == end || head == it->begin_key()); -} - -void imdd_manager::move_head(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned new_head) { - SASSERT(new_head >= head); - SASSERT(head >= it->begin_key()); - SASSERT(head <= it->end_key()); - SASSERT(new_head <= it->end_key()); - if (new_head < it->end_key()) { - head = new_head+1; - SASSERT(head <= it->end_key()); - } - else { - SASSERT(new_head == it->end_key()); - ++it; - if (it != end) - head = it->begin_key(); - } -} - -/** - \brief Copy the entries starting at head upto the given limit (no included). - That is, we are copying the entries in the interval [head, limit). -*/ -void imdd_manager::copy_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, - unsigned limit, sbuffer & result) { - SASSERT(it != end); - SASSERT(head <= it->end_key()); - SASSERT(head >= it->begin_key()); - SASSERT(head < limit); - while (head < limit && it != end) { - if (it->end_key() < limit) { - result.push_back(entry(head, it->end_key(), it->val())); - ++it; - if (it != end) - head = it->begin_key(); - } - else { - SASSERT(it->end_key() >= limit); - result.push_back(entry(head, limit-1, it->val())); - head = limit; - } - } - SASSERT(head == limit || it == end || head == it->begin_key()); -} - -imdd * imdd_manager::mk_union_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - if (d1 == d2) - return d1; - if (destructive_update_at(destructive, d1)) { - if (d1->get_arity() == 1) { - imdd_children::iterator it = d2->begin_children(); - imdd_children::iterator end = d2->end_children(); - for (; it != end; ++it) - add_child(d1, it->begin_key(), it->end_key(), 0); - return d1; - } - else { - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - imdd_children::iterator it1 = d1->m_children.find_geq(it2->begin_key()); - imdd_children::iterator end1 = d1->end_children(); - sbuffer to_insert; - unsigned head1 = it1 != end1 ? it1->begin_key() : UINT_MAX; - SASSERT(it2 != end2); - unsigned head2 = it2->begin_key(); - while (true) { - if (it1 == end1) { - // copy it2 to d1 - // Remark: we don't need to copy to to_insert, since we will not be using it1 anymore. - // That is, we can directly insert into d1->m_children. - if (it2 != end2) { - add_child(d1, head2, it2->end_key(), it2->val()); - ++it2; - for (; it2 != end2; ++it2) - add_child(d1, it2->begin_key(), it2->end_key(), it2->val()); - } - break; - } - - if (it2 == end2) { - break; - } - - if (head1 < head2) { - it1.move_to(head2); - head1 = it1 != end1 ? (it1->begin_key() < head2?head2:it1->begin_key()): UINT_MAX; - } - else if (head1 > head2) { - copy_upto(head2, it2, end2, head1, to_insert); - } - else { - SASSERT(head1 == head2); - unsigned tail = std::min(it1->end_key(), it2->end_key()); - imdd * new_child = 0; - SASSERT(d1->get_arity() > 1); - bool cover = head1 == it1->begin_key() && tail == it1->end_key(); - // If cover == true, then the it1->val() (curr_child) is completely covered by - // the new_child, and it is not needed anymore. - // So, we can perform a destructive update. - new_child = mk_union_core(it1->val(), it2->val(), cover, memoize_res); - if (new_child != it1->val()) - to_insert.push_back(entry(head1, tail, new_child)); - move_head(head1, it1, end1, tail); - move_head(head2, it2, end2, tail); - } - } - sbuffer::const_iterator it3 = to_insert.begin(); - sbuffer::const_iterator end3 = to_insert.end(); - for (; it3 != end3; ++it3) - add_child(d1, it3->begin_key(), it3->end_key(), it3->val()); - return d1; - } - } - else { - imdd * r = 0; - if (is_cached(m_union_cache, d1, d2, r)) - return r; - - unsigned arity = d1->get_arity(); - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it1 = d1->begin_children(); - imdd_children::iterator end1 = d1->end_children(); - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - SASSERT(it1 != end1); - SASSERT(it2 != end2); - unsigned head1 = it1->begin_key(); - unsigned head2 = it2->begin_key(); - bool children_memoized = true; - while (true) { - if (it1 == end1) { - // copy it2 to result - push_back_entries(head2, it2, end2, push_back, children_memoized); - break; - } - if (it2 == end2) { - // copy it1 to result - push_back_entries(head1, it1, end1, push_back, children_memoized); - break; - } - - if (head1 < head2) { - push_back_upto(head1, it1, end1, head2, push_back, children_memoized); - } - else if (head1 > head2) { - push_back_upto(head2, it2, end2, head1, push_back, children_memoized); - } - else { - SASSERT(head1 == head2); - unsigned tail = std::min(it1->end_key(), it2->end_key()); - imdd * new_child = 0; - if (arity > 1) { - new_child = mk_union_core(it1->val(), it2->val(), false, memoize_res); - update_memoized_flag(new_child, children_memoized); - } - push_back(head1, tail, new_child); - move_head(head1, it1, end1, tail); - move_head(head2, it2, end2, tail); - } - } - r = memoize_new_imdd_if(memoize_res && children_memoized, r); - cache_result(m_union_cache, d1, d2, r); - return r; - } -} - -void imdd_manager::reset_union_cache() { - m_union_cache.reset(); -} - -imdd * imdd_manager::mk_union_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2) - return d1; - if (d1->empty()) - return d2; - if (d2->empty()) - return d1; - delay_dealloc delay(*this); - reset_union_cache(); - return mk_union_core(d1, d2, destructive, memoize_res); -} - -void imdd_manager::mk_union_core_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2 || d2->empty()) - return; - if (d1->empty()) { - d1 = d2; - return; - } - d1 = mk_union_core(d1, d2, true, memoize_res); -} - -void imdd_manager::mk_union_core(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2 || d2->empty()) { - r = d1; - return; - } - if (d1->empty()) { - r = d2; - return; - } - TRACE("mk_union_core", - tout << "d1:\n"; - display_ll(tout, d1); - tout << "d2:\n"; - display_ll(tout, d2);); - r = mk_union_core(d1, d2, false, memoize_res); -} - -imdd * imdd_manager::mk_complement_core(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - SASSERT(!d->empty()); - SASSERT(*mins <= *maxs); - unsigned arity = d->get_arity(); - imdd_ref new_child(*this); - bool new_children_memoized = true; -#undef INIT_NEW_CHILD -#define INIT_NEW_CHILD() { \ - if (arity > 1 && new_child == 0) { \ - init_add_facts_new_children(num - 1, mins + 1, maxs + 1, memoize_res); \ - new_child = m_add_facts_new_children[num-1]; \ - SASSERT(new_child != 0); \ - if (!new_child->is_memoized()) new_children_memoized = false; \ - }} - - if (false && destructive_update_at(destructive, d)) { - // TODO - NOT_IMPLEMENTED_YET(); - return 0; - } - else { - destructive = false; - imdd * r = 0; - if (is_cached(m_complement_cache, d, r)) - return r; - - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - unsigned prev_key = *mins; - for (; it != end; ++it) { - SASSERT(it->begin_key() >= *mins); - SASSERT(it->end_key() <= *maxs); - if (prev_key < it->begin_key()) { - INIT_NEW_CHILD(); - push_back(prev_key, it->begin_key() - 1, new_child); - } - if (arity > 1) { - imdd * new_curr = mk_complement_core(it->val(), num - 1, mins + 1, maxs + 1, false, memoize_res); - if (new_curr != 0) { - push_back(it->begin_key(), it->end_key(), new_curr); - if (!new_curr->is_memoized()) - new_children_memoized = false; - } - } - prev_key = it->end_key() + 1; - } - - if (prev_key <= *maxs) { - INIT_NEW_CHILD(); - push_back(prev_key, *maxs, new_child); - } - - if (r->empty()) { - delete_imdd(r); - r = 0; - } - - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_complement_cache, d, r); - return r; - } -} - -imdd * imdd_manager::mk_complement_main(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res) { - unsigned arity = d->get_arity(); - SASSERT(arity == num); - // reuse m_add_facts_new_children for creating the universe-set IMDDs - m_add_facts_new_children.reset(); - m_add_facts_new_children.resize(num+1, 0); - - if (d->empty()) { - // return the universe-set - init_add_facts_new_children(num, mins, maxs, memoize_res); - return m_add_facts_new_children[num]; - } - - delay_dealloc delay(*this); - m_complement_cache.reset(); - imdd * r = mk_complement_core(d, num, mins, maxs, destructive, memoize_res); - if (r == 0) - return _mk_empty(arity); - else - return r; -} - -/** - \brief Replace the IMDD children with new_children. - The function will also decrement the ref-counter of every IMDD in new_children, since - it assumes the counter was incremented when the IMDD was inserted into the buffer. -*/ -void imdd::replace_children(sl_imdd_manager & m, sbuffer & new_children) { - m_children.reset(m); - imdd_children::push_back_proc push_back(m, m_children); - svector::iterator it = new_children.begin(); - svector::iterator end = new_children.end(); - for (; it != end; ++it) { - SASSERT(it->val() == 0 || it->val()->get_ref_count() > 0); - push_back(it->begin_key(), it->end_key(), it->val()); - SASSERT(it->val() == 0 || it->val()->get_ref_count() > 1); - if (it->val() != 0) - it->val()->dec_ref(); - } -} - -imdd * imdd_manager::mk_filter_equal_core(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res) { - SASSERT(!d->empty()); - SASSERT(vidx >= 0 && vidx < d->get_arity()); - unsigned arity = d->get_arity(); - if (destructive_update_at(destructive, d)) { - if (vidx == 0) { - imdd * child = 0; - if (d->m_children.find(value, child)) { - imdd_ref ref(*this); - ref = child; // protect child, we don't want the following reset to delete it. - d->m_children.reset(m_sl_manager); - add_child(d, value, child); - return d; - } - else { - return 0; - } - } - SASSERT(arity > 1); - sbuffer to_insert; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = mk_filter_equal_core(curr_child, vidx-1, value, true, memoize_res); - if (new_child != 0) { - new_child->inc_ref(); // protect new child, we will be resetting d->m_children later. - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - } - if (to_insert.empty()) { - return 0; - } - d->replace_children(m_sl_manager, to_insert); - return d; - } - - imdd * r = 0; - if (is_cached(m_filter_equal_cache, d, r)) - return r; - bool new_children_memoized = true; - - if (vidx == 0) { - // found filter variable - imdd * child = 0; - if (d->m_children.find(value, child)) { - r = _mk_empty(arity); - add_child(r, value, child); - if (child && !child->is_memoized()) - new_children_memoized = false; - } - else { - r = 0; - } - } - else { - SASSERT(arity > 1); - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = mk_filter_equal_core(curr_child, vidx-1, value, false, memoize_res); - if (new_child != 0) { - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - } - - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_filter_equal_cache, d, r); - return r; -} - -imdd * imdd_manager::mk_filter_equal_main(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res) { - unsigned arity = d->get_arity(); - SASSERT(vidx >= 0 && vidx < arity); - if (d->empty()) - return d; - delay_dealloc delay(*this); - m_filter_equal_cache.reset(); - imdd * r = mk_filter_equal_core(d, vidx, value, destructive, memoize_res); - if (r == 0) - return _mk_empty(arity); - else - return r; -} - - -/** - \brief create map from imdd nodes to the interval of variables that are covered by variable. - -*/ - -static void mk_interval_set_intersect(sl_manager_base &m, sl_interval_set const& src, unsigned b, unsigned e, sl_interval_set& dst) { - sl_interval_set::iterator it = src.find_geq(b); - sl_interval_set::iterator end = src.end(); - for (; it != end; ++it) { - unsigned b1 = it->begin_key(); - unsigned e1 = it->end_key(); - if (e < b1) { - break; - } - if (b1 < b) { - b1 = b; - } - if (e < e1) { - e1 = e; - } - SASSERT(b <= b1 && b1 <= e1 && e1 <= e); - dst.insert(m, b1, e1); - } -} - -static void mk_interval_set_union(sl_manager_base &m, sl_interval_set& dst, sl_interval_set const& src) { - sl_interval_set::iterator it = src.begin(), end = src.end(); - for (; it != end; ++it) { - dst.insert(m, it->begin_key(), it->end_key()); - } -} - -void imdd_manager::reset_fi_intervals(sl_imanager& m) { - for (unsigned i = 0; i < m_alloc_is.size(); ++i) { - m_alloc_is[i]->deallocate(m); - dealloc(m_alloc_is[i]); - } - m_alloc_is.reset(); - m_imdd2interval_set.reset(); -} - -sl_interval_set const* imdd_manager::init_fi_intervals(sl_imanager& m, imdd* d, unsigned var, unsigned num_found) { - sl_interval_set* result = 0; -#define ALLOC_RESULT() if (!result) { result = alloc(sl_interval_set, m); m_alloc_is.push_back(result); } - if (m_imdd2interval_set.find(d, result)) { - return result; - } - bool _is_fi_var = is_fi_var(var); - unsigned new_num_found = _is_fi_var?num_found+1:num_found; - bool last_fi_var = (new_num_found == m_fi_num_vars); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd_children::entry const & curr_entry = *it; - unsigned b = curr_entry.begin_key(); - unsigned e = curr_entry.end_key(); - - if (last_fi_var) { - ALLOC_RESULT(); - result->insert(m, b, e, 1); - } - else { - SASSERT(d->get_arity() > 1); - imdd* curr_child = curr_entry.val(); - sl_interval_set const* is2 = init_fi_intervals(m, curr_child, var+1, new_num_found); - if (!is2) { - continue; - } - if (_is_fi_var) { - sl_interval_set is3(m); - mk_interval_set_intersect(m, *is2, b, e, is3); - if (!is3.empty()) { - ALLOC_RESULT(); - mk_interval_set_union(m, *result, is3); - } - is3.deallocate(m); - } - else { - if (is2 && result != is2) { - ALLOC_RESULT(); - mk_interval_set_union(m, *result, *is2); - } - } - } - } - m_imdd2interval_set.insert(d, result); - return result; -} - -inline mk_fi_result mk(imdd * d) { - mk_fi_result r; - r.m_d = d; - return r; -} - -inline mk_fi_result mk(fi_cache_entry * e) { - mk_fi_result r; - r.m_entry = e; - return r; -} - -fi_cache_entry * imdd_manager::mk_fi_cache_entry(imdd * d, unsigned lower, unsigned upper, unsigned num_pairs, imdd_value_pair pairs[]) { - void * mem = m_fi_entries.allocate(sizeof(fi_cache_entry) + sizeof(imdd_value_pair) * num_pairs); - DEBUG_CODE({ - for (unsigned i = 0; i < num_pairs; i++) { - SASSERT(pairs[i].first != 0); - } - }); - return new (mem) fi_cache_entry(d, lower, upper, num_pairs, pairs); -} - -struct imdd_value_pair_lt_value { - bool operator()(imdd_value_pair const & p1, imdd_value_pair const & p2) const { - return p1.second < p2.second; - } -}; - -// Review note: identical nodes should be unit intervals? -// 1 -> 1 -> x - -mk_fi_result imdd_manager::mk_filter_identical_core(imdd * d, unsigned var, unsigned num_found, unsigned lower, unsigned upper, - bool destructive, bool memoize_res) { - TRACE("imdd", tout << "#" << d->get_id() << " var: " << var << " num_found: " << num_found << - " lower: " << lower << " upper: " << upper << "\n";); - - unsigned arity = d->get_arity(); -#define MK_EMPTY_ENTRY (num_found == 0 && destructive_update_at(destructive, d))?mk(static_cast(0)):mk(static_cast(0)) - sl_interval_set* node_ranges = m_imdd2interval_set.find(d); - if (!node_ranges) { - return MK_EMPTY_ENTRY; - } - sl_interval_set::iterator sl_it = node_ranges->find_geq(lower); - if (sl_it == node_ranges->end() || upper < sl_it->begin_key()) { - return MK_EMPTY_ENTRY; - } - - bool new_children_memoized = true; - bool _is_fi_var = is_fi_var(var); - if (num_found == 0) { - SASSERT(arity > 1); - if (destructive_update_at(destructive, d)) { - sbuffer to_insert; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - if (_is_fi_var) { - fi_cache_entry * child_entry = (mk_filter_identical_core(curr_child, - var+1, - 1, - it->begin_key(), - it->end_key(), - false, - memoize_res)).m_entry; - for (unsigned i = 0; child_entry && i < child_entry->m_num_result; i++) { - imdd * new_child = child_entry->m_result[i].first; - unsigned value = child_entry->m_result[i].second; - to_insert.push_back(entry(value, value, new_child)); - } - } - else { - imdd * new_child = (mk_filter_identical_core(curr_child, var+1, 0, 0, UINT_MAX, false, memoize_res)).m_d; - if (new_child != 0) { - new_child->inc_ref(); - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - } - } - if (to_insert.empty()) { - return mk(static_cast(0)); - } - d->replace_children(m_sl_manager, to_insert); - return mk(d); - } - else { - // num_found == 0 && variable is not part of the filter && no destructive update. - imdd * r = 0; - if (is_cached(m_fi_top_cache, d, r)) - return mk(r); - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - if (_is_fi_var) { - fi_cache_entry * entry = (mk_filter_identical_core(curr_child, - var+1, - 1, - it->begin_key(), - it->end_key(), - false, - memoize_res)).m_entry; - for (unsigned i = 0; entry && i < entry->m_num_result; i++) { - imdd * new_child = entry->m_result[i].first; - unsigned value = entry->m_result[i].second; - push_back(value, value, new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - } - else { - imdd * new_child = (mk_filter_identical_core(curr_child, var+1, 0, 0, UINT_MAX, false, memoize_res)).m_d; - if (new_child != 0) { - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - } - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_fi_top_cache, d, r); - return mk(r); - } - } - else { - SASSERT(num_found > 0); - fi_cache_entry d_entry(d, lower, upper); - fi_cache_entry * r_entry; - if (d->is_shared() && m_fi_bottom_cache.find(&d_entry, r_entry)) - return mk(r_entry); - sbuffer result; - if (_is_fi_var) { - unsigned new_num_found = num_found + 1; - bool last_fi_var = (new_num_found == m_fi_num_vars); - imdd_children::iterator it = d->m_children.find_geq(lower); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - unsigned curr_lower = it->begin_key(); - unsigned curr_upper = it->end_key(); - if (curr_lower < lower) - curr_lower = lower; - if (curr_upper > upper) - curr_upper = upper; - if (curr_lower <= curr_upper) { - if (last_fi_var) { - for (unsigned i = curr_lower; i <= curr_upper; i++) { - imdd * new_d = _mk_empty(arity); - add_child(new_d, i, curr_child); - new_d = memoize_new_imdd_if(memoize_res && (curr_child == 0 || curr_child->is_memoized()), new_d); - result.push_back(imdd_value_pair(new_d, i)); - } - } - else { - fi_cache_entry * new_entry = (mk_filter_identical_core(curr_child, - var+1, - new_num_found, - curr_lower, - curr_upper, - false, - memoize_res)).m_entry; - for (unsigned i = 0; new_entry && i < new_entry->m_num_result; i++) { - SASSERT(new_entry->m_result[i].first != 0); - imdd * curr_child = new_entry->m_result[i].first; - unsigned value = new_entry->m_result[i].second; - imdd * new_d = _mk_empty(arity); - add_child(new_d, value, curr_child); - SASSERT(curr_child != 0); - new_d = memoize_new_imdd_if(memoize_res && curr_child->is_memoized(), new_d); - result.push_back(imdd_value_pair(new_d, value)); - } - } - } - if (curr_upper == upper) - break; - } - } - else { - SASSERT(arity > 1); - u_map value2imdd; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - SASSERT(curr_child != 0); - fi_cache_entry * new_entry = (mk_filter_identical_core(curr_child, - var+1, - num_found, - lower, - upper, - false, - memoize_res)).m_entry; - for (unsigned i = 0; new_entry && i < new_entry->m_num_result; i++) { - unsigned value = new_entry->m_result[i].second; - imdd * new_child = new_entry->m_result[i].first; - imdd * new_d = 0; - if (!value2imdd.find(value, new_d)) { - new_d = _mk_empty(arity); - value2imdd.insert(value, new_d); - result.push_back(imdd_value_pair(new_d, value)); - } - SASSERT(new_d != 0); - add_child(new_d, it->begin_key(), it->end_key(), new_child); - } - } - std::sort(result.begin(), result.end(), imdd_value_pair_lt_value()); - } - r_entry = mk_fi_cache_entry(d, lower, upper, result.size(), result.c_ptr()); - if (d->is_shared()) - m_fi_bottom_cache.insert(r_entry); - return mk(r_entry); - } -} - -imdd * imdd_manager::mk_filter_identical_main(imdd * d, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) { - if (d->empty() || num_vars < 2) - return d; - TRACE("imdd", - tout << "vars: "; - for (unsigned i = 0; i < num_vars; ++i) { - tout << vars[i] << " "; - } - tout << "\n"; - tout << "rows: " << get_num_rows(d) << "\n"; - display_ll(tout, d); ); - unsigned arity = d->get_arity(); - DEBUG_CODE(for (unsigned i = 0; i < num_vars; ++i) SASSERT(vars[i] < arity);); - m_fi_num_vars = num_vars; - m_fi_begin_vars = vars; - m_fi_end_vars = vars + num_vars; - delay_dealloc delay(*this); - m_fi_bottom_cache.reset(); - m_fi_top_cache.reset(); - m_fi_entries.reset(); - - sl_imanager imgr; - init_fi_intervals(imgr, d, 0, 0); - - TRACE("imdd_verbose", - ptr_addr_hashtable ht; - imdd2intervals::iterator it = m_imdd2interval_set.begin(); - imdd2intervals::iterator end = m_imdd2interval_set.end(); - for (; it != end; ++it) { - tout << it->m_key->get_id() << " "; - if (it->m_value) { - if (ht.contains(it->m_value)) { - tout << "dup\n"; - continue; - } - ht.insert(it->m_value); - sl_interval_set::iterator sit = it->m_value->begin(); - sl_interval_set::iterator send = it->m_value->end(); - for (; sit != send; ++sit) { - tout << "[" << sit->begin_key() << ":" << sit->end_key() << "] "; - } - } - else { - tout << "{}"; - } - tout << "\n"; - }); - mk_fi_result r = mk_filter_identical_core(d, 0, 0, 0, UINT_MAX, destructive, memoize_res); - reset_fi_intervals(imgr); - - TRACE("imdd", if (r.m_d) display_ll(tout << "result\n", r.m_d);); - if (r.m_d == 0) - return _mk_empty(arity); - else - return r.m_d; -} - -void imdd_manager::swap_in(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars) { - // variables are sorted. - DEBUG_CODE(for (unsigned i = 0; i + 1 < num_vars; ++i) SASSERT(vars[i] < vars[i+1]);); - imdd_ref tmp1(*this), tmp2(*this); - tmp1 = d; - SASSERT(num_vars > 1); - unsigned v1 = vars[0]+1; // next position to swap to. - for (unsigned i = 1; i < num_vars; ++i) { - unsigned v2 = vars[i]; - SASSERT(v1 <= v2); - for (unsigned j = v2-1; j >= v1; --j) { - mk_swap(tmp1, tmp2, j); - tmp1 = tmp2; - } - ++v1; - } - TRACE("imdd", - for (unsigned i = 0; i < num_vars; ++i) tout << vars[i] << " "; - tout << "\n"; - display_ll(tout << "in\n", d); - display_ll(tout << "out\n", tmp1); - tout << "\n";); - r = tmp1; -} - -void imdd_manager::swap_out(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars) { - // variables are sorted. - DEBUG_CODE(for (unsigned i = 0; i + 1 < num_vars; ++i) SASSERT(vars[i] < vars[i+1]);); - imdd_ref tmp1(*this), tmp2(*this); - tmp1 = d; - SASSERT(num_vars > 1); - unsigned v1 = vars[0]+num_vars-1; // position of next variable to be swapped. - for (unsigned i = num_vars; i > 1; ) { - --i; - unsigned v2 = vars[i]; - for (unsigned j = v1; j < v2; ++j) { - mk_swap(tmp1, tmp2, j); - tmp1 = tmp2; - } - --v1; - } - TRACE("imdd", - for (unsigned i = 0; i < num_vars; ++i) tout << vars[i] << " "; - tout << "\n"; - display_ll(tout << "out\n", d); - display_ll(tout << "in\n", tmp1); - tout << "\n";); - r = tmp1; -} - -void imdd_manager::filter_identical_core2(imdd* d, unsigned num_vars, unsigned b, unsigned e, ptr_vector& ch) { - SASSERT(num_vars > 0); - imdd* r = 0; - - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->m_children.end(); - - for (; it != end; ++it) { - unsigned b1 = it->begin_key(); - unsigned e1 = it->end_key(); - if (e < b1) { - break; - } - if (b1 < b) { - b1 = b; - } - if (e <= e1) { - e1 = e; - } - SASSERT(b <= b1 && b1 <= e1 && e1 <= e); - imdd* curr_child = it->val(); - if (num_vars == 1) { - for (unsigned i = b1; i <= e1; ++i) { - r = _mk_empty(d->get_arity()); - add_child(r, i, i, it->val()); - r = memoize_new_imdd_if(!it->val() || it->val()->is_memoized(), r); - ch.push_back(r); - } - continue; - } - ptr_vector ch2; - filter_identical_core2(curr_child, num_vars-1, b1, e1, ch2); - for (unsigned i = 0; i < ch2.size(); ++i) { - r = _mk_empty(d->get_arity()); - unsigned key = ch2[i]->begin_children()->begin_key(); - SASSERT(ch2[i]->begin_children()->end_key() == key); - add_child(r, key, key, ch2[i]); - r = memoize_new_imdd_if(ch2[i]->is_memoized(), r); - ch.push_back(r); - } - } -} - -imdd* imdd_manager::filter_identical_core2(imdd* d, unsigned var, unsigned num_vars, bool memoize_res) { - imdd* r = 0; - if (m_filter_identical_cache.find(d, r)) { - return r; - } - - bool children_memoized = true; - - r = _mk_empty(d->get_arity()); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - - if (var > 0) { - for (; it != end; ++it) { - imdd* curr_child = it->val(); - imdd* new_child = filter_identical_core2(curr_child, var-1, num_vars, memoize_res); - if (new_child) { - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) { - children_memoized = false; - } - } - } - } - else { - ptr_vector ch; - - for (; it != end; ++it) { - imdd* curr_child = it->val(); - filter_identical_core2(curr_child, num_vars-1, it->begin_key(), it->end_key(), ch); - } - for (unsigned i = 0; i < ch.size(); ++i) { - unsigned key = ch[i]->begin_children()->begin_key(); - SASSERT(ch[i]->begin_children()->end_key() == key); - push_back(key, key, ch[i]); - if (!ch[i]->is_memoized()) { - children_memoized = false; - } - } - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - m_filter_identical_cache.insert(d, r); - r = memoize_new_imdd_if(r && memoize_res && children_memoized, r); - return r; -} - -void imdd_manager::filter_identical_main2(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) { - m_filter_identical_cache.reset(); - imdd_ref tmp1(*this), tmp2(*this); - swap_in(d, tmp1, num_vars, vars); - tmp2 = filter_identical_core2(tmp1, vars[0], num_vars, memoize_res); - if (!tmp2) { - r = _mk_empty(d->get_arity()); - } - else { - swap_out(tmp2, r, num_vars, vars); - } - m_filter_identical_cache.reset(); -} - -void imdd_manager::filter_identical_main3(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) { - for (unsigned i = 0; i+1 < num_vars; ++i) { - imdd_ref tmp(*this); - tmp = r; - filter_identical_main3(tmp, r, vars[i], false, vars[i+1], false, memoize_res); - } -} - -void imdd_manager::filter_identical_main3(imdd * d, imdd_ref& r, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res) { - r = filter_identical_loop3(d, v1, del1, v2, del2, memoize_res); - if (r == 0) { - r = _mk_empty(d->get_arity()-del1-del2); - } - m_filter_identical_cache.reset(); -} - -imdd* imdd_manager::filter_identical_loop3(imdd * d, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res) { - imdd* r; - if (m_filter_identical_cache.find(d, r)) { - return r; - } - if (v1 == 0) { - return filter_identical_mk_nodes(d, v2, del1, del2, memoize_res); - } - - r = _mk_empty(d->get_arity()-del1-del2); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - bool children_memoized = true; - - for (; it != end; ++it) { - imdd* curr_child = it->val(); - imdd* new_child = filter_identical_loop3(curr_child, v1-1, del1, v2-1, del2, memoize_res); - if (!new_child) { - continue; - } - if (new_child->empty()) { - delete_imdd(new_child); - continue; - } - if (!new_child->is_memoized()) { - children_memoized = false; - } - push_back(it->begin_key(), it->end_key(), new_child); - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - m_filter_identical_cache.insert(d, r); - r = memoize_new_imdd_if(r && memoize_res && children_memoized, r); - return r; -} - -void imdd_manager::merge_intervals(svector& dst, svector const& src) { - svector& tmp = m_i_nodes_tmp; - tmp.reset(); - // invariant: intervals are sorted. - for (unsigned i = 0, j = 0; i < src.size() || j < dst.size();) { - SASSERT(!(i + 1 < src.size()) || src[i].m_hi < src[i+1].m_lo); - SASSERT(!(i + 1 < dst.size()) || dst[i].m_hi < dst[i+1].m_lo); - SASSERT(!(i < src.size()) || src[i].m_lo <= src[i].m_hi); - SASSERT(!(i < dst.size()) || dst[i].m_lo <= dst[i].m_hi); - if (i < src.size() && j < dst.size()) { - if (src[i].m_lo == dst[j].m_lo) { - tmp.push_back(src[i]); - ++i; - ++j; - } - else if (src[i].m_lo < dst[j].m_lo) { - tmp.push_back(src[i]); - ++i; - } - else { - tmp.push_back(dst[j]); - ++j; - } - } - else if (i < src.size()) { - tmp.push_back(src[i]); - ++i; - } - else { - tmp.push_back(dst[j]); - ++j; - } - } - dst.reset(); - dst.append(tmp); -} - -/** - * Propagate intervals down: what intervals can reach which nodes. - */ -imdd* imdd_manager::filter_identical_mk_nodes(imdd* d, unsigned v, bool del1, bool del2, bool memoize_res) { - SASSERT(v > 0); - - TRACE("imdd", display_ll(tout << "v: " << v << "\n", d);); - - // - // (0) - // Create map d |-> [I] from nodes to ordered set of disjoint intervals that visit the node. - // - // For each level up to 'v' create a list of nodes visited - // insert to a map the set of intervals that visit the node. - // - m_nodes.reset(); - filter_id_map& nodes = m_nodes; - imdd* d1, *d2, *d3; - vector > levels; - levels.push_back(ptr_vector()); - - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd* curr_child; - for (; it != end; ++it) { - curr_child = it->val(); - svector& iv = nodes.init(curr_child); - if (iv.empty()) { - levels.back().push_back(curr_child); - } - iv.push_back(interval(it->begin_key(), it->end_key())); - } - - for (unsigned j = 0; j+1 < v; ++j) { - levels.push_back(ptr_vector()); - for (unsigned i = 0; i < levels[j].size(); ++i) { - d1 = levels[j][i]; - svector& i_nodes = nodes.init(d1); - it = d1->begin_children(); - end = d1->end_children(); - for(; it != end; ++it) { - imdd* curr_child = it->val(); - svector& i_nodes2 = nodes.init(curr_child); - if (i_nodes2.empty()) { - levels[j+1].push_back(curr_child); - i_nodes2.append(i_nodes); - } - else { - merge_intervals(i_nodes2, i_nodes); - } - } - } - } - - TRACE("imdd", - for (unsigned i = 0; i < levels.size(); ++i) { - tout << "Level: " << i << "\n"; - for (unsigned j = 0; j < levels[i].size(); ++j) { - tout << levels[i][j]->get_id() << " "; - svector const& i_nodes = nodes.init(levels[i][j]); - for (unsigned k = 0; k < i_nodes.size(); ++k) { - tout << i_nodes[k].m_lo << ":" << i_nodes[k].m_hi << " "; - } - tout << "\n"; - } - } - ); - - - // - // (1) - // Intersect with children: - // - d [l1:h1:ch1][l2:h2:ch2][...] - // => produce_units: d |-> [uI1 |-> d'[uI1:ch1], uI2 |-> d''[uI2:ch1], ...] // unit intervals - // => del2: d |-> [I |-> union of ch1, ch2, .. under intersection] - // => del1 & !del2: d |-> [I |-> d'[I:ch]] // intersections of intervals. - // - - m_nodes_dd.reset(); - filter_idd_map& nodes_dd = m_nodes_dd; - SASSERT(levels.size() == v); - for (unsigned i = 0; i < levels[v-1].size(); ++i) { - d1 = levels[v-1][i]; - svector const & i_nodes = nodes.init(d1); - it = d1->begin_children(); - end = d1->end_children(); - unsigned j = 0; - svector& i_nodes_dd = nodes_dd.init(d1); - while (it != end && j < i_nodes.size()) { - unsigned lo1 = it->begin_key(); - unsigned hi1 = it->end_key(); - unsigned lo2 = i_nodes[j].m_lo; - unsigned hi2 = i_nodes[j].m_hi; - if (hi2 < lo1) { - ++j; - } - else if (hi1 < lo2) { - ++it; - } - // lo1 <= hi2 && lo2 <= hi1 - else { - curr_child = it->val(); - unsigned lo = std::max(lo1, lo2); - unsigned hi = std::min(hi1, hi2); - SASSERT(lo <= hi); - - if (!del1 && !del2) { - for (unsigned k = lo; k <= hi; ++k) { - imdd* d2 = _mk_empty(d1->get_arity()); - add_child(d2, k, k, curr_child); - i_nodes_dd.push_back(interval_dd(k, k, d2)); - } - } - else if (del2) { - i_nodes_dd.push_back(interval_dd(lo, hi, curr_child)); // retrofill after loop. - } - else { - imdd* d2 = _mk_empty(d1->get_arity()); - add_child(d2, lo, hi, curr_child); - i_nodes_dd.push_back(interval_dd(lo, hi, d2)); - } - if (hi2 <= hi) { - ++j; - } - if (hi1 <= hi) { - ++it; - } - } - } - // take union of accumulated children. - // retrofill union inside list. - if (del2) { - d2 = 0; - for (unsigned k = 0; k < i_nodes_dd.size(); ++k) { - d3 = i_nodes_dd[k].m_dd; - if (!d2) { - d2 = d3; - } - else { - d2 = mk_union_core(d2, d3, true, memoize_res); - } - } - for (unsigned k = 0; k < i_nodes_dd.size(); ++k) { - i_nodes_dd[k].m_dd = d2; - } - } - } - - TRACE("imdd", print_filter_idd(tout, nodes_dd);); - - // - // (2) - // Move up: - // d1 |-> [I1] // intervals visiting d1 - // d1 |-> [lo:hi:child] // children of d1 - // child |-> [I2 |-> child'] // current decomposition - // result: - // d3 = d1' |-> [lo:hi:child'] - // d1 |-> [I3 |-> d3] for I3 in merge of [I1] and [I2] - // - // The merge is defined as the intersection of intervals that reside in I1 and - // the fractions in I2. They are decomposed so that all intervals are covered. - // By construction I2 are contained in I1, - // but they may be overlapping among different I2. - // - - for (unsigned i = v-1; i > 0; ) { - --i; - for (unsigned j = 0; j < levels[i].size(); ++j) { - d1 = levels[i][j]; - m_i_nodes_dd.reset(); - svector i_nodes = nodes.init(d1); - svector& i_nodes_dd = nodes_dd.init(d1); - it = d1->begin_children(); - end = d1->end_children(); - unsigned num_children = 0; - for( ; it != end; ++it, ++num_children); - - m_offsets.reset(); - unsigned_vector& offsets = m_offsets; - offsets.resize(num_children); - it = d1->begin_children(); - for( ; it != end; ++it) { - curr_child = it->val(); - refine_intervals(i_nodes, nodes_dd.init(curr_child)); - } - - for (unsigned k = 0; k < i_nodes.size(); ++k) { - interval const& intv = i_nodes[k]; - d3 = _mk_empty(d1->get_arity()-del2); - it = d1->begin_children(); - for(unsigned child_id = 0; it != end; ++it, ++child_id) { - curr_child = it->val(); - svector const& ch_nodes_dd = nodes_dd.init(curr_child); - unsigned offset = offsets[child_id]; - TRACE("imdd_verbose", tout << intv.m_lo << ":" << intv.m_hi << "\n"; - for (unsigned l = offset; l < ch_nodes_dd.size(); ++l) { - tout << ch_nodes_dd[l].m_lo << ":" << ch_nodes_dd[l].m_hi << " " << ch_nodes_dd[l].m_dd->get_id() << " "; - } - tout << "\n"; - ); - - unsigned hi, lo; - d2 = 0; - while (offset < ch_nodes_dd.size() && !d2) { - lo = ch_nodes_dd[offset].m_lo; - hi = ch_nodes_dd[offset].m_hi; - if (intv.m_hi < lo) { - break; - } - if (hi < intv.m_lo) { - ++offset; - continue; - } - SASSERT(lo <= intv.m_lo); - SASSERT(intv.m_hi <= hi); - d2 = ch_nodes_dd[offset].m_dd; - if (intv.m_hi == hi) { - ++offset; - } - } - offsets[child_id] = offset; - if (d2) { - add_child(d3, it->begin_key(), it->end_key(), d2); - } - } - if (d3->empty()) { - delete_imdd(d3); - } - else { - i_nodes_dd.push_back(interval_dd(intv.m_lo, intv.m_hi, d3)); - } - } - TRACE("imdd", tout << d1->get_id() << ": "; print_interval_dd(tout, i_nodes_dd);); - - } - } - - TRACE("imdd", print_filter_idd(tout, nodes_dd);); - - // - // (3) - // Finalize: - // d |-> [I1:child] // children of d - // child |-> [I2 |-> child'] // current decomposition - // result: - // d' = union of child' // if del1 - // d' |-> [I2:child'] // if !del1 - // - - - it = d->begin_children(); - end = d->end_children(); - d1 = _mk_empty(d->get_arity()-del1-del2); - m_i_nodes_dd.reset(); - m_i_nodes_tmp.reset(); - svector& i_nodes_dd = m_i_nodes_dd; - svector& i_nodes_tmp = m_i_nodes_dd_tmp; - for (; it != end; ++it) { - curr_child = it->val(); - i_nodes_tmp.reset(); - svector const& i_nodes_dd1 = nodes_dd.init(curr_child); - for (unsigned i = 0, j = 0; i < i_nodes_dd.size() || j < i_nodes_dd1.size(); ) { - if (i < i_nodes_dd.size() && j < i_nodes_dd1.size()) { - interval_dd const& iv1 = i_nodes_dd[i]; - interval_dd const& iv2 = i_nodes_dd1[j]; - if (iv1.m_lo == iv2.m_lo) { - SASSERT(iv1.m_hi == iv2.m_hi); - SASSERT(iv1.m_dd == iv2.m_dd); - i_nodes_tmp.push_back(iv1); - ++i; - ++j; - } - else if (iv1.m_lo < iv2.m_lo) { - SASSERT(iv1.m_hi < iv2.m_lo); - i_nodes_tmp.push_back(iv1); - ++i; - } - else { - SASSERT(iv2.m_hi < iv1.m_lo); - i_nodes_tmp.push_back(iv2); - ++j; - } - } - else if (i < i_nodes_dd.size()) { - i_nodes_tmp.push_back(i_nodes_dd[i]); - ++i; - } - else if (j < i_nodes_dd1.size()) { - i_nodes_tmp.push_back(i_nodes_dd1[j]); - ++j; - } - } - i_nodes_dd.reset(); - i_nodes_dd.append(i_nodes_tmp); - } - - for (unsigned i = 0; i < i_nodes_dd.size(); ++i) { - imdd* ch = i_nodes_dd[i].m_dd; - unsigned lo = i_nodes_dd[i].m_lo; - unsigned hi = i_nodes_dd[i].m_hi; - if (del1) { - d1 = mk_union_core(d1, ch, true, memoize_res); - } - else { - add_child(d1, lo, hi, ch); - } - } - - TRACE("imdd", display_ll(tout, d1);); - - return d1; -} - - -void imdd_manager::print_interval_dd(std::ostream& out, svector const& m) { - for (unsigned i = 0; i < m.size(); ++i) { - out << m[i].m_lo << ":" << m[i].m_hi << " " << m[i].m_dd->get_id() << " "; - } - out << "\n"; -} - -void imdd_manager::print_filter_idd(std::ostream& out, filter_idd_map const& m) { - filter_idd_map::iterator it = m.begin(), end = m.end(); - for (unsigned i = 0; it != end; ++it, ++i) { - out << i << ": "; - print_interval_dd(out, *it); - } -} - -/** - \brief partition intervals of i_nodes by sub-intervals that are used in i_nodes_dd. - Assumption: - - All values covered in i_nodes_dd are present in i_nodes. -*/ - -void imdd_manager::refine_intervals(svector& i_nodes, svector const& i_nodes_dd) { - m_i_nodes.reset(); - svector& result = m_i_nodes; - unsigned sz1 = i_nodes.size(); - unsigned sz2 = i_nodes_dd.size(); - for (unsigned i = 0, j = 0; i < sz1; ++i) { - interval& iv = i_nodes[i]; - result.push_back(iv); - unsigned lo = iv.m_lo; - unsigned hi = iv.m_hi; - for (; j < sz2; ++j) { - unsigned lo1 = i_nodes_dd[j].m_lo; - unsigned hi1 = i_nodes_dd[j].m_hi; - SASSERT(lo <= hi); - SASSERT(lo1 <= hi1); - if (hi < lo1) { - break; - } - if (lo < lo1) { - result.back().m_hi = lo1-1; - result.push_back(interval(lo1, hi)); - lo = lo1; - } - SASSERT(lo1 <= lo); - if (lo > hi1) { - continue; - } - if (hi1 < hi) { - result.back().m_hi = hi1; - result.push_back(interval(hi1+1, hi)); - lo = hi1+1; - } - } - } - i_nodes.reset(); - i_nodes.append(result); -} - - - -void imdd_manager::mk_project_core(imdd * d, imdd_ref & r, unsigned var, unsigned num_found, bool memoize_res) { - unsigned arity = d->get_arity(); - bool _is_proj_var = is_proj_var(var); - - if (_is_proj_var && arity == (m_proj_num_vars - num_found)) { - r = 0; // 0 here means the unit element (aka the 0-tuple). - return; - } - - imdd * _r = 0; - if (is_cached(m_proj_cache, d, _r)) { - r = _r; - return; - } - - bool new_children_memoized = true; - if (_is_proj_var) { - SASSERT(d != 0); - SASSERT(d->get_arity() > 1); - unsigned new_var = var + 1; - unsigned new_num_found = num_found + 1; - bool found_all = new_num_found == m_proj_num_vars; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_ref tmp_r(*this); - CTRACE("imdd", it == end, display_ll(tout, d);); - SASSERT(it != end); - for (; it != end; ++it) { - imdd_ref new_child(*this); - if (found_all) - new_child = it->val(); - else - mk_project_core(it->val(), new_child, new_var, new_num_found, memoize_res); - - if (tmp_r == 0) - tmp_r = new_child; - else - mk_union_core(tmp_r, new_child, tmp_r, memoize_res); - SASSERT(tmp_r != 0); - } - SASSERT(tmp_r != 0); - SASSERT(tmp_r->get_arity() == d->get_arity() - (m_proj_num_vars - num_found)); - r = tmp_r; - } - else { - SASSERT(num_found < m_proj_num_vars); - unsigned new_var = var+1; - _r = _mk_empty(arity - (m_proj_num_vars - num_found)); - imdd_children::push_back_proc push_back(m_sl_manager, _r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd_ref new_child(*this); - mk_project_core(curr_child, new_child, new_var, num_found, memoize_res); - push_back(it->begin_key(), it->end_key(), new_child); - if (new_child != 0 && !new_child->is_memoized()) - new_children_memoized = false; - } - SASSERT(!_r->empty()); - _r = memoize_new_imdd_if(memoize_res && new_children_memoized, _r); - r = _r; - } - cache_result(m_proj_cache, d, r); -} - -void imdd_manager::mk_project_dupdt_core(imdd_ref & d, unsigned var, unsigned num_found, bool memoize_res) { - unsigned arity = d->get_arity(); - bool _is_proj_var = is_proj_var(var); - - if (_is_proj_var && arity == (m_proj_num_vars - num_found)) { - d = 0; // 0 here means the unit element (aka the 0-tuple). - return; - } - - if (!destructive_update_at(true, d)) { - mk_project_core(d, d, var, num_found, memoize_res); - return; - } - - if (_is_proj_var) { - SASSERT(d != 0); - SASSERT(d->get_arity() > 1); - unsigned new_var = var + 1; - unsigned new_num_found = num_found + 1; - bool found_all = new_num_found == m_proj_num_vars; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_ref r(*this); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); - if (!found_all) { - mk_project_dupdt_core(new_child, new_var, new_num_found, memoize_res); - } - if (r == 0) - r = new_child; - else - mk_union_core_dupdt(r, new_child, memoize_res); - } - // we traverse the children of d, and decrement the reference counter of each one of them. - d->m_children.reset_no_decref(m_sl_manager); - d = r; - SASSERT(r != 0); - SASSERT(r->get_arity() == d->get_arity() - (m_proj_num_vars - num_found)); - } - else { - SASSERT(!_is_proj_var); - sbuffer to_insert; - unsigned new_var = var+1; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); - mk_project_dupdt_core(new_child, new_var, num_found, memoize_res); - if (new_child) - new_child->inc_ref(); // protect new child, since we will insert it into to_insert - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - SASSERT(!to_insert.empty()); - d->replace_children(m_sl_manager, to_insert); - } -} - -void imdd_manager::mk_project_init(unsigned num_vars, unsigned * vars) { - m_proj_num_vars = num_vars; - m_proj_begin_vars = vars; - m_proj_end_vars = vars + num_vars; - m_proj_cache.reset(); - reset_union_cache(); -} - -void imdd_manager::mk_project_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res) { - if (num_vars == 0) { - return; - } - unsigned arity = d->get_arity(); - SASSERT(num_vars < arity); - if (d->empty()) { - d = _mk_empty(arity - num_vars); - return; - } - delay_dealloc delay(*this); - mk_project_init(num_vars, vars); - mk_project_dupdt_core(d, 0, 0, memoize_res); -} - -void imdd_manager::mk_project(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res) { - if (num_vars == 0) { - r = d; - return; - } - unsigned arity = d->get_arity(); - SASSERT(num_vars < arity); - if (d->empty()) { - r = _mk_empty(arity - num_vars); - return; - } - delay_dealloc delay(*this); - mk_project_init(num_vars, vars); - mk_project_core(d, r, 0, 0, memoize_res); - - STRACE("imdd_trace", tout << "mk_project(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num_vars; i++) tout << vars[i] << ", "; - tout << memoize_res << ");\n";); -} - -void imdd_manager::mk_join( - imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - bool memoize_res) { - SASSERT(vars1.size() == vars2.size()); - imdd_ref tmp(*this); - unsigned d1_arity = d1->get_arity(); - mk_product(d1, d2, tmp); - for (unsigned i = 0; i < vars1.size(); ++i) { - unsigned vars[2] = { vars1[i], vars2[i] + d1_arity }; - mk_filter_identical_dupdt(tmp, 2, vars); - } - r = tmp; // memoize_new_imdd_if(memoize_res, tmp); -} - -#if 0 -void imdd_manager::mk_join_project( - imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - unsigned_vector const& proj_vars, bool memoize_res) { - SASSERT(vars1.size() == vars2.size()); - imdd_ref tmp(*this); - unsigned d1_arity = d1->get_arity(); - mk_product(d1, d2, tmp); - for (unsigned i = 0; i < vars1.size(); ++i) { - unsigned vars[2] = { vars1[i], vars2[i] + d1_arity }; - mk_filter_identical(tmp, tmp, 2, vars); - } - mk_project(tmp, tmp, proj_vars.size(), proj_vars.c_ptr()); - TRACE("dl", - tout << "vars: "; - for (unsigned i = 0; i < vars1.size(); ++i) tout << vars1[i] << ":" << vars2[i] << " "; - tout << "\n"; - tout << "proj: "; - for (unsigned i = 0; i < proj_vars.size(); ++i) tout << proj_vars[i] << " "; - tout << "\n"; - tout << "arity: " << d1->get_arity() << " + " << d2->get_arity() << "\n"; - display_ll(tout << "d1\n", d1); - display_ll(tout << "d2\n", d2); - display_ll(tout << "result\n", tmp); - tout << "\n"; - ); - - r = tmp; // memoize_new_imdd_if(memoize_res, tmp); -} -#endif - - -void imdd_manager::mk_join_project( - imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - unsigned_vector const& proj_vars, bool memoize_res) { - SASSERT(vars1.size() == vars2.size()); - imdd_ref tmp(*this); - mk_product(d1, d2, tmp); - unsigned d1_arity = d1->get_arity(); - unsigned sz = tmp->get_arity(); - - // set up schedule for join-project. - unsigned_vector remap; - svector projected; - unsigned_vector ref_count; - for (unsigned i = 0; i < sz; ++i) { - remap.push_back(i); - } - projected.resize(sz, false); - ref_count.resize(sz, 0); - for (unsigned i = 0; i < proj_vars.size(); ++i) { - projected[proj_vars[i]] = true; - } - for (unsigned i = 0; i < vars1.size(); ++i) { - ref_count[vars1[i]]++; - ref_count[vars2[i]+d1_arity]++; - } - -#define UPDATE_REMAP() \ - for (unsigned i = 0, j = 0; i < sz; ++i) { \ - remap[i] = j; \ - if (!projected[i] || ref_count[i] > 0) { \ - ++j; \ - } \ - } \ - - - UPDATE_REMAP(); - // project unused variables: - unsigned_vector proj2; - for (unsigned i = 0; i < sz; ++i) { - if (projected[i] && ref_count[i] == 0) { - proj2.push_back(i); - } - } - mk_project(tmp, tmp, proj2.size(), proj2.c_ptr()); - - for (unsigned i = 0; i < vars1.size(); ++i) { - unsigned idx1 = vars1[i]; - unsigned idx2 = vars2[i]+d1_arity; - ref_count[idx1]--; - ref_count[idx2]--; - bool del1 = ref_count[idx1] == 0 && projected[idx1]; - bool del2 = ref_count[idx2] == 0 && projected[idx2]; - - filter_identical_main3(tmp, tmp, remap[idx1], del1, remap[idx2], del2, memoize_res); - if (del1 || del2) { - UPDATE_REMAP(); - } - } - TRACE("imdd", - tout << "vars: "; - for (unsigned i = 0; i < vars1.size(); ++i) tout << vars1[i] << ":" << vars2[i] << " "; - tout << "\n"; - tout << "proj: "; - for (unsigned i = 0; i < proj_vars.size(); ++i) tout << proj_vars[i] << " "; - tout << "\n"; - tout << "arity: " << d1->get_arity() << " + " << d2->get_arity() << "\n"; - display_ll(tout << "d1\n", d1); - display_ll(tout << "d2\n", d2); - display_ll(tout << "result\n", tmp); - tout << "\n"; - ); - - r = tmp; // memoize_new_imdd_if(memoize_res, tmp); -} - - - -imdd * imdd_manager::mk_swap_new_child(unsigned lower, unsigned upper, imdd * grandchild) { - if (m_swap_new_child == 0) { - m_swap_new_child = _mk_empty(grandchild == 0 ? 1 : grandchild->get_arity() + 1); - add_child(m_swap_new_child, lower, upper, grandchild); - if (grandchild && !grandchild->is_memoized()) - m_swap_granchildren_memoized = false; - inc_ref(m_swap_new_child); - } - SASSERT(m_swap_new_child != 0); - return m_swap_new_child; -} - -void imdd_manager::mk_swap_acc1_dupdt(imdd_ref & d, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res) { - SASSERT(d->get_ref_count() == 1); - sbuffer to_insert; - imdd_children::ext_iterator it; - imdd_children::ext_iterator end; - d->m_children.move_geq(it, lower); - while (it != end && lower <= upper) { - imdd_children::entry const & curr_entry = *it; - unsigned curr_entry_begin_key = curr_entry.begin_key(); - unsigned curr_entry_end_key = curr_entry.end_key(); - imdd * curr_entry_val = curr_entry.val(); - bool move_head = true; - if (upper < curr_entry_begin_key) - break; - if (lower < curr_entry_begin_key) { - to_insert.push_back(entry(lower, curr_entry_begin_key - 1, grandchild)); - lower = curr_entry_begin_key; - } - SASSERT(lower >= curr_entry_begin_key); - imdd * curr_grandchild = curr_entry_val; - imdd_ref new_grandchild(*this); - SASSERT((curr_grandchild == 0) == (grandchild == 0)); - if (curr_grandchild != 0) { - bool cover = lower == curr_entry_begin_key && upper >= curr_entry_end_key; - // If cover == true, then the curr_child is completely covered, and it is not needed anymore. - // So, we can perform a destructive update. - if (cover) { - new_grandchild = curr_grandchild; // take over the ownership of curr_grandchild - it.erase(m_sl_manager); - move_head = false; // it.erase is effectively moving the head. - mk_union_core_dupdt(new_grandchild, grandchild, memoize_res); - } - else { - mk_union_core(curr_grandchild, grandchild, new_grandchild, memoize_res); - } - if (!new_grandchild->is_memoized()) - m_swap_granchildren_memoized = false; - // protect new_grandchild, since it will be stored in to_insert. - new_grandchild->inc_ref(); - } - if (upper >= curr_entry_end_key) { - to_insert.push_back(entry(lower, curr_entry_end_key, new_grandchild)); - } - else { - to_insert.push_back(entry(lower, upper, new_grandchild)); - } - lower = curr_entry_end_key + 1; - if (move_head) - ++it; - } - svector::iterator it2 = to_insert.begin(); - svector::iterator end2 = to_insert.end(); - for (; it2 != end2; ++it2) { - imdd_children::entry const & curr_entry = *it2; - add_child(d, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val()); - if (curr_entry.val()) - curr_entry.val()->dec_ref(); // to_insert will be destroyed, so decrement ref. - } - if (lower <= upper) { - add_child(d, lower, upper, grandchild); - } - TRACE("mk_swap_bug", tout << "after mk_swap_acc1_dupt\n" << mk_ll_pp(d, *this) << "\n";); -} - -void imdd_manager::mk_swap_acc1(imdd * d, imdd_ref & r, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res) { - copy(d, r); - imdd_children::iterator it = d->m_children.find_geq(lower); - imdd_children::iterator end = d->end_children(); - for (; it != end && lower <= upper; ++it) { - imdd_children::entry const & curr_entry = *it; - if (upper < curr_entry.begin_key()) - break; - if (lower < curr_entry.begin_key()) { - SASSERT(lower <= curr_entry.begin_key() - 1); - add_child(r, lower, curr_entry.begin_key() - 1, grandchild); - lower = curr_entry.begin_key(); - } - SASSERT(lower >= curr_entry.begin_key()); - imdd * curr_grandchild = curr_entry.val(); - SASSERT((curr_grandchild == 0) == (grandchild == 0)); - imdd_ref new_curr_grandchild(*this); - mk_union_core(curr_grandchild, grandchild, new_curr_grandchild, memoize_res); - if (new_curr_grandchild && !new_curr_grandchild->is_memoized()) - m_swap_granchildren_memoized = false; - if (upper >= curr_entry.end_key()) { - add_child(r, lower, curr_entry.end_key(), new_curr_grandchild); - } - else { - SASSERT(upper < curr_entry.end_key()); - SASSERT(lower <= upper); - add_child(r, lower, upper, new_curr_grandchild); - } - lower = curr_entry.end_key() + 1; - } - if (lower <= upper) { - add_child(r, lower, upper, grandchild); - } - TRACE("mk_swap_bug", tout << "after mk_swap_acc1\n" << mk_ll_pp(r, *this) << "\n";); -} - -/** - \brief Auxiliary function for mk_swap_core -*/ -void imdd_manager::mk_swap_acc2(imdd_ref & r, unsigned lower1, unsigned upper1, unsigned lower2, unsigned upper2, imdd * grandchild, bool memoize_res) { - SASSERT((r->get_arity() == 2 && grandchild == 0) || - (r->get_arity() == grandchild->get_arity() + 2)); - SASSERT(r->get_ref_count() <= 1); // we perform destructive updates on r. - SASSERT(!r->is_memoized()); - TRACE("mk_swap_bug", - tout << mk_ll_pp(r, *this) << "\n"; - tout << "adding\n[" << lower1 << ", " << upper1 << "] -> [" << lower2 << ", " << upper2 << "] ->\n"; - tout << mk_ll_pp(grandchild, *this) << "\n";); - - sbuffer to_insert; - imdd_children::ext_iterator it; - imdd_children::ext_iterator end; - r->m_children.move_geq(it, lower1); - SASSERT(m_swap_new_child == 0); - - while(it != end && lower1 <= upper1) { - imdd_children::entry const & curr_entry = *it; - unsigned curr_entry_begin_key = curr_entry.begin_key(); - unsigned curr_entry_end_key = curr_entry.end_key(); - imdd * curr_entry_val = curr_entry.val(); - bool move_head = true; - TRACE("mk_swap_bug", tout << lower1 << " " << upper1 << " " << curr_entry_begin_key << "\n";); - if (upper1 < curr_entry_begin_key) - break; - if (lower1 < curr_entry_begin_key) { - imdd * new_child = mk_swap_new_child(lower2, upper2, grandchild); - SASSERT(lower1 <= curr_entry_begin_key - 1); - add_child(r, lower1, curr_entry_begin_key - 1, new_child); - lower1 = curr_entry_begin_key; - } - SASSERT(lower1 >= curr_entry_begin_key); - imdd * curr_child = curr_entry_val; - SASSERT(curr_child != 0); - SASSERT(!curr_child->is_memoized()); - imdd_ref new_curr_child(*this); - bool destructive = curr_child->get_ref_count() == 1 && lower1 == curr_entry_begin_key && upper1 >= curr_entry_end_key; - if (destructive) { - new_curr_child = curr_child; // take over curr_child. - it.erase(m_sl_manager); - move_head = false; // it.erase is effectively moving the head. - mk_swap_acc1_dupdt(new_curr_child, lower2, upper2, grandchild, memoize_res); - } - else { - mk_swap_acc1(curr_child, new_curr_child, lower2, upper2, grandchild, memoize_res); - } - new_curr_child->inc_ref(); // it will be stored in to_insert - if (upper1 >= curr_entry_end_key) { - to_insert.push_back(entry(lower1, curr_entry_end_key, new_curr_child)); - } - else { - to_insert.push_back(entry(lower1, upper1, new_curr_child)); - } - lower1 = curr_entry_end_key + 1; - if (move_head) - ++it; - } - svector::iterator it2 = to_insert.begin(); - svector::iterator end2 = to_insert.end(); - for (; it2 != end2; ++it2) { - imdd_children::entry const & curr_entry = *it2; - add_child(r, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val()); - SASSERT(curr_entry.val()); - curr_entry.val()->dec_ref(); // to_insert will be destroyed, so decrement ref. - } - if (lower1 <= upper1) { - imdd * new_child = mk_swap_new_child(lower2, upper2, grandchild); - add_child(r, lower1, upper1, new_child); - } - if (m_swap_new_child != 0) { - dec_ref(m_swap_new_child); - m_swap_new_child = 0; - } - TRACE("mk_swap_bug", tout << "after mk_swap_acc2\n" << mk_ll_pp(r, *this) << "\n";); -} - -/** - \brief Memoize the given IMDD assuming all grandchildren of d are memoized. -*/ -imdd * imdd_manager::mk_swap_memoize(imdd * d) { - if (d->is_memoized()) - return d; - bool children_memoized = true; - imdd * new_d = _mk_empty(d->get_arity()); - imdd_children::push_back_proc push_back(m_sl_manager, new_d->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * child = it->val(); - imdd * new_child = memoize(child); - if (!new_child->is_memoized()) - children_memoized = false; - push_back(it->begin_key(), it->end_key(), new_child); - } - - if (children_memoized && is_simple_node(new_d)) { - imdd * new_can_d = memoize(new_d); - if (new_can_d != new_d) { - SASSERT(new_d->get_ref_count() == 0); - delete_imdd(new_d); - } - new_d = new_can_d; - } - return new_d; -} - -/** - \brief Swap the two top vars. -*/ -void imdd_manager::mk_swap_top_vars(imdd * d, imdd_ref & r, bool memoize_res) { - SASSERT(d->get_arity() >= 2); - r = _mk_empty(d->get_arity()); - m_swap_granchildren_memoized = true; - m_swap_new_child = 0; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - SASSERT(curr_child != 0); - SASSERT(m_swap_new_child == 0); - imdd_children::iterator it2 = curr_child->begin_children(); - imdd_children::iterator end2 = curr_child->end_children(); - for (; it2 != end2; ++it2) { - imdd * grandchild = it2->val(); - mk_swap_acc2(r, it2->begin_key(), it2->end_key(), it->begin_key(), it->end_key(), grandchild, memoize_res); - } - SASSERT(m_swap_new_child == 0); - } - - if (memoize_res && m_swap_granchildren_memoized) { - r = mk_swap_memoize(r); - } - TRACE("mk_swap_bug", tout << "result:\n" << mk_ll_pp(r, *this) << "\n";); -} - -void imdd_manager::mk_swap_core(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - - imdd * _r = 0; - if (is_cached(m_swap_cache, d, _r)) { - r = _r; - return; - } - - if (vidx == 0) { - mk_swap_top_vars(d, r, memoize_res); - } - else { - SASSERT(vidx > 0); - bool new_children_memoized = true; - unsigned new_vidx = vidx - 1; - _r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, _r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd_ref new_child(*this); - mk_swap_core(curr_child, new_child, new_vidx, memoize_res); - push_back(it->begin_key(), it->end_key(), new_child); - if (new_child != 0 && !new_child->is_memoized()) - new_children_memoized = false; - } - SASSERT(!_r->empty()); - _r = memoize_new_imdd_if(memoize_res && new_children_memoized, _r); - r = _r; - } - cache_result(m_swap_cache, d, r); -} - -void imdd_manager::mk_swap_dupdt_core(imdd_ref & d, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - - if (!destructive_update_at(true, d)) { - mk_swap_core(d, d, vidx, memoize_res); - return; - } - - if (vidx == 0) { - mk_swap_top_vars(d, d, memoize_res); - return; - } - - SASSERT(vidx > 0); - sbuffer to_insert; - unsigned new_vidx = vidx-1; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); - mk_swap_dupdt_core(new_child, new_vidx, memoize_res); - new_child->inc_ref(); // protect new child, since we insert into to_insert - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - SASSERT(!to_insert.empty()); - d->replace_children(m_sl_manager, to_insert); -} - -void imdd_manager::mk_swap_dupdt(imdd_ref & d, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - SASSERT(vidx < arity - 1); - if (d->empty()) { - return; - } - m_swap_cache.reset(); - reset_union_cache(); - delay_dealloc delay(*this); - mk_swap_dupdt_core(d, vidx, memoize_res); -} - -void imdd_manager::mk_swap(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - SASSERT(vidx < arity - 1); - if (d->empty()) { - r = d; - return; - } - TRACE("mk_swap_bug", tout << "mk_swap: " << vidx << "\n"; display_ll(tout, d); ); - TRACE("mk_swap_bug2", tout << "mk_swap: " << vidx << "\n"; display_ll(tout, d); ); - m_swap_cache.reset(); - reset_union_cache(); - delay_dealloc delay(*this); - mk_swap_core(d, r, vidx, memoize_res); - TRACE("mk_swap_bug2", tout << "mk_swap result\n"; display_ll(tout, r); ); - STRACE("imdd_trace", tout << "mk_swap(0x" << d << ", 0x" << r.get() << ", " << vidx << ", " << memoize_res << ");\n";); -} - -imdd_manager::iterator::iterator(imdd_manager const & m, imdd const * d) { - if (d->empty()) { - m_done = true; - return; - } - m_done = false; - unsigned arity = d->get_arity(); - m_iterators.resize(arity); - m_element.resize(arity); - begin_iterators(d, 0); -} - -void imdd_manager::iterator::begin_iterators(imdd const * curr, unsigned start_idx) { - for (unsigned i = start_idx; i < get_arity(); i++) { - m_iterators[i] = curr->begin_children(); - imdd_children::iterator & it = m_iterators[i]; - SASSERT(!it.at_end()); - m_element[i] = it->begin_key(); - curr = it->val(); - } -} - -imdd_manager::iterator & imdd_manager::iterator::operator++() { - unsigned i = get_arity(); - while (i > 0) { - --i; - imdd_children::iterator & it = m_iterators[i]; - if (m_element[i] < it->end_key()) { - m_element[i]++; - begin_iterators(it->val(), i+1); - return *this; - } - else { - ++it; - if (!it.at_end()) { - m_element[i] = it->begin_key(); - begin_iterators(it->val(), i+1); - return *this; - } - } - } - m_done = true; // at end... - return *this; -} - -bool imdd_manager::iterator::operator==(iterator const & it) const { - if (m_done && it.m_done) - return true; - if (m_done || it.m_done) - return false; - if (m_element.size() != it.m_element.size()) - return false; - unsigned sz = m_element.size(); - for (unsigned i = 0; i < sz; i++) - if (m_element[i] != it.m_element[i]) - return false; - return true; -} - -bool imdd_manager::is_equal_core(imdd * d1, imdd * d2) { - if (d1 == d2) - return true; - if (d1->is_memoized() && d2->is_memoized()) - return false; - SASSERT(d1->get_arity() == d2->get_arity()); - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - bool shared = d1->is_shared() && d2->is_shared(); - bool r; - if (shared && m_is_equal_cache.find(d1, d2, r)) - return r; - - if (d1->get_arity() == 1) { - r = d1->m_children.is_equal(d2->m_children); - } - else { - imdd_children::iterator it1 = d1->begin_children(); - imdd_children::iterator end1 = d1->end_children(); - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - SASSERT(it1 != end1); - SASSERT(it2 != end2); - if (it1->begin_key() != it2->begin_key()) { - r = false; - } - else { - while (true) { - if (it1 == end1) { - r = (it2 == end2); - break; - } - if (it2 == end2) { - r = (it1 == end1); - break; - } - SASSERT(it1->val() != 0 && it2->val() != 0); - if (!is_equal_core(it1->val(), it2->val())) { - r = false; - break; - } - if (it1->end_key() < it2->end_key()) { - unsigned prev_end_key = it1->end_key(); - ++it1; - if (it1 == end1 || it1->begin_key() != prev_end_key + 1) { - r = false; - break; - } - } - else if (it1->end_key() > it2->end_key()) { - unsigned prev_end_key = it2->end_key(); - ++it2; - if (it2 == end2 || it2->begin_key() != prev_end_key + 1) { - r = false; - break; - } - } - else { - SASSERT(it1->end_key() == it2->end_key()); - ++it1; - ++it2; - } - } - } - } - if (shared) - m_is_equal_cache.insert(d1, d2, r); - return r; -} - -bool imdd_manager::is_equal(imdd * d1, imdd * d2) { - if (d1 == d2) - return true; - if (d1->is_memoized() && d2->is_memoized()) - return false; - if (d1->get_arity() != d2->get_arity()) - return false; - if (d1->empty() && d2->empty()) - return true; - if (d1->empty() || d2->empty()) - return false; - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - m_is_equal_cache.reset(); - return is_equal_core(d1, d2); -} - -/** - \brief Return true if the given imdd contains the tuple (values[0], ..., values[num-1]) -*/ -bool imdd_manager::contains(imdd * d, unsigned num, unsigned const * values) const { - SASSERT(d->get_arity() == num); - imdd * curr = d; - for (unsigned i = 0; i < num; i++) { - imdd * child; - if (!curr->m_children.find(values[i], child)) - return false; - curr = child; - } - return true; -} - -inline bool overlap(unsigned b1, unsigned e1, unsigned b2, unsigned e2) { - // [b1, e1] [b2, e2] - if (e1 < b2) - return false; - // [b2, e2] [b1, e1] - if (e2 < b1) - return false; - return true; -} - -bool imdd_manager::subsumes_core(imdd * d1, imdd * d2) { - if (d1 == d2) - return true; - SASSERT(d1->get_arity() == d2->get_arity()); - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - bool shared = d1->is_shared() && d2->is_shared(); - bool r; - if (shared && m_subsumes_cache.find(d1, d2, r)) - return r; - - imdd_children::iterator it1 = d1->begin_children(); - imdd_children::iterator end1 = d1->end_children(); - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - SASSERT(it1 != end1); - SASSERT(it2 != end2); - if (it1->begin_key() > it2->begin_key()) { - r = false; - goto subsumes_end; - } - - if (d1->get_arity() == 1) { - // leaf case - while(true) { - SASSERT(it1 != end1); - SASSERT(it2 != end2); - it1.move_to(it2->begin_key()); - if (it1 == end1 || - it1->begin_key() > it2->begin_key() || // missed beginning of it2 curr entry - it1->end_key() < it2->end_key() // missed end of it2 curr entry - ) { - r = false; - goto subsumes_end; - } - SASSERT(it1->end_key() >= it2->end_key()); - ++it2; - if (it2 == end2) { - r = true; - goto subsumes_end; - } - } - } - else { - // non-leaf case - while (true) { - SASSERT(it1 != end1); - SASSERT(it2 != end2); - SASSERT(it1->val() != 0); - SASSERT(it2->val() != 0); - it1.move_to(it2->begin_key()); - if (it1 == end1 || - it1->begin_key() > it2->begin_key() // missed beginning of it2 curr entry - ) { - r = false; - goto subsumes_end; - } - // the beginning of it2 is inside it1 - SASSERT(it2->begin_key() >= it1->begin_key() && it2->begin_key() <= it1->end_key()); - // it1: [ ][ ][ ] - // it2 [ ] - while (true) { - SASSERT(it1 != end1); - SASSERT(it2 != end2); - // there is a overlap between the current entry in it1 and the current entry in it2 - SASSERT(overlap(it1->begin_key(), it1->end_key(), - it2->begin_key(), it2->end_key())); - if (!subsumes_core(it1->val(), it2->val())) { - r = false; - goto subsumes_end; - } - if (it1->end_key() >= it2->end_key()) { - ++it2; // processed the whole entry in it2 - break; - } - SASSERT(it1->end_key() < it2->end_key()); - unsigned prev_end_key = it1->end_key(); - ++it1; - if (it1 == end1 || it1->begin_key() != prev_end_key + 1) { - r = false; - goto subsumes_end; - } - } - if (it2 == end2) { - r = true; - goto subsumes_end; - } - } - } - subsumes_end: - if (shared) - m_subsumes_cache.insert(d1, d2, r); - return r; -} - -/** - \brief Return true is d1 is a superset of d2, or equal to d2. -*/ -bool imdd_manager::subsumes(imdd * d1, imdd * d2) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2) - return true; - if (d2->empty()) - return true; - if (d1->empty()) { - SASSERT(!d2->empty()); - return false; - } - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - m_subsumes_cache.reset(); - return subsumes_core(d1, d2); -} - -void imdd_manager::remove_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers) { - SASSERT(d->get_arity() == num); - d = remove_facts_main(d, num, lowers, uppers, true, true); -} - -void imdd_manager::remove_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers) { - SASSERT(d->get_arity() == num); - r = remove_facts_main(d, num, lowers, uppers, false, true); -} - -imdd * imdd_manager::remove_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - delay_dealloc delay(*this); - m_remove_facts_cache.reset(); - return remove_facts_core(d, num, lowers, uppers, destructive, memoize_res); -} - -imdd * imdd_manager::remove_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num && num > 0); - - imdd * new_d = 0; - unsigned b = *lowers; - unsigned e = *uppers; - sbuffer to_insert, to_remove; - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->end_children(); - bool is_destructive = destructive_update_at(destructive, d); - - if (!is_destructive && is_cached(m_remove_facts_cache, d, new_d)) { - return new_d; - } - - if (num == 1) { - new_d = remove_main(d, *lowers, *uppers, destructive, memoize_res); - if (!is_destructive) { - cache_result(m_remove_facts_cache, d, new_d); - } - return new_d; - } - - if (it == end || e < it->begin_key()) { - if (!is_destructive) { - cache_result(m_remove_facts_cache, d, d); - } - return d; - } - - if (!is_destructive) { - new_d = copy_main(d); - } - else { - new_d = d; - } - for (; it != end && b <= e; ++it) { - // - // remove ([b:e]::rest), [b_key:e_key] * ch) = - // { [b_key:e_key] * ch } if e < b_key - // { [b_key:e_key] * ch' } if b <= b_key <= e_key <= e - // { [b_key:e]*ch' [e+1:e_key]*ch } if b <= b_key <= e < e_key - // { [b_key:b-1]*ch [b:e]*ch', [e+1:e_key]*ch } if b_key < b <= e < e_key - // { [b_key:b-1]*ch [b:e_key]*ch' } if b_key < b <= e_key <= e - // where - // ch' = remove(rest, ch) - // assumption: remove_child retains the cases where ch is in the tree. - // - - imdd_children::entry const & curr_entry = *it; - unsigned b_key = curr_entry.begin_key(); - unsigned e_key = curr_entry.end_key(); - imdd* curr_child = curr_entry.val(); - imdd* new_curr_child = 0; - - if (e < b_key) { - break; - } - - new_curr_child = remove_facts_core(curr_child, num-1, lowers+1, uppers+1, destructive, memoize_res); - - if (new_curr_child == curr_child) { - continue; - } - - if (new_curr_child != 0) { - if (b <= b_key && e_key <= e) { - to_insert.push_back(entry(b_key, e_key, new_curr_child)); - } - if (b <= b_key && e < e_key) { - to_insert.push_back(entry(b_key, e, new_curr_child)); - } - if (b_key < b && e < e_key) { - to_insert.push_back(entry(b, e, new_curr_child)); - } - if (b_key < b && e_key <= e) { - to_insert.push_back(entry(b, e_key, new_curr_child)); - } - } - if (is_destructive) { - to_remove.push_back(entry(b, e, 0)); - } - else { - remove_child(new_d, b, e); - } - b = e_key + 1; - } - for (unsigned i = 0; i < to_remove.size(); ++i) { - remove_child(new_d, to_remove[i].begin_key(), to_remove[i].end_key()); - } - for (unsigned i = 0; i < to_insert.size(); ++i) { - add_child(new_d, to_insert[i].begin_key(), to_insert[i].end_key(), to_insert[i].val()); - } - if (!is_destructive) { - cache_result(m_remove_facts_cache, d, new_d); - } - return new_d; -} - -void imdd_manager::display(std::ostream & out, imdd const * d, unsigned_vector & intervals, bool & first) const { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - if (d->get_arity() == 1) { - for (; it != end; ++it) { - SASSERT(it->val() == 0); - if (first) { - first = false; - } - else { - out << ",\n "; - } - out << "("; - unsigned sz = intervals.size(); - for (unsigned i = 0; i < sz; i+=2) { - out << "[" << intervals[i] << ", " << intervals[i+1] << "]"; - out << ", "; - } - out << "[" << it->begin_key() << ", " << it->end_key() << "])"; - } - } - else { - for (; it != end; ++it) { - intervals.push_back(it->begin_key()); - intervals.push_back(it->end_key()); - display(out, it->val(), intervals, first); - intervals.pop_back(); - intervals.pop_back(); - } - } -} - -void imdd_manager::display(std::ostream & out, imdd const * d) const { - unsigned_vector intervals; - bool first = true; - out << "{"; - display(out, d, intervals, first); - out << "}"; -} - -struct display_ll_context { - typedef map, default_eq > imdd2uint; - std::ostream & m_out; - unsigned m_next_id; - imdd2uint m_map; - display_ll_context(std::ostream & out):m_out(out), m_next_id(1) {} - - void display_tabs(unsigned num_tabs) { - for (unsigned i = 0; i < num_tabs; i++) - m_out << " "; - } - - void display_node(unsigned num_tabs, imdd const * d) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - if (it == end) { - m_out << "{}"; - return; - } - if (d->get_arity() == 1) { - // leaf - m_out << "{"; - for (bool first = true; it != end; ++it) { - if (first) - first = false; - else - m_out << ", "; - m_out << "[" << it->begin_key() << ", " << it->end_key() << "]"; - } - m_out << "}"; - } - else { - m_out << "{\n"; - display_tabs(num_tabs); - while (it != end) { - m_out << " [" << it->begin_key() << ", " << it->end_key() << "] -> "; - display(num_tabs+1, it->val()); - ++it; - if (it != end) { - m_out << "\n"; - display_tabs(num_tabs); - } - else { - m_out << "}"; - } - } - } - m_out << (d->is_memoized() ? "*" : "") << "$" << d->memory(); - } - - void display(unsigned num_tabs, imdd const * d) { - if (d == 0) { - m_out << ""; - return; - } - unsigned id; - if (m_map.find(const_cast(d), id)) { - m_out << "#" << id; - } - else if (d->is_shared()) { - id = m_next_id; - m_next_id++; - m_map.insert(const_cast(d), id); - m_out << "#" << id << ":"; - display_node(num_tabs, d); - } - else { - display_node(num_tabs, d); - } - } -}; - -void imdd_manager::display_ll(std::ostream & out, imdd const * d) const { - display_ll_context ctx(out); - ctx.display(0, d); - out << "\n"; -} - -struct addone_proc { - unsigned m_r; - addone_proc():m_r(0) {} - void operator()(imdd * d) { m_r++; } -}; - -/** - \brief Return the number of nodes in an IMDD. - This is *not* a constant time operation. - It is linear on the size of the IMDD -*/ -unsigned imdd_manager::get_num_nodes(imdd const * d) const { - addone_proc p; - for_each(const_cast(d), p); - return p.m_r; -} - -struct addmem_proc { - unsigned m_r; - addmem_proc():m_r(0) {} - void operator()(imdd * d) { m_r += d->memory(); } -}; - -/** - \brief Return the amount of memory consumed by the given IMDD. -*/ -unsigned imdd_manager::memory(imdd const * d) const { - addmem_proc p; - for_each(const_cast(d), p); - return p.m_r; -} - -/** - \brief Return number of rows the given IMDD represents. -*/ -size_t imdd_manager::get_num_rows(imdd const* root) const { - obj_map counts; - ptr_vector todo; - todo.push_back(root); - while (!todo.empty()) { - imdd const* d = todo.back(); - if (counts.contains(d)) { - todo.pop_back(); - continue; - } - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - bool all_valid = true; - size_t count = 0, c; - for (; it != end; ++it) { - imdd const* ch = it->val(); - if (!ch) { - count += (it->end_key()-it->begin_key()+1); - } - else if (counts.find(ch, c)) { - count += c*(it->end_key()-it->begin_key()+1); - } - else { - all_valid = false; - todo.push_back(ch); - } - } - if (all_valid) { - todo.pop_back(); - counts.insert(d, count); - } - } - return counts.find(root); -} - -imdd * imdd_manager::add_bounded_var_core(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res) { - if (d == 0) { - SASSERT(before_vidx == 0); - imdd * r = _mk_empty(1); - add_child(r, lower, upper, 0); - r = memoize_new_imdd_if(memoize_res, r); - return r; - } - imdd * r = 0; - if (is_cached(m_add_bounded_var_cache, d, r)) - return r; - if (before_vidx == 0) { - imdd * r = _mk_empty(d->get_arity() + 1); - add_child(r, lower, upper, d); - r = memoize_new_imdd_if(d->is_memoized() && memoize_res, r); - return r; - } - - if (destructive_update_at(destructive, d)) { - sbuffer to_insert; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = add_bounded_var_core(curr_child, before_vidx-1, lower, upper, true, memoize_res); - new_child->inc_ref(); // we will be resetting d->m_children later. - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - SASSERT(!to_insert.empty()); - d->replace_children(m_sl_manager, to_insert); - return d; - } - - bool new_children_memoized = true; - r = _mk_empty(d->get_arity() + 1); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = add_bounded_var_core(curr_child, before_vidx-1, lower, upper, false, memoize_res); - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_add_bounded_var_cache, d, r); - return r; -} - -/** - \brief Add a variable (bounded by lower and upper) before the variable before_var. - - That is, for each tuple (v_1, ..., v_n) in the IMDD \c d, the resultant IMDD contains the - tuples - - (v_1, ..., v_{after_vidx}, lower, ..., v_n) - (v_1, ..., v_{after_vidx}, lower+1, ..., v_n) - ... - (v_1, ..., v_{after_vidx}, upper, ..., v_n) - - This function is mainly used to implement mk_filter. - - \pre after_vidx < d->get_arity() -*/ -imdd * imdd_manager::add_bounded_var_main(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res) { - SASSERT(before_vidx <= d->get_arity()); - if (d->empty()) - return d; - m_add_bounded_var_cache.reset(); - delay_dealloc delay(*this); - imdd * r = add_bounded_var_core(d, before_vidx, lower, upper, destructive, memoize_res); - return r; -} - -filter_cache_entry * imdd_manager::mk_filter_cache_entry(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r) { - void * mem = m_filter_entries.allocate(filter_cache_entry::get_obj_size(ctx_sz)); - return new (mem) filter_cache_entry(d, r, ctx_sz, ctx); -} - -imdd * imdd_manager::is_mk_filter_cached(imdd * d, unsigned ctx_sz, unsigned * ctx) { - if (!d->is_shared()) - return 0; - m_filter_entries.push_scope(); - filter_cache_entry * tmp_entry = mk_filter_cache_entry(d, ctx_sz, ctx, 0); - filter_cache_entry * e = 0; - bool r = m_filter_cache.find(tmp_entry, e); - m_filter_entries.pop_scope(); - if (!r || e->m_r->is_dead()) - return 0; - return e->m_r; -} - -void imdd_manager::cache_mk_filter(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r) { - if (d->is_shared()) { - filter_cache_entry * new_entry = mk_filter_cache_entry(d, ctx_sz, ctx, r); - m_filter_cache.insert(new_entry); - } -} - -void imdd_manager::init_mk_filter(unsigned arity, unsigned num_vars, unsigned * vars) { - m_filter_cache.reset(); - m_filter_entries.reset(); - m_filter_context.reset(); - m_filter_num_vars = num_vars; - m_filter_begin_vars = vars; - m_filter_end_vars = vars + num_vars; - DEBUG_CODE({ - for (unsigned i = 0; i < num_vars; i++) { - SASSERT(vars[i] < arity); - } - }); -} - -template -void imdd_manager::mk_filter_dupdt_core(imdd_ref & d, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res) { - SASSERT(!d->empty()); - - if (!destructive_update_at(true, d)) { - mk_filter_core(d, d, vidx, num_found, proc, memoize_res); - return; - } - - bool _is_filter_var = is_filter_var(vidx); - if (_is_filter_var) - num_found++; - unsigned new_vidx = vidx+1; - imdd_ref new_r(*this); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); // take ownership of the current child. - if (!_is_filter_var) { - mk_filter_dupdt_core(new_child, new_vidx, num_found, proc, memoize_res); - add_bounded_var_dupdt(new_child, num_found, it->begin_key(), it->end_key(), memoize_res); - if (new_r == 0) - new_r = new_child; - else - mk_union_core_dupdt(new_r, new_child, memoize_res); - } - else { - m_filter_context.push_back(it->begin_key()); - m_filter_context.push_back(it->end_key()); - if (num_found < m_filter_num_vars) { - mk_filter_dupdt_core(new_child, new_vidx, num_found, proc, memoize_res); - } - else { - proc(m_filter_context.c_ptr(), new_child, new_child, memoize_res); - } - m_filter_context.pop_back(); - m_filter_context.pop_back(); - if (new_r == 0) - new_r = new_child; - else - mk_union_core_dupdt(new_r, new_child, memoize_res); - } - } - d->m_children.reset(m_sl_manager); - d = new_r; -} - -template -void imdd_manager::mk_filter_core(imdd * d, imdd_ref & r, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res) { - SASSERT(!d->empty()); - - imdd * _r = is_mk_filter_cached(d, m_filter_context.size(), m_filter_context.c_ptr()); - if (_r) { - r = _r; - return; - } - - bool _is_filter_var = is_filter_var(vidx); - if (_is_filter_var) - num_found++; - unsigned new_vidx = vidx+1; - imdd_ref new_r(*this); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd_ref new_child(*this); - if (!_is_filter_var) { - mk_filter_core(curr_child, new_child, new_vidx, num_found, proc, memoize_res); - imdd_ref tmp(*this); - add_bounded_var(new_child, tmp, num_found, it->begin_key(), it->end_key(), memoize_res); - if (new_r == 0) - new_r = tmp; - else - mk_union_core(new_r, tmp, new_r, memoize_res); - } - else { - m_filter_context.push_back(it->begin_key()); - m_filter_context.push_back(it->end_key()); - if (num_found < m_filter_num_vars) { - mk_filter_core(curr_child, new_child, new_vidx, num_found, proc, memoize_res); - } - else { - proc(m_filter_context.c_ptr(), curr_child, new_child, memoize_res); - } - m_filter_context.pop_back(); - m_filter_context.pop_back(); - if (new_r == 0) - new_r = new_child; - else - mk_union_core(new_r, new_child, new_r, memoize_res); - } - } - r = new_r; - cache_mk_filter(d, m_filter_context.size(), m_filter_context.c_ptr(), r); -} - -template -void imdd_manager::mk_filter_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res) { - if (d->empty()) - return; - unsigned arity = d->get_arity(); - init_mk_filter(arity, num_vars, vars); - mk_filter_dupdt_core(d, 0, 0, proc, memoize_res); - if (d == 0) - d = _mk_empty(arity); -} - -template -void imdd_manager::mk_filter(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res) { - if (d->empty()) { - r = d; - return; - } - unsigned arity = d->get_arity(); - init_mk_filter(arity, num_vars, vars); - mk_filter_core(d, r, 0, 0, proc, memoize_res); - if (r == 0) - r = _mk_empty(arity); -} - -imdd * imdd_manager::mk_distinct_imdd(unsigned l1, unsigned u1, unsigned l2, unsigned u2, imdd * d, bool memoize_res) { - unsigned d_arity; - if (d == 0) { - d_arity = 0; - } - else { - d_arity = d->get_arity(); - memoize_res = memoize_res && d->is_memoized(); - } - imdd * r = _mk_empty(d_arity + 2); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - - TRACE("mk_distinct_imdd", tout << "STARTING: " << l1 << " " << u1 << " " << l2 << " " << u2 << "\n";); - -#define ADD_ENTRY(L1, U1, L2, U2) { \ - TRACE("mk_distinct_imdd", tout << "[" << L1 << " " << U1 << " " << L2 << " " << U2 << "]\n";); \ - imdd * new_child = _mk_empty(d_arity + 1); \ - add_child(new_child, L2, U2, d); \ - new_child = memoize_new_imdd_if(memoize_res, new_child); \ - push_back(L1, U1, new_child);} - - if (u1 < l2 || u2 < l1) { - ADD_ENTRY(l1, u1, l2, u2); // the intervals are disjoint - } - else { - if (l1 < l2) { - SASSERT(u1 >= l2); - // [l1 ... - // [l2 ... - // --> - // [l1, l2-1] X [l2, u2] - ADD_ENTRY(l1, l2-1, l2, u2); - } - - unsigned l = std::max(l1, l2); - unsigned u = std::min(u1, u2); - // [l, l] X [l2, l-1] // if l-1 >= l2 (i.e., l > l2) - // [l, l] X [l+1, u2] - // [l+1, l+1] X [l2, l] - // [l+1, l+1] X [l+2, u2] - // [l+2, l+2] X [l2, l+1] - // [l+2, l+2] X [l+3, u2] - // ... - // [u, u] X [l2, u-1] - // [u, u] X [u+1, u2] // if u+1 <= u2 (i.e., u < u2) - for (unsigned i = l; i <= u; i++) { - if (i > l2 && i < u2) { - // ADD_ENTRY(i, i, l2, i-1); - // ADD_ENTRY(i, i, i+1, u2); - imdd * new_child = _mk_empty(d_arity + 1); - add_child(new_child, l2, i-1, d); - add_child(new_child, i+1, u2, d); - new_child = memoize_new_imdd_if(memoize_res, new_child); - push_back(i, i, new_child); - } - else if (i > l2) { - SASSERT(!(i < u2)); - ADD_ENTRY(i, i, l2, i-1); - } - else if (i < u2) { - SASSERT(!(i > l2)); - ADD_ENTRY(i, i, i+1, u2); - } - } - - if (u1 > u2) { - // ... u1] - // ... u2] - // --> - // [u2+1, u1] X [l2, u2] - SASSERT(u2 >= l1); - ADD_ENTRY(u2+1, u1, l2, u2); - } - } -#undef ADD_ENTRY - - r = memoize_new_imdd_if(memoize_res, r); - return r; -} - -/** - \brief Auxiliary functor used to implement mk_filter_distinct -*/ -struct distinct_proc { - imdd_manager & m_manager; - - distinct_proc(imdd_manager & m): - m_manager(m) { - } - - void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res) { - r = m_manager.mk_distinct_imdd(lowers_uppers[0], lowers_uppers[1], lowers_uppers[2], lowers_uppers[3], d, memoize_res); - } -}; - -void imdd_manager::mk_filter_distinct_dupdt(imdd_ref & d, unsigned v1, unsigned v2, bool memoize_res) { - unsigned vars[2] = { v1, v2 }; - distinct_proc proc(*this); - mk_filter_dupdt(d, 2, vars, proc, memoize_res); -} - -void imdd_manager::mk_filter_distinct(imdd * d, imdd_ref & r, unsigned v1, unsigned v2, bool memoize_res) { - unsigned vars[2] = { v1, v2 }; - distinct_proc proc(*this); - mk_filter(d, r, 2, vars, proc, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_distinct(0x" << d << ", 0x" << r.get() << ", " << v1 << ", " << v2 << ", " << memoize_res << ");\n";); -} - -imdd * imdd_manager::mk_disequal_imdd(unsigned l1, unsigned u1, unsigned value, imdd * d, bool memoize_res) { - unsigned d_arity; - if (d == 0) { - d_arity = 0; - } - else { - d_arity = d->get_arity(); - memoize_res = memoize_res && d->is_memoized(); - } - imdd * r = _mk_empty(d_arity + 1); - if (value < l1 || value > u1) { - add_child(r, l1, u1, d); - } - else { - SASSERT(l1 <= value && value <= u1); - if (l1 < value) { - add_child(r, l1, value-1, d); - } - if (value < u1) { - add_child(r, value+1, u1, d); - } - } - r = memoize_new_imdd_if(memoize_res, r); - return r; -} - -struct disequal_proc { - imdd_manager & m_manager; - unsigned m_value; - - disequal_proc(imdd_manager & m, unsigned v): - m_manager(m), - m_value(v) { - } - - void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res) { - r = m_manager.mk_disequal_imdd(lowers_uppers[0], lowers_uppers[1], m_value, d, memoize_res); - } -}; - -void imdd_manager::mk_filter_disequal_dupdt(imdd_ref & d, unsigned var, unsigned value, bool memoize_res) { - unsigned vars[1] = { var }; - disequal_proc proc(*this, value); - mk_filter_dupdt(d, 1, vars, proc, memoize_res); -} - -void imdd_manager::mk_filter_disequal(imdd * d, imdd_ref & r, unsigned var, unsigned value, bool memoize_res) { - unsigned vars[1] = { var }; - disequal_proc proc(*this, value); - mk_filter(d, r, 1, vars, proc, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_disequal(0x" << d << ", 0x" << r.get() << ", " << var << ", " << value << ", " << memoize_res << ");\n";); -} - - diff --git a/src/muz_qe/imdd.h b/src/muz_qe/imdd.h deleted file mode 100644 index b2da275bf..000000000 --- a/src/muz_qe/imdd.h +++ /dev/null @@ -1,849 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - imdd.h - -Abstract: - - Interval based Multiple-valued Decision Diagrams. - -Author: - - Leonardo de Moura (leonardo) 2010-10-13. - -Revision History: - ---*/ -#ifndef _IMDD_H_ -#define _IMDD_H_ - -#include"id_gen.h" -#include"hashtable.h" -#include"map.h" -#include"obj_hashtable.h" -#include"obj_pair_hashtable.h" -#include"buffer.h" -#include"interval_skip_list.h" -#include"region.h" -#include"obj_ref.h" - -class imdd; -class imdd_manager; - -/** - \brief Manager for skip-lists used to implement IMDD nodes. -*/ -class sl_imdd_manager : public random_level_manager { - imdd_manager * m_manager; // real manager - small_object_allocator & m_alloc; - friend class imdd_manager; -public: - sl_imdd_manager(small_object_allocator & alloc):m_alloc(alloc) {} - 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(imdd * v); - void dec_ref_eh(imdd * v); -}; - -#define IMDD_BUCKET_CAPACITY 128 -#define IMDD_MAX_LEVEL 32 - -typedef interval_skip_list, - IMDD_BUCKET_CAPACITY, - IMDD_MAX_LEVEL, - true, /* support ref-counting */ - sl_imdd_manager> > imdd_children; - -typedef interval_skip_list, - IMDD_BUCKET_CAPACITY, - IMDD_MAX_LEVEL, - false, - sl_manager_base > > sl_interval_set; - -/* - Notes: - - - We use reference counting for garbage collecting IMDDs nodes. - - - Each IMDD node has a "memoized" flag. If the flag is true, the we use hash-consing for this node. - - - The children of a memoized node must be memoized. - - - The children of a non-memoized node may be memoized. - - - The "memoized" flag cannot be reset after it was set. - - - The result of some operations may be cached. We only use caching for - operations processing memoized nodes. - - - For non-memoized nodes, if m_ref_count <= 1, destructive updates may be performed by some operations. - - - IMPORTANT: "memoized" flag == false doesn't imply m_ref_count <= 1. -*/ - - -/** - \brief IMDDs -*/ -class imdd { - -protected: - friend class imdd_manager; - - unsigned m_id; //!< Unique ID - unsigned m_ref_count; - unsigned m_arity:30; - unsigned m_memoized:1; - unsigned m_dead:1; - imdd_children m_children; - - void inc_ref() { - m_ref_count ++; - } - - void dec_ref() { - SASSERT(m_ref_count > 0); - m_ref_count --; - } - - void mark_as_memoized(bool flag = true) { - SASSERT(is_memoized() != flag); - m_memoized = flag; - } - - void mark_as_dead() { SASSERT(!m_dead); m_dead = true; } - - void replace_children(sl_imdd_manager & m, sbuffer & new_children); - -public: - imdd(sl_imdd_manager & m, unsigned id, unsigned arity):m_id(id), m_ref_count(0), m_arity(arity), m_memoized(false), m_dead(false), m_children(m) {} - unsigned get_id() const { return m_id; } - unsigned get_ref_count() const { return m_ref_count; } - bool is_memoized() const { return m_memoized; } - bool is_shared() const { return m_ref_count > 1; } - bool is_dead() const { return m_dead; } - unsigned get_arity() const { return m_arity; } - imdd_children::iterator begin_children() const { return m_children.begin(); } - imdd_children::iterator end_children() const { return m_children.end(); } - unsigned hc_hash() const; // hash code for hash-consing. - bool hc_equal(imdd const * other) const; // eq function for hash-consing - bool empty() const { return m_children.empty(); } - unsigned hash() const { return m_id; } - unsigned memory() const { return sizeof(imdd) + m_children.memory() - sizeof(imdd_children); } -}; - -// ----------------------------------- -// -// IMDD hash-consing -// -// ----------------------------------- - -// this is the internal hashing functor for hash-consing IMDDs. -struct imdd_hash_proc { - unsigned operator()(imdd const * d) const { return d->hc_hash(); } -}; - -// This is the internal comparison functor for hash-consing IMDDs. -struct imdd_eq_proc { - bool operator()(imdd const * d1, imdd const * d2) const { return d1->hc_equal(d2); } -}; - -typedef ptr_hashtable imdd_table; -typedef obj_hashtable imdd_cache; -typedef obj_map imdd2imdd_cache; -typedef obj_pair_map imdd_pair2imdd_cache; -typedef obj_pair_map imdd_pair2bool_cache; -typedef obj_map imdd2intervals; - -typedef std::pair imdd_value_pair; - -struct fi_cache_entry { - imdd * m_d; - unsigned m_lower; - unsigned m_upper; - unsigned m_hash; - unsigned m_num_result; - imdd_value_pair m_result[0]; - - void mk_hash() { - m_hash = hash_u_u(m_d->get_id(), hash_u_u(m_lower, m_upper)); - } - - fi_cache_entry(imdd * d, unsigned l, unsigned u): - m_d(d), - m_lower(l), - m_upper(u) { - mk_hash(); - } - - fi_cache_entry(imdd * d, unsigned l, unsigned u, unsigned num, imdd_value_pair result[]): - m_d(d), - m_lower(l), - m_upper(u), - m_num_result(num) { - mk_hash(); - memcpy(m_result, result, sizeof(imdd_value_pair)*num); - } - - unsigned hash() const { - return m_hash; - } - - bool operator==(fi_cache_entry const & other) const { - return - m_d == other.m_d && - m_lower == other.m_lower && - m_upper == other.m_upper; - } -}; - -typedef obj_hashtable imdd_fi_cache; -typedef union { - imdd * m_d; - fi_cache_entry * m_entry; -} mk_fi_result; - -struct filter_cache_entry { - imdd * m_d; - imdd * m_r; - unsigned m_hash; - unsigned m_ctx_size; - unsigned m_ctx[0]; // lower and upper bounds that are part of the context. - - static unsigned get_obj_size(unsigned ctx_size) { - return sizeof(filter_cache_entry) + ctx_size * sizeof(unsigned); - } - - void mk_hash() { - if (m_ctx_size > 0) - m_hash = string_hash(reinterpret_cast(m_ctx), m_ctx_size * sizeof(unsigned), m_d->get_id()); - else - m_hash = m_d->get_id(); - } - - filter_cache_entry(imdd * d, imdd * r, unsigned ctx_size, unsigned * ctx): - m_d(d), - m_r(r), - m_ctx_size(ctx_size) { - memcpy(m_ctx, ctx, sizeof(unsigned)*m_ctx_size); - mk_hash(); - } - - unsigned hash() const { - return m_hash; - } - - bool operator==(filter_cache_entry const & other) const { - if (m_d != other.m_d) - return false; - if (m_ctx_size != other.m_ctx_size) - return false; - for (unsigned i = 0; i < m_ctx_size; i++) - if (m_ctx[i] != other.m_ctx[i]) - return false; - return true; - } -}; - -typedef obj_hashtable imdd_mk_filter_cache; - -typedef obj_ref imdd_ref; - -class imdd_manager { - typedef imdd_children::entry entry; - small_object_allocator m_alloc; - id_gen m_id_gen; - vector m_tables; // we keep a table for each height. - sl_imdd_manager m_sl_manager; - unsigned m_simple_max_entries; //!< maximum number of entries in a "simple" node. - bool m_delay_dealloc; - ptr_vector m_to_delete; //!< list of IMDDs marked as dead. These IMDDs may still be in cache tables. - - // generic cache and todo-lists - ptr_vector m_worklist; - imdd_cache m_visited; - - void mark_as_dead(imdd * d); - void deallocate_imdd(imdd * d); - void delete_imdd(imdd * d); - - class delay_dealloc; - friend class delay_dealloc; - - class delay_dealloc { - imdd_manager & m_manager; - bool m_delay_dealloc_value; - unsigned m_to_delete_size; - public: - delay_dealloc(imdd_manager & m): - m_manager(m), - m_delay_dealloc_value(m_manager.m_delay_dealloc), - m_to_delete_size(m_manager.m_to_delete.size()) { - m_manager.m_delay_dealloc = true; - } - ~delay_dealloc(); - }; - - bool is_simple_node(imdd * d) const; - - void add_child(imdd * d, unsigned lower, unsigned upper, imdd * child) { - d->m_children.insert(m_sl_manager, lower, upper, child); - } - - void add_child(imdd * d, unsigned value, imdd * child) { - add_child(d, value, value, child); - } - - void remove_child(imdd * d, unsigned lower, unsigned upper) { - d->m_children.remove(m_sl_manager, lower, upper); - } - - imdd * copy_main(imdd * d); - - imdd * insert_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res); - - imdd * remove_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res); - - imdd2imdd_cache m_mk_product_cache; - struct null2imdd_proc; - struct mk_product_proc; - friend struct mk_product_proc; - imdd * mk_product_core(imdd * d1, imdd * d2, bool destructive, bool memoize); - imdd * mk_product_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res); - - imdd2imdd_cache m_add_facts_cache; - ptr_vector m_add_facts_new_children; - void init_add_facts_new_children(unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res); - imdd * add_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - imdd * add_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - - imdd2imdd_cache m_remove_facts_cache; - imdd * remove_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - imdd * remove_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - - - imdd2imdd_cache m_defrag_cache; - imdd * defrag_core(imdd * d); - - imdd_pair2imdd_cache m_union_cache; - void push_back_entries(unsigned head, imdd_children::iterator & it, imdd_children::iterator & end, - imdd_children::push_back_proc & push_back, bool & children_memoized); - void push_back_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned limit, - imdd_children::push_back_proc & push_back, bool & children_memoized); - void move_head(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned new_head); - void copy_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned limit, sbuffer & result); - void reset_union_cache(); - imdd * mk_union_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res); - imdd * mk_union_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res); - void mk_union_core_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res); - void mk_union_core(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res); - - imdd_pair2bool_cache m_is_equal_cache; - bool is_equal_core(imdd * d1, imdd * d2); - - imdd_pair2bool_cache m_subsumes_cache; - bool subsumes_core(imdd * d1, imdd * d2); - - imdd2imdd_cache m_complement_cache; - imdd * mk_complement_core(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res); - imdd * mk_complement_main(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res); - - imdd2imdd_cache m_filter_equal_cache; - imdd * mk_filter_equal_core(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res); - imdd * mk_filter_equal_main(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res); - - - // original - imdd2intervals m_imdd2interval_set; - ptr_vector m_alloc_is; - typedef sl_manager_base sl_imanager; - void reset_fi_intervals(sl_imanager& m); - sl_interval_set const* init_fi_intervals(sl_imanager& m, imdd* d, unsigned var, unsigned num_found); - - imdd2imdd_cache m_fi_top_cache; - imdd_fi_cache m_fi_bottom_cache; - unsigned m_fi_num_vars; - unsigned * m_fi_begin_vars; - unsigned * m_fi_end_vars; - region m_fi_entries; - bool is_fi_var(unsigned v) const { return std::find(m_fi_begin_vars, m_fi_end_vars, v) != m_fi_end_vars; } - fi_cache_entry * mk_fi_cache_entry(imdd * d, unsigned lower, unsigned upper, unsigned num_pairs, imdd_value_pair pairs[]); - mk_fi_result mk_filter_identical_core(imdd * d, unsigned offset, unsigned num_found, unsigned lower, unsigned upper, - bool destructive, bool memoize_res); - imdd * mk_filter_identical_main(imdd * d, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res); - - // v2 - obj_map m_filter_identical_cache; - void filter_identical_core2(imdd* d, unsigned num_vars, unsigned b, unsigned e, ptr_vector& ch); - imdd* filter_identical_core2(imdd* d, unsigned var, unsigned num_vars, bool memoize_res); - void filter_identical_main2(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res); - void swap_in(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars); - void swap_out(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars); - - // v3 - struct interval { - unsigned m_lo; - unsigned m_hi; - interval(unsigned lo, unsigned hi): m_lo(lo), m_hi(hi) {} - }; - struct interval_dd : public interval { - imdd* m_dd; - interval_dd(unsigned lo, unsigned hi, imdd* d): interval(lo, hi), m_dd(d) {} - }; - template - class id_map { - unsigned m_T; - unsigned_vector m_Ts; - svector*> m_vecs; - unsigned_vector m_alloc; - unsigned m_first_free; - void hard_reset() { - std::for_each(m_vecs.begin(), m_vecs.end(), delete_proc >()); - m_alloc.reset(); - m_first_free = 0; - m_vecs.reset(); - m_Ts.reset(); - m_T = 0; - } - - void allocate_entry(unsigned id) { - if (m_vecs[id]) { - return; - } - while (m_first_free < m_alloc.size()) { - if (m_vecs[m_first_free] && m_Ts[m_first_free] < m_T) { - svector* v = m_vecs[m_first_free]; - m_vecs[m_first_free] = 0; - m_vecs[id] = v; - ++m_first_free; - return; - } - ++m_first_free; - } - m_vecs[id] = alloc(svector); - m_alloc.push_back(id); - } - public: - id_map():m_T(0) {} - ~id_map() { hard_reset(); } - void reset() { ++m_T; m_first_free = 0; if (m_T == UINT_MAX) hard_reset(); } - svector& init(imdd* d) { - unsigned id = d->get_id(); - if (id >= m_vecs.size()) { - m_vecs.resize(id+1); - m_Ts.resize(id+1); - } - if (m_Ts[id] < m_T) { - allocate_entry(id); - m_vecs[id]->reset(); - m_Ts[id] = m_T; - } - return *m_vecs[id]; - } - - typedef svector data; - struct iterator { - unsigned m_offset; - id_map const& m; - iterator(unsigned o, id_map const& m):m_offset(o),m(m) {} - data const & operator*() const { return *m.m_vecs[m_offset]; } - data const * operator->() const { return &(operator*()); } - data * operator->() { return &(operator*()); } - iterator & operator++() { ++m_offset; return move_to_used(); } - iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const & it) const { return m_offset == it.m_offset; } - bool operator!=(iterator const & it) const { return m_offset != it.m_offset; } - iterator & move_to_used() { - while (m_offset < m.m_vecs.size() && - m.m_Ts[m_offset] < m.m_T) { - ++m_offset; - } - return *this; - } - }; - iterator begin() const { return iterator(0, *this).move_to_used(); } - iterator end() const { return iterator(m_vecs.size(), *this); } - }; - typedef id_map filter_id_map; - typedef id_map filter_idd_map; - filter_id_map m_nodes; - filter_idd_map m_nodes_dd; - svector m_i_nodes_dd, m_i_nodes_dd_tmp; - svector m_i_nodes, m_i_nodes_tmp; - unsigned_vector m_offsets; - void filter_identical_main3(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res); - void filter_identical_main3(imdd * d, imdd_ref& r, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res); - imdd* filter_identical_loop3(imdd * d, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res); - void refine_intervals(svector& i_nodes, svector const& i_nodes_dd); - void merge_intervals(svector& dst, svector const& src); - imdd* filter_identical_mk_nodes(imdd* d, unsigned v, bool del1, bool del2, bool memoize_res); - void print_filter_idd(std::ostream& out, filter_idd_map const& m); - void print_interval_dd(std::ostream& out, svector const& m); - - - - unsigned m_proj_num_vars; - unsigned * m_proj_begin_vars; - unsigned * m_proj_end_vars; - imdd2imdd_cache m_proj_cache; - bool is_proj_var(unsigned v) const { return std::find(m_proj_begin_vars, m_proj_end_vars, v) != m_proj_end_vars; } - void mk_project_init(unsigned num_vars, unsigned * vars); - void mk_project_core(imdd * d, imdd_ref & r, unsigned var, unsigned num_found, bool memoize_res); - void mk_project_dupdt_core(imdd_ref & d, unsigned var, unsigned num_found, bool memoize_res); - - imdd2imdd_cache m_swap_cache; - imdd * m_swap_new_child; - bool m_swap_granchildren_memoized; - imdd * mk_swap_new_child(unsigned lower, unsigned upper, imdd * child); - void mk_swap_acc1_dupdt(imdd_ref & d, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res); - void mk_swap_acc1(imdd * d, imdd_ref & r, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res); - void mk_swap_acc2(imdd_ref & r, unsigned lower1, unsigned upper1, unsigned lower2, unsigned upper2, imdd * grandchild, bool memoize_res); - void mk_swap_top_vars(imdd * d, imdd_ref & r, bool memoize_res); - imdd * mk_swap_memoize(imdd * d); - void mk_swap_core(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res); - void mk_swap_dupdt_core(imdd_ref & d, unsigned vidx, bool memoize_res); - - imdd2imdd_cache m_add_bounded_var_cache; - imdd * add_bounded_var_core(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res); - imdd * add_bounded_var_main(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res); - - friend struct distinct_proc; - imdd * mk_distinct_imdd(unsigned l1, unsigned u1, unsigned l2, unsigned u2, imdd * d, bool memoize_res = true); - - imdd_mk_filter_cache m_filter_cache; - region m_filter_entries; - unsigned m_filter_num_vars; - unsigned * m_filter_begin_vars; - unsigned * m_filter_end_vars; - unsigned_vector m_filter_context; - bool is_filter_var(unsigned v) const { return std::find(m_filter_begin_vars, m_filter_end_vars, v) != m_filter_end_vars; } - filter_cache_entry * mk_filter_cache_entry(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r); - imdd * is_mk_filter_cached(imdd * d, unsigned ctx_sz, unsigned * ctx); - void cache_mk_filter(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r); - void init_mk_filter(unsigned arity, unsigned num_vars, unsigned * vars); - template - void mk_filter_dupdt_core(imdd_ref & d, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res); - template - void mk_filter_core(imdd * d, imdd_ref & r, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res); - /** - \brief Filter the elements of the given IMDD using the given filter. - - The FilterProc template parameter is a filter for computing subsets of sets of the form: - - [L_1, U_1] X [L_2, U_2] X ... X [L_n, U_n] X d (where d is an IMDD) - - where n == num_vars - - The subset of elements is returned as an IMDD. - - FilterProc must have a method of the form: - - void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res); - - The size of the array lowers_uppers is 2*num_vars - - The arity of the resultant IMDD must be num_vars + d->get_arity(). - */ - template - void mk_filter_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res = true); - template - void mk_filter(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res = true); - - imdd * mk_disequal_imdd(unsigned l1, unsigned u1, unsigned value, imdd * d, bool memoize_res); - friend struct disequal_proc; - -public: - imdd_manager(); - - void inc_ref(imdd * d) { - if (d) - d->inc_ref(); - } - - void dec_ref(imdd * d) { - if (d) { - d->dec_ref(); - if (d->get_ref_count() == 0) - delete_imdd(d); - } - } - - unsigned get_num_nodes(imdd const * d) const; - - // count number of keys (rows) in table as if table is uncompressed. - size_t get_num_rows(imdd const* d) const; - - unsigned memory(imdd const * d) const; - -private: - imdd * _mk_empty(unsigned arity); - -public: - imdd * mk_empty(unsigned arity) { - imdd * r = _mk_empty(arity); - STRACE("imdd_trace", tout << "mk_empty(" << arity << ", 0x" << r << ");\n";); - return r; - } - -private: - imdd * memoize(imdd * d); - -public: - void memoize(imdd_ref const & d, imdd_ref & r) { r = memoize(d.get()); } - - void memoize(imdd_ref & d) { d = memoize(d.get()); } - - imdd * memoize_new_imdd_if(bool cond, imdd * r) { - if (cond && is_simple_node(r)) { - SASSERT(!r->is_shared()); - imdd * can_r = memoize(r); - if (can_r != r) { - SASSERT(r->get_ref_count() == 0); - delete_imdd(r); - } - return can_r; - } - return r; - } - -public: - void defrag(imdd_ref & d); - - void unmemoize(imdd * d); - - void unmemoize_rec(imdd * d); - - void copy(imdd * d, imdd_ref & r) { r = copy_main(d); } - - void insert_dupdt(imdd_ref & d, unsigned b, unsigned e, bool memoize_res = true) { - d = insert_main(d, b, e, true, memoize_res); - } - - void insert(imdd * d, imdd_ref & r, unsigned b, unsigned e, bool memoize_res = true) { - r = insert_main(d, b, e, false, memoize_res); - } - - void mk_product_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res = true) { - d1 = mk_product_main(d1.get(), d2, true, memoize_res); - } - - void mk_product(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res = true) { - r = mk_product_main(d1, d2, false, memoize_res); - STRACE("imdd_trace", tout << "mk_product(0x" << d1 << ", 0x" << d2 << ", 0x" << r.get() << ", " << memoize_res << ");\n";); - } - - void mk_union_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res = true) { - d1 = mk_union_main(d1.get(), d2, true, memoize_res); - } - - void mk_union(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res = true) { - r = mk_union_main(d1, d2, false, memoize_res); - STRACE("imdd_trace", tout << "mk_union(0x" << d1 << ", 0x" << d2 << ", 0x" << r.get() << ", " << memoize_res << ");\n";); - } - - void mk_complement_dupdt(imdd_ref & d, unsigned num, unsigned const * mins, unsigned const * maxs, bool memoize_res = true) { - d = mk_complement_main(d, num, mins, maxs, true, memoize_res); - } - - void mk_complement(imdd * d, imdd_ref & r, unsigned num, unsigned const * mins, unsigned const * maxs, bool memoize_res = true) { - r = mk_complement_main(d, num, mins, maxs, false, memoize_res); - - STRACE("imdd_trace", tout << "mk_complement(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num; i++) tout << mins[i] << ", " << maxs[i] << ", "; - tout << memoize_res << ");\n";); - } - - void mk_filter_equal_dupdt(imdd_ref & d, unsigned vidx, unsigned value, bool memoize_res = true) { - d = mk_filter_equal_main(d, vidx, value, true, memoize_res); - } - - void mk_filter_equal(imdd * d, imdd_ref & r, unsigned vidx, unsigned value, bool memoize_res = true) { - r = mk_filter_equal_main(d, vidx, value, false, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_equal(0x" << d << ", 0x" << r.get() << ", " << vidx << ", " << value << ", " << memoize_res << ");\n";); - } - - void mk_filter_identical_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res = true) { - // d = mk_filter_identical_main(d, num_vars, vars, true, memoize_res); - filter_identical_main3(d, d, num_vars, vars, true, memoize_res); - } - - void mk_filter_identical(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res = true) { - filter_identical_main3(d, r, num_vars, vars, false, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_identical(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num_vars; i++) tout << vars[i] << ", "; - tout << memoize_res << ");\n";); - } - - void mk_project_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res = true); - - void mk_project(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res = true); - - // swap vidx and vidx+1 - void mk_swap_dupdt(imdd_ref & d, unsigned vidx, bool memoize_res = true); - - // swap vidx and vidx+1 - void mk_swap(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res = true); - - void add_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res = true) { - d = add_facts_main(d, num, lowers, uppers, true, memoize_res); - } - - void add_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res = true) { - r = add_facts_main(d, num, lowers, uppers, false, memoize_res); - - STRACE("imdd_trace", tout << "add_facts(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num; i++) tout << lowers[i] << ", " << uppers[i] << ", "; - tout << memoize_res << ");\n";); - } - - void add_fact_dupdt(imdd_ref & d, unsigned num, unsigned const * values, bool memoize_res = true) { - add_facts_dupdt(d, num, values, values, memoize_res); - } - - void add_fact(imdd * d, imdd_ref & r, unsigned num, unsigned const * values, bool memoize_res = true) { - add_facts(d, r, num, values, values, memoize_res); - } - - void add_bounded_var_dupdt(imdd_ref & d, unsigned before_vidx, unsigned lower, unsigned upper, bool memoize_res = true) { - d = add_bounded_var_main(d, before_vidx, lower, upper, true, memoize_res); - } - - void add_bounded_var(imdd * d, imdd_ref & r, unsigned before_vidx, unsigned lower, unsigned upper, bool memoize_res = true) { - r = add_bounded_var_main(d, before_vidx, lower, upper, false, memoize_res); - } - - void mk_filter_distinct_dupdt(imdd_ref & d, unsigned v1, unsigned v2, bool memoize_res = true); - - void mk_filter_distinct(imdd * d, imdd_ref & r, unsigned v1, unsigned v2, bool memoize_res = true); - - void mk_filter_disequal_dupdt(imdd_ref & d, unsigned var, unsigned value, bool memoize_res = true); - - void mk_filter_disequal(imdd * d, imdd_ref & r, unsigned var, unsigned value, bool memoize_res = true); - - void mk_join(imdd * d1, imdd * d2, imdd_ref & r, unsigned_vector const& vars1, unsigned_vector const& vars2, bool memoize_res = true); - - void mk_join_project(imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - unsigned_vector const& proj_vars, bool memoize_res = true); - - void mk_join_dupdt(imdd_ref & d1, imdd * d2, unsigned num_vars, unsigned const * vars1, unsigned const * vars2, bool memoize_res = true); - - void remove_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers); - - void remove_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers); - - bool is_equal(imdd * d1, imdd * d2); - - bool contains(imdd * d, unsigned num, unsigned const * values) const; - - bool contains(imdd * d, unsigned v) const { return contains(d, 1, &v); } - - bool contains(imdd * d, unsigned v1, unsigned v2) const { unsigned vs[2] = {v1, v2}; return contains(d, 2, vs); } - - bool contains(imdd * d, unsigned v1, unsigned v2, unsigned v3) const { unsigned vs[3] = {v1,v2,v3}; return contains(d, 3, vs); } - - bool subsumes(imdd * d1, imdd * d2); - - bool is_subset(imdd * d1, imdd * d2) { return subsumes(d2, d1); } - -private: - void display(std::ostream & out, imdd const * d, unsigned_vector & intervals, bool & first) const; - -public: - void display(std::ostream & out, imdd const * d) const; - - void display_ll(std::ostream & out, imdd const * d) const; - - /** - \brief Execute proc (once) in each node in the IMDD rooted by d. - */ - template - void for_each(imdd * d, Proc & proc) const { - // for_each is a generic procedure, we don't know what proc will actually do. - // So, it is not safe to reuse m_worklist and m_visited. - ptr_buffer worklist; - imdd_cache visited; - worklist.push_back(d); - while (!worklist.empty()) { - d = worklist.back(); - worklist.pop_back(); - if (d->is_shared() && visited.contains(d)) - continue; - if (d->is_shared()) - visited.insert(d); - proc(d); - if (d->get_arity() > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) - worklist.push_back(it->val()); - } - } - } - - class iterator { - bool m_done; - svector m_iterators; - svector m_element; - - void begin_iterators(imdd const * curr, unsigned start_idx); - - public: - iterator():m_done(true) {} - iterator(imdd_manager const & m, imdd const * d); - - unsigned get_arity() const { return m_element.size(); } - unsigned * operator*() const { return m_element.c_ptr(); } - iterator & operator++(); - - bool operator==(iterator const & it) const; - bool operator!=(iterator const & it) const { return !operator==(it); } - }; - - friend class iterator; - - iterator begin(imdd const * d) const { return iterator(*this, d); } - iterator end(imdd const * d) const { return iterator(); } -}; - -inline std::ostream & operator<<(std::ostream & out, imdd_ref const & r) { - r.get_manager().display(out, r.get()); - return out; -} - -struct mk_imdd_pp { - imdd * m_d; - imdd_manager & m_manager; - mk_imdd_pp(imdd * d, imdd_manager & m):m_d(d), m_manager(m) {} -}; - -inline mk_imdd_pp mk_pp(imdd * d, imdd_manager & m) { - return mk_imdd_pp(d, m); -} - -inline std::ostream & operator<<(std::ostream & out, mk_imdd_pp const & pp) { - pp.m_manager.display(out, pp.m_d); - return out; -} - -struct mk_imdd_ll_pp : public mk_imdd_pp { - mk_imdd_ll_pp(imdd * d, imdd_manager & m):mk_imdd_pp(d, m) {} -}; - -inline mk_imdd_ll_pp mk_ll_pp(imdd * d, imdd_manager & m) { - return mk_imdd_ll_pp(d, m); -} - -inline std::ostream & operator<<(std::ostream & out, mk_imdd_ll_pp const & pp) { - pp.m_manager.display_ll(out, pp.m_d); - return out; -} - -#endif /* _IMDD_H_ */ - diff --git a/src/muz_qe/interval_skip_list.h b/src/muz_qe/interval_skip_list.h deleted file mode 100644 index 7865e6ca5..000000000 --- a/src/muz_qe/interval_skip_list.h +++ /dev/null @@ -1,1876 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - interval_skip_list.h - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-10-01. - -Revision History: - ---*/ -#ifndef _INTERVAL_SKIP_LIST_H_ -#define _INTERVAL_SKIP_LIST_H_ - -#include"skip_list_base.h" - -/* - Interval skip lists implement a mapping from keys to values. - The key must be a total order. - - It compress consecutive entries k->v and (k+1)->v by - using intervals. Internally, we have [k1, k2]->v to represent - the set of entries k1->v, (k1+1)->v, ..., k2->v. - - For improving cache behavior, the entries are packed in - buckets. As regular skip-lists, a node/bucket may have - several next/forward pointers. - - Interval skip lists can also encode sets. In this case, - there is no value type. We achieve that by allowing the template - to be instantiated with an arbitrary "entry" class for encoding - [k1, k2]->v. This class must provide 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 - bool operator==(entry const & other) const; - - And a definition for the key and value types. -*/ - -/** - \brief Default interval_skip_list entry. - It is useful for implementing mappings. -*/ -template -class default_islist_entry { -public: - typedef Key key; - typedef Value value; -private: - key m_begin_key; - key m_end_key; - value m_value; -public: - default_islist_entry() {} - default_islist_entry(key const & b, key const & e, value const & v): - m_begin_key(b), - m_end_key(e), - m_value(v) { - } - key const & begin_key() const { return m_begin_key; } - key const & end_key() const { return m_end_key; } - value const & val() const { return m_value; } - void set_begin_key(key const & k) { m_begin_key = k; } - void set_end_key(key const & k) { m_end_key = k; } - void set_val(value const & v) { m_value = v; } - void display(std::ostream & out) const { - out << "[" << begin_key() << ", " << end_key() << "] -> " << val(); - } -}; - -/** - \brief Default interval_skip_list entry for encoding sets. -*/ -template -struct default_set_islist_entry { -public: - typedef Key key; - typedef unsigned value; // don't care type -private: - key m_begin_key; - key m_end_key; -public: - default_set_islist_entry() {} - default_set_islist_entry(key const & b, key const & e): - m_begin_key(b), - m_end_key(e) { - } - key const & begin_key() const { return m_begin_key; } - key const & end_key() const { return m_end_key; } - unsigned const & val() const { static unsigned v = 0; return v; } - void set_begin_key(key const & k) { m_begin_key = k; } - void set_end_key(key const & k) { m_end_key = k; } - void set_val(value const & v) { /* do nothing */ } - void display(std::ostream & out) const { - out << "[" << begin_key() << ", " << end_key() << "]"; - } -}; - - -/** - \brief An interval skip list. - - See comments at skip_list_base.h -*/ -template -class interval_skip_list : public skip_list_base { -protected: - typedef typename skip_list_base::bucket bucket; -public: - typedef typename Traits::manager manager; - typedef typename Traits::entry entry; - typedef typename entry::key key; - typedef typename entry::value value; - -protected: - bool lt(key const & k1, key const & k2) const { return skip_list_base::lt(k1, k2); } - - static key const & first_key(bucket const * bt) { return bt->first_entry().begin_key(); } - - static key const & last_key(bucket const * bt) { return bt->last_entry().end_key(); } - - static void set_entry(bucket * bt, unsigned idx, key const & b, key const & e, value const & v) { - entry & en = bt->get(idx); - en.set_begin_key(b); - en.set_end_key(e); - en.set_val(v); - } - - /** - \brief Add first entry to the list. - - \remark This method will invoke inc_ref_eh for v. - */ - void insert_first_entry(manager & m, key const & b, key const & e, value const & v) { - entry en; - en.set_begin_key(b); - en.set_end_key(e); - en.set_val(v); - skip_list_base::insert_first_entry(m, en); - } - - /** - \brief Return true if the given key \c k is in the entry \c e. That is, - k \in [e.begin_key(), e.end_key()]. - */ - bool contains(entry const & e, key const & k) const { return this->leq(e.begin_key(), k) && this->leq(k, e.end_key()); } - - /** - \brief Return true if the given key \c k is in the entry \c e. That is, - k \in [e.begin_key(), e.end_key()]. If that is the case, then store e.value() in v. - Otherwise, return false. - */ - bool contains(entry const & e, key const & k, value & v) const { - if (contains(e, k)) { - v = e.val(); - return true; - } - else { - return false; - } - } - - /** - \brief Search for a key in a bucket starting at position s_idx using binary search. - - Return true if k was found in the bucket and store the index of - the entry containing \c k in \c idx. - - Otherwise, return false and \c idx will contain the index - s.t. - (idx == s_idx || entry[idx-1].end_key() < k) && (idx == bt->size() || k < entry[idx].begin_key()) - */ - bool find_core(bucket const * bt, unsigned s_idx, key const & k, unsigned & idx) const { - if (s_idx >= bt->size()) { - idx = s_idx; - return false; - } - int low = s_idx; - int high = bt->size() - 1; - for (;;) { - int mid = low + ((high - low) / 2); - entry const & mid_entry = bt->get(mid); - if (this->gt(k, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - idx = static_cast(mid) + 1; - SASSERT(idx >= s_idx); - SASSERT(idx == s_idx || lt(bt->get(idx - 1).end_key(), k)); - SASSERT(idx == bt->size() || lt(k, bt->get(idx).begin_key())); - return false; - } - - } - else if (lt(k, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - idx = static_cast(mid); - SASSERT(idx >= s_idx); - SASSERT(idx == s_idx || lt(bt->get(idx - 1).end_key(), k)); - SASSERT(idx == bt->size() || lt(k, bt->get(idx).begin_key())); - return false; - } - } - else { - SASSERT(contains(mid_entry, k)); - SASSERT(mid >= 0); - idx = static_cast(mid); - SASSERT(idx >= s_idx); - return true; - } - } - } - - /** - \brief Search for a key in a bucket using binary search. - - Return true if k was found in the bucket and store the index of - the entry containing \c k in \c idx. - - Otherwise, return false and \c idx will contain the index - s.t. - (idx == 0 || entry[idx-1].end_key() < k) && (idx == bt->size() || k < entry[idx].begin_key() - */ - bool find_core(bucket const * bt, key const & k, unsigned & idx) const { - bool r = find_core(bt, 0, k, idx); - SASSERT(!r || contains(bt->get(idx), k)); - SASSERT( r || idx == 0 || lt(bt->get(idx - 1).end_key(), k)); - SASSERT( r || idx == bt->size() || lt(k, bt->get(idx).begin_key())); - return r; - } - - /** - \brief Search for the given key in the interval skip list. - Return true if the key is stored in the list, and store the location of the entry that contains the k in the - pair (bt, idx). The location should be read as the key is in the entry idx of the bucket bt. - Otherwise, returns false and (bt, idx) contains: - Case 1: bt != 0 && 0 < idx < bt->size() && bt->get(idx-1).end_key() < k && k < bt->get(idx).begin_key() - Case 2: bt != 0 && idx == 0 && (pred_bucket(bt) == m_header || last_key(pred_bucket(bt)) < k) && k < first_key(bt) - Case 3: bt == 0 && idx == 0 && k is greater than all keys in the list. - bt != m_header - - Even when find_core returns false, the pair (bt, idx) can be used to create an iterator object - to traverse keys >= k. - */ - template - bool find_core(key const & k, bucket * & bt, unsigned & idx, bucket * pred_vect[]) const { - bucket * curr = this->m_header; - unsigned i = this->m_header->level(); - bucket * next; - while (i > 0) { - i--; - for (;;) { - next = curr->get_next(i); - if (next != 0 && lt(first_key(next), k)) - curr = next; - else - break; - } - if (UpdatePredVect) - pred_vect[i] = curr; - } - - SASSERT(next == curr->get_next(0)); - - // the search_key must be in the current bucket, or in the first entry of the next bucket (if the next bucket is not 0). - SASSERT(curr->empty() || lt(first_key(curr), k)); - SASSERT(next == 0 || this->geq(first_key(next), k)); - DEBUG_CODE({ - if (UpdatePredVect && next != 0) - for (unsigned i = 0; i < next->level(); i++) - SASSERT(pred_vect[i]->get_next(i) == next); - }); - - if (next != 0 && contains(next->first_entry(), k)) { - bt = next; - idx = 0; - return true; - } - - bool r = find_core(curr, k, idx); - if (idx == curr->size()) { - SASSERT(!r); - bt = next; - idx = 0; - } - else { - SASSERT(idx < curr->size()); - bt = curr; - } - SASSERT(bt != this->m_header); - SASSERT((bt == 0 && idx == 0) || (bt != 0 && idx < bt->size())); - SASSERT(!r || contains(bt->get(idx), k)); - SASSERT(r || - // Case 1 - (bt != 0 && 0 < idx && idx < bt->size() && lt(bt->get(idx-1).end_key(), k) && lt(k, bt->get(idx).begin_key())) || - // Case 2 - (bt != 0 && idx == 0 && (this->pred_bucket(bt) == this->m_header || lt(last_key(this->pred_bucket(bt)), k)) && lt(k, first_key(bt))) || - // Case 3 - (bt == 0 && idx == 0) // k is greater than all keys in the list. - ); - return r; - } - - /** - \brief Return true if the two entries (that satisfy lt(e1, e2)) can be merged. - */ - bool can_be_merged(entry const & e1, entry const & e2) const { - return this->val_eq(e1.val(), e2.val()) && this->eq(this->succ(e1.end_key()), e2.begin_key()); - } - - /** - \brief Try to merge the last entry with bt with the first entry of its successor. - - \remark pred_vect contains the predecessors of the successor of bt. - */ - void merge_first_of_succ_if_possible(manager & m, bucket * bt, bucket * pred_vect[]) { - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); - bucket * next_bucket = bt->get_next(0); - if (next_bucket != 0) { - entry & curr_entry = bt->last_entry(); - entry & next_entry = next_bucket->get(0); - if (can_be_merged(curr_entry, next_entry)) { - curr_entry.set_end_key(next_entry.end_key()); - this->del_entry(m, next_bucket, 0); // del_entry invokes dec_ref_eh - if (next_bucket->empty()) - this->del_bucket(m, next_bucket, pred_vect); - } - } - } - - /** - \brief Try to merge the entry at position idx with the next entry if possible. - */ - void merge_next_if_possible(manager & m, bucket * bt, unsigned idx, bucket * pred_vect[]) { - SASSERT(!bt->empty()); - if (idx + 1 == bt->size()) { - // it is the last element - merge_first_of_succ_if_possible(m, bt, pred_vect); - } - else { - entry & curr_entry = bt->get(idx); - entry & next_entry = bt->get(idx+1); - if (can_be_merged(curr_entry, next_entry)) { - curr_entry.set_end_key(next_entry.end_key()); - this->del_entry(m, bt, idx+1); // del_entry invokes dec_ref_eh - } - } - } - - /** - \brief Try to merge the entry at position idx with the previous entry if possible. - - \remark This method assumes that idx > 0. - */ - void merge_prev_if_possible(manager & m, bucket * bt, unsigned idx) { - SASSERT(idx > 0); - entry & curr_entry = bt->get(idx); - entry & prev_entry = bt->get(idx-1); - if (can_be_merged(prev_entry, curr_entry)) { - prev_entry.set_end_key(curr_entry.end_key()); - this->del_entry(m, bt, idx); // del_entry invokes dec_ref_eh - } - } - - /** - \brief Delete entries starting at indices [s_idx, e_idx), assuming e_idx < bt->size() - - \remark The pre-condition guarantees that the bucket will not be empty after the entries - are deleted. - - \remark If RECYCLE_ENTRY is true, then method will try to recycle position s_idx if it is deleted, and will return true. - Position s_idx will be an empty slot in this case. - */ - template - bool del_entries(manager & m, bucket * bt, unsigned s_idx, unsigned e_idx) { - bool result = false; - if (RECYCLE_ENTRY && s_idx + 1 < e_idx) { - // The position s_idx will be recycled, but the reference to the value stored there - // will be lost. - this->dec_ref(m, bt->get(s_idx).val()); - s_idx++; - result = true; - } - TRACE("del_entries_upto_bug", this->display(tout, bt); tout << "[" << s_idx << ", " << e_idx << ")\n";); - SASSERT(e_idx >= s_idx); - SASSERT(e_idx < bt->size()); - // move entries - unsigned num_removed = e_idx - s_idx; - entry * dest_it = bt->get_entries() + s_idx; - entry * source_it = bt->get_entries() + e_idx; - entry * source_end = bt->get_entries() + bt->size(); - if (Traits::ref_count) { - // invoke dec_ref_eh for entries between dest_it and source_it, since they are being removed - entry * it = dest_it; - for (; it < source_it; ++it) { - this->dec_ref(m, it->val()); - } - } - for (; source_it < source_end; ++dest_it, ++source_it) { - *dest_it = *source_it; - } - // update size - bt->shrink(num_removed); - SASSERT(!bt->empty()); - TRACE("del_entries_upto_bug", this->display(tout, bt);); - return result; - } - - /** - \brief Delete all keys (starting at position s_idx) in the given bucket that are <= k. - The method assumes that k < bt->last_key(). - This condition guarantees that the bucket will not be empty after removing the keys. - - - \remark If RECYCLE_ENTRY is true, then method will try to recycle position s_idx if it is deleted, and will return true. - Position s_idx will be an empty slot in this case. - */ - template - bool del_last_entries_upto(manager & m, bucket * bt, unsigned s_idx, key const & k) { - SASSERT(s_idx < bt->size()); - SASSERT(lt(k, last_key(bt))); - int low = s_idx; - int high = bt->size() - 1; - SASSERT(low <= high); - for (;;) { - int mid = low + ((high - low) / 2); - SASSERT(mid < static_cast(bt->size())); - entry & mid_entry = bt->get(mid); - if (this->gt(k, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - // mid entry must be deleted since k > mid_entry.end_key(). - TRACE("del_entries_upto_bug", tout << "exit 1) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - SASSERT(mid + 1 < static_cast(bt->size())); // Reason: method pre-condition: lt(k, last_key(bt)) - return del_entries(m, bt, s_idx, mid + 1); - } - } - else if (lt(k, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - // mid entry must not be deleted since k < mid_entry.begin_key(). - TRACE("del_entries_upto_bug", tout << "exit 2) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - SASSERT(mid < static_cast(bt->size())); // Reason: loop invariant - return del_entries(m, bt, s_idx, mid); - } - } - else { - SASSERT(contains(mid_entry, k)); - if (lt(k, mid_entry.end_key())) { - TRACE("del_entries_upto_bug", tout << "exit 3) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - mid_entry.set_begin_key(this->succ(k)); - SASSERT(mid < static_cast(bt->size())); // Reason: loop invariant - return del_entries(m, bt, s_idx, mid); - } - else { - // mid_entry should also be removed - TRACE("del_entries_upto_bug", tout << "exit 4) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - SASSERT(mid + 1 < static_cast(bt->size())); // Reason: method pre-condition: lt(k, last_key(bt)) - return del_entries(m, bt, s_idx, mid + 1); - } - } - } - } - - /** - \brief Keep deleting keys <= k in bt and its successors. - */ - void del_entries_upto_loop(manager & m, bucket * bt, key const & k, bucket * pred_vect []) { - SASSERT(this->check_pred_vect(bt, pred_vect)); - while (bt != 0) { - key const & bt_last_key = last_key(bt); - if (lt(k, bt_last_key)) { - del_last_entries_upto(m, bt, 0, k); - return; - } - else if (this->eq(k, bt_last_key)) { - this->del_bucket(m, bt, pred_vect); - return; - } - else { - SASSERT(this->gt(k, bt_last_key)); - bucket * next = bt->get_next(0); - this->del_bucket(m, bt, pred_vect); - bt = next; - // continue deleting... - } - } - } - - /** - \brief Delete entries starting at position 0 such that keys are <= k. - - If INSERT == true, then try to save/recycle an entry. Return true, if - managed to recycle the entry. - - The bucket bt may be deleted when INSERT==false and k >= last_key(bt). - - - pred_vect must contain the predecessors of bt. - - - next_pred_vect is an uninitialized predecessor vector. It may be initialized - when INSERT == true. If needed it is initialized using - update_predecessor_vector(pred_vect, bt, next_pred_vect); - */ - template - bool del_entries_upto(manager & m, bucket * bt, key const & k, bucket * pred_vect[], bucket * next_pred_vect[]) { - SASSERT(this->check_pred_vect(bt, pred_vect)); // pred_vect contains the predecessors of bt. - if (lt(k, first_key(bt))) { - // nothing to be done... - return false; // didn't manage to recycle entry. - } - - key const & bt_last_key = last_key(bt); - TRACE("del_entries_upto_bug", tout << "bt_last_key: " << bt_last_key << "\n";); - if (this->lt(k, bt_last_key)) { - return del_last_entries_upto(m, bt, 0, k); - } - else { - if (INSERT) { - // Invoke DEC-REF for all entries in bt - this->dec_ref(m, bt); - // REMARK: the slot 0 will be reused, but the element there is gone. - bt->set_size(1); - if (this->gt(k, bt_last_key)) { - bucket * next = bt->get_next(0); - if (next != 0) { - this->update_predecessor_vector(pred_vect, bt, next_pred_vect); - del_entries_upto_loop(m, next, k, next_pred_vect); - } - } - return true; // recycled entry. - } - else { - bucket * next = bt->get_next(0); - this->del_bucket(m, bt, pred_vect); // it will invoke dec_ref_eh for all values in bt. - // pred_vect does not need to be updated since it contains the predecessors of - // bt, since bt was deleted they are now the predecessors of its successor. - if (next != 0) { - del_entries_upto_loop(m, next, k, pred_vect); - } - return false; // don't care in this case, since it is not an insertion. - } - } - } - - /** - \brief Delete entries starting at position s_idx (> 0) such that keys are <= k. - The bucket bt cannot be deleted since s_idx > 0. - - If INSERT == true, then try to save/recycle an entry. Return true, if - managed to recycle the entry. - - - pred_vect must contain the predecessors of bt->get_next(0). - */ - template - bool del_entries_upto(manager & m, bucket * bt, unsigned s_idx, key const & k, bucket * pred_vect[]) { - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); // pred_vect contains the predecessors of the successor of bt. - SASSERT(s_idx > 0); - TRACE("del_entries_upto_bug", - tout << "INSERT: " << INSERT << "\n"; - tout << "del_entries_upto, s_idx: " << s_idx << ", k: " << k << "\n"; - this->display(tout, bt); - tout << "\n"; - this->display_predecessor_vector(tout, pred_vect);); - - if (s_idx >= bt->size()) { - // nothing to do in bt, moving to successors... - del_entries_upto_loop(m, bt->get_next(0), k, pred_vect); - return false; // didn't manage to recycle an entry - } - - if (lt(k, bt->get(s_idx).begin_key())) { - // nothing to be done... - return false; // didn't manage to recycle an entry - } - - key const & bt_last_key = last_key(bt); - TRACE("del_entries_upto_bug", tout << "bt_last_key: " << bt_last_key << "\n";); - if (lt(k, bt_last_key)) { - return del_last_entries_upto(m, bt, s_idx, k); - } - else { - if (this->gt(k, bt_last_key)) { - del_entries_upto_loop(m, bt->get_next(0), k, pred_vect); - } - if (Traits::ref_count) { - // Invoke dec_ref_eh for all values in [s_idx, bt->size()) - unsigned sz = bt->size(); - for (unsigned i = s_idx; i < sz; i++) - this->dec_ref(m, bt->get(i).val()); - } - if (INSERT) { - SASSERT(s_idx < bt->size()); - bt->set_size(s_idx + 1); - return true; // recycled an entry - - } - else { - bt->set_size(s_idx); - return false; // don't care. it is not an insertion. - } - } - } - - /** - \brief Insert entry [b,e]->v in the beginning of the bucket bt. - */ - void insert_begin(manager & m, bucket * bt, key const & b, key const & e, value const & v, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "insert_begin: [" << b << ", " << e << "] -> " << v << "\n"; this->display(tout, bt);); - SASSERT(this->check_pred_vect(bt, pred_vect)); - SASSERT(!bt->empty()); - SASSERT(bt->size() <= bt->capacity()); - SASSERT(this->leq(b, first_key(bt))); - bucket * next_pred_vect[Traits::max_level]; - next_pred_vect[0] = 0; - - this->inc_ref(m, v); - - // Delete entries that will be overlapped by new entry. - // Try to reuse a slot that was deleted... - bool recycled = del_entries_upto(m, bt, e, pred_vect, next_pred_vect); - if (recycled) { - set_entry(bt, 0, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - TRACE("interval_skip_list_bug", this->display_physical(tout);); - if (next_pred_vect[0] != 0) { - // the vector next_pred_vect was initialized by del_entries_upto. - merge_next_if_possible(m, bt, 0, next_pred_vect); - } - else { - this->update_predecessor_vector(pred_vect, bt); - merge_next_if_possible(m, bt, 0, pred_vect); - } - return; - } - // check if can merge with first entry in the bucket. - entry & fe = bt->first_entry(); - if (this->val_eq(fe.val(), v) && this->eq(fe.begin_key(), this->succ(e))) { - // can merge - fe.set_begin_key(b); - // A new reference to v was not created. So, we must invoke dec_ref_eh since we increased the counter above. - this->dec_ref(m, v); - return; - } - // Is there space for the new entry? - if (bt->size() == bt->capacity()) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - } - else { - // there is no space - this->splice(m, bt, pred_vect); - } - } - this->open_space(bt, 0); - set_entry(bt, 0, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - SASSERT(!can_be_merged(bt->get(0), bt->get(1))); - } - - /** - \brief Insert the entry [b, e]->v at position idx. - */ - void insert_at(manager & m, bucket * bt, unsigned idx, key const & b, key const & e, value const & v, bucket * pred_vect[]) { - SASSERT(idx > 0); - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); - - this->inc_ref(m, v); - TRACE("insert_at_bug", tout << "before del_entries_upto:\n"; this->display_physical(tout);); - - bool recycled = del_entries_upto(m, bt, idx, e, pred_vect); - - TRACE("insert_at_bug", tout << "after del_entries_upto:\n"; this->display_physical(tout);); - - if (recycled) { - set_entry(bt, idx, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - merge_next_if_possible(m, bt, idx, pred_vect); - merge_prev_if_possible(m, bt, idx); - TRACE("insert_at_bug", tout << "using recycled:\n"; this->display_physical(tout);); - return; - } - - // Is there space for the new entry? - if (bt->size() == bt->capacity()) { - // there is no space - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - // there is no need to update pred_vect, since the list contains only one bucket. - } - else { - this->splice(m, bt, pred_vect); - bucket * new_next = bt->get_next(0); - SASSERT(bt->size() == bt->capacity()/2); - if (idx == bt->capacity()/2) { - entry & bt_last_entry = bt->last_entry(); - if (this->val_eq(bt_last_entry.val(), v) && this->eq(bt_last_entry.end_key(), this->pred(b))) { - // merged with the last key of bt - bt_last_entry.set_end_key(e); - // A new reference to v was not created. So, we must invoke dec_ref_eh since we increased the counter above. - this->dec_ref(m, v); - return; - } - entry & new_next_first_entry = new_next->first_entry(); - if (this->val_eq(new_next_first_entry.val(), v) && this->eq(new_next_first_entry.begin_key(), this->succ(e))) { - // merged with the first key of new_next - new_next_first_entry.set_begin_key(b); - // A new reference to v was not created. So, we must invoke dec_ref_eh since we increased the counter above. - this->dec_ref(m, v); - return; - } - // insert in the end of bt. - bt->set_size(bt->capacity()/2 + 1); - set_entry(bt, bt->capacity()/2, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - return; - } - else if (idx > bt->capacity()/2) { - idx -= bt->capacity()/2; - SASSERT(idx > 0); - bt = new_next; - this->update_predecessor_vector(pred_vect, bt); - } - } - } - SASSERT(idx > 0); - this->open_space(bt, idx); - set_entry(bt, idx, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - merge_next_if_possible(m, bt, idx, pred_vect); - merge_prev_if_possible(m, bt, idx); - TRACE("insert_at_bug", tout << "using open-space:\n"; this->display_physical(tout);); - } - - /** - \brief Insert the entry [b,e]->v into the bucket bt. - - pred_vect contains the predecessors of the successor of bt (i.e., bt->get_next(0)) - */ - void insert_inside(manager & m, bucket * bt, key const & b, key const & e, value const & v, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "insert_inside: [" << b << ", " << e << "] -> " << v << "\n";); - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); - SASSERT(!bt->empty()); - SASSERT(bt->size() <= bt->capacity()); - // perform binary search to find position to insert [b, e]->v - int low = 0; - int high = bt->size() - 1; - for (;;) { - int mid = low + ((high - low) / 2); - entry & mid_entry = bt->get(mid); - if (this->gt(b, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - // insert after mid_entry since b > mid_entry.end_key(). - insert_at(m, bt, mid+1, b, e, v, pred_vect); - return; - } - } - else if (lt(b, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - // insert before mid_entry since b < mid_entry.begin_key(). - SASSERT(mid > 0); // Reason: insert_begin would have been called instead. - insert_at(m, bt, mid, b, e, v, pred_vect); - return; - } - } - else { - SASSERT(contains(mid_entry, b)); - TRACE("insert_inside_bug", tout << "insert_inside:\n"; this->display(tout, bt);); - if (this->val_eq(mid_entry.val(), v)) { - if (this->gt(e, mid_entry.end_key())) { - // No need to create space. - // We did not create a new reference to v. - mid_entry.set_end_key(e); - del_entries_upto(m, bt, mid+1, e, pred_vect); - merge_next_if_possible(m, bt, mid, pred_vect); - return; - } - } - else { - if (this->gt(b, mid_entry.begin_key())) { - if (this->lt(e, mid_entry.end_key())) { - // New interval is the middle of existing interval - - // We must INVOKE add_ref_eh for mid_entry.val() and v. - this->inc_ref(m, v); - this->inc_ref(m, mid_entry.val()); // mid_entry was split in two. - - // we need two new entries. - if (bt->size() >= bt->capacity() - 1) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - } - else { - this->splice(m, bt, pred_vect); - int new_sz = bt->size(); - bucket * new_next = bt->get_next(0); - if (mid >= new_sz) { - mid -= new_sz; - SASSERT(mid >= 0); - bt = new_next; - } - } - } - this->open_2spaces(bt, mid); - entry & mid1_entry = bt->get(mid); - entry & new_entry = bt->get(mid+1); - entry & mid2_entry = bt->get(mid+2); - mid2_entry = mid1_entry; - mid1_entry.set_end_key(this->pred(b)); - new_entry.set_begin_key(b); - new_entry.set_end_key(e); - new_entry.set_val(v); - mid2_entry.set_begin_key(this->succ(e)); - } - else { - mid_entry.set_end_key(this->pred(b)); - insert_at(m, bt, mid+1, b, e, v, pred_vect); - } - } - else { - SASSERT(this->eq(b, mid_entry.begin_key())); - SASSERT(mid > 0); // Reason: insert_begin would have been called instead. - insert_at(m, bt, mid, b, e, v, pred_vect); - } - } - return; - } - } - } - - /** - \brief Remove [b,e]->v from the beginning of the bucket bt. - */ - void remove_begin(manager & m, bucket * bt, key const & b, key const & e, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "remove_begin: [" << b << ", " << e << "]\n";); - SASSERT(!bt->empty()); - SASSERT(pred_vect[0]->get_next(0) == bt); - del_entries_upto(m, bt, e, pred_vect, 0); - } - - /** - \brief Remove [b,e]->v from the bucket bt. - */ - void remove_inside(manager & m, bucket * bt, key const & b, key const & e, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "remove_inside: [" << b << ", " << e << "]\n";); - // perform binary search to find position to insert [b, e]->v - int low = 0; - int high = bt->size() - 1; - for (;;) { - int mid = low + ((high - low) / 2); - entry & mid_entry = bt->get(mid); - if (this->gt(b, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - // insert after mid_entry since b > mid_entry.end_key(). - del_entries_upto(m, bt, mid+1, e, pred_vect); - return; - } - } - else if (this->lt(b, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - // insert before mid_entry since b < mid_entry.begin_key(). - SASSERT(mid > 0); // Reason: remove_begin would have been called instead. - del_entries_upto(m, bt, mid, e, pred_vect); - return; - } - } - else { - SASSERT(contains(mid_entry, b)); - if (this->gt(b, mid_entry.begin_key())) { - if (this->lt(e, mid_entry.end_key())) { - // The removed interval is inside of an existing interval. - - // mid_entry will be split in two. So, we must invoke add_ref_eh for mid_entry.val() - this->inc_ref(m, mid_entry.val()); - - // We need to break mid_entry in two parts. - if (bt->size() == bt->capacity()) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - SASSERT(bt->size() < bt->capacity()); - } - else { - this->splice(m, bt, pred_vect); - if (mid >= static_cast(bt->size())) { - // mid_entry moved to new (successor) bucket - mid -= bt->size(); - bt = bt->get_next(0); - } - } - } - this->open_space(bt, mid); - entry & mid1_entry = bt->get(mid); - entry & mid2_entry = bt->get(mid+1); - mid1_entry.set_end_key(this->pred(b)); - mid2_entry.set_begin_key(this->succ(e)); - } - else { - mid_entry.set_end_key(this->pred(b)); - del_entries_upto(m, bt, mid+1, e, pred_vect); - } - } - else { - SASSERT(this->eq(b, mid_entry.begin_key())); - SASSERT(mid > 0); // Reason: remove_begin would have been called instead. - del_entries_upto(m, bt, mid, e, pred_vect); - } - return; - } - } - } - -public: - interval_skip_list() { - } - - interval_skip_list(manager & m):skip_list_base(m) { - } - - ~interval_skip_list() { - } - - /** - \brief Copy the elements of other. - This method assumes that the *this* skip-list is empty. - */ - void copy(manager & m, interval_skip_list const & other) { - SASSERT(this->empty()); - other.clone_core(m, this); - } - - /** - \brief Return the smallest key stored in the interval skip list. - */ - key const & smallest() const { - SASSERT(!this->empty()); - return this->first_bucket()->get(0).begin_key(); - } - - /** - \brief Search for the given key in the interval skip list. - Return true if the key is stored in the list, and store the associated value in \c v. - */ - bool contains(key const & k, value & v) const { - bucket * bt; - unsigned idx; - if (find_core(k, bt, idx, 0)) { - v = bt->get(idx).val(); - return true; - } - return false; - } - - /** - \brief Alias for #contains. - */ - bool find(key const & k, value & v) const { - return contains(k, v); - } - -private: - /** - \brief Search for a bucket based on the key \c k. - - curr, next and pred_vect are output arguments. - - pred_vect must be an array of size level(). - - Post-conditions: - - pred_vect contains the predecessors of next. - That is, pred_vect[i] is the predecessor of level i. - - next is the successor of curr. - - pred_vect[0] == curr. - - curr == m_header || first_key(curr) < k - - next == 0 || k <= first_key(next) - */ - void find_bucket(key const & k, bucket * & curr, bucket * & next, bucket * pred_vect[]) { - SASSERT(this->level() > 0); - curr = this->m_header; - unsigned i = curr->level(); - SASSERT(i > 0); - while (i > 0) { - i--; - for (;;) { - next = curr->get_next(i); - if (next != 0 && lt(first_key(next), k)) - curr = next; - else - break; - } - pred_vect[i] = curr; - } - - SASSERT(next == curr->get_next(0)); - SASSERT(pred_vect[0] == curr); - DEBUG_CODE({ - if (next != 0) - for (unsigned i = 0; i < next->level(); i++) - SASSERT(pred_vect[i]->get_next(i) == next); - }); - SASSERT(curr == this->m_header || lt(first_key(curr), k)); - SASSERT(next == 0 || this->leq(k, first_key(next))); - } - -public: - - /** - \brief Insert the entries [i -> v] for every i \in [b, e]. - */ - void insert(manager & m, key const & b, key const & e, value const & v) { - SASSERT(this->leq(b, e)); - if (this->empty()) { - insert_first_entry(m, b, e, v); - return; - } - - // find the bucket where the new entries should be stored. - - // pred_vect[i] contains a pointer to the rightmost bucket of - // level i or higher that is to the left of the location of - // the insertion. - bucket * pred_vect[Traits::max_level]; - bucket * curr, * next; - find_bucket(b, curr, next, pred_vect); - - if (curr == this->m_header) { - SASSERT(next != 0); - // entry must be inserted in the first bucket. - SASSERT(this->first_bucket() == next); - insert_begin(m, next, b, e, v, pred_vect); - } - else if (next == 0 || this->gt(first_key(next), b)) { - insert_inside(m, curr, b, e, v, pred_vect); - } - else { - SASSERT(!curr->empty()); - SASSERT(!next->empty()); - SASSERT(next != 0); - SASSERT(this->eq(first_key(next), b)); - // Bucket curr is the predecessor of next. - SASSERT(curr->get_next(0) == next); - - // check if we can merge with last entry of curr - entry & curr_last_entry = curr->last_entry(); - if (this->val_eq(curr_last_entry.val(), v) && this->eq(curr_last_entry.end_key(), this->pred(b))) { - // No new reference to v was create, we don't need to invok inc_ref_eh - curr_last_entry.set_end_key(e); - del_entries_upto(m, next, e, pred_vect, 0); - merge_first_of_succ_if_possible(m, curr, pred_vect); - return; - } - insert_begin(m, next, b, e, v, pred_vect); - } - } - - /** - \brief Insert key [k->v]. - */ - void insert(manager & m, key const & k, value const & v) { - insert(m, k, k, v); - } - - class push_back_proc; - friend class push_back_proc; - - /** - \brief Functor for efficiently inserting elements in the end of the skip list. - - \remark The context becomes invalid if the skip-list is updated by other methods. - */ - class push_back_proc { - friend class interval_skip_list; - manager & m_manager; - interval_skip_list & m_list; - bucket * m_pred_vect[Traits::max_level]; - - bucket * last_bucket() const { return m_pred_vect[0]; } - - public: - push_back_proc(manager & m, interval_skip_list & l): - m_manager(m), - m_list(l) { - // initialize m_pred_vect - unsigned lvl = m_list.level(); - bucket * curr = m_list.m_header; - bucket * next; - unsigned i = lvl; - while (i > 0) { - i--; - for (;;) { - next = curr->get_next(i); - if (next != 0) - curr = next; - else - break; - } - m_pred_vect[i] = curr; - } - SASSERT(next == 0); - } - - interval_skip_list & list() { - return m_list; - } - - bool empty() const { - return m_list.empty(); - } - - key const & last_key() const { - return last_bucket()->last_entry().end_key(); - } - - void operator()(key const & b, key const & e, value const & v) { - SASSERT(m_list.leq(b, e)); - if (m_list.empty()) { - m_list.insert_first_entry(m_manager, b, e, v); - bucket * new_bucket = m_list.first_bucket(); - skip_list_base::update_predecessor_vector(m_pred_vect, new_bucket); - } - else { - bucket * bt = last_bucket(); - entry & et = bt->last_entry(); - SASSERT(m_list.lt(et.end_key(), b)); - // first check if new entry can be merged with the last entry in the list - if (m_list.val_eq(et.val(), v) && m_list.eq(et.end_key(), m_list.pred(b))) { - // can merge - et.set_end_key(e); - return; - } - // insert in the last bucket - unsigned sz = bt->size(); - if (sz >= bt->capacity()) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(m_list.first_bucket() == bt && m_list.first_bucket()->get_next(0) == 0); - m_list.expand_first_bucket(m_manager); - bt = m_list.first_bucket(); - SASSERT(bt->size() < bt->capacity()); - skip_list_base::update_predecessor_vector(m_pred_vect, bt); - sz = bt->size(); - } - else { - // last bucket is full... creating new bucket... - unsigned new_bucket_lvl = m_manager.random_level(Traits::max_level); - bucket * new_bucket = interval_skip_list::mk_bucket(m_manager, new_bucket_lvl); - m_list.update_list_level(m_manager, new_bucket_lvl, m_pred_vect); - for (unsigned i = 0; i < new_bucket_lvl; i++) { - SASSERT(m_pred_vect[i]->get_next(i) == 0); - m_pred_vect[i]->set_next(i, new_bucket); - m_pred_vect[i] = new_bucket; - SASSERT(m_pred_vect[i]->get_next(i) == 0); - } - SASSERT(last_bucket() == new_bucket); - bt = new_bucket; - sz = 0; - } - } - SASSERT(sz < bt->capacity()); - m_list.inc_ref(m_manager, v); - bt->expand(1); - interval_skip_list::set_entry(bt, sz, b, e, v); - } - } - }; - - /** - \brief For each i \in [b, e] remove any entry [i->v] if it is in the list. - */ - void remove(manager & m, key const & b, key const & e) { - SASSERT(this->leq(b, e)); - if (this->empty()) - return; - bucket * pred_vect[Traits::max_level]; - bucket * curr, * next; - - find_bucket(b, curr, next, pred_vect); - - if (curr == this->m_header) { - SASSERT(next != 0); - remove_begin(m, next, b, e, pred_vect); - } - else if (next == 0 || this->gt(first_key(next), b)) { - remove_inside(m, curr, b, e, pred_vect); - } - else { - SASSERT(next != 0); - SASSERT(this->eq(first_key(next), b)); - remove_begin(m, next, b, e, pred_vect); - } - } - - /** - \brief Remove entry [k->v] for some v, if it is in the list. - */ - void remove(manager & m, key const & k) { - remove(m, k, k); - } - - /** - \brief Alias for #remove. - */ - void erase(manager & m, key const & b, key const & e) { - remove(m, b, e); - } - - /** - \brief Alias for #remove. - */ - void erase(manager & m, key const & k) { - remove(m, k, k); - } - - /** - \begin Traverse the list applying the functor f. - The functor must have a method - - bool operator()(key const & b, key const & e, value const & v) - - The method will invoke f(b, e, v) whenever the entries [i -> v] for i \in [b, e] are - in the list. - - If the functor returns false, then the traversal is interrupted. - */ - template - void for_each(Functor & f) const { - SASSERT(this->m_header->empty()); - bucket * curr = this->first_bucket(); - while (curr != 0) { - unsigned sz = curr->size(); - for (unsigned i = 0; i < sz; i++) { - entry const & e = curr->get(i); - if (!f(e.begin_key(), e.end_key(), e.val())) - return; - } - curr = curr->get_next(0); - } - } - - /** - \brief Return the next/successor buffer, but skipping buffers that do not contains keys greater than or equal to k. - */ - bucket * next_bucket(bucket const * bt, key const & k) const { - bucket * curr = bt->get_next(0); // move to successor - if (curr == 0) - return 0; - unsigned i = curr->level(); - unsigned max = i; - bucket * next = 0; - while (i > 0) { - --i; - for (;;) { - next = curr->get_next(i); - if (next != 0 && this->leq(first_key(next), k)) { - TRACE("interval_skip_list", tout << "next_bucket(" << k << "), i: " << i << " skipping #" << this->get_bucket_idx(curr); - tout << ", moving to: #" << this->get_bucket_idx(next) << "\n"; this->display(tout, next);); - curr = next; - if (curr->level() > max) { - max = curr->level(); - i = curr->level(); - TRACE("interval_skip_list", tout << "max: " << max << ", curr->level(): " << curr->level() << ", i: " << i << "\n";); - break; - } - } - else { - break; - } - } - } - SASSERT(i == 0); - SASSERT(curr->get_next(0) == next); - SASSERT(next == 0 || lt(k, first_key(next))); - return curr; - } - - class iterator; - friend class iterator; - - class iterator { - interval_skip_list const * m_list; - bucket const * m_curr; - unsigned m_idx; - public: - iterator():m_list(0), m_curr(0), m_idx(0) {} - iterator(interval_skip_list const * l, bucket const * b = 0, unsigned idx = 0):m_list(l), m_curr(b), m_idx(idx) {} - entry const & operator*() const { return m_curr->get(m_idx); } - entry const * operator->() const { return &(operator*()); } - iterator & operator++() { - SASSERT(m_curr); - m_idx++; - if (m_idx >= m_curr->size()) { - m_idx = 0; - m_curr = m_curr->get_next(0); - } - return *this; - } - iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - - bool at_end() const { - return m_curr == 0; - } - - /** - \brief Move the iterator to the next entry of the form ([b, e] -> v) s.t. - - 1) k in [b, e], or - 2) b > k and for every entry ([b',e']->v') between the old current entry and ([b,e]->v), we - have k > e' - - If such entry does not exist, then the iterator is moved to the end. - That is, at_end() returns true. - */ - void move_to(key const & k) { - SASSERT(m_curr); - SASSERT(m_idx < m_curr->size()); - entry const & curr_entry = m_curr->get(m_idx); - if (m_list->gt(k, curr_entry.end_key())) { - m_list->find_core(m_curr, m_idx+1, k, m_idx); - if (m_idx < m_curr->size()) - return; // found new position - SASSERT(m_idx == m_curr->size()); - m_curr = m_list->next_bucket(m_curr, k); - if (m_curr != 0) { - // search for k in the current buffer. - m_list->find_core(m_curr, k, m_idx); - if (m_idx == m_curr->size()) { - // k is greater than all keys in the list. - m_curr = 0; - m_idx = 0; - } - } - else { - SASSERT(m_curr == 0); - m_idx = 0; - } - } - } - - bool operator==(iterator const & it) const { - SASSERT(m_list == it.m_list); - return m_curr == it.m_curr && m_idx == it.m_idx; - } - - bool operator!=(iterator const & it) const { - SASSERT(m_list == it.m_list); - return m_curr != it.m_curr; - } - - /** - \brief Take the ownership of the current value and reset it to 0. - - \warning This method should be used with extreme care, since it puts the interval skip list - in an inconsistent state that must be restored. This method should be only used by developers - familiar with the interval skip-lists internal invariants. - For users not familiar with internal invariants, they should only use the reset method in the skip list class - after this method is invoked. - - Store in r the current value. - */ - template - void take_curr_ownership(manager & m, ObjRef & r) { - SASSERT(!at_end()); - entry & curr = const_cast(operator*()); // <<< HACK - r = curr.val(); - if (Traits::ref_count) - m.dec_ref_eh(curr.val()); - curr.set_val(0); - } - }; - - iterator begin() const { return iterator(this, this->first_bucket()); } - - iterator end() const { return iterator(this); } - - /** - \brief Return an iterator starting at the first entry that contains k. - If the skip-list does not contain k, then return the "end()" iterator. - */ - iterator find(key const & k) const { - bucket * bt; - unsigned idx; - if (find_core(k, bt, idx, 0)) { - return iterator(this, bt, idx); - } - else { - return end(); - } - } - - iterator find_geq(key const & k) const { - bucket * bt; - unsigned idx; - find_core(k, bt, idx, 0); - return iterator(this, bt, idx); - } - - /** - \brief Return true if the skip lists are equal. - */ - bool is_equal(interval_skip_list const & other) const { - iterator it1 = begin(); - iterator end1 = end(); - iterator it2 = other.begin(); - iterator end2 = other.end(); - for (; it1 != end1 && it2 != end2; it1++, it2++) { - entry const & e1 = *it1; - entry const & e2 = *it2; - if (!this->eq(e1.begin_key(), e2.begin_key())) - return false; - if (!this->eq(e1.end_key(), e2.end_key())) - return false; - if (!this->val_eq(e1.val(), e2.val())) - return false; - } - return true; - } - - /** - \brief Update the values stored in the skip-list by appling the given - functor to them. The functor must provide the operation: - - value operator()(value const & v); - - The functor must be injective. That is - - x != y implies f(x) != f(y) - - If a non-injective functor is used, then the resultant skip-list may - not be in a consistent state. - */ - template - void update_values(manager & m, InjectiveFunction & f) { - if (!this->empty()) { - iterator it = begin(); - iterator it_end = end(); - for (; it != it_end; ++it) { - entry & curr = const_cast(*it); - value const & old_val = curr.val(); - value new_val = f(old_val); - this->inc_ref(m, new_val); - this->dec_ref(m, old_val); - curr.set_val(new_val); - } - SASSERT(check_invariant()); - } - } - - class ext_iterator; - friend class ext_iterator; - - class ext_iterator { - friend class interval_skip_list; - - interval_skip_list * m_list; - bucket * m_curr; - unsigned m_idx; - bucket * m_pred_vect[Traits::max_level]; - - void move_next_bucket() { - m_list->update_predecessor_vector(m_pred_vect, m_curr); - m_idx = 0; - m_curr = m_curr->get_next(0); - } - - public: - ext_iterator():m_list(0), m_curr(0), m_idx(0) {} - - entry const & operator*() const { return m_curr->get(m_idx); } - - entry const * operator->() const { return &(operator*()); } - - ext_iterator & operator++() { - SASSERT(m_curr); - m_idx++; - if (m_idx >= m_curr->size()) - move_next_bucket(); - return *this; - } - - ext_iterator operator++(int) { ext_iterator tmp = *this; ++*this; return tmp; } - - bool at_end() const { - return m_curr == 0; - } - - bool operator==(ext_iterator const & it) const { - return m_curr == it.m_curr && m_idx == it.m_idx; - } - - bool operator!=(ext_iterator const & it) const { - return m_curr != it.m_curr; - } - - void erase(manager & m) { - SASSERT(!at_end()); - SASSERT(m_curr->size() > 0); - if (m_curr->size() > 1) { - m_list->del_entry(m, m_curr, m_idx); - if (m_idx >= m_curr->size()) - move_next_bucket(); - } - else { - SASSERT(m_curr->size() == 1); - bucket * old_curr = m_curr; - m_curr = m_curr->get_next(0); - m_list->del_bucket(m, old_curr, m_pred_vect); - } - } - }; - - void move_begin(ext_iterator & it) { - if (!this->empty()) { - it.m_list = this; - it.m_curr = this->first_bucket(); - it.m_idx = 0; - unsigned lvl = this->level(); - for (unsigned i = 0; i < lvl; i++) - it.m_pred_vect[i] = this->m_header; - } - else { - it.m_curr = 0; - it.m_idx = 0; - } - } - - void move_geq(ext_iterator & it, key const & k) { - it.m_list = this; - find_core(k, it.m_curr, it.m_idx, it.m_pred_vect); - } - -private: - /** - \brief Auxiliary data-structure used to implement the join of two interval_skip_lists. - To implement an efficient join, we want to be able to skip as many entries as possible. - */ - struct join_state { - bucket * m_bucket; - unsigned m_entry_idx; - key m_head; // it it a key in [m_bucket->m_entries[m_entry_idx].begin_key(), m_bucket->m_entries[m_entry_idx].end_key()] - public: - join_state(bucket * bt): - m_bucket(bt), - m_entry_idx(0), - m_head(bt->first_entry().begin_key()) { - } - - bool done() const { - return m_bucket == 0; - } - - key const & head() const { - SASSERT(!done()); - return m_head; - } - - key const & tail() const { - SASSERT(!done()); - SASSERT(m_entry_idx < m_bucket->size()); - return m_bucket->get(m_entry_idx).end_key(); - } - - value const & val() const { - SASSERT(!done()); - return m_bucket->get(m_entry_idx).val(); - } - }; - - /** - \brief Create a join_state auxiliary data-structure for performing a join starting at key k. - */ - join_state mk_join_state(key const & k) const { - return join_state(next_bucket(this->m_header, k)); - } - - /** - \brief Move the join_state towards k. - */ - void move_js(join_state & js, key const & k) const { - SASSERT(!js.done()); - if (this->leq(k, js.tail())) { - // We can't skip the current entry, because k in inside it. - // So, we just update the head. - js.m_head = k; - } - else { - // Moving to the next entry. - js.m_entry_idx++; - if (js.m_entry_idx < js.m_bucket->size()) { - // Update js.m_head with the beginning of the next entry. - js.m_head = js.m_bucket->get(js.m_entry_idx).begin_key(); - } - else { - // We processed all entries in the current bucket. So, set state to js.m_move_next. - js.m_bucket = next_bucket(js.m_bucket, k); - js.m_entry_idx = 0; - if (js.m_bucket != 0) - js.m_head = first_key(js.m_bucket); - } - } - } - -public: - - /** - \brief Join two interval_skip_lists and apply the given functor in the process. - - The functor must have a method - - bool operator()(key const & b, key const & e, value const & v1, value const & v2) - - The method will invoke f(b, e, v1, v2) whenever forall i \in [b, e] entries [i -> v1] are in the *this* list, and [i->v2] in the *other* list. - */ - template - void join(interval_skip_list const & other, Functor & f) { - if (this->empty() || other.empty()) - return; - key const & f1 = smallest(); - key const & f2 = other.smallest(); - key const & smallest_key = leq(f1, f2) ? f1 : f2; - join_state s1 = mk_join_state(smallest_key); - join_state s2 = other.mk_join_state(smallest_key); - while (!s1.done() && !s2.done()) { - key const & h1 = s1.head(); - key const & h2 = s2.head(); - if (eq(h1, h2)) { - key const & t1 = s1.tail(); - key const & t2 = s2.tail(); - key const & t = leq(t1, t2) ? t1 : t2; - f(h1, t, s1.val(), s2.val()); - key next_key = succ(t); - move_js(s1, next_key); - move_js(s2, next_key); - } - else if (lt(h1, h2)) { - move_js(s1, h2); - } - else { - SASSERT(lt(h2, h1)); - move_js(s2, h1); - } - } - } - -#ifdef Z3DEBUG -private: - bool check_invariant(entry const & e) const { - SASSERT(this->leq(e.begin_key(), e.end_key())); - return true; - } - - /** - \brief Return true if the last key of \c e1 is less than the first key of \c e2. - */ - bool lt(entry const & e1, entry const & e2) const { - return lt(e1.end_key(), e2.begin_key()); - } - - bool check_invariant(bucket const * bt) const { - SASSERT(bt->size() <= bt->capacity()); - SASSERT(bt == this->m_header || !bt->empty()); - for (unsigned i = 0; i < bt->size(); i++) { - entry const & curr = bt->get(i); - check_invariant(curr); - if (i > 0) { - entry const & prev = bt->get(i-1); - CTRACE("interval_skip_list", !lt(prev, curr), this->display_physical(tout);); - SASSERT(lt(prev, curr)); - CTRACE("interval_skip_list", can_be_merged(prev, curr), this->display_physical(tout);); - SASSERT(!can_be_merged(prev, curr)); - } - } - return true; - } - -public: - bool check_invariant() const { - SASSERT(this->m_header->empty()); - for (unsigned i = 0; i < this->m_header->level(); i++) { - // traverse buckets using get_next(i) pointers - bucket const * curr = this->m_header->get_next(i); - while (curr != 0) { - SASSERT(!curr->empty()); // only the header is empty. - bucket const * next = curr->get_next(i); - if (next != 0) { - SASSERT(next->level() >= i); - SASSERT(i == 0 || this->is_reachable_at_i(curr, next, i-1)); - SASSERT(!next->empty()); - entry const & last_of_curr = curr->last_entry(); - entry const & first_of_next = next->first_entry(); - CTRACE("interval_skip_list", !lt(last_of_curr, first_of_next), - this->display_physical(tout); - tout << "\ncurr:\n"; - this->display(tout, curr); - tout << "\nnext:\n"; - this->display(tout, next);); - SASSERT(lt(last_of_curr, first_of_next)); - CTRACE("interval_skip_list", can_be_merged(last_of_curr, first_of_next), - this->display_physical(tout); - tout << "\ncurr:\n"; - this->display(tout, curr); - tout << "\nnext:\n"; - this->display(tout, next);); - SASSERT(!can_be_merged(last_of_curr, first_of_next)); - } - curr = next; - } - } - bucket const * curr = this->m_header; - while (curr != 0) { - check_invariant(curr); - curr = curr->get_next(0); - } - return true; - } -#endif - - static void display_size_info(std::ostream & out) { - skip_list_base::display_size_info_core(out, sizeof(interval_skip_list)); - } - - /** - \brief Return the amount of memory consumed by the list. - */ - unsigned memory() const { - return this->memory_core(sizeof(interval_skip_list)); - } - -}; - -/** - \brief Traits for instantiating a mapping from unsigned to Value using the interval_skip_list template. -*/ -template, - unsigned MaxCapacity=32, - unsigned MaxLevel=32, - bool RefCount=false, - typename Manager=sl_manager_base > -struct unsigned_interval_skip_list_traits : private EqProc { - typedef default_islist_entry entry; - typedef Manager manager; - typedef typename entry::key key; - typedef typename entry::value value; - static const unsigned max_capacity = MaxCapacity; - static const unsigned initial_capacity = 2; - static const unsigned max_level = MaxLevel; - static const bool ref_count = RefCount; - - bool lt(key const & k1, key const & k2) const { return k1 < k2; } - bool eq(key const & k1, key const & k2) const { return k1 == k2; } - key succ(key const & k) const { return k + 1; } - key pred(key const & k) const { SASSERT(k > 0); return k - 1; } - bool val_eq(value const & v1, value const & v2) const { return EqProc::operator()(v1, v2); } -}; - -/** - \brief Traits for instantiating a set of unsigned values using the interval_skip_list template. -*/ -template > -struct unsigned_set_interval_skip_list_traits { - typedef default_set_islist_entry entry; - typedef Manager manager; - typedef typename entry::key key; - typedef typename entry::value value; - static const unsigned max_capacity = MaxCapacity; - static const unsigned initial_capacity = 2; - static const unsigned max_level = MaxLevel; - static const bool ref_count = false; - - bool lt(key const & k1, key const & k2) const { return k1 < k2; } - bool eq(key const & k1, key const & k2) const { return k1 == k2; } - key succ(key const & k) const { return k + 1; } - key pred(key const & k) const { SASSERT(k > 0); return k - 1; } - bool val_eq(value const & v1, value const & v2) const { return true; } -}; - -/** - \brief Generic adapater for generating a set-like API on top of the map API -*/ -template -class map2set_adapter : public Map { - typedef typename Map::manager manager; - typedef typename Map::key key; - typedef typename Map::value value; - - template - struct for_each_functor_adapter : public Functor { - for_each_functor_adapter(Functor const & f):Functor(f) { - } - bool operator()(key const & b, key const & e, value const & v) { - return Functor::operator()(b, e); - } - }; - -public: - map2set_adapter(manager & m): - Map(m) { - SASSERT(this->m_header != 0); - } - - void insert(manager & m, key const & b, key const & e) { - value _dummy; - Map::insert(m, b, e, _dummy); - } - - void insert(manager & m, key const & k) { - value _dummy; - Map::insert(m, k, k, _dummy); - } - - bool contains(key const & k) const { - value _dummy; - return Map::contains(k); - } - - bool find(key const & k) const { - return contains(k); - } - - /** - \begin Traverse the set applying the functor f. - The functor must have a method - - bool operator()(key const & b, key const & e) - - The method will invoke f(b, e) whenever the interval [b, e] are - in the set. - - If the functor returns false, then the traversal is interrupted. - */ - template - void for_each(Functor & f) const { - for_each_functor_adapter F(f); - Map::for_each(F); - } -}; - - -/** - \brief A set of unsigned values using interval_skip_list. -*/ -template > -class unsigned_isp_set : public map2set_adapter > > { -public: - unsigned_isp_set(Manager & m): - map2set_adapter > >(m) { - } -}; - -#endif /* _INTERVAL_SKIP_LIST_H_ */ - - diff --git a/src/muz_qe/pdr_quantifiers.cpp b/src/muz_qe/pdr_quantifiers.cpp deleted file mode 100644 index 5cc97893a..000000000 --- a/src/muz_qe/pdr_quantifiers.cpp +++ /dev/null @@ -1,660 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - pdr_quantifiers.cpp - -Abstract: - - Module for handling quantifiers in rule bodies. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-05-19. - -Revision History: - ---*/ - -#include "pdr_quantifiers.h" -#include "pdr_context.h" -#include "qe.h" -#include "var_subst.h" -#include "dl_rule_set.h" -#include "ast_smt2_pp.h" -#include "model_smt2_pp.h" -#include "ast_smt_pp.h" -#include "expr_abstract.h" -#include "dl_mk_extract_quantifiers.h" -#include "qe_lite.h" -#include "well_sorted.h" -#include "expr_safe_replace.h" - - -namespace pdr { - - /** - \brief model check a potential model against quantifiers in bodies of rules. - - \return true if the model rooted in 'root' is checks with the quantifiers, otherwise - 'false' and a set of instantiations that contradict the current model. - */ - - static void get_nodes(model_node& root, ptr_vector& nodes) { - ptr_vector todo; - todo.push_back(&root); - while (!todo.empty()) { - model_node* n = todo.back(); - todo.pop_back(); - nodes.push_back(n); - todo.append(n->children().size(), n->children().c_ptr()); - } - } - - quantifier_model_checker::~quantifier_model_checker() { - obj_map::iterator it = m_reachable.begin(), end = m_reachable.end(); - for (; it != end; ++it) { - m.dec_ref(it->m_value); - } - } - - void quantifier_model_checker::generalize_binding(expr_ref_vector const& binding, vector& bindings) { - expr_ref_vector new_binding(m); - generalize_binding(binding, 0, new_binding, bindings); - } - - void quantifier_model_checker::generalize_binding( - expr_ref_vector const& binding, unsigned idx, - expr_ref_vector& new_binding, vector& bindings) { - if (idx == binding.size()) { - bool non_zero = false; - for (unsigned i = 0; i < binding.size(); ++i) { - if (new_binding[i].get()) { - non_zero = true; - } - else { - new_binding[i] = binding[i]; - } - } - if (non_zero) { - TRACE("pdr", - for (unsigned i = 0; i < new_binding.size(); ++i) { - tout << mk_pp(new_binding[i].get(), m) << " "; - } - tout << "\n";); - bindings.push_back(new_binding); - } - return; - } - model_node& node = *m_current_node; - expr_ref_vector ands(m); - expr* e1, *e2; - datalog::flatten_and(node.state(), ands); - new_binding.push_back(0); - generalize_binding(binding, idx + 1, new_binding, bindings); - for (unsigned i = 0; i < ands.size(); ++i) { - if (m.is_eq(ands[i].get(), e1, e2)) { - if (e2 == binding[idx]) { - new_binding[new_binding.size()-1] = e1; - generalize_binding(binding, idx + 1, new_binding, bindings); - } - } - } - new_binding.pop_back(); - } - - - void quantifier_model_checker::add_binding(quantifier* q, expr_ref_vector& binding) { - if (binding.size() != q->get_num_decls()) { - // not a full binding. It may happen that the quantifier got simplified. - return; - } - apply_binding(q, binding); - vector bindings; - generalize_binding(binding, bindings); - for (unsigned i = 0; i < bindings.size(); ++i) { - apply_binding(q, bindings[i]); - } - } - - void quantifier_model_checker::apply_binding(quantifier* q, expr_ref_vector& binding) { - datalog::scoped_no_proof _scp(m); - app_ref_vector& var_inst = m_current_pt->get_inst(m_current_rule); - expr_ref e(m); - var_subst vs(m, false); - inv_var_shifter invsh(m); - vs(q->get_expr(), binding.size(), binding.c_ptr(), e); - invsh(e, q->get_num_decls(), e); - expr_ref_vector inst(m); - inst.append(var_inst.size(), (expr*const*)var_inst.c_ptr()); - inst.reverse(); - expr_abstract(m, 0, inst.size(), inst.c_ptr(), e, e); - if (m_instantiations.contains(to_app(e))) { - return; - } - m_instantiated_rules.push_back(m_current_rule); - m_instantiations.push_back(to_app(e)); - TRACE("pdr", tout << mk_pp(q, m) << "\n"; - tout << "binding: "; - for (unsigned i = 0; i < binding.size(); ++i) { - tout << mk_pp(binding[i].get(), m) << " "; - } - tout << "\n"; - tout << "inst: "; - for (unsigned i = 0; i < var_inst.size(); ++i) { - tout << mk_pp(var_inst[i].get(), m) << " "; - } - tout << "\n"; - tout << mk_pp(e, m) << "\n"; - ); - } - - - // As & not Body_i is satisfiable - // then instantiate with model for parameters to Body_i - - void quantifier_model_checker::find_instantiations(quantifier_ref_vector const& qs, unsigned level) { - find_instantiations_proof_based(qs, level); - } - - class collect_insts { - ast_manager& m; - ptr_vector m_binding; - vector m_bindings; - ptr_vector m_quantifiers; - public: - collect_insts(ast_manager& m): m(m) { } - - void operator()(expr* n) { - expr* not_q_or_i, *e1, *e2, *e3; - if (m.is_quant_inst(n, not_q_or_i, m_binding)) { - VERIFY(m.is_or(not_q_or_i, e1, e2)); - VERIFY(m.is_not(e1, e3)); - SASSERT(is_quantifier(e3)); - m_quantifiers.push_back(to_quantifier(e3)); - m_bindings.push_back(expr_ref_vector(m,m_binding.size(), m_binding.c_ptr())); - m_binding.reset(); - } - else if ((m.is_rewrite(n, e1, e2) || - (m.is_rewrite_star(n) && - (e3 = to_app(n)->get_arg(to_app(n)->get_num_args()-1), - e1 = to_app(e3)->get_arg(0), - e2 = to_app(e3)->get_arg(1), - true))) && - is_quantifier(e1) && m.is_false(e2)) { - quantifier* q = to_quantifier(e1); - m_quantifiers.push_back(q); - m_bindings.push_back(expr_ref_vector(m)); - expr_ref_vector& b = m_bindings.back(); - for (unsigned i = 0; i < q->get_num_decls(); ++i) { - b.push_back(m.mk_fresh_const("V", q->get_decl_sort(i))); - } - } - } - - void reset() { - m_quantifiers.reset(); - m_bindings.reset(); - } - - unsigned size() const { return m_quantifiers.size(); } - ptr_vector const& quantifiers() const { return m_quantifiers; } - vector const& bindings() const { return m_bindings; } - - }; - - void quantifier_model_checker::find_instantiations_proof_based(quantifier_ref_vector const& qs, unsigned level) { - bool found_instance = false; - - datalog::scoped_proof _scp(m); - - expr_ref_vector fmls(m); - smt_params fparams; - SASSERT(m.proofs_enabled()); - fparams.m_mbqi = true; - - fmls.push_back(m_A.get()); - fmls.append(m_Bs); - TRACE("pdr", - tout << "assert\n"; - for (unsigned i = 0; i < fmls.size(); ++i) { - tout << mk_pp(fmls[i].get(), m) << "\n"; - }); - - smt::kernel solver(m, fparams); - for (unsigned i = 0; i < fmls.size(); ++i) { - solver.assert_expr(fmls[i].get()); - } - lbool result = solver.check(); - - TRACE("pdr", tout << result << "\n";); - - if (m_rules_model_check != l_false) { - m_rules_model_check = result; - } - - if (result != l_false) { - return; - } - - map qid_map; - quantifier* q; - for (unsigned i = 0; i < qs.size(); ++i) { - q = qs[i]; - qid_map.insert(q->get_qid(), q); - } - - proof* p = solver.get_proof(); - TRACE("pdr", tout << mk_ismt2_pp(p, m) << "\n";); - collect_insts collector(m); - for_each_expr(collector, p); - ptr_vector const& quants = collector.quantifiers(); - - for (unsigned i = 0; i < collector.size(); ++i) { - symbol qid = quants[i]->get_qid(); - if (!qid_map.find(qid, q)) { - TRACE("pdr", tout << "Could not find quantifier " << mk_pp(quants[i], m) << "\n";); - continue; - } - expr_ref_vector const& binding = collector.bindings()[i]; - - TRACE("pdr", tout << "Instantiating:\n" << mk_pp(quants[i], m) << "\n"; - for (unsigned j = 0; j < binding.size(); ++j) { - tout << mk_pp(binding[j], m) << " "; - } - tout << "\n";); - - expr_ref_vector new_binding(m); - for (unsigned j = 0; j < binding.size(); ++j) { - new_binding.push_back(binding[j]); - } - add_binding(q, new_binding); - found_instance = true; - } - if (found_instance) { - m_rules_model_check = l_false; - } - else if (m_rules_model_check != l_false) { - m_rules_model_check = l_undef; - } - } - - - /** - For under-approximations: - - m_reachable: set of reachable states, per predicate - - rules: P(x) :- B[x,y] & Fa z . Q(y,z) - Q(y,z) :- C[y,z,u] & Fa w . R(u,w) - - qis: Fa z . Q(y,z) - - M: model satisfying P(x) & B[x,y] - - B'[x,y]: body with reachable states substituted for predicates. - - Q'[y,z]: reachable states substituted for Q. - - S'[x]: Ex y . B'[x,y] & Fa z . Q'[y, z] - - Method: - - 1. M |= Fa z . Q'[y, z] => done - - Weaker variant: - Check B[x,y] & Fa z . Q'[y, z] for consistency. - - 2. Otherwise, extract instantiations. - - 3. Update reachable (for next round): - - Q'[y,z] := Q'[y,z] \/ C'[y,z,u] & Fa w . R'(u,w) - - */ - - - /** - For over-approximations: - - - pt - predicate transformer for rule: - P(x) :- Body1(x,y) || Body2(x,z) & (Fa u . Q(u,x,z)). - - rule - P(x) :- Body2(x,z) - - - qis - Fa u . Q(u,x,z) - - - A := node.state(x) && Body2(x,y) - - - - Bs := array of Bs of the form: - . Fa u . Q(u, P_x, P_y) - instantiate quantifier to P variables. - . B := inv(Q_0,Q_1,Q_2) - . B := inv(u, P_x, P_y) := B[u/Q_0, P_x/Q_1, P_y/Q_2] - . B := Fa u . inv(u, P_x, P_y) - - */ - - void quantifier_model_checker::update_reachable(func_decl* f, expr* e) { - expr* e_old; - m.inc_ref(e); - if (m_reachable.find(f, e_old)) { - m.dec_ref(e_old); - } - m_reachable.insert(f, e); - } - - - expr_ref quantifier_model_checker::get_reachable(func_decl* p) { - expr* e = 0; - if (!m_reachable.find(p, e)) { - e = m_ctx.get_pred_transformer(p).initial_state(); - update_reachable(p, e); - } - return expr_ref(e, m); - } - - void quantifier_model_checker::add_over_approximations(quantifier_ref_vector& qis, model_node& n) { - add_approximations(qis, n, true); - } - - void quantifier_model_checker::add_under_approximations(quantifier_ref_vector& qis, model_node& n) { - add_approximations(qis, n, false); - } - - void quantifier_model_checker::add_approximations(quantifier_ref_vector& qis, model_node& n, bool is_over) { - pred_transformer& pt = n.pt(); - manager& pm = pt.get_pdr_manager(); - unsigned level = n.level(); - expr_ref_vector Bs(m); - expr_ref B(m), v(m); - quantifier_ref q(m); - datalog::scoped_no_proof _no_proof(m); - scoped_ptr rep = mk_default_expr_replacer(m); - for (unsigned j = 0; j < qis.size(); ++j) { - q = qis[j].get(); - SASSERT(is_forall(q)); - app_ref_vector& inst = pt.get_inst(m_current_rule); - TRACE("pdr", - tout << "q:\n" << mk_pp(q, m) << "\n"; - tout << "level: " << level << "\n"; - model_smt2_pp(tout, m, n.get_model(), 0); - m_current_rule->display(m_ctx.get_context(), tout << "rule:\n"); - ); - - var_subst vs(m, false); - vs(q, inst.size(), (expr*const*)inst.c_ptr(), B); - q = to_quantifier(B); - TRACE("pdr", tout << "q instantiated:\n" << mk_pp(q, m) << "\n";); - - app* a = to_app(q->get_expr()); - func_decl* f = a->get_decl(); - pred_transformer& pt2 = m_ctx.get_pred_transformer(f); - if (is_over) { - B = pt2.get_formulas(level - 1, false); - } - else { - B = get_reachable(f); - SASSERT(is_well_sorted(m, B)); - } - TRACE("pdr", tout << "B:\n" << mk_pp(B, m) << "\n";); - - expr_safe_replace sub(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - v = m.mk_const(pm.o2n(pt2.sig(i),0)); - sub.insert(v, a->get_arg(i)); - } - sub(B); - TRACE("pdr", tout << "B substituted:\n" << mk_pp(B, m) << "\n";); - datalog::flatten_and(B, Bs); - for (unsigned i = 0; i < Bs.size(); ++i) { - m_Bs.push_back(m.update_quantifier(q, Bs[i].get())); - } - } - } - - /** - \brief compute strongest post-conditions for each predicate transformer. - (or at least something sufficient to change the set of current counter-examples) - */ - void quantifier_model_checker::weaken_under_approximation() { - - datalog::rule_set::decl2rules::iterator it = m_rules.begin_grouped_rules(), end = m_rules.end_grouped_rules(); - - for (; it != end; ++it) { - func_decl* p = it->m_key; - datalog::rule_vector& rules = *it->m_value; - expr_ref_vector bodies(m); - for (unsigned i = 0; i < rules.size(); ++i) { - bodies.push_back(strongest_post_condition(*rules[i])); - } - update_reachable(p, m.mk_or(bodies.size(), bodies.c_ptr())); - } - } - - expr_ref quantifier_model_checker::strongest_post_condition(datalog::rule& r) { - pred_transformer& pt = m_ctx.get_pred_transformer(r.get_decl()); - manager& pm = pt.get_pdr_manager(); - quantifier_ref_vector* qis = 0; - m_quantifiers.find(&r, qis); - expr_ref_vector body(m), inst(m); - expr_ref fml(m), v(m); - app* a; - func_decl* p; - svector names; - unsigned ut_size = r.get_uninterpreted_tail_size(); - unsigned t_size = r.get_tail_size(); - var_subst vs(m, false); - ptr_vector vars; - uint_set empty_index_set; - qe_lite qe(m); - - r.get_vars(vars); - - if (qis) { - quantifier_ref_vector const& qi = *qis; - for (unsigned i = 0; i < qi.size(); ++i) { - quantifier* q = qi[i]; - fml = q->get_expr(); - a = to_app(fml); - p = a->get_decl(); - expr* p_reach = get_reachable(p); - pred_transformer& pt2 = m_ctx.get_pred_transformer(p); - expr_safe_replace sub(m); - for (unsigned j = 0; j < a->get_num_args(); ++j) { - v = m.mk_const(pm.o2n(pt2.sig(j),0)); - sub.insert(v, a->get_arg(j)); - } - sub(p_reach, fml); - uint_set is; - for (unsigned j = 0; j < q->get_num_decls(); ++j) { - is.insert(j); - } - fml = m.mk_not(fml); - qe(is, true, fml); - fml = m.mk_not(fml); - body.push_back(m.update_quantifier(q, fml)); - } - } - - a = r.get_head(); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - v = m.mk_var(vars.size()+i, m.get_sort(a->get_arg(i))); - body.push_back(m.mk_eq(v, a->get_arg(i))); - } - for (unsigned i = 0; i < ut_size; ++i) { - a = r.get_tail(i); - p = a->get_decl(); - pred_transformer& pt2 = m_ctx.get_pred_transformer(p); - expr* p_reach = get_reachable(p); - expr_safe_replace sub(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - v = m.mk_const(pm.o2n(pt2.sig(i),0)); - sub.insert(v, a->get_arg(i)); - } - sub(p_reach, fml); - body.push_back(fml); - } - for (unsigned i = ut_size; i < t_size; ++i) { - body.push_back(r.get_tail(i)); - } - fml = m.mk_and(body.size(), body.c_ptr()); - vars.reverse(); - for (unsigned i = 0; i < vars.size(); ++i) { - names.push_back(symbol(i)); - } - if (!vars.empty()) { - fml = m.mk_exists(vars.size(), vars.c_ptr(), names.c_ptr(), fml); - SASSERT(is_well_sorted(m, fml)); - } - - for (unsigned i = 0; i < r.get_head()->get_num_args(); ++i) { - inst.push_back(m.mk_const(pm.o2n(pt.sig(i),0))); - } - vs(fml, inst.size(), inst.c_ptr(), fml); - SASSERT(is_well_sorted(m, fml)); - if (!vars.empty()) { - fml = to_quantifier(fml)->get_expr(); - qe(empty_index_set, false, fml); - fml = m.mk_exists(vars.size(), vars.c_ptr(), names.c_ptr(), fml); - SASSERT(is_well_sorted(m, fml)); - m_ctx.get_context().get_rewriter()(fml); - } - SASSERT(is_well_sorted(m, fml)); - - IF_VERBOSE(0, verbose_stream() << "instantiate to:\n" << mk_pp(fml, m) << "\n";); - return fml; - } - - - void quantifier_model_checker::model_check_node(model_node& node) { - TRACE("pdr", node.display(tout, 0);); - pred_transformer& pt = node.pt(); - manager& pm = pt.get_pdr_manager(); - expr_ref A(m), C(m); - expr_ref_vector As(m); - m_Bs.reset(); - // - // nodes from leaves that are repeated - // inside the search tree don't have models. - // - if (!node.get_model_ptr()) { - return; - } - m_current_rule = node.get_rule(); - m_current_pt = &pt; - m_current_node = &node; - if (!m_current_rule) { - return; - } - - quantifier_ref_vector* qis = 0; - m_quantifiers.find(m_current_rule, qis); - if (!qis) { - return; - } - unsigned level = node.level(); - if (level == 0) { - return; - } - - As.push_back(pt.get_propagation_formula(m_ctx.get_pred_transformers(), level)); - As.push_back(node.state()); - As.push_back(pt.rule2tag(m_current_rule)); - m_A = pm.mk_and(As); - - // Add quantifiers: - // add_over_approximations(*qis, node); - add_under_approximations(*qis, node); - - TRACE("pdr", - tout << "A:\n" << mk_pp(m_A, m) << "\n"; - tout << "quantifier:\n"; - for (unsigned i = 0; i < qis->size(); ++i) { - tout << mk_pp((*qis)[i].get(), m) << " "; - } - tout << "\n"; - tout << "B:\n"; - for (unsigned i = 0; i < m_Bs.size(); ++i) { - tout << mk_pp(m_Bs[i].get(), m) << "\n"; - } - ast_smt_pp pp(m); - pp.add_assumption(m_A); - for (unsigned i = 0; i < m_Bs.size(); ++i) { - pp.add_assumption(m_Bs[i].get()); - } - pp.display_smt2(tout, m.mk_true()); - ); - - find_instantiations(*qis, level); - } - - lbool quantifier_model_checker::model_check(model_node& root) { - m_instantiations.reset(); - m_instantiated_rules.reset(); - m_rules_model_check = l_true; - ptr_vector nodes; - get_nodes(root, nodes); - for (unsigned i = nodes.size(); i > 0; ) { - --i; - model_check_node(*nodes[i]); - } - if (m_rules_model_check == l_false) { - weaken_under_approximation(); - } - return m_rules_model_check; - } - - void quantifier_model_checker::refine() { - datalog::mk_extract_quantifiers eq(m_ctx.get_context()); - datalog::rule_manager& rm = m_rules.get_rule_manager(); - datalog::rule_set new_rules(m_rules.get_context()); - datalog::rule_set::iterator it = m_rules.begin(), end = m_rules.end(); - for (; it != end; ++it) { - datalog::rule* r = *it; - datalog::var_counter vc(true); - unsigned max_var = vc.get_max_var(*r); - app_ref_vector body(m); - for (unsigned i = 0; i < m_instantiations.size(); ++i) { - if (r == m_instantiated_rules[i]) { - eq.ensure_predicate(m_instantiations[i].get(), max_var, body); - } - } - if (body.empty()) { - new_rules.add_rule(r); - } - else { - for (unsigned i = 0; i < r->get_tail_size(); ++i) { - body.push_back(r->get_tail(i)); - } - quantifier_ref_vector* qs = 0; - m_quantifiers.find(r, qs); - m_quantifiers.remove(r); - datalog::rule_ref new_rule(rm); - new_rule = rm.mk(r->get_head(), body.size(), body.c_ptr(), 0, r->name(), false); - new_rules.add_rule(new_rule); - m_quantifiers.insert(new_rule, qs); - IF_VERBOSE(1, - verbose_stream() << "instantiating quantifiers\n"; - r->display(m_ctx.get_context(), verbose_stream()); - verbose_stream() << "replaced by\n"; - new_rule->display(m_ctx.get_context(), verbose_stream());); - } - } - new_rules.close(); - m_rules.reset(); - m_rules.add_rules(new_rules); - m_rules.close(); - m_ctx.update_rules(m_rules); - TRACE("pdr", m_rules.display(tout);); - } - - lbool quantifier_model_checker::check() { - lbool result = model_check(m_ctx.get_root()); - if (result == l_false) { - refine(); - } - return result; - } -}; - diff --git a/src/muz_qe/pdr_quantifiers.h b/src/muz_qe/pdr_quantifiers.h deleted file mode 100644 index 941fab3d9..000000000 --- a/src/muz_qe/pdr_quantifiers.h +++ /dev/null @@ -1,117 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - pdr_quantifiers.h - -Abstract: - - Module for handling quantifiers in rule bodies. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-05-19. - -Revision History: - ---*/ - -#ifndef _PDR_QUANTIFIERS_H_ -#define _PDR_QUANTIFIERS_H_ - -#include "ast.h" -#include "lbool.h" -#include "dl_rule.h" -#include "obj_pair_hashtable.h" - -namespace datalog { - class rule_set; -}; - -namespace pdr { - - class model_node; - class pred_transformer; - class context; - - class quantifier_model_checker { - context& m_ctx; - ast_manager& m; - obj_map& m_quantifiers; - datalog::rule_set& m_rules; - - obj_map m_reachable; // set of reachable states - expr_ref m_A; - expr_ref_vector m_Bs; - pred_transformer* m_current_pt; - datalog::rule const* m_current_rule; - model_node* m_current_node; - lbool m_rules_model_check; - app_ref_vector m_instantiations; - ptr_vector m_instantiated_rules; - - void model_check_node(model_node& node); - - void weaken_under_approximation(); - - void find_instantiations(quantifier_ref_vector const& qs, unsigned level); - - void find_instantiations_proof_based(quantifier_ref_vector const& qs, unsigned level); - - void add_binding(quantifier* q, expr_ref_vector& binding); - - void apply_binding(quantifier* q, expr_ref_vector& binding); - - void generalize_binding(expr_ref_vector const& binding, vector& bindings); - - void generalize_binding(expr_ref_vector const& binding, unsigned idx, expr_ref_vector& new_binding, vector& bindings); - - void refine(); - - - /** - \brief model check a potential model against quantifiers in bodies of rules. - - \return true if the model rooted in 'root' is checks with the quantifiers, otherwise - 'false' and a set of instantiations that contradict the current model. - */ - - lbool model_check(model_node& root); - - void add_over_approximations(quantifier_ref_vector& qis, model_node& n); - - void add_under_approximations(quantifier_ref_vector& qis, model_node& n); - - void add_approximations(quantifier_ref_vector& qis, model_node& n, bool is_over); - - expr_ref get_reachable(func_decl* f); - - void update_reachable(func_decl* f, expr* e); - - expr_ref strongest_post_condition(datalog::rule& r); - - public: - quantifier_model_checker( - context& ctx, - ast_manager& m, - obj_map& quantifiers, - datalog::rule_set& rules) : - m_ctx(ctx), - m(m), - m_quantifiers(quantifiers), - m_rules(rules), - m_A(m), - m_Bs(m), - m_current_pt(0), - m_current_rule(0), - m_current_node(0), - m_instantiations(m) {} - - ~quantifier_model_checker(); - - lbool check(); - }; - -}; -#endif diff --git a/src/muz_qe/rel_context.h b/src/muz_qe/rel_context.h deleted file mode 100644 index b05532d14..000000000 --- a/src/muz_qe/rel_context.h +++ /dev/null @@ -1,115 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - rel_context.h - -Abstract: - - context for relational datalog engine. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-12-3. - -Revision History: - - Extracted from dl_context - ---*/ -#ifndef _REL_CONTEXT_H_ -#define _REL_CONTEXT_H_ -#include "ast.h" -#include "dl_relation_manager.h" -#include "lbool.h" - -namespace datalog { - - class context; - - class rel_context { - typedef vector > fact_vector; - - context& m_context; - ast_manager& m; - relation_manager m_rmanager; - expr_ref m_answer; - volatile bool m_cancel; - relation_base * m_last_result_relation; - decl_set m_output_preds; - fact_vector m_table_facts; - - void reset_negated_tables(); - - relation_plugin & get_ordinary_relation_plugin(symbol relation_name); - - void reset_tables(); - - public: - rel_context(context& ctx); - - ~rel_context(); - - relation_manager & get_rmanager(); - const relation_manager & get_rmanager() const; - ast_manager& get_manager() { return m; } - context& get_context() { return m_context; } - relation_base & get_relation(func_decl * pred); - relation_base * try_get_relation(func_decl * pred) const; - expr_ref get_last_answer() { return m_answer; } - - bool output_profile() const; - - - lbool query(expr* q); - lbool query(unsigned num_rels, func_decl * const* rels); - - 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); - - void cancel() { m_cancel = true; } - - void cleanup() { m_cancel = false; } - - - /** - \brief Restrict the set of used predicates to \c res. - - The function deallocates unsused relations, it does not deal with rules. - */ - void restrict_predicates(const decl_set & res); - - void collect_predicates(decl_set & res); - - void set_output_predicate(func_decl * pred); - bool is_output_predicate(func_decl * pred) { return m_output_preds.contains(pred); } - const decl_set & get_output_predicates() const { return m_output_preds; } - - - /** - \brief query result if it contains fact. - */ - bool result_contains_fact(relation_fact const& f); - - void add_fact(func_decl* pred, relation_fact const& fact); - - void add_fact(func_decl* pred, table_fact const& fact); - - /** - \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); - - void display_output_facts(std::ostream & out) const; - void display_facts(std::ostream & out) const; - - lbool saturate(); - - }; -}; - -#endif /* _REL_CONTEXT_H_ */ diff --git a/src/muz_qe/skip_list_base.h b/src/muz_qe/skip_list_base.h deleted file mode 100644 index feaa00763..000000000 --- a/src/muz_qe/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/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index be60fb052..50466c276 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -993,8 +993,22 @@ namespace nlsat { } return; } - else if (s == -1 && !is_even) { - atom_sign = -atom_sign; + else { + // We have shown the current factor is a constant MODULO the sign of the leading coefficient (of the equation used to rewrite the factor). + if (!info.m_lc_const) { + // If the leading coefficient is not a constant, we must store this information as an extra assumption. + if (d % 2 == 0 || // d is even + is_even || // rewriting a factor of even degree, sign flip doesn't matter + _a->get_kind() == atom::EQ) { // rewriting an equation, sign flip doesn't matter + info.add_lc_diseq(); + } + else { + info.add_lc_ineq(); + } + } + if (s == -1 && !is_even) { + atom_sign = -atom_sign; + } } } else { diff --git a/src/parsers/smt/smtparser.cpp b/src/parsers/smt/smtparser.cpp index b6b40c01a..d26190541 100644 --- a/src/parsers/smt/smtparser.cpp +++ b/src/parsers/smt/smtparser.cpp @@ -1472,7 +1472,7 @@ private: SASSERT(sorts.size() > 0); - idbuilder* pop_q = new (region) pop_quantifier(this, (head_symbol == m_forall), weight, qid, skid, patterns, no_patterns, sorts, vars, local_scope, current); + idbuilder* pop_q = new (region) pop_quantifier(this, (head_symbol == m_forall), weight, qid, skid, patterns, no_patterns, sorts, vars, local_scope); expr_ref_vector * empty_v = alloc(expr_ref_vector, m_manager); up.push_back(new (region) parse_frame(current, pop_q, empty_v, 0, m_binding_level)); @@ -2522,7 +2522,7 @@ private: class pop_quantifier : public idbuilder { public: pop_quantifier(smtparser * smt, bool is_forall, int weight, symbol const& qid, symbol const& skid, expr_ref_buffer & patterns, expr_ref_buffer & no_patterns, sort_ref_buffer & sorts, - svector& vars, symbol_table & local_scope, proto_expr* p_expr): + svector& vars, symbol_table & local_scope): m_smt(smt), m_is_forall(is_forall), m_weight(weight), @@ -2531,8 +2531,7 @@ private: m_patterns(m_smt->m_manager), m_no_patterns(m_smt->m_manager), m_sorts(m_smt->m_manager), - m_local_scope(local_scope), - m_p_expr(p_expr) { + m_local_scope(local_scope) { SASSERT(sorts.size() == vars.size()); m_vars.append(vars); @@ -2619,7 +2618,6 @@ private: sort_ref_buffer m_sorts; svector m_vars; symbol_table& m_local_scope; - proto_expr* m_p_expr; }; class builtin_builder : public idbuilder { diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 0698f4eaf..b5af6353e 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -1159,7 +1159,7 @@ namespace smt2 { m_num_expr_frames++; unsigned num_vars = parse_sorted_vars(); if (num_vars == 0) - throw parser_exception("invalied quantifier, list of sorted variables is empty"); + throw parser_exception("invalid quantifier, list of sorted variables is empty"); } symbol parse_indexed_identifier_core() { diff --git a/src/parsers/smt2/smt2scanner.cpp b/src/parsers/smt2/smt2scanner.cpp index 0f6101a93..f653cbb25 100644 --- a/src/parsers/smt2/smt2scanner.cpp +++ b/src/parsers/smt2/smt2scanner.cpp @@ -243,7 +243,6 @@ namespace smt2 { } scanner::scanner(cmd_context & ctx, std::istream& stream, bool interactive): - m_ctx(ctx), m_interactive(interactive), m_spos(0), m_curr(0), // avoid Valgrind warning diff --git a/src/parsers/smt2/smt2scanner.h b/src/parsers/smt2/smt2scanner.h index 7b74c752f..631c71f17 100644 --- a/src/parsers/smt2/smt2scanner.h +++ b/src/parsers/smt2/smt2scanner.h @@ -31,7 +31,6 @@ namespace smt2 { class scanner { private: - cmd_context & m_ctx; bool m_interactive; int m_spos; // position in the current line of the stream char m_curr; // current char; diff --git a/src/muz_qe/nlarith_util.cpp b/src/qe/nlarith_util.cpp similarity index 99% rename from src/muz_qe/nlarith_util.cpp rename to src/qe/nlarith_util.cpp index 5f8a24e99..c555b71f1 100644 --- a/src/muz_qe/nlarith_util.cpp +++ b/src/qe/nlarith_util.cpp @@ -1028,7 +1028,6 @@ namespace nlarith { }; class sqrt_subst : public isubst { - bool m_even; sqrt_form const& m_s; public: sqrt_subst(imp& i, sqrt_form const& s): isubst(i), m_s(s) {} 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 bb65f8bf8..d63f0ae00 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) { @@ -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)) { @@ -1093,8 +1093,7 @@ namespace qe { bool has_branch(rational const& branch_id) const { return m_branch_index.contains(branch_id); } search_tree* child(rational const& branch_id) const { - unsigned idx; - VERIFY(m_branch_index.find(branch_id, idx)); + unsigned idx = m_branch_index.find(branch_id); return m_children[idx]; } @@ -1927,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); } @@ -1963,7 +1963,6 @@ namespace qe { expr_ref m_assumption; bool m_produce_models; ptr_vector m_plugins; - unsigned m_name_counter; // fresh-id volatile bool m_cancel; bool m_eliminate_variables_as_block; @@ -1973,7 +1972,6 @@ namespace qe { m_fparams(p), m_assumption(m), m_produce_models(m_fparams.m_model), - m_name_counter(0), m_cancel(false), m_eliminate_variables_as_block(true) { @@ -2496,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(); 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/qe/qe_arith.cpp b/src/qe/qe_arith.cpp new file mode 100644 index 000000000..360c2ac8c --- /dev/null +++ b/src/qe/qe_arith.cpp @@ -0,0 +1,306 @@ +/*++ +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" +#include "expr_functors.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; + scoped_ptr m_var; + + struct cant_project {}; + + void is_linear(rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { + expr* t1, *t2; + rational mul1; + if (t == m_var->x()) { + c += mul; + } + else if (a.is_mul(t, t1, t2) && a.is_numeral(t1, mul1)) { + is_linear(mul* mul1, t2, c, ts); + } + else if (a.is_mul(t, t1, t2) && a.is_numeral(t2, mul1)) { + 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(mul, ap->get_arg(i), c, ts); + } + } + else if (a.is_sub(t, t1, t2)) { + is_linear(mul, t1, c, ts); + is_linear(-mul, t2, c, ts); + } + else if (a.is_uminus(t, t1)) { + 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 ((*m_var)(t)) { + IF_VERBOSE(1, verbose_stream() << "can't project:" << 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(expr* lit, rational& c, expr_ref& t, bool& is_strict) { + if (!(*m_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( 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( 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( mul, e1, c, ts); + is_linear(-mul, e2, c, ts); + s = m.get_sort(e1); + is_strict = false; + } + else { + IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(lit, m) << "\n";); + 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, 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); + expr_ref t(m); + bool 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); + 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()); + } + } + lits.reset(); + lits.append(new_lits); + if (num_pos == 0 || num_neg == 0) { + return; + } + 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 { + lits.push_back(mk_lt(i, max_t)); + } + } + } + } + + unsigned find_max(model& mdl, bool do_pos) { + unsigned result; + bool found = false; + 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]; + if (ac.is_pos() == do_pos) { + VERIFY(mdl.eval(m_ineq_terms[i].get(), val)); + VERIFY(a.is_numeral(val, r)); + 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_c = ac; + found = true; + } + } + } + SASSERT(found); + if (a.is_int(m_var->x()) && !found_c.is_one()) { + throw cant_project(); + } + 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 result1(m), result2(m); + if (m_ineq_strict[i] || m_ineq_strict[j]) { + result1 = a.mk_lt(ts, z); + } + else { + result1 = a.mk_le(ts, z); + } + m_rw(result1, result2); + return result2; + } + + // 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); + expr_ref result1(m), result2(m); + if (m_ineq_strict[j] && !m_ineq_strict[i]) { + result1 = a.mk_lt(bt, as); + } + else { + result1 = a.mk_le(bt, as); + } + m_rw(result1, result2); + return result2; + } + + + 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) { + app_ref_vector new_vars(m); + expr_ref_vector result(lits); + for (unsigned i = 0; i < vars.size(); ++i) { + 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) { + IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(v, m) << "\n";); + new_vars.push_back(v); + } + } + vars.reset(); + vars.append(new_vars); + return qe::mk_and(result); + } + }; + + 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); + } + + 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 new file mode 100644 index 000000000..c8f7e8b8d --- /dev/null +++ b/src/qe/qe_arith.h @@ -0,0 +1,18 @@ + +#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); + + expr_ref arith_project(model& model, app_ref_vector& vars, expr* fml); +}; + +#endif diff --git a/src/muz_qe/qe_arith_plugin.cpp b/src/qe/qe_arith_plugin.cpp similarity index 98% rename from src/muz_qe/qe_arith_plugin.cpp rename to src/qe/qe_arith_plugin.cpp index 1baee51fa..7bf0978f6 100644 --- a/src/muz_qe/qe_arith_plugin.cpp +++ b/src/qe/qe_arith_plugin.cpp @@ -20,7 +20,7 @@ Revision History: #include "qe.h" #include "ast_pp.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "bool_rewriter.h" #include "bv_decl_plugin.h" #include "arith_decl_plugin.h" @@ -93,7 +93,7 @@ namespace qe { expr_ref m_one_r; expr_ref m_tmp; public: - scoped_ptr m_replace; + expr_safe_replace m_replace; bool_rewriter m_bool_rewriter; arith_rewriter m_arith_rewriter; @@ -111,7 +111,7 @@ namespace qe { m_zero_r(m_arith.mk_numeral(numeral(0), false), m), m_one_r(m_arith.mk_numeral(numeral(1), false), m), m_tmp(m), - m_replace(mk_default_expr_replacer(m)), + m_replace(m), m_bool_rewriter(m), m_arith_rewriter(m) { } @@ -827,7 +827,7 @@ namespace qe { while (index <= up) { expr* n = mk_numeral(index); result = body; - m_replace->apply_substitution(x, n, result); + m_replace.apply_substitution(x, n, result); ors.push_back(result); ++index; } @@ -857,7 +857,7 @@ namespace qe { mk_flat_and(e1, body, result); app_ref z(m); mk_bounded_var(up, z_bv, z); - m_replace->apply_substitution(x, z, result); + m_replace.apply_substitution(x, z, result); } @@ -966,7 +966,7 @@ namespace qe { << mk_pp(e, m) << "\n"; ); expr_ref result(fml, m); - m_replace->apply_substitution(x, e, result); + m_replace.apply_substitution(x, e, result); simplify(result); TRACE("qe", tout << "singular solved:\n" @@ -1044,7 +1044,7 @@ namespace qe { tout << " = 0\n"; ); expr_ref result(fml, m); - m_replace->apply_substitution(x, p1, result); + m_replace.apply_substitution(x, p1, result); simplify(result); m_ctx.elim_var(index-1, result, p1); TRACE("qe", tout << "Reduced: " << mk_pp(result, m) << "\n";); @@ -2080,7 +2080,7 @@ public: app* atm = atoms[i]; t1 = m_util.mk_add(m_util.mk_mul(coeffs[i], z), terms[i]); m_util.mk_divides(divisors[i], t1, new_atom); - m_util.m_replace->apply_substitution(atm, new_atom.get(), result); + m_util.m_replace.apply_substitution(atm, new_atom.get(), result); m_ctx.add_constraint(false, mk_not(atm), new_atom); m_ctx.add_constraint(false, mk_not(new_atom), atm); @@ -2121,7 +2121,7 @@ public: m_util.simplify(mod_term2); m_ctx.add_constraint(false, m.mk_eq(mod_term2, m_util.mk_zero(mod_term2))); - m_util.m_replace->apply_substitution(atm, z1, result); + m_util.m_replace.apply_substitution(atm, z1, result); // // conjoin (coeff*z + rest - z1) mod k == 0 to result @@ -2153,7 +2153,7 @@ public: for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, is_lower)[i]; m_ctx.add_constraint(true, mk_not(e)); - m_util.m_replace->apply_substitution(e, m.mk_false(), result); + m_util.m_replace.apply_substitution(e, m.mk_false(), result); } } @@ -2162,7 +2162,7 @@ public: for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, !is_lower)[i]; m_ctx.add_constraint(true, e); - m_util.m_replace->apply_substitution(e, m.mk_true(), result); + m_util.m_replace.apply_substitution(e, m.mk_true(), result); } } @@ -2276,7 +2276,7 @@ public: else { m_ctx.add_constraint(true, e); } - m_util.m_replace->apply_substitution(atm, m.mk_true(), result); + m_util.m_replace.apply_substitution(atm, m.mk_true(), result); continue; } @@ -2293,7 +2293,7 @@ public: (same_strict && i < index); mk_bound(result_is_strict, is_lower, a, t, b, s, tmp); - m_util.m_replace->apply_substitution(e, tmp.get(), result); + m_util.m_replace.apply_substitution(e, tmp.get(), result); TRACE("qe", tout << (result_is_strict?"strict result":"non-strict result") << "\n"; @@ -2330,7 +2330,7 @@ public: s = x_t.mk_term(b, s); b = x_t.mk_coeff(b); m_util.mk_resolve(x, strict_resolve, a, t, b, s, tmp); - m_util.m_replace->apply_substitution(e, tmp.get(), result); + m_util.m_replace.apply_substitution(e, tmp.get(), result); m_ctx.add_constraint(true, mk_not(e), tmp); @@ -2398,7 +2398,7 @@ public: weights_t m_weights; th_rewriter m_rewriter; nlarith::util m_util; - scoped_ptr m_replacer; + expr_safe_replace m_replace; expr_ref_vector m_trail; factor_rewriter_star m_factor_rw; bool m_produce_models; @@ -2407,7 +2407,7 @@ public: qe_solver_plugin(m, m.mk_family_id("arith"), ctx), m_rewriter(m), m_util(m), - m_replacer(mk_default_expr_replacer(m)), + m_replace(m), m_trail(m), m_factor_rw(m), m_produce_models(produce_models) { @@ -2480,12 +2480,11 @@ public: SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < brs->size()); unsigned j = vl.get_unsigned(); - expr_substitution sub(m); + m_replace.reset(); for (unsigned i = 0; i < brs->preds().size(); ++i) { - sub.insert(to_app(brs->preds(i)), brs->subst(j)[i]); + m_replace.insert(brs->preds(i), brs->subst(j)[i]); } - m_replacer->set_substitution(&sub); - (*m_replacer)(fml); + m_replace(fml); expr_ref tmp(m.mk_and(brs->constraints(j), fml), m); m_factor_rw(tmp, fml); if (def) { diff --git a/src/muz_qe/qe_array_plugin.cpp b/src/qe/qe_array_plugin.cpp similarity index 96% rename from src/muz_qe/qe_array_plugin.cpp rename to src/qe/qe_array_plugin.cpp index 106a42338..c9de1d745 100644 --- a/src/muz_qe/qe_array_plugin.cpp +++ b/src/qe/qe_array_plugin.cpp @@ -1,7 +1,7 @@ #include "qe.h" #include "array_decl_plugin.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "ast_pp.h" #include "arith_decl_plugin.h" @@ -11,13 +11,13 @@ namespace qe { class array_plugin : public qe_solver_plugin { - scoped_ptr m_replace; + expr_safe_replace m_replace; public: array_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("array"), ctx), - m_replace(mk_default_expr_replacer(m)) + m_replace(m) { } @@ -123,7 +123,7 @@ namespace qe { if (m_ctx.is_var(a, idx) && !m_ctx.contains(idx)(rhs)) { expr_ref result(fml, m); - m_replace->apply_substitution(a, rhs, result); + m_replace.apply_substitution(a, rhs, result); m_ctx.elim_var(idx, result, rhs); return true; } @@ -175,7 +175,7 @@ namespace qe { tout << "eq: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n"; ); expr_ref result(fml, m); - m_replace->apply_substitution(A, store_B_i_t, result); + m_replace.apply_substitution(A, store_B_i_t, result); m_ctx.add_var(B); m_ctx.elim_var(idx, result, store_B_i_t); return true; @@ -248,7 +248,7 @@ namespace qe { tout << "eq: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n"; ); expr_ref result(fml, m); - m_replace->apply_substitution(A, store_t, result); + m_replace.apply_substitution(A, store_t, result); m_ctx.elim_var(idx, result, store_t); return true; } diff --git a/src/muz_qe/qe_bool_plugin.cpp b/src/qe/qe_bool_plugin.cpp similarity index 91% rename from src/muz_qe/qe_bool_plugin.cpp rename to src/qe/qe_bool_plugin.cpp index 782644c28..39a46ae55 100644 --- a/src/muz_qe/qe_bool_plugin.cpp +++ b/src/qe/qe_bool_plugin.cpp @@ -25,18 +25,18 @@ Notes: --*/ #include "qe.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "ast_pp.h" #include "model_evaluator.h" namespace qe { class bool_plugin : public qe_solver_plugin { - scoped_ptr m_replace; + expr_safe_replace m_replace; public: bool_plugin(i_solver_context& ctx, ast_manager& m): qe_solver_plugin(m, m.get_basic_family_id(), ctx), - m_replace(mk_default_expr_replacer(m)) + m_replace(m) {} virtual void assign(contains_app& x, expr* fml, rational const& vl) { @@ -51,7 +51,7 @@ namespace qe { virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { SASSERT(vl.is_one() || vl.is_zero()); expr* tf = (vl.is_one())?m.mk_true():m.mk_false(); - m_replace->apply_substitution(x.x(), tf, 0, fml); + m_replace.apply_substitution(x.x(), tf, fml); if (def) { *def = tf; } @@ -103,12 +103,12 @@ namespace qe { app* a = to_app(e); expr* e1; if (m_ctx.is_var(a, idx)) { - m_replace->apply_substitution(a, m.mk_true(), 0, fml); + m_replace.apply_substitution(a, m.mk_true(), fml); m_ctx.elim_var(idx, fml, m.mk_true()); return true; } else if (m.is_not(e, e1) && m_ctx.is_var(e1, idx)) { - m_replace->apply_substitution(to_app(e1), m.mk_false(), 0, fml); + m_replace.apply_substitution(to_app(e1), m.mk_false(), fml); m_ctx.elim_var(idx, fml, m.mk_false()); return true; } @@ -148,7 +148,7 @@ namespace qe { } // only occurrences of 'x' must be in positive atoms def = m.mk_true(); - m_replace->apply_substitution(x, def, 0, fml); + m_replace.apply_substitution(x, def, fml); return true; } else if (!p && n) { @@ -161,7 +161,7 @@ namespace qe { if (x != *it && contains_x(*it)) return false; } def = m.mk_false(); - m_replace->apply_substitution(x, def, 0, fml); + m_replace.apply_substitution(x, def, fml); return true; } else if (contains_x(fml)) { diff --git a/src/muz_qe/qe_bv_plugin.cpp b/src/qe/qe_bv_plugin.cpp similarity index 91% rename from src/muz_qe/qe_bv_plugin.cpp rename to src/qe/qe_bv_plugin.cpp index cae567111..df1f8c619 100644 --- a/src/muz_qe/qe_bv_plugin.cpp +++ b/src/qe/qe_bv_plugin.cpp @@ -21,19 +21,19 @@ Notes: --*/ #include "qe.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "bv_decl_plugin.h" #include "model_evaluator.h" namespace qe { class bv_plugin : public qe_solver_plugin { - scoped_ptr m_replace; - bv_util m_bv; + expr_safe_replace m_replace; + bv_util m_bv; public: bv_plugin(i_solver_context& ctx, ast_manager& m): qe_solver_plugin(m, m.mk_family_id("bv"), ctx), - m_replace(mk_default_expr_replacer(m)), + m_replace(m), m_bv(m) {} @@ -48,7 +48,7 @@ namespace qe { virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { app_ref c(m_bv.mk_numeral(vl, m_bv.get_bv_size(x.x())), m); - m_replace->apply_substitution(x.x(), c, 0, fml); + m_replace.apply_substitution(x.x(), c, fml); if (def) { *def = m_bv.mk_numeral(vl, m_bv.get_bv_size(x.x())); } diff --git a/src/muz_qe/qe_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 97% rename from src/muz_qe/qe_datatype_plugin.cpp rename to src/qe/qe_datatype_plugin.cpp index 0b51f26af..9b77de42a 100644 --- a/src/muz_qe/qe_datatype_plugin.cpp +++ b/src/qe/qe_datatype_plugin.cpp @@ -95,7 +95,7 @@ #include "qe.h" #include "datatype_decl_plugin.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "obj_pair_hashtable.h" #include "for_each_expr.h" #include "ast_pp.h" @@ -415,7 +415,7 @@ namespace qe { typedef obj_pair_map subst_map; datatype_util m_datatype_util; - scoped_ptr m_replace; + expr_safe_replace m_replace; eqs_cache m_eqs_cache; subst_map m_subst_cache; ast_ref_vector m_trail; @@ -424,7 +424,7 @@ namespace qe { datatype_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("datatype"), ctx), m_datatype_util(m), - m_replace(mk_default_expr_replacer(m)), + m_replace(m), m_trail(m) { } @@ -518,7 +518,7 @@ namespace qe { subst_clos* sub = 0; if (m_subst_cache.find(x.x(), c, sub)) { - m_replace->apply_substitution(x.x(), sub->first, 0, fml); + m_replace.apply_substitution(x.x(), sub->first, fml); add_def(sub->first, def); for (unsigned i = 0; i < sub->second.size(); ++i) { m_ctx.add_var(sub->second[i]); @@ -541,7 +541,7 @@ namespace qe { m_trail.push_back(t); add_def(t, def); - m_replace->apply_substitution(x.x(), t, 0, fml); + m_replace.apply_substitution(x.x(), t, fml); sub->first = t; m_subst_cache.insert(x.x(), c, sub); } @@ -673,7 +673,7 @@ namespace qe { fml = m.mk_and(is_c, fml); app_ref fresh_x(m.mk_fresh_const("x", s), m); m_ctx.add_var(fresh_x); - m_replace->apply_substitution(x, fresh_x, 0, fml); + m_replace.apply_substitution(x, fresh_x, fml); add_def(fresh_x, def); TRACE("qe", tout << "Add recognizer " << mk_pp(is_c, m) << "\n";); return; @@ -697,33 +697,33 @@ namespace qe { for (unsigned i = 0; i < eqs.num_recognizers(); ++i) { app* rec = eqs.recognizer(i); if (rec->get_decl() == r) { - m_replace->apply_substitution(rec, m.mk_true(), fml); + m_replace.apply_substitution(rec, m.mk_true(), fml); } else { - m_replace->apply_substitution(rec, m.mk_false(), fml); + m_replace.apply_substitution(rec, m.mk_false(), fml); } } for (unsigned i = 0; i < eqs.num_unsat(); ++i) { - m_replace->apply_substitution(eqs.unsat_atom(i), m.mk_false(), fml); + m_replace.apply_substitution(eqs.unsat_atom(i), m.mk_false(), fml); } if (idx < eqs.num_eqs()) { expr* t = eqs.eq(idx); expr* c = eqs.eq_cond(idx); add_def(t, def); - m_replace->apply_substitution(x, t, fml); + m_replace.apply_substitution(x, t, fml); if (!m.is_true(c)) { fml = m.mk_and(c, fml); } } else { for (unsigned i = 0; i < eqs.num_eqs(); ++i) { - m_replace->apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); + m_replace.apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); } for (unsigned i = 0; i < eqs.num_neqs(); ++i) { - m_replace->apply_substitution(eqs.neq_atom(i), m.mk_false(), fml); + m_replace.apply_substitution(eqs.neq_atom(i), m.mk_false(), fml); } if (def) { sort* s = m.get_sort(x); diff --git a/src/muz_qe/qe_dl_plugin.cpp b/src/qe/qe_dl_plugin.cpp similarity index 94% rename from src/muz_qe/qe_dl_plugin.cpp rename to src/qe/qe_dl_plugin.cpp index 15e972e88..a93301b4f 100644 --- a/src/muz_qe/qe_dl_plugin.cpp +++ b/src/qe/qe_dl_plugin.cpp @@ -1,6 +1,6 @@ #include "qe.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" #include "dl_decl_plugin.h" #include "obj_pair_hashtable.h" #include "ast_pp.h" @@ -12,14 +12,12 @@ namespace qe { // dl_plugin class eq_atoms { - ast_manager& m; expr_ref_vector m_eqs; expr_ref_vector m_neqs; app_ref_vector m_eq_atoms; app_ref_vector m_neq_atoms; public: eq_atoms(ast_manager& m): - m(m), m_eqs(m), m_neqs(m), m_eq_atoms(m), @@ -37,7 +35,7 @@ namespace qe { class dl_plugin : public qe_solver_plugin { typedef obj_pair_map eqs_cache; - scoped_ptr m_replace; + expr_safe_replace m_replace; datalog::dl_decl_util m_util; expr_ref_vector m_trail; eqs_cache m_eqs_cache; @@ -46,7 +44,7 @@ namespace qe { public: dl_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("datalog_relation"), ctx), - m_replace(mk_default_expr_replacer(m)), + m_replace(m), m_util(m), m_trail(m) { @@ -142,7 +140,7 @@ namespace qe { void subst_small_domain(contains_app & x,eq_atoms& eqs, unsigned v,expr_ref & fml) { expr_ref vl(m_util.mk_numeral(v, m.get_sort(x.x())), m); - m_replace->apply_substitution(x.x(), vl, fml); + m_replace.apply_substitution(x.x(), vl, fml); } // assumes that all disequalities can be satisfied. @@ -150,15 +148,15 @@ namespace qe { SASSERT(w <= eqs.num_eqs()); if (w < eqs.num_eqs()) { expr* e = eqs.eq(w); - m_replace->apply_substitution(x.x(), e, fml); + m_replace.apply_substitution(x.x(), e, fml); } else { for (unsigned i = 0; i < eqs.num_eqs(); ++i) { - m_replace->apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); + m_replace.apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); } for (unsigned i = 0; i < eqs.num_neqs(); ++i) { - m_replace->apply_substitution(eqs.neq_atom(i), m.mk_true(), fml); + m_replace.apply_substitution(eqs.neq_atom(i), m.mk_true(), fml); } } } diff --git a/src/muz_qe/qe_lite.cpp b/src/qe/qe_lite.cpp similarity index 98% rename from src/muz_qe/qe_lite.cpp rename to src/qe/qe_lite.cpp index 5f018895c..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" @@ -201,9 +200,15 @@ namespace eq { return (*m_is_variable)(e); } - bool is_neg_var(ast_manager & m, expr * e) { + bool is_neg_var(ast_manager & m, expr * e, var*& v) { expr* e1; - return m.is_not(e, e1) && is_variable(e1); + if (m.is_not(e, e1) && is_variable(e1)) { + v = to_var(e1); + return true; + } + else { + return false; + } } @@ -328,18 +333,19 @@ namespace eq { bool is_var_eq(expr * e, ptr_vector& vs, expr_ref_vector & ts) { expr* lhs, *rhs; + var* v; // (= VAR t), (iff VAR t), (iff (not VAR) t), (iff t (not VAR)) cases if (m.is_eq(e, lhs, rhs) || m.is_iff(e, lhs, rhs)) { // (iff (not VAR) t) (iff t (not VAR)) cases if (!is_variable(lhs) && !is_variable(rhs) && m.is_bool(lhs)) { - if (!is_neg_var(m, lhs)) { + if (!is_neg_var(m, lhs, v)) { std::swap(lhs, rhs); } - if (!is_neg_var(m, lhs)) { + if (!is_neg_var(m, lhs, v)) { return false; } - vs.push_back(to_var(lhs)); + vs.push_back(v); ts.push_back(m.mk_not(rhs)); TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); return true; @@ -378,9 +384,9 @@ namespace eq { } // VAR = false case - if (is_neg_var(m, e)) { + if (is_neg_var(m, e, v)) { ts.push_back(m.mk_false()); - vs.push_back(to_var(to_app(e)->get_arg(0))); + vs.push_back(v); TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); return true; } @@ -689,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; } } @@ -792,9 +798,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) { @@ -820,6 +832,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(); @@ -1410,6 +1423,7 @@ namespace fm { fm(ast_manager & _m): m(_m), + m_is_variable(0), m_allocator("fm-elim"), m_util(m), m_bvar2expr(m), @@ -1417,6 +1431,9 @@ namespace fm { m_new_fmls(m), m_inconsistent_core(m) { m_cancel = false; + updt_params(); + m_counter = 0; + m_inconsistent = false; } ~fm() { @@ -2369,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()); @@ -2382,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); @@ -2492,7 +2509,13 @@ class qe_lite_tactic : public tactic { new_f = f; m_qe(new_f, new_pr); if (produce_proofs) { - new_pr = m.mk_modus_ponens(g->pr(i), new_pr); + expr* fact = m.get_fact(new_pr); + if (to_app(fact)->get_arg(0) != to_app(fact)->get_arg(1)) { + new_pr = m.mk_modus_ponens(g->pr(i), new_pr); + } + else { + new_pr = g->pr(i); + } } g->update(i, new_f, new_pr, g->dep(i)); } @@ -2512,15 +2535,15 @@ public: m_params(p) { m_imp = alloc(imp, m, p); } - - virtual tactic * translate(ast_manager & m) { - return alloc(qe_lite_tactic, m, m_params); - } virtual ~qe_lite_tactic() { dealloc(m_imp); } + virtual tactic * translate(ast_manager & m) { + return alloc(qe_lite_tactic, m, m_params); + } + virtual void updt_params(params_ref const & p) { m_params = p; // m_imp->updt_params(p); 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 85% rename from src/muz_qe/qe_sat_tactic.h rename to src/qe/qe_sat_tactic.h index c539216be..15228c534 100644 --- a/src/muz_qe/qe_sat_tactic.h +++ b/src/qe/qe_sat_tactic.h @@ -26,7 +26,7 @@ Revision History: namespace qe { - tactic * mk_sat_tactic(ast_manager& m, params_ref const& p); + tactic * mk_sat_tactic(ast_manager& m, params_ref const& p = params_ref()); }; /* 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..77396ac49 --- /dev/null +++ b/src/qe/qe_util.cpp @@ -0,0 +1,132 @@ +#include "qe_util.h" +#include "bool_rewriter.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); + } + + 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 new file mode 100644 index 000000000..f1a99ec6c --- /dev/null +++ b/src/qe/qe_util.h @@ -0,0 +1,41 @@ +/*++ +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); + + expr_ref mk_and(expr_ref_vector const& fmls); + + expr_ref mk_or(expr_ref_vector const& fmls); + +} +#endif diff --git a/src/muz_qe/vsubst_tactic.cpp b/src/qe/vsubst_tactic.cpp similarity index 100% rename from src/muz_qe/vsubst_tactic.cpp rename to src/qe/vsubst_tactic.cpp diff --git a/src/muz_qe/vsubst_tactic.h b/src/qe/vsubst_tactic.h similarity index 100% rename from src/muz_qe/vsubst_tactic.h rename to src/qe/vsubst_tactic.h diff --git a/src/sat/sat_asymm_branch.cpp b/src/sat/sat_asymm_branch.cpp index 81d631e4e..b8ac520b2 100644 --- a/src/sat/sat_asymm_branch.cpp +++ b/src/sat/sat_asymm_branch.cpp @@ -72,11 +72,14 @@ namespace sat { int limit = -static_cast(m_asymm_branch_limit); std::stable_sort(s.m_clauses.begin(), s.m_clauses.end(), clause_size_lt()); m_counter -= s.m_clauses.size(); + SASSERT(s.m_qhead == s.m_trail.size()); clause_vector::iterator it = s.m_clauses.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = s.m_clauses.end(); try { for (; it != end; ++it) { + if (s.inconsistent()) + break; SASSERT(s.m_qhead == s.m_trail.size()); if (m_counter < limit || s.inconsistent()) { *it2 = *it; @@ -111,6 +114,7 @@ namespace sat { bool asymm_branch::process(clause & c) { TRACE("asymm_branch_detail", tout << "processing: " << c << "\n";); SASSERT(s.scope_lvl() == 0); + SASSERT(s.m_qhead == s.m_trail.size()); #ifdef Z3DEBUG unsigned trail_sz = s.m_trail.size(); #endif @@ -143,6 +147,7 @@ namespace sat { SASSERT(!s.inconsistent()); SASSERT(s.scope_lvl() == 0); SASSERT(trail_sz == s.m_trail.size()); + SASSERT(s.m_qhead == s.m_trail.size()); if (i == sz - 1) { // clause size can't be reduced. s.attach_clause(c); @@ -181,15 +186,18 @@ namespace sat { s.assign(c[0], justification()); s.del_clause(c); s.propagate_core(false); + SASSERT(s.inconsistent() || s.m_qhead == s.m_trail.size()); return false; // check_missed_propagation() may fail, since m_clauses is not in a consistent state. case 2: SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); s.mk_bin_clause(c[0], c[1], false); s.del_clause(c); + SASSERT(s.m_qhead == s.m_trail.size()); return false; default: c.shrink(new_sz); s.attach_clause(c); + SASSERT(s.m_qhead == s.m_trail.size()); return true; } } diff --git a/src/sat/sat_clause_use_list.h b/src/sat/sat_clause_use_list.h index ccf4b4a5b..0859439d5 100644 --- a/src/sat/sat_clause_use_list.h +++ b/src/sat/sat_clause_use_list.h @@ -20,6 +20,7 @@ Revision History: #define _SAT_CLAUSE_USE_LIST_H_ #include"sat_types.h" +#include"trace.h" namespace sat { @@ -35,6 +36,7 @@ namespace sat { #endif public: clause_use_list() { + STRACE("clause_use_list_bug", tout << "[cul_created] " << this << "\n";); #ifdef LAZY_USE_LIST m_size = 0; #endif @@ -51,22 +53,33 @@ namespace sat { bool empty() const { return size() == 0; } void insert(clause & c) { - SASSERT(!m_clauses.contains(&c)); SASSERT(!c.was_removed()); + STRACE("clause_use_list_bug", tout << "[cul_insert] " << this << " " << &c << "\n";); + SASSERT(!m_clauses.contains(&c)); + SASSERT(!c.was_removed()); m_clauses.push_back(&c); #ifdef LAZY_USE_LIST m_size++; #endif } + void erase_not_removed(clause & c) { + STRACE("clause_use_list_bug", tout << "[cul_erase_not_removed] " << this << " " << &c << "\n";); #ifdef LAZY_USE_LIST - SASSERT(m_clauses.contains(&c)); SASSERT(!c.was_removed()); m_clauses.erase(&c); m_size--; + SASSERT(m_clauses.contains(&c)); + SASSERT(!c.was_removed()); + m_clauses.erase(&c); + m_size--; #else m_clauses.erase(&c); #endif } + void erase(clause & c) { + STRACE("clause_use_list_bug", tout << "[cul_erase] " << this << " " << &c << "\n";); #ifdef LAZY_USE_LIST - SASSERT(m_clauses.contains(&c)); SASSERT(c.was_removed()); m_size--; + SASSERT(m_clauses.contains(&c)); + SASSERT(c.was_removed()); + m_size--; #else m_clauses.erase(&c); #endif @@ -80,6 +93,7 @@ namespace sat { } bool check_invariant() const; + // iterate & compress class iterator { clause_vector & m_clauses; diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 623c73758..ba0c67235 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -146,8 +146,11 @@ namespace sat { m_need_cleanup = false; m_use_list.init(s.num_vars()); init_visited(); - if (learned) + bool learned_in_use_lists = false; + if (learned) { register_clauses(s.m_learned); + learned_in_use_lists = true; + } register_clauses(s.m_clauses); if (!learned && (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls)) @@ -179,7 +182,7 @@ namespace sat { if (!m_need_cleanup) { if (vars_eliminated) { // must remove learned clauses with eliminated variables - cleanup_clauses(s.m_learned, true, true); + cleanup_clauses(s.m_learned, true, true, learned_in_use_lists); } CASSERT("sat_solver", s.check_invariant()); TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); @@ -187,8 +190,8 @@ namespace sat { return; } cleanup_watches(); - cleanup_clauses(s.m_learned, true, vars_eliminated); - cleanup_clauses(s.m_clauses, false, vars_eliminated); + cleanup_clauses(s.m_learned, true, vars_eliminated, learned_in_use_lists); + cleanup_clauses(s.m_clauses, false, vars_eliminated, true); TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); CASSERT("sat_solver", s.check_invariant()); free_memory(); @@ -221,7 +224,7 @@ namespace sat { } } - void simplifier::cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated) { + void simplifier::cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists) { clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = cs.end(); @@ -245,7 +248,7 @@ namespace sat { } } - if (cleanup_clause(c)) { + if (cleanup_clause(c, in_use_lists)) { s.del_clause(c); continue; } @@ -516,7 +519,7 @@ namespace sat { Return true if the clause is satisfied */ - bool simplifier::cleanup_clause(clause & c) { + bool simplifier::cleanup_clause(clause & c, bool in_use_list) { bool r = false; unsigned sz = c.size(); unsigned j = 0; @@ -529,7 +532,11 @@ namespace sat { break; case l_false: m_need_cleanup = true; - m_use_list.get(l).erase_not_removed(c); + if (in_use_list && !c.frozen()) { + // Remark: if in_use_list is false, then the given clause was not added to the use lists. + // Remark: frozen clauses are not added to the use lists. + m_use_list.get(l).erase_not_removed(c); + } break; case l_true: r = true; @@ -611,7 +618,7 @@ namespace sat { clause_use_list & occurs = m_use_list.get(l); occurs.erase_not_removed(c); m_sub_counter -= occurs.size()/2; - if (cleanup_clause(c)) { + if (cleanup_clause(c, true /* clause is in the use lists */)) { // clause was satisfied TRACE("elim_lit", tout << "clause was satisfied\n";); remove_clause(c); @@ -625,7 +632,8 @@ namespace sat { case 1: TRACE("elim_lit", tout << "clause became unit: " << c[0] << "\n";); propagate_unit(c[0]); - remove_clause(c); + // propagate_unit will delete c. + // remove_clause(c); return; case 2: TRACE("elim_lit", tout << "clause became binary: " << c[0] << " " << c[1] << "\n";); @@ -805,7 +813,7 @@ namespace sat { m_sub_counter--; TRACE("subsumption", tout << "next: " << c << "\n";); if (s.m_trail.size() > m_last_sub_trail_sz) { - if (cleanup_clause(c)) { + if (cleanup_clause(c, true /* clause is in the use_lists */)) { remove_clause(c); continue; } @@ -816,7 +824,8 @@ namespace sat { } if (sz == 1) { propagate_unit(c[0]); - remove_clause(c); + // propagate_unit will delete c. + // remove_clause(c); continue; } if (sz == 2) { diff --git a/src/sat/sat_simplifier.h b/src/sat/sat_simplifier.h index 44e2f0dee..96d346598 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -125,7 +125,7 @@ namespace sat { void collect_subsumed0(clause const & c1, clause_vector & out); void back_subsumption0(clause & c1); - bool cleanup_clause(clause & c); + bool cleanup_clause(clause & c, bool in_use_list); bool cleanup_clause(literal_vector & c); void propagate_unit(literal l); void elim_lit(clause & c, literal l); @@ -136,7 +136,7 @@ namespace sat { void subsume(); void cleanup_watches(); - void cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated); + void cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists); bool is_external(bool_var v) const; bool was_eliminated(bool_var v) const; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 399fb2c61..3e9b60260 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1745,6 +1745,12 @@ namespace sat { 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)) @@ -1754,9 +1760,15 @@ namespace sat { watch_list::const_iterator it = wlist.begin(); watch_list::const_iterator end = wlist.end(); 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. + // 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()) { literal l2 = it->get_literal(); - if (is_marked_lit(~l2)) { + if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } @@ -1764,11 +1776,11 @@ namespace sat { else if (it->is_ternary_clause()) { literal l2 = it->get_literal1(); literal l3 = it->get_literal2(); - if (is_marked_lit(l2) && is_marked_lit(~l3)) { + if (is_marked_lit(l2) && is_marked_lit(~l3) && l0 != ~l3) { // eliminate ~l3 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l3); } - else if (is_marked_lit(~l2) && is_marked_lit(l3)) { + else if (is_marked_lit(~l2) && is_marked_lit(l3) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l2); } @@ -1786,7 +1798,41 @@ namespace sat { literal_vector::iterator end = implied_lits->end(); for (; it != end; ++it) { literal l2 = *it; - if (is_marked_lit(~l2)) { + // Here, we must check l0 != ~l2. + // l \/ l2 is an implied binary clause. + // However, it may have been deduced using a lemma that has been deleted. + // For example, consider the following sequence of events: + // + // 1. Initial clause database: + // + // l \/ ~p1 + // 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 + // ... + // + // 2. Now suppose we learned the lemma + // + // p1 \/ p2 \/ p3 \/ p4 \/ l2 (*) + // + // 3. Probing is executed and we notice hat (~l => l2) when we assign l to false. + // 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). + // + // 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 (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index 44a9d9b66..a487a99b4 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" @@ -43,17 +45,7 @@ static datalog::context * g_ctx = 0; static datalog::rule_set * g_orig_rules; static datalog::instruction_block * g_code; static datalog::execution_context * g_ectx; -static smt_params * g_params; -datalog_params::datalog_params(): - m_default_table("sparse"), - m_default_table_checked(false) -{} - -// void datalog_params::register_params(ini_params& p) { -// p.register_symbol_param("DEFAULT_TABLE", m_default_table, "Datalog engine: default table (sparse)"); -// p.register_bool_param("DEFAULT_TABLE_CHECKED", m_default_table_checked, "Wrap default table with a sanity checker"); -// } static void display_statistics( std::ostream& out, @@ -61,7 +53,6 @@ static void display_statistics( datalog::rule_set& orig_rules, datalog::instruction_block& code, datalog::execution_context& ex_ctx, - smt_params& params, bool verbose ) { @@ -86,7 +77,7 @@ static void display_statistics( out << "--------------\n"; out << "instructions \n"; - code.display(ctx.get_rel_context(), out); + code.display(*ctx.get_rel_context(), out); out << "--------------\n"; out << "big relations \n"; @@ -94,7 +85,7 @@ static void display_statistics( } out << "--------------\n"; out << "relation sizes\n"; - ctx.get_rel_context().get_rmanager().display_relation_sizes(out); + ctx.get_rel_context()->get_rmanager().display_relation_sizes(out); if (verbose) { out << "--------------\n"; @@ -109,7 +100,7 @@ static void display_statistics( static void display_statistics() { if (g_ctx) { - display_statistics(std::cout, *g_ctx, *g_orig_rules, *g_code, *g_ectx, *g_params, true); + display_statistics(std::cout, *g_ctx, *g_orig_rules, *g_code, *g_ectx, true); } } @@ -127,19 +118,17 @@ static void on_ctrl_c(int) { unsigned read_datalog(char const * file) { IF_VERBOSE(1, verbose_stream() << "Z3 Datalog Engine\n";); - datalog_params dl_params; 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")); - params.set_sym("default_table", dl_params.m_default_table); - params.set_bool("default_table_checked", dl_params.m_default_table_checked); - datalog::context ctx(m, s_params, params); - datalog::relation_manager & rmgr = ctx.get_rel_context().get_rmanager(); + 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); rmgr.register_plugin(alloc(datalog::finite_product_relation_plugin, inner_plg, rmgr)); @@ -175,8 +164,6 @@ unsigned read_datalog(char const * file) { TRACE("dl_compiler", ctx.display(tout);); datalog::rule_set original_rules(ctx.get_rules()); - datalog::decl_set original_predicates; - ctx.collect_predicates(original_predicates); datalog::instruction_block rules_code; datalog::instruction_block termination_code; @@ -188,7 +175,6 @@ unsigned read_datalog(char const * file) { g_orig_rules = &original_rules; g_code = &rules_code; g_ectx = &ex_ctx; - g_params = &s_params; try { g_piece_timer.reset(); @@ -196,22 +182,19 @@ 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 { - model_converter_ref mc; // ignored - proof_converter_ref pc; // ignored - ctx.transform_rules(mc, pc); + ctx.get_rel_context()->transform_rules(); datalog::compiler::compile(ctx, ctx.get_rules(), rules_code, termination_code); - TRACE("dl_compiler", rules_code.display(ctx.get_rel_context(), tout);); + TRACE("dl_compiler", rules_code.display(*ctx.get_rel_context(), tout);); rules_code.make_annotations(ex_ctx); ex_ctx.set_timelimit(timeout); - SASSERT(!ex_ctx.should_terminate()); early_termination = !rules_code.perform(ex_ctx); if(early_termination) { @@ -240,7 +223,6 @@ unsigned read_datalog(char const * file) { termination_code.reset(); ex_ctx.reset(); ctx.reopen(); - ctx.restrict_predicates(original_predicates); ctx.replace_rules(original_rules); ctx.close(); } @@ -248,19 +230,17 @@ unsigned read_datalog(char const * file) { TRACE("dl_compiler", ctx.display(tout); - rules_code.display(ctx.get_rel_context(), tout);); + rules_code.display(*ctx.get_rel_context(), tout);); - if (ctx.get_params().output_tuples()) { - ctx.get_rel_context().display_output_facts(std::cout); + if (ctx.output_tuples()) { + ctx.get_rel_context()->display_output_facts(ctx.get_rules(), std::cout); } - display_statistics( std::cout, ctx, original_rules, rules_code, ex_ctx, - s_params, false); } @@ -272,7 +252,6 @@ unsigned read_datalog(char const * file) { original_rules, rules_code, ex_ctx, - s_params, true); return ERR_MEMOUT; } diff --git a/src/shell/datalog_frontend.h b/src/shell/datalog_frontend.h index e53e35c89..8022d5046 100644 --- a/src/shell/datalog_frontend.h +++ b/src/shell/datalog_frontend.h @@ -19,11 +19,6 @@ Revision History: #ifndef _DATALOG_FRONTEND_H_ #define _DATALOG_FRONTEND_H_ -struct datalog_params { - symbol m_default_table; - bool m_default_table_checked; - datalog_params(); -}; unsigned read_datalog(char const * file); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 63e604719..e0fa4a1f2 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -27,8 +27,8 @@ Revision History: #include"z3_log_frontend.h" #include"warning.h" #include"version.h" -#include"datalog_frontend.h" #include"dimacs_frontend.h" +#include"datalog_frontend.h" #include"timeout.h" #include"z3_exception.h" #include"error_codes.h" diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index 0e570321e..3197f8cff 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -55,13 +55,19 @@ static void display_statistics() { } static void on_timeout() { - display_statistics(); - exit(0); + #pragma omp critical (g_display_stats) + { + display_statistics(); + exit(0); + } } static void on_ctrl_c(int) { signal (SIGINT, SIG_DFL); - display_statistics(); + #pragma omp critical (g_display_stats) + { + display_statistics(); + } raise(SIGINT); } @@ -84,9 +90,12 @@ unsigned read_smtlib_file(char const * benchmark_file) { } } - display_statistics(); - register_on_timeout_proc(0); - g_solver = 0; + #pragma omp critical (g_display_stats) + { + display_statistics(); + register_on_timeout_proc(0); + g_solver = 0; + } return solver.get_error_code(); } @@ -105,7 +114,6 @@ unsigned read_smtlib2_commands(char const * file_name) { install_subpaving_cmds(ctx); g_cmd_context = &ctx; - register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); bool result = true; @@ -121,8 +129,12 @@ unsigned read_smtlib2_commands(char const * file_name) { result = parse_smt2_commands(ctx, std::cin, true); } - display_statistics(); - g_cmd_context = 0; + + #pragma omp critical (g_display_stats) + { + display_statistics(); + g_cmd_context = 0; + } return result ? 0 : 1; } diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index 3155d9c58..4775e44a4 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -408,7 +408,7 @@ void asserted_formulas::apply_quasi_macros() { TRACE("before_quasi_macros", display(tout);); expr_ref_vector new_exprs(m_manager); proof_ref_vector new_prs(m_manager); - quasi_macros proc(m_manager, m_macro_manager, *m_bsimp, m_simplifier); + quasi_macros proc(m_manager, m_macro_manager, m_simplifier); while (proc(m_asserted_formulas.size() - m_asserted_qhead, m_asserted_formulas.c_ptr() + m_asserted_qhead, m_asserted_formula_prs.c_ptr() + m_asserted_qhead, @@ -653,7 +653,7 @@ void asserted_formulas::propagate_values() { // will be (silently) eliminated, and models produced by Z3 will not contain them. flush_cache(); } - TRACE("propagate_values", tout << "afer:\n"; display(tout);); + TRACE("propagate_values", tout << "after:\n"; display(tout);); } void asserted_formulas::propagate_booleans() { diff --git a/src/smt/database.h b/src/smt/database.h index 2e71cbae4..1975fe3c3 100644 --- a/src/smt/database.h +++ b/src/smt/database.h @@ -1,4 +1,4 @@ -char const * g_pattern_database = +static char const * g_pattern_database = "(benchmark patterns \n" " :status unknown \n" " :logic ALL \n" diff --git a/src/smt/database.smt b/src/smt/database.smt index 186dd9b95..2f5e5e1c9 100644 --- a/src/smt/database.smt +++ b/src/smt/database.smt @@ -311,4 +311,4 @@ (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pats { (?select (?select (?asElems e) a) i) }) - ) \ No newline at end of file + ) diff --git a/src/smt/diff_logic.h b/src/smt/diff_logic.h index 49d7313d1..6fd156e41 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.h @@ -21,8 +21,10 @@ Revision History: #include"vector.h" #include"heap.h" +#include"statistics.h" #include"trace.h" #include"warning.h" +#include"uint_set.h" typedef int dl_var; @@ -118,7 +120,7 @@ const edge_id null_edge_id = -1; template class dl_graph { - struct statistics { + struct stats { unsigned m_propagation_cost; unsigned m_implied_literal_cost; unsigned m_num_implied_literals; @@ -131,16 +133,16 @@ class dl_graph { m_num_helpful_implied_literals = 0; m_num_relax = 0; } - statistics() { reset(); } - void display(std::ostream& out) const { - out << "num. prop. steps. " << m_propagation_cost << "\n"; - out << "num. impl. steps. " << m_implied_literal_cost << "\n"; - out << "num. impl. lits. " << m_num_implied_literals << "\n"; - out << "num. impl. conf lits. " << m_num_helpful_implied_literals << "\n"; - out << "num. bound relax. " << m_num_relax << "\n"; + stats() { reset(); } + void collect_statistics(::statistics& st) const { + st.update("dl prop steps", m_propagation_cost); + st.update("dl impl steps", m_implied_literal_cost); + st.update("dl impl lits", m_num_implied_literals); + st.update("dl impl conf lits", m_num_helpful_implied_literals); + st.update("dl bound relax", m_num_relax); } }; - statistics m_stats; + stats m_stats; typedef typename Ext::numeral numeral; typedef typename Ext::explanation explanation; typedef vector assignment; @@ -264,7 +266,6 @@ class dl_graph { m_assignment[e.get_target()] - m_assignment[e.get_source()] <= e.get_weight(); } - public: // An assignment is feasible if all edges are feasible. bool is_feasible() const { @@ -306,14 +307,6 @@ private: return true; } - // 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. - void acc_assignment(dl_var v, const numeral & inc) { - TRACE("diff_logic_bug", tout << "update v: " << v << " += " << inc << " m_assignment[v] " << m_assignment[v] << "\n";); - m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); - m_assignment[v] += inc; - } // Restore the assignment using the information in m_assignment_stack. // This method is called when make_feasible fails. @@ -472,8 +465,9 @@ public: m_bw(m_mark) { } - void display_statistics(std::ostream& out) const { - m_stats.display(out); + + void collect_statistics(::statistics& st) const { + m_stats.collect_statistics(st); } // Create/Initialize a variable with the given id. @@ -655,10 +649,8 @@ public: throw default_exception("edges are not inconsistent"); } -#if 1 - // experimental feature: + // allow theory to introduce shortcut lemmas. prune_edges(edges, f); -#endif for (unsigned i = 0; i < edges.size(); ++i) { edge const& e = m_edges[edges[i]]; @@ -752,7 +744,6 @@ public: f.new_edge(src, dst, idx2-idx1+1, edges.begin()+idx1); } - // Create a new scope. // That is, save the number of edges in the graph. void push() { @@ -829,6 +820,16 @@ public: } } + // 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. + void acc_assignment(dl_var v, const numeral & inc) { + TRACE("diff_logic_bug", tout << "update v: " << v << " += " << inc << " m_assignment[v] " << m_assignment[v] << "\n";); + m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); + m_assignment[v] += inc; + } + + struct every_var_proc { bool operator()(dl_var v) const { return true; @@ -839,6 +840,36 @@ public: display_core(out, every_var_proc()); } + void display_agl(std::ostream & out) const { + uint_set vars; + typename edges::const_iterator it = m_edges.begin(); + typename edges::const_iterator end = m_edges.end(); + for (; it != end; ++it) { + edge const& e = *it; + if (e.is_enabled()) { + vars.insert(e.get_source()); + vars.insert(e.get_target()); + } + } + out << "digraph "" {\n"; + + unsigned n = m_assignment.size(); + for (unsigned v = 0; v < n; v++) { + if (vars.contains(v)) { + out << "\"" << v << "\" [label=\"" << v << ":" << m_assignment[v] << "\"]\n"; + } + } + it = m_edges.begin(); + for (; it != end; ++it) { + edge const& e = *it; + if (e.is_enabled()) { + out << "\"" << e.get_source() << "\"->\"" << e.get_target() << "\"[label=\"" << e.get_weight() << "\"]\n"; + } + } + + out << "}\n"; + } + template void display_core(std::ostream & out, FilterAssignmentProc p) const { display_edges(out); @@ -1002,6 +1033,38 @@ public: } } + void compute_zero_succ(dl_var v, int_vector& succ) { + unsigned n = m_assignment.size(); + m_dfs_time.reset(); + m_dfs_time.resize(n, -1); + m_dfs_time[v] = 0; + succ.push_back(v); + numeral gamma; + for (unsigned i = 0; i < succ.size(); ++i) { + v = succ[i]; + edge_id_vector & edges = m_out_edges[v]; + typename edge_id_vector::iterator it = edges.begin(); + typename edge_id_vector::iterator end = edges.end(); + for (; it != end; ++it) { + edge_id e_id = *it; + edge & e = m_edges[e_id]; + if (!e.is_enabled()) { + continue; + } + SASSERT(e.get_source() == v); + set_gamma(e, gamma); + if (gamma.is_zero()) { + dl_var target = e.get_target(); + if (m_dfs_time[target] == -1) { + succ.push_back(target); + m_dfs_time[target] = 0; + } + } + } + + } + } + numeral get_assignment(dl_var v) const { return m_assignment[v]; } @@ -1643,7 +1706,3 @@ public: #endif /* _DIFF_LOGIC_H_ */ -#if 0 - - -#endif diff --git a/src/smt/expr_context_simplifier.cpp b/src/smt/expr_context_simplifier.cpp index b23bb3bdc..66252c3cf 100644 --- a/src/smt/expr_context_simplifier.cpp +++ b/src/smt/expr_context_simplifier.cpp @@ -311,7 +311,7 @@ bool expr_context_simplifier::is_false(expr* e) const { // expr_strong_context_simplifier::expr_strong_context_simplifier(smt_params& p, ast_manager& m): - m_manager(m), m_params(p), m_arith(m), m_id(0), m_fn(0,m), m_solver(m, p) { + m_manager(m), m_arith(m), m_fn(0,m), m_solver(m, p) { sort* i_sort = m_arith.mk_int(); m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } diff --git a/src/smt/expr_context_simplifier.h b/src/smt/expr_context_simplifier.h index 982b65878..a1d01b78c 100644 --- a/src/smt/expr_context_simplifier.h +++ b/src/smt/expr_context_simplifier.h @@ -57,9 +57,7 @@ private: class expr_strong_context_simplifier { ast_manager& m_manager; - smt_params & m_params; arith_util m_arith; - unsigned m_id; func_decl_ref m_fn; smt::kernel m_solver; diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index c9d6ead88..d92eef5b8 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -1849,11 +1849,9 @@ namespace smt { unsigned m_curr_max_generation; // temporary var used to store a copy of m_max_generation unsigned m_num_args; unsigned m_oreg; - unsigned m_ireg; enode * m_n1; enode * m_n2; enode * m_app; - instruction * m_alt; const bind * m_b; ptr_vector m_used_enodes; unsigned m_curr_used_enodes_size; diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 33c9a11ec..07c68f759 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -29,6 +29,7 @@ def_module_params(module_name='smt', ('qi.cost', STRING, '(+ weight generation)', 'expression specifying what is the cost of a given quantifier instantiation'), ('qi.max_multi_patterns', UINT, 0, 'specify the number of extra multi patterns'), ('bv.reflect', BOOL, True, 'create enode for every bit-vector term'), + ('bv.enable_int2bv', BOOL, False, 'enable support for int2bv and bv2int operators'), ('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'), ('arith.solver', UINT, 2, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation'), diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index 52fef8ca4..30bc65b6d 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -26,7 +26,9 @@ enum arith_solver_id { AS_NO_ARITH, AS_DIFF_LOGIC, AS_ARITH, - AS_DENSE_DIFF_LOGIC + AS_DENSE_DIFF_LOGIC, + AS_UTVPI, + AS_HORN }; enum bound_prop_mode { diff --git a/src/smt/params/theory_bv_params.cpp b/src/smt/params/theory_bv_params.cpp index c2a31c59d..d3f386ab4 100644 --- a/src/smt/params/theory_bv_params.cpp +++ b/src/smt/params/theory_bv_params.cpp @@ -22,4 +22,5 @@ Revision History: void theory_bv_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_bv_reflect = p.bv_reflect(); + m_bv_enable_int2bv2int = p.bv_enable_int2bv(); } 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); } } diff --git a/src/smt/proto_model/value_factory.h b/src/smt/proto_model/value_factory.h index b7df55f43..5bd0a1f75 100644 --- a/src/smt/proto_model/value_factory.h +++ b/src/smt/proto_model/value_factory.h @@ -180,10 +180,22 @@ 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() && 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 + set->m_next) { + return 0; + } } SASSERT(result != 0); return result; diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index e69b7a1b6..c4a48d9f8 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -94,7 +94,7 @@ namespace smt { } obj_map const & get_elems() const { return m_elems; } - + void insert(expr * n, unsigned generation) { if (m_elems.contains(n)) return; @@ -102,6 +102,14 @@ namespace smt { m_elems.insert(n, generation); SASSERT(!m_manager.is_model_value(n)); } + + void remove(expr * n) { + // We can only remove n if it is in m_elems, AND m_inv was not initialized yet. + SASSERT(m_elems.contains(n)); + SASSERT(m_inv.empty()); + m_elems.erase(n); + m_manager.dec_ref(n); + } void display(std::ostream & out) const { obj_map::iterator it = m_elems.begin(); @@ -525,6 +533,30 @@ namespace smt { } } + // For each instantiation_set, reemove entries that do not evaluate to values. + void cleanup_instantiation_sets() { + ptr_vector to_delete; + ptr_vector::const_iterator it = m_nodes.begin(); + ptr_vector::const_iterator end = m_nodes.end(); + for (; it != end; ++it) { + node * curr = *it; + if (curr->is_root()) { + instantiation_set * s = curr->get_instantiation_set(); + to_delete.reset(); + obj_map const & elems = s->get_elems(); + for (obj_map::iterator it = elems.begin(); it != elems.end(); it++) { + expr * n = it->m_key; + expr * n_val = eval(n, true); + if (!m_manager.is_value(n_val)) + to_delete.push_back(n); + } + for (ptr_vector::iterator it = to_delete.begin(); it != to_delete.end(); it++) { + s->remove(*it); + } + } + } + } + void display_nodes(std::ostream & out) const { display_key2node(out, m_uvars); display_A_f_is(out); @@ -545,6 +577,7 @@ namespace smt { r = 0; else r = tmp; + TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n----->\n" << mk_pp(r, m_manager) << "\n";); m_eval_cache.insert(n, r); m_eval_cache_range.push_back(r); return r; @@ -1047,6 +1080,7 @@ namespace smt { public: void fix_model(expr_ref_vector & new_constraints) { + cleanup_instantiation_sets(); m_new_constraints = &new_constraints; func_decl_set partial_funcs; collect_partial_funcs(partial_funcs); @@ -1535,8 +1569,23 @@ namespace smt { n1->insert_exception(m_t); } - virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { - // do nothing... + virtual void populate_inst_sets(quantifier * q, auf_solver & slv, context * ctx) { + unsigned num_vars = q->get_num_decls(); + ast_manager & m = ctx->get_manager(); + sort * s = q->get_decl_sort(num_vars - m_var_i - 1); + if (m.is_uninterp(s)) { + // For uninterpreted sorst, we add all terms in the context. + // See Section 4.1 in the paper "Complete Quantifier Instantiation" + node * S_q_i = slv.get_uvar(q, m_var_i); + ptr_vector::const_iterator it = ctx->begin_enodes(); + ptr_vector::const_iterator end = ctx->end_enodes(); + for (; it != end; ++it) { + enode * n = *it; + if (ctx->is_relevant(n) && get_sort(n->get_owner()) == s) { + S_q_i->insert(n->get_owner(), n->get_generation()); + } + } + } } }; @@ -1924,7 +1973,8 @@ namespace smt { m_mutil.mk_add(t1, t2, r); } - bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t) const { + bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t, bool & inv) const { + inv = false; // true if invert the sign TRACE("is_var_and_ground", tout << "is_var_and_ground: " << mk_ismt2_pp(lhs, m_manager) << " " << mk_ismt2_pp(rhs, m_manager) << "\n";); if (is_var(lhs) && is_ground(rhs)) { v = to_var(lhs); @@ -1939,7 +1989,6 @@ namespace smt { return true; } else { - bool inv = false; // true if invert the sign expr_ref tmp(m_manager); if (is_var_plus_ground(lhs, inv, v, tmp) && is_ground(rhs)) { if (inv) @@ -1959,6 +2008,11 @@ namespace smt { return false; } + bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t) const { + bool inv; + return is_var_and_ground(lhs, rhs, v, t, inv); + } + bool is_x_eq_t_atom(expr * n, var * & v, expr_ref & t) const { if (!is_app(n)) return false; @@ -2011,22 +2065,28 @@ namespace smt { if (sign) { bool r = is_le_ge(atom) && is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, t); CTRACE("is_x_gle_t", r, tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m_manager) << "\n--->\n" - << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n";); + << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n"; + tout << "sign: " << sign << "\n";); return r; } else { if (is_le_ge(atom)) { expr_ref tmp(m_manager); - if (is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, tmp)) { + bool le = is_le(atom); + bool inv = false; + if (is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, tmp, inv)) { + if (inv) + le = !le; sort * s = m_manager.get_sort(tmp); expr_ref one(m_manager); one = mk_one(s); - if (is_le(atom)) + if (le) mk_add(tmp, one, t); else mk_sub(tmp, one, t); TRACE("is_x_gle_t", tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m_manager) << "\n--->\n" - << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n";); + << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n"; + tout << "sign: " << sign << "\n";); return true; } } diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 1f020cdd3..f91a58f87 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -22,6 +22,8 @@ Revision History: #include"theory_arith.h" #include"theory_dense_diff_logic.h" #include"theory_diff_logic.h" +#include"theory_horn_ineq.h" +#include"theory_utvpi.h" #include"theory_array.h" #include"theory_array_full.h" #include"theory_bv.h" @@ -723,6 +725,18 @@ namespace smt { m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params)); } break; + case AS_HORN: + if (m_params.m_arith_int_only) + m_context.register_plugin(alloc(smt::theory_ihi, m_manager)); + else + m_context.register_plugin(alloc(smt::theory_rhi, m_manager)); + break; + case AS_UTVPI: + if (m_params.m_arith_int_only) + m_context.register_plugin(alloc(smt::theory_iutvpi, m_manager)); + else + m_context.register_plugin(alloc(smt::theory_rutvpi, m_manager)); + break; default: if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.cpp b/src/smt/tactic/ctx_solver_simplify_tactic.cpp index 25c41c2a2..8cefee8da 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.cpp +++ b/src/smt/tactic/ctx_solver_simplify_tactic.cpp @@ -23,7 +23,7 @@ Notes: #include"smt_kernel.h" #include"ast_pp.h" #include"mk_simplified_app.h" - +#include"ast_util.h" class ctx_solver_simplify_tactic : public tactic { ast_manager& m; @@ -33,10 +33,14 @@ class ctx_solver_simplify_tactic : public tactic { arith_util m_arith; mk_simplified_app m_mk_app; func_decl_ref m_fn; + obj_map m_fns; unsigned m_num_steps; + volatile bool m_cancel; public: ctx_solver_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()): - m(m), m_params(p), m_solver(m, m_front_p), m_arith(m), m_mk_app(m), m_fn(m), m_num_steps(0) { + m(m), m_params(p), m_solver(m, m_front_p), + m_arith(m), m_mk_app(m), m_fn(m), m_num_steps(0), + m_cancel(false) { sort* i_sort = m_arith.mk_int(); m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } @@ -45,7 +49,13 @@ public: return alloc(ctx_solver_simplify_tactic, m, m_params); } - virtual ~ctx_solver_simplify_tactic() {} + virtual ~ctx_solver_simplify_tactic() { + obj_map::iterator it = m_fns.begin(), end = m_fns.end(); + for (; it != end; ++it) { + m.dec_ref(it->m_value); + } + m_fns.reset(); + } virtual void updt_params(params_ref const & p) { m_solver.updt_params(p); @@ -76,23 +86,53 @@ public: virtual void cleanup() { reset_statistics(); m_solver.reset(); + m_cancel = false; } + protected: + virtual void set_cancel(bool f) { m_solver.set_cancel(f); + m_cancel = false; } void reduce(goal& g) { SASSERT(g.is_well_sorted()); - m_num_steps = 0; expr_ref fml(m); tactic_report report("ctx-solver-simplify", g); if (g.inconsistent()) return; ptr_vector fmls; g.get_formulas(fmls); - fml = m.mk_and(fmls.size(), fmls.c_ptr()); + fml = mk_and(m, fmls.size(), fmls.c_ptr()); + m_solver.push(); reduce(fml); + m_solver.pop(1); + SASSERT(m_solver.get_scope_level() == 0); + TRACE("ctx_solver_simplify_tactic", + for (unsigned i = 0; i < fmls.size(); ++i) { + tout << mk_pp(fmls[i], m) << "\n"; + } + tout << "=>\n"; + tout << mk_pp(fml, m) << "\n";); + DEBUG_CODE( + { + m_solver.push(); + expr_ref fml1(m); + fml1 = mk_and(m, fmls.size(), fmls.c_ptr()); + fml1 = m.mk_iff(fml, fml1); + fml1 = m.mk_not(fml1); + m_solver.assert_expr(fml1); + lbool is_sat = m_solver.check(); + TRACE("ctx_solver_simplify_tactic", tout << "is non-equivalence sat?: " << is_sat << "\n";); + if (is_sat != l_false) { + TRACE("ctx_solver_simplify_tactic", + tout << "result is not equivalent to input\n"; + tout << mk_pp(fml1, m) << "\n";); + UNREACHABLE(); + } + m_solver.pop(1); + }); g.reset(); g.assert_expr(fml, 0, 0); IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(ctx-solver-simplify :num-steps " << m_num_steps << ")\n";); @@ -106,21 +146,23 @@ protected: svector is_checked; svector parent_ids, self_ids; expr_ref_vector fresh_vars(m), trail(m); - expr_ref res(m); - obj_map > cache; + expr_ref res(m), tmp(m); + obj_map > cache; unsigned id = 1; - expr* n = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); - expr* n2, *fml; + expr_ref n2(m), fml(m); unsigned path_id = 0, self_pos = 0; app * a; unsigned sz; std::pair path_r; ptr_vector found; + expr_ref_vector args(m); + expr_ref n = mk_fresh(id, m.mk_bool_sort()); + trail.push_back(n); fml = result.get(); - m_solver.assert_expr(m.mk_not(m.mk_iff(fml, n))); + tmp = m.mk_not(m.mk_iff(fml, n)); + m_solver.assert_expr(tmp); - trail.push_back(n); todo.push_back(fml); names.push_back(n); is_checked.push_back(false); @@ -128,9 +170,9 @@ protected: self_ids.push_back(0); m_solver.push(); - while (!todo.empty()) { + while (!todo.empty() && !m_cancel) { expr_ref res(m); - ptr_buffer args; + args.reset(); expr* e = todo.back(); unsigned pos = parent_ids.back(); n = names.back(); @@ -139,11 +181,8 @@ protected: if (cache.contains(e)) { goto done; } - if (!m.is_bool(e)) { - res = e; - goto done; - } if (m.is_bool(e) && !checked && simplify_bool(n, res)) { + TRACE("ctx_solver_simplify_tactic", tout << "simplified: " << mk_pp(e, m) << " |-> " << mk_pp(res, m) << "\n";); goto done; } if (!is_app(e)) { @@ -164,35 +203,35 @@ protected: found.reset(); // arguments already simplified. for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); - if (!m.is_bool(arg)) { - args.push_back(arg); - } - else if (cache.find(arg, path_r) && !found.contains(arg)) { + if (cache.find(arg, path_r) && !found.contains(arg)) { // // This is a single traversal version of the context // simplifier. It simplifies only the first occurrence of - // a formula with respect to the context. + // a sub-term with respect to the context. // found.push_back(arg); if (path_r.first == self_pos) { - TRACE("ctx_solver_simplify_tactic", tout << "cached " << mk_pp(arg, m) << "\n";); + TRACE("ctx_solver_simplify_tactic", tout << "cached " << mk_pp(arg, m) << " |-> " << mk_pp(path_r.second, m) << "\n";); args.push_back(path_r.second); } - else { + else if (m.is_bool(arg)) { res = local_simplify(a, n, id, i); TRACE("ctx_solver_simplify_tactic", - tout << "Already cached: " << path_r.first << " " << mk_pp(res, m) << "\n";); + tout << "Already cached: " << path_r.first << " " << mk_pp(arg, m) << " |-> " << mk_pp(res, m) << "\n";); + args.push_back(res); + } + else { args.push_back(arg); } } else if (!n2 && !found.contains(arg)) { - n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); + n2 = mk_fresh(id, m.get_sort(arg)); + trail.push_back(n2); todo.push_back(arg); parent_ids.push_back(self_pos); self_ids.push_back(0); names.push_back(n2); - trail.push_back(n2); args.push_back(n2); is_checked.push_back(false); } @@ -205,7 +244,8 @@ protected: // child needs to be visited. if (n2) { m_solver.push(); - m_solver.assert_expr(m.mk_eq(res, n)); + tmp = m.mk_eq(res, n); + m_solver.assert_expr(tmp); continue; } @@ -224,12 +264,14 @@ protected: is_checked.pop_back(); m_solver.pop(1); } - VERIFY(cache.find(fml, path_r)); - result = path_r.second; + if (!m_cancel) { + VERIFY(cache.find(fml, path_r)); + result = path_r.second; + } } bool simplify_bool(expr* n, expr_ref& res) { - + expr_ref tmp(m); m_solver.push(); m_solver.assert_expr(n); lbool is_sat = m_solver.check(); @@ -240,7 +282,8 @@ protected: } m_solver.push(); - m_solver.assert_expr(m.mk_not(n)); + tmp = m.mk_not(n); + m_solver.assert_expr(tmp); is_sat = m_solver.check(); m_solver.pop(1); if (is_sat == l_false) { @@ -251,11 +294,25 @@ protected: return false; } + expr_ref mk_fresh(unsigned& id, sort* s) { + func_decl* fn; + if (m.is_bool(s)) { + fn = m_fn; + } + else if (!m_fns.find(s, fn)) { + fn = m.mk_func_decl(symbol(0xbeef101 + id), m_arith.mk_int(), s); + m.inc_ref(fn); + m_fns.insert(s, fn); + } + return expr_ref(m.mk_app(fn, m_arith.mk_numeral(rational(id++), true)), m); + } + + expr_ref local_simplify(app* a, expr* n, unsigned& id, unsigned index) { SASSERT(index < a->get_num_args()); SASSERT(m.is_bool(a->get_arg(index))); - expr_ref n2(m), result(m); - n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); + expr_ref n2(m), result(m), tmp(m); + n2 = mk_fresh(id, m.get_sort(a->get_arg(index))); ptr_buffer args; for (unsigned i = 0; i < a->get_num_args(); ++i) { if (i == index) { @@ -267,9 +324,10 @@ protected: } m_mk_app(a->get_decl(), args.size(), args.c_ptr(), result); m_solver.push(); - m_solver.assert_expr(m.mk_eq(result, n)); + tmp = m.mk_eq(result, n); + m_solver.assert_expr(tmp); if (!simplify_bool(n2, result)) { - result = a; + result = a->get_arg(index); } m_solver.pop(1); return result; diff --git a/src/muz_qe/unit_subsumption_tactic.cpp b/src/smt/tactic/unit_subsumption_tactic.cpp similarity index 100% rename from src/muz_qe/unit_subsumption_tactic.cpp rename to src/smt/tactic/unit_subsumption_tactic.cpp diff --git a/src/muz_qe/unit_subsumption_tactic.h b/src/smt/tactic/unit_subsumption_tactic.h similarity index 100% rename from src/muz_qe/unit_subsumption_tactic.h rename to src/smt/tactic/unit_subsumption_tactic.h diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index fafe73a79..195d78e25 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -408,7 +408,7 @@ namespace smt { mk_axiom(eqz, upper); rational k; if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) && - k.is_pos() && k < rational(512)) { + k.is_pos() && k < rational(8)) { rational j(0); #if 1 literal_buffer lits; diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index c2c50567f..3f96a1d62 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -688,7 +688,7 @@ namespace smt { } } } - + void theory_array_base::propagate_select_to_store_parents(enode * r, enode * sel, svector & todo) { SASSERT(r->get_root() == r); SASSERT(is_select(sel)); @@ -880,7 +880,7 @@ namespace smt { } else { theory_var r = mg_find(v); - void * else_val = m_else_values[r]; + void * else_val = m_else_values[r]; // DISABLED. It seems wrong, since different nodes can share the same // else_val according to the mg class. // SASSERT(else_val == 0 || get_context().is_relevant(UNTAG(app*, else_val))); diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 4140f683c..7302ccfd4 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -46,7 +46,6 @@ namespace smt { unsigned m_num_conflicts; unsigned m_num_assertions; unsigned m_num_th2core_eqs; - unsigned m_num_th2core_prop; unsigned m_num_core2th_eqs; unsigned m_num_core2th_diseqs; @@ -59,109 +58,30 @@ namespace smt { } }; - class dl_conflict : public simple_justification { - public: - dl_conflict(region & r, unsigned nls, literal const * lits): simple_justification(r, nls, lits) { } - - virtual proof * mk_proof(conflict_resolution & cr) { - NOT_IMPLEMENTED_YET(); - return 0; - } - }; - - template class theory_diff_logic : public theory, private Ext { typedef typename Ext::numeral numeral; - class implied_eq_justification : public justification { - theory_diff_logic & m_theory; - theory_var m_v1; - theory_var m_v2; - unsigned m_timestamp; - public: - implied_eq_justification(theory_diff_logic & theory, theory_var v1, theory_var v2, unsigned ts): - m_theory(theory), - m_v1(v1), - m_v2(v2), - m_timestamp(ts) { - } - - virtual void get_antecedents(conflict_resolution & cr) { - m_theory.get_eq_antecedents(m_v1, m_v2, m_timestamp, cr); - } - - virtual proof * mk_proof(conflict_resolution & cr) { NOT_IMPLEMENTED_YET(); return 0; } - }; - - class implied_bound_justification : public justification { - theory_diff_logic& m_theory; - edge_id m_subsumed_edge; - edge_id m_bridge_edge; - public: - implied_bound_justification(theory_diff_logic & theory, edge_id se, edge_id be): - m_theory(theory), - m_subsumed_edge(se), - m_bridge_edge(be) { - } - - virtual void get_antecedents(conflict_resolution & cr) { - m_theory.get_implied_bound_antecedents(m_bridge_edge, m_subsumed_edge, cr); - } - - virtual proof * mk_proof(conflict_resolution & cr) { NOT_IMPLEMENTED_YET(); return 0; } - }; - - enum atom_kind { - LE_ATOM, - EQ_ATOM - }; - class atom { - protected: - atom_kind m_kind; bool_var m_bvar; bool m_true; + int m_pos; + int m_neg; public: - atom(atom_kind k, bool_var bv) : m_kind(k), m_bvar(bv), m_true(false) {} - virtual ~atom() {} - atom_kind kind() const { return m_kind; } - bool_var get_bool_var() const { return m_bvar; } - bool is_true() const { return m_true; } - void assign_eh(bool is_true) { m_true = is_true; } - virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; - }; - - class le_atom : public atom { - int m_pos; - int m_neg; - public: - le_atom(bool_var bv, int pos, int neg): - atom(LE_ATOM, bv), + atom(bool_var bv, int pos, int neg): + m_bvar(bv), m_true(false), m_pos(pos), m_neg(neg) { } - virtual ~le_atom() {} + ~atom() {} + bool_var get_bool_var() const { return m_bvar; } + bool is_true() const { return m_true; } + void assign_eh(bool is_true) { m_true = is_true; } int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } int get_pos() const { return m_pos; } int get_neg() const { return m_neg; } - virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; - }; - - class eq_atom : public atom { - app_ref m_le; - app_ref m_ge; - public: - eq_atom(bool_var bv, app_ref& le, app_ref& ge): - atom(EQ_ATOM, bv), - m_le(le), - m_ge(ge) - {} - virtual ~eq_atom() {} - virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; - app* get_le() const { return m_le.get(); } - app* get_ge() const { return m_ge.get(); } + std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; }; typedef ptr_vector atoms; @@ -239,19 +159,7 @@ namespace smt { unsigned m_asserted_qhead_old; }; - class theory_diff_logic_del_eh : public clause_del_eh { - theory_diff_logic& m_super; - public: - theory_diff_logic_del_eh(theory_diff_logic& s) : m_super(s) {} - virtual ~theory_diff_logic_del_eh() {} - virtual void operator()(ast_manager&, clause* cls) { - TRACE("dl_activity", tout << "deleting " << cls << "\n";); - m_super.del_clause_eh(cls); - dealloc(this); - } - }; - - smt_params & m_params; + smt_params & m_params; arith_util m_util; arith_eq_adapter m_arith_eq_adapter; theory_diff_logic_statistics m_stats; @@ -259,8 +167,6 @@ namespace smt { theory_var m_zero_int; // cache the variable representing the zero variable. theory_var m_zero_real; // cache the variable representing the zero variable. int_vector m_scc_id; // Cheap equality propagation - bool m_modified_since_eq_prop; // true if new constraints were asserted - // since last eq propagation. eq_prop_info_set m_eq_prop_info_set; // set of existing equality prop infos ptr_vector m_eq_prop_infos; @@ -289,20 +195,14 @@ namespace smt { virtual theory_var mk_var(enode* n); virtual theory_var mk_var(app* n); - - void mark_as_modified_since_eq_prop(); - - void unmark_as_modified_since_eq_prop(); - - bool propagate_cheap_equalities(); - + void compute_delta(); void found_non_diff_logic_expr(expr * n); - bool is_interpreted(app* n) const; - - void del_clause_eh(clause* cls); + bool is_interpreted(app* n) const { + return get_family_id() == n->get_family_id(); + } public: theory_diff_logic(ast_manager& m, smt_params & params): @@ -312,7 +212,6 @@ namespace smt { m_arith_eq_adapter(*this, params, m_util), m_zero_int(null_theory_var), m_zero_real(null_theory_var), - m_modified_since_eq_prop(false), m_asserted_qhead(0), m_num_core_conflicts(0), m_num_propagation_calls(0), @@ -323,7 +222,7 @@ namespace smt { m_nc_functor(*this) { } - ~theory_diff_logic() { + virtual ~theory_diff_logic() { reset_eh(); } @@ -360,7 +259,7 @@ namespace smt { m_arith_eq_adapter.restart_eh(); } - virtual void relevant_eh(app* e); + virtual void relevant_eh(app* e) {} virtual void init_search_eh() { m_arith_eq_adapter.init_search_eh(); diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 9f2c97a84..390803957 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -31,34 +31,15 @@ Revision History: using namespace smt; + template -std::ostream& theory_diff_logic::atom::display(theory_diff_logic const& th, std::ostream& out) const { +std::ostream& theory_diff_logic::atom::display(theory_diff_logic const& th, std::ostream& out) const { context& ctx = th.get_context(); lbool asgn = ctx.get_assignment(m_bvar); //SASSERT(asgn == l_undef || ((asgn == l_true) == m_true)); bool sign = (l_undef == asgn) || m_true; return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; -} - -template -std::ostream& theory_diff_logic::eq_atom::display(theory_diff_logic const& th, std::ostream& out) const { - atom::display(th, out); - lbool asgn = th.get_context().get_assignment(this->m_bvar); - if (l_undef == asgn) { - out << "unassigned\n"; - } - else { - out << mk_pp(m_le.get(), m_le.get_manager()) << " " - << mk_pp(m_ge.get(), m_ge.get_manager()) << "\n"; - } - return out; -} - -template -std::ostream& theory_diff_logic::le_atom::display(theory_diff_logic const& th, std::ostream& out) const { - atom::display(th, out); - lbool asgn = th.get_context().get_assignment(this->m_bvar); if (l_undef == asgn) { out << "unassigned\n"; } @@ -94,7 +75,6 @@ void theory_diff_logic::init(context * ctx) { e = ctx->mk_enode(zero, false, false, true); SASSERT(!is_attached_to_var(e)); m_zero_real = mk_var(e); - } @@ -277,7 +257,7 @@ bool theory_diff_logic::internalize_atom(app * n, bool gate_ctx) { k -= this->m_epsilon; } edge_id neg = m_graph.add_edge(target, source, k, ~l); - le_atom * a = alloc(le_atom, bv, pos, neg); + atom * a = alloc(atom, bv, pos, neg); m_atoms.push_back(a); m_bool_var2atom.insert(bv, a); @@ -318,7 +298,7 @@ template void theory_diff_logic::assign_eh(bool_var v, bool is_true) { m_stats.m_num_assertions++; atom * a = 0; - m_bool_var2atom.find(v, a); + VERIFY (m_bool_var2atom.find(v, a)); SASSERT(a); SASSERT(get_context().get_assignment(v) != l_undef); SASSERT((get_context().get_assignment(v) == l_true) == is_true); @@ -330,10 +310,11 @@ void theory_diff_logic::assign_eh(bool_var v, bool is_true) { template void theory_diff_logic::collect_statistics(::statistics & st) const { st.update("dl conflicts", m_stats.m_num_conflicts); - st.update("dl propagations", m_stats.m_num_th2core_prop); st.update("dl asserts", m_stats.m_num_assertions); st.update("core->dl eqs", m_stats.m_num_core2th_eqs); + st.update("core->dl diseqs", m_stats.m_num_core2th_diseqs); m_arith_eq_adapter.collect_statistics(st); + m_graph.collect_statistics(st); } template @@ -374,15 +355,6 @@ final_check_status theory_diff_logic::final_check_eh() { // either will already be zero (as we don't do mixed constraints). m_graph.set_to_zero(m_zero_int, m_zero_real); SASSERT(is_consistent()); - - -#if 0 - TBD: - if (propagate_cheap_equalities()) { - return FC_CONTINUE; - } -#endif - if (m_non_diff_logic_exprs) { return FC_GIVEUP; } @@ -506,61 +478,14 @@ bool theory_diff_logic::propagate_atom(atom* a) { if (ctx.inconsistent()) { return false; } - switch(a->kind()) { - case LE_ATOM: { - int edge_id = dynamic_cast(a)->get_asserted_edge(); - if (!m_graph.enable_edge(edge_id)) { - set_neg_cycle_conflict(); - return false; - } -#if 0 - if (m_params.m_arith_bound_prop != BP_NONE) { - svector subsumed; - m_graph.find_subsumed1(edge_id, subsumed); - for (unsigned i = 0; i < subsumed.size(); ++i) { - int subsumed_edge_id = subsumed[i]; - literal l = m_graph.get_explanation(subsumed_edge_id); - context & ctx = get_context(); - region& r = ctx.get_region(); - ++m_stats.m_num_th2core_prop; - ctx.assign(l, new (r) implied_bound_justification(*this, subsumed_edge_id, edge_id)); - } - - } -#endif - break; - } - case EQ_ATOM: - if (!a->is_true()) { - SASSERT(ctx.get_assignment(a->get_bool_var()) == l_false); - // eq_atom * ea = dynamic_cast(a); - } - break; + int edge_id = a->get_asserted_edge(); + if (!m_graph.enable_edge(edge_id)) { + set_neg_cycle_conflict(); + return false; } return true; } - - -template -void theory_diff_logic::mark_as_modified_since_eq_prop() { - if (!m_modified_since_eq_prop) { - get_context().push_trail(value_trail(m_modified_since_eq_prop)); - m_modified_since_eq_prop = true; - } -} - -template -void theory_diff_logic::unmark_as_modified_since_eq_prop() { - get_context().push_trail(value_trail(m_modified_since_eq_prop)); - m_modified_since_eq_prop = false; -} - -template -void theory_diff_logic::del_clause_eh(clause* cls) { - -} - template void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { @@ -609,7 +534,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges atom* a = 0; m_bool_var2atom.find(bv, a); SASSERT(a); - edge_id e_id = static_cast(a)->get_pos(); + edge_id e_id = a->get_pos(); literal_vector lits; for (unsigned i = 0; i < num_edges; ++i) { @@ -633,11 +558,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges lits.size(), lits.c_ptr(), params.size(), params.c_ptr()); } - clause_del_eh* del_eh = alloc(theory_diff_logic_del_eh, *this); - clause* cls = ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, del_eh); - if (!cls) { - dealloc(del_eh); - } + ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); if (dump_lemmas()) { char const * logic = m_is_lia ? "QF_LIA" : "QF_LRA"; ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); @@ -677,7 +598,6 @@ void theory_diff_logic::set_neg_cycle_conflict() { literal_vector const& lits = m_nc_functor.get_lits(); context & ctx = get_context(); TRACE("arith_conflict", - //display(tout); tout << "conflict: "; for (unsigned i = 0; i < lits.size(); ++i) { ctx.display_literal_info(tout, lits[i]); @@ -802,7 +722,6 @@ theory_var theory_diff_logic::mk_num(app* n, rational const& r) { template theory_var theory_diff_logic::mk_var(enode* n) { - mark_as_modified_since_eq_prop(); theory_var v = theory::mk_var(n); TRACE("diff_logic_vars", tout << "mk_var: " << v << "\n";); m_graph.init_var(v); @@ -810,10 +729,6 @@ theory_var theory_diff_logic::mk_var(enode* n) { return v; } -template -bool theory_diff_logic::is_interpreted(app* n) const { - return n->get_family_id() == get_family_id(); -} template theory_var theory_diff_logic::mk_var(app* n) { @@ -854,7 +769,6 @@ void theory_diff_logic::reset_eh() { m_asserted_atoms .reset(); m_stats .reset(); m_scopes .reset(); - m_modified_since_eq_prop = false; m_asserted_qhead = 0; m_num_core_conflicts = 0; m_num_propagation_calls = 0; @@ -865,70 +779,6 @@ void theory_diff_logic::reset_eh() { } -template -bool theory_diff_logic::propagate_cheap_equalities() { - bool result = false; - TRACE("dl_new_eq", get_context().display(tout);); - context& ctx = get_context(); - region& reg = ctx.get_region(); - SASSERT(m_eq_prop_info_set.empty()); - SASSERT(m_eq_prop_infos.empty()); - if (m_modified_since_eq_prop) { - m_graph.compute_zero_edge_scc(m_scc_id); - int n = get_num_vars(); - for (theory_var v = 0; v < n; v++) { - rational delta_r; - theory_var x_v = expand(true, v, delta_r); - numeral delta(delta_r); - int scc_id = m_scc_id[x_v]; - if (scc_id != -1) { - delta += m_graph.get_assignment(x_v); - TRACE("eq_scc", tout << v << " " << x_v << " " << scc_id << " " << delta << "\n";); - eq_prop_info info(scc_id, delta); - typename eq_prop_info_set::entry * entry = m_eq_prop_info_set.find_core(&info); - if (entry == 0) { - eq_prop_info * new_info = alloc(eq_prop_info, scc_id, delta, v); - m_eq_prop_info_set.insert(new_info); - m_eq_prop_infos.push_back(new_info); - } - else { - // new equality found - theory_var r = entry->get_data()->get_root(); - - enode * n1 = get_enode(v); - enode * n2 = get_enode(r); - if (n1->get_root() != n2->get_root()) { - // r may be an alias (i.e., it is not realy in the graph). So, I should expand it. - // nsb: ?? - rational r_delta; - theory_var x_r = expand(true, r, r_delta); - - justification* j = new (reg) implied_eq_justification(*this, x_v, x_r, m_graph.get_timestamp()); - (void)j; - - m_stats.m_num_th2core_eqs++; - // TBD: get equality into core. - - NOT_IMPLEMENTED_YET(); - // new_eq_eh(x_v, x_r, *j); - result = true; - } - } - } - } - m_eq_prop_info_set.reset(); - std::for_each(m_eq_prop_infos.begin(), m_eq_prop_infos.end(), delete_proc()); - m_eq_prop_infos.reset(); - unmark_as_modified_since_eq_prop(); - } - - TRACE("dl_new_eq", get_context().display(tout);); - SASSERT(!m_modified_since_eq_prop); - - return result; -} - - template void theory_diff_logic::compute_delta() { m_delta = rational(1); @@ -1001,30 +851,9 @@ bool theory_diff_logic::is_consistent() const { lbool asgn = ctx.get_assignment(bv); if (ctx.is_relevant(ctx.bool_var2expr(bv)) && asgn != l_undef) { SASSERT((asgn == l_true) == a->is_true()); - switch(a->kind()) { - case LE_ATOM: { - le_atom* le = dynamic_cast(a); - int edge_id = le->get_asserted_edge(); - SASSERT(m_graph.is_enabled(edge_id)); - SASSERT(m_graph.is_feasible(edge_id)); - break; - } - case EQ_ATOM: { - eq_atom* ea = dynamic_cast(a); - bool_var bv1 = ctx.get_bool_var(ea->get_le()); - bool_var bv2 = ctx.get_bool_var(ea->get_ge()); - lbool val1 = ctx.get_assignment(bv1); - lbool val2 = ctx.get_assignment(bv2); - if (asgn == l_true) { - SASSERT(val1 == l_true); - SASSERT(val2 == l_true); - } - else { - SASSERT(val1 == l_false || val2 == l_false); - } - break; - } - } + int edge_id = a->get_asserted_edge(); + SASSERT(m_graph.is_enabled(edge_id)); + SASSERT(m_graph.is_feasible(edge_id)); } } return m_graph.is_feasible(); @@ -1087,7 +916,6 @@ void theory_diff_logic::new_eq_or_diseq(bool is_eq, theory_var v1, theory_v // assign the corresponding equality literal. // - mark_as_modified_since_eq_prop(); app_ref eq(m), s2(m), t2(m); app* s1 = get_enode(s)->get_owner(); @@ -1142,10 +970,6 @@ void theory_diff_logic::new_diseq_eh(theory_var v1, theory_var v2) { } -template -void theory_diff_logic::relevant_eh(app* e) { -} - struct imp_functor { conflict_resolution & m_cr; diff --git a/src/smt/theory_dl.cpp b/src/smt/theory_dl.cpp index 758f78c2c..4ef1bd8e0 100644 --- a/src/smt/theory_dl.cpp +++ b/src/smt/theory_dl.cpp @@ -68,14 +68,11 @@ namespace smt { bv_util& b() { return m_bv; } class dl_value_proc : public smt::model_value_proc { - smt::model_generator & m_mg; theory_dl& m_th; smt::enode* m_node; public: - dl_value_proc(smt::model_generator & m, theory_dl& th, smt::enode* n): - m_mg(m), m_th(th), m_node(n) - { } + dl_value_proc(theory_dl& th, smt::enode* n) : m_th(th), m_node(n) {} virtual void get_dependencies(buffer & result) {} @@ -94,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); @@ -165,8 +162,8 @@ namespace smt { m.register_factory(alloc(dl_factory, m_util, m.get_model())); } - virtual smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator & m) { - return alloc(dl_value_proc, m, *this, n); + virtual smt::model_value_proc * mk_value(smt::enode * n) { + return alloc(dl_value_proc, *this, n); } virtual void apply_sort_cnstr(enode * n, sort * s) { diff --git a/src/smt/theory_horn_ineq.cpp b/src/smt/theory_horn_ineq.cpp new file mode 100644 index 000000000..978b5b003 --- /dev/null +++ b/src/smt/theory_horn_ineq.cpp @@ -0,0 +1,236 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_horn_ineq.h + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-18 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ +#include "theory_horn_ineq.h" +#include "theory_horn_ineq_def.h" + +namespace smt { + + template class theory_horn_ineq; + template class theory_horn_ineq; + + // similar to test_diff_logic: + + horn_ineq_tester::horn_ineq_tester(ast_manager& m): m(m), a(m) {} + + bool horn_ineq_tester::operator()(expr* e) { + m_todo.reset(); + m_pols.reset(); + pos_mark.reset(); + neg_mark.reset(); + m_todo.push_back(e); + m_pols.push_back(l_true); + while (!m_todo.empty()) { + expr* e = m_todo.back(); + lbool p = m_pols.back(); + m_todo.pop_back(); + m_pols.pop_back(); + switch (p) { + case l_true: + if (pos_mark.is_marked(e)) { + continue; + } + pos_mark.mark(e, true); + break; + case l_false: + if (neg_mark.is_marked(e)) { + continue; + } + neg_mark.mark(e, true); + break; + case l_undef: + if (pos_mark.is_marked(e) && neg_mark.is_marked(e)) { + continue; + } + pos_mark.mark(e, true); + neg_mark.mark(e, true); + break; + } + if (!test_expr(p, e)) { + return false; + } + } + return true; + } + + vector > const& horn_ineq_tester::get_linearization() const { + return m_terms; + } + + bool horn_ineq_tester::test_expr(lbool p, expr* e) { + expr* e1, *e2, *e3; + if (is_var(e)) { + return true; + } + if (!is_app(e)) { + return false; + } + app* ap = to_app(e); + if (m.is_and(ap) || m.is_or(ap)) { + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + m_todo.push_back(ap->get_arg(i)); + m_pols.push_back(p); + } + } + else if (m.is_not(e, e1)) { + m_todo.push_back(e); + m_pols.push_back(~p); + } + else if (m.is_ite(e, e1, e2, e3)) { + m_todo.push_back(e1); + m_pols.push_back(l_undef); + m_todo.push_back(e2); + m_pols.push_back(p); + m_todo.push_back(e3); + m_pols.push_back(p); + } + else if (m.is_iff(e, e1, e2)) { + m_todo.push_back(e1); + m_pols.push_back(l_undef); + m_todo.push_back(e2); + m_pols.push_back(l_undef); + m_todo.push_back(e2); + } + else if (m.is_implies(e, e1, e2)) { + m_todo.push_back(e1); + m_pols.push_back(~p); + m_todo.push_back(e2); + m_pols.push_back(p); + } + else if (m.is_eq(e, e1, e2)) { + return linearize(e1, e2) == diff; + } + else if (m.is_true(e) || m.is_false(e)) { + // no-op + } + else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) || + a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { + if (p == l_false) { + std::swap(e2, e1); + } + classify_t cl = linearize(e1, e2); + switch(p) { + case l_undef: + return cl == diff; + case l_true: + case l_false: + return cl == horn || cl == diff; + } + } + else if (!is_uninterp_const(e)) { + return false; + } + return true; + } + + bool horn_ineq_tester::operator()(unsigned num_fmls, expr* const* fmls) { + for (unsigned i = 0; i < num_fmls; ++i) { + if (!(*this)(fmls[i])) { + return false; + } + } + return true; + } + + horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e, rational(1))); + return linearize(); + } + + horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e1, expr* e2) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e1, rational(1))); + m_terms.push_back(std::make_pair(e2, rational(-1))); + return linearize(); + } + + horn_ineq_tester::classify_t horn_ineq_tester::linearize() { + + m_weight.reset(); + m_coeff_map.reset(); + + while (!m_terms.empty()) { + expr* e1, *e2; + rational num; + rational mul = m_terms.back().second; + expr* e = m_terms.back().first; + m_terms.pop_back(); + if (a.is_add(e)) { + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul)); + } + } + else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_sub(e, e1, e2)) { + m_terms.push_back(std::make_pair(e1, mul)); + m_terms.push_back(std::make_pair(e2, -mul)); + } + else if (a.is_uminus(e, e1)) { + m_terms.push_back(std::make_pair(e1, -mul)); + } + else if (a.is_numeral(e, num)) { + m_weight += num*mul; + } + else if (a.is_to_real(e, e1)) { + m_terms.push_back(std::make_pair(e1, mul)); + } + else if (!is_uninterp_const(e)) { + return non_horn; + } + else { + m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul; + } + } + unsigned num_negative = 0; + unsigned num_positive = 0; + bool is_unit_pos = true, is_unit_neg = true; + obj_map::iterator it = m_coeff_map.begin(); + obj_map::iterator end = m_coeff_map.end(); + for (; it != end; ++it) { + rational r = it->m_value; + if (r.is_zero()) { + continue; + } + m_terms.push_back(std::make_pair(it->m_key, r)); + if (r.is_pos()) { + is_unit_pos = is_unit_pos && r.is_one(); + num_positive++; + } + else { + is_unit_neg = is_unit_neg && r.is_minus_one(); + num_negative++; + } + } + if (num_negative <= 1 && is_unit_pos && is_unit_neg && num_positive <= 1) { + return diff; + } + if (num_positive <= 1 && is_unit_pos) { + return horn; + } + if (num_negative <= 1 && is_unit_neg) { + return co_horn; + } + return non_horn; + } + + +} diff --git a/src/smt/theory_horn_ineq.h b/src/smt/theory_horn_ineq.h new file mode 100644 index 000000000..ed96f25f6 --- /dev/null +++ b/src/smt/theory_horn_ineq.h @@ -0,0 +1,328 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_horn_ineq.h + +Abstract: + + A*x <= weight + D*x, coefficients to A and D are non-negative, + D is a diagonal matrix. + Coefficients to weight may have both signs. + + Label variables by weight. + Select inequality that is not satisfied. + Set delta(LHS) := 0 + Set delta(RHS(x)) := weight(x) - b + Propagate weight increment through inequalities. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-18 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ + +#ifndef _THEORY_HORN_INEQ_H_ +#define _THEORY_HORN_INEQ_H_ + +#include"rational.h" +#include"inf_rational.h" +#include"inf_int_rational.h" +#include"inf_eps_rational.h" +#include"smt_theory.h" +#include"arith_decl_plugin.h" +#include"smt_justification.h" +#include"map.h" +#include"smt_params.h" +#include"arith_eq_adapter.h" +#include"smt_model_generator.h" +#include"numeral_factory.h" +#include"smt_clause.h" + +namespace smt { + + class horn_ineq_tester { + ast_manager& m; + arith_util a; + ptr_vector m_todo; + svector m_pols; + ast_mark pos_mark, neg_mark; + obj_map m_coeff_map; + rational m_weight; + vector > m_terms; + + public: + enum classify_t { + co_horn, + horn, + diff, + non_horn + }; + horn_ineq_tester(ast_manager& m); + + // test if formula is in the Horn inequality fragment: + bool operator()(expr* fml); + bool operator()(unsigned num_fmls, expr* const* fmls); + + // linearize inequality/equality + classify_t linearize(expr* e); + classify_t linearize(expr* e1, expr* e2); + + // retrieve linearization + vector > const& get_linearization() const; + rational const& get_weight() const { return m_weight; } + private: + bool test_expr(lbool p, expr* e); + classify_t linearize(); + }; + + template + class theory_horn_ineq : public theory, private Ext { + + typedef typename Ext::numeral numeral; + typedef typename Ext::inf_numeral inf_numeral; + typedef literal explanation; + typedef theory_var th_var; + typedef svector th_var_vector; + typedef unsigned clause_id; + typedef vector > coeffs; + + class clause; + class graph; + class assignment_trail; + class parent_trail; + + class atom { + protected: + bool_var m_bvar; + bool m_true; + int m_pos; + int m_neg; + public: + atom(bool_var bv, int pos, int neg) : + m_bvar(bv), m_true(false), + m_pos(pos), m_neg(neg) {} + ~atom() {} + bool_var get_bool_var() const { return m_bvar; } + bool is_true() const { return m_true; } + void assign_eh(bool is_true) { m_true = is_true; } + int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } + int get_pos() const { return m_pos; } + int get_neg() const { return m_neg; } + std::ostream& display(theory_horn_ineq const& th, std::ostream& out) const; + }; + typedef svector atoms; + + struct scope { + unsigned m_atoms_lim; + unsigned m_asserted_atoms_lim; + unsigned m_asserted_qhead_old; + }; + + struct stats { + unsigned m_num_conflicts; + unsigned m_num_assertions; + unsigned m_num_core2th_eqs; + unsigned m_num_core2th_diseqs; + + void reset() { + memset(this, 0, sizeof(*this)); + } + + stats() { + reset(); + } + }; + + stats m_stats; + smt_params m_params; + arith_util a; + arith_eq_adapter m_arith_eq_adapter; + th_var m_zero_int; // cache the variable representing the zero variable. + th_var m_zero_real; // cache the variable representing the zero variable. + + graph* m_graph; + atoms m_atoms; + unsigned_vector m_asserted_atoms; // set of asserted atoms + unsigned m_asserted_qhead; + u_map m_bool_var2atom; + svector m_scopes; + + double m_agility; + bool m_lia; + bool m_lra; + bool m_non_horn_ineq_exprs; + + horn_ineq_tester m_test; + + + arith_factory * m_factory; + rational m_delta; + rational m_lambda; + + + // Set a conflict due to a negative cycle. + void set_neg_cycle_conflict(); + + // Create a new theory variable. + virtual th_var mk_var(enode* n); + + virtual th_var mk_var(expr* n); + + void compute_delta(); + + void found_non_horn_ineq_expr(expr * n); + + bool is_interpreted(app* n) const { + return n->get_family_id() == get_family_id(); + } + + public: + theory_horn_ineq(ast_manager& m); + + virtual ~theory_horn_ineq(); + + virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_horn_ineq, get_manager()); } + + virtual char const * get_name() const { return "horn-inequality-logic"; } + + /** + \brief See comment in theory::mk_eq_atom + */ + virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); } + + virtual void init(context * ctx); + + virtual bool internalize_atom(app * atom, bool gate_ctx); + + virtual bool internalize_term(app * term); + + virtual void internalize_eq_eh(app * atom, bool_var v); + + virtual void assign_eh(bool_var v, bool is_true); + + virtual void new_eq_eh(th_var v1, th_var v2) { + m_arith_eq_adapter.new_eq_eh(v1, v2); + } + + virtual bool use_diseqs() const { return true; } + + virtual void new_diseq_eh(th_var v1, th_var v2) { + m_arith_eq_adapter.new_diseq_eh(v1, v2); + } + + virtual void push_scope_eh(); + + virtual void pop_scope_eh(unsigned num_scopes); + + virtual void restart_eh() { + m_arith_eq_adapter.restart_eh(); + } + + virtual void relevant_eh(app* e) {} + + virtual void init_search_eh() { + m_arith_eq_adapter.init_search_eh(); + } + + virtual final_check_status final_check_eh(); + + virtual bool is_shared(th_var v) const { + return false; + } + + virtual bool can_propagate() { + SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); + return m_asserted_qhead != m_asserted_atoms.size(); + } + + virtual void propagate(); + + virtual justification * why_is_diseq(th_var v1, th_var v2) { + UNREACHABLE(); + return 0; + } + + virtual void reset_eh(); + + virtual void init_model(model_generator & m); + + virtual model_value_proc * mk_value(enode * n, model_generator & mg); + + virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const { + return true; + } + + virtual void display(std::ostream & out) const; + + virtual void collect_statistics(::statistics & st) const; + + private: + + virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_eqs++; + new_eq_or_diseq(true, v1, v2, j); + } + + virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_diseqs++; + new_eq_or_diseq(false, v1, v2, j); + } + + void negate(coeffs& coeffs, rational& weight); + numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; + void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); + + void del_atoms(unsigned old_size); + + void propagate_core(); + + bool propagate_atom(atom const& a); + + th_var mk_term(app* n); + + th_var mk_num(app* n, rational const& r); + + bool is_consistent() const; + + th_var expand(bool pos, th_var v, rational & k); + + void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); + + th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; } + + th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } + + void inc_conflicts(); + + }; + + struct rhi_ext { + typedef inf_rational inf_numeral; + typedef inf_eps_rational numeral; + numeral m_epsilon; + numeral m_minus_infty; + rhi_ext() : m_epsilon(inf_rational(rational(), true)), m_minus_infty(rational(-1),inf_rational()) {} + }; + + struct ihi_ext { + typedef rational inf_numeral; + typedef inf_eps_rational numeral; + numeral m_epsilon; + numeral m_minus_infty; + ihi_ext() : m_epsilon(rational(1)), m_minus_infty(rational(-1),rational(0)) {} + }; + + typedef theory_horn_ineq theory_rhi; + typedef theory_horn_ineq theory_ihi; +}; + + + + +#endif /* _THEORY_HORN_INEQ_H_ */ diff --git a/src/smt/theory_horn_ineq_def.h b/src/smt/theory_horn_ineq_def.h new file mode 100644 index 000000000..f4fa7e8d7 --- /dev/null +++ b/src/smt/theory_horn_ineq_def.h @@ -0,0 +1,1166 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_horn_ineq_def.h + +Abstract: + + A*x <= b + D*x, coefficients to A and D are non-negative, + D is a diagonal matrix. + Coefficients to b may have both signs. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-18 + +Revision History: + +--*/ + +#ifndef _THEORY_HORN_INEQ_DEF_H_ +#define _THEORY_HORN_INEQ_DEF_H_ +#include "theory_horn_ineq.h" +#include "ast_pp.h" +#include "smt_context.h" + +namespace smt { + + static const unsigned null_clause_id = UINT_MAX; + + /** + A clause represents an inequality of the form + + v1*c1 + v2*c2 + .. + v_n*c_n + w <= v*c + + where + - m_vars: [v1,v2,...,v_n] + - m_coeffs: [c1,c2,...,c_n] + - m_var: v + - m_coeff: c + - m_weight: w + + */ + template + class theory_horn_ineq::clause { + vector m_coeffs; // coefficients of body. + svector m_vars; // variables of body. + rational m_coeff; // coefficient of head. + th_var m_var; // head variable. + numeral m_weight; // constant to add + literal m_explanation; + bool m_enabled; + public: + clause(unsigned sz, rational const* coeffs, th_var const* vars, + rational const& coeff, th_var var, numeral const& w, + const literal& ex): + m_coeffs(sz, coeffs), + m_vars(sz, vars), + m_coeff(coeff), + m_var(var), + m_weight(w), + m_explanation(ex), + m_enabled(false) { + DEBUG_CODE( + { + for (unsigned i = 0; i < size(); ++i) { + SASSERT(coeffs[i].is_pos()); + } + SASSERT(coeff.is_pos()); + }); + } + + th_var vars(unsigned i) const { return m_vars[i]; } + rational const& coeffs(unsigned i) const { return m_coeffs[i]; } + th_var var() const { return m_var; } + rational const& coeff() const { return m_coeff; } + const numeral & get_weight() const { return m_weight; } + const literal & get_explanation() const { return m_explanation; } + bool is_enabled() const { return m_enabled; } + unsigned size() const { return m_vars.size(); } + + void enable() { m_enabled = true; } + void disable() { m_enabled = false; } + + void display(std::ostream& out) const { + out << (is_enabled()?"+ ":"- "); + for (unsigned i = 0; i < size(); ++i) { + if (i > 0 && coeffs(i).is_pos()) { + out << " + "; + } + display(out, coeffs(i), vars(i)); + } + if (!get_weight().is_zero()) { + out << " + " << get_weight(); + } + display(out << " <= ", coeff(), var()); + out << "\n"; + } + + private: + + void display(std::ostream& out, rational const& c, th_var v) const { + if (!c.is_one()) { + out << c << "*"; + } + out << "v" << v; + } + }; + + template + class theory_horn_ineq::assignment_trail { + th_var m_var; + numeral m_old_value; + public: + assignment_trail(th_var v, const numeral & val): + m_var(v), + m_old_value(val) {} + th_var get_var() const { return m_var; } + const numeral & get_old_value() const { return m_old_value; } + }; + + template + class theory_horn_ineq::parent_trail { + th_var m_var; + clause_id m_old_value; + public: + parent_trail(th_var v, clause_id val): + m_var(v), + m_old_value(val) {} + th_var get_var() const { return m_var; } + clause_id get_old_value() const { return m_old_value; } + }; + + + template + class theory_horn_ineq::graph : private Ext { + + typedef vector assignment_stack; + typedef vector parent_stack; + typedef unsigned_vector clause_id_vector; + + struct stats { + unsigned m_propagation_cost; + + void reset() { + memset(this, 0, sizeof(*this)); + } + }; + + struct scope { + unsigned m_clauses_lim; + unsigned m_enabled_clauses_lim; + unsigned m_assignment_lim; + unsigned m_parent_lim; + scope(unsigned e, unsigned enabled, unsigned alim, unsigned plim): + m_clauses_lim(e), + m_enabled_clauses_lim(enabled), + m_assignment_lim(alim), + m_parent_lim(plim) { + } + }; + + stats m_stats; + vector m_clauses; + vector m_assignment; // per var + clause_id_vector m_parent; // per var + assignment_stack m_assignment_stack; // stack for restoring the assignment + parent_stack m_parent_stack; // stack for restoring parents + clause_id_vector m_enabled_clauses; + vector m_out_clauses; // use-list for clauses. + vector m_in_clauses; // clauses that have variable in head. + // forward reachability + unsigned_vector m_onstack; + unsigned m_ts; + unsigned_vector m_todo; + literal_vector m_lits; + vector m_coeffs; + th_var m_zero; + clause_id m_unsat_clause; + svector m_trail_stack; + + + public: + + graph(): m_ts(0), m_zero(null_theory_var), m_unsat_clause(null_clause_id) {} + + void reset() { + m_clauses .reset(); + m_assignment .reset(); + m_parent .reset(); + m_assignment_stack .reset(); + m_parent_stack .reset(); + m_out_clauses .reset(); + m_in_clauses .reset(); + m_enabled_clauses .reset(); + m_onstack .reset(); + m_ts = 0; + m_lits .reset(); + m_trail_stack .reset(); + m_unsat_clause = null_clause_id; + } + + + void traverse_neg_cycle1(bool /*stronger_lemmas*/) { + TRACE("horn_ineq", display(tout);); + SASSERT(!m_enabled_clauses.empty()); + clause_id id = m_unsat_clause; + SASSERT(id != null_clause_id); + SASSERT(!is_feasible(m_clauses[id])); + clause_id_vector todo; + vector muls; + todo.push_back(id); + muls.push_back(rational(1)); + u_map lits; + while (!todo.empty()) { + id = todo.back(); + rational mul = muls.back(); + todo.pop_back(); + muls.pop_back(); + clause const& cl = m_clauses[id]; + literal lit = cl.get_explanation(); + if (lit != null_literal) { + lits.insert_if_not_there2(id, rational(0))->get_data().m_value += mul; + } + for (unsigned i = 0; i < cl.size(); ++i) { + id = m_parent[cl.vars(i)]; + if (id != null_clause_id) { + todo.push_back(id); + muls.push_back(mul*cl.coeffs(i)); + } + } + } + u_map::iterator it = lits.begin(), end = lits.end(); + m_lits.reset(); + m_coeffs.reset(); + for (; it != end; ++it) { + m_lits.push_back(m_clauses[it->m_key].get_explanation()); + m_coeffs.push_back(it->m_value); + } + + // TODO: use topological sort to tune traversal of parents to linear. + // (current traversal can be exponential). + // TODO: negative cycle traversal with inline resolution to find + // stronger conflict clauses. + // follow heuristic used in theory_diff_logic_def.h: + } + + unsigned get_num_clauses() const { + return m_clauses.size(); + } + + literal_vector const& get_lits() const { + return m_lits; + } + + vector const& get_coeffs() const { + return m_coeffs; + } + + numeral get_assignment(th_var v) const { + return m_assignment[v]; + } + + numeral eval_body(clause const& cl) const { + numeral v(0); + for (unsigned i = 0; i < cl.size(); ++i) { + v += cl.coeffs(i)*m_assignment[cl.vars(i)]; + } + v += cl.get_weight(); + return v; + } + + numeral eval_body(clause_id id) const { + return eval_body(m_clauses[id]); + } + + numeral eval_head(clause_id id) const { + return eval_head(m_clauses[id]); + } + + numeral eval_head(clause const& cl) const { + return cl.coeff()*m_assignment[cl.var()]; + } + + clause const& get_clause(clause_id id) const { + return m_clauses[id]; + } + + void display_clause(std::ostream& out, clause_id id) const { + if (id == null_clause_id) { + out << "null\n"; + } + else { + m_clauses[id].display(out); + } + } + + void display(std::ostream& out) const { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + display_clause(out, i); + } + for (unsigned i = 0; i < m_assignment.size(); ++i) { + out << m_assignment[i] << "\n"; + } + } + + void collect_statistics(::statistics& st) const { + st.update("hi_propagation_cst", m_stats.m_propagation_cost); + } + + void push() { + m_trail_stack.push_back(scope(m_clauses.size(), m_enabled_clauses.size(), + m_assignment_stack.size(), m_parent_stack.size())); + } + + void pop(unsigned num_scopes) { + unsigned lvl = m_trail_stack.size(); + SASSERT(num_scopes <= lvl); + unsigned new_lvl = lvl - num_scopes; + scope & s = m_trail_stack[new_lvl]; + // restore enabled clauses + for (unsigned i = m_enabled_clauses.size(); i > s.m_enabled_clauses_lim; ) { + --i; + m_clauses[m_enabled_clauses[i]].disable(); + } + m_enabled_clauses.shrink(s.m_enabled_clauses_lim); + + // restore assignment stack + for (unsigned i = m_assignment_stack.size(); i > s.m_assignment_lim; ) { + --i; + m_assignment[m_assignment_stack[i].get_var()] = m_assignment_stack[i].get_old_value(); + } + m_assignment_stack.shrink(s.m_assignment_lim); + + // restore parent stack + for (unsigned i = m_parent_stack.size(); i > s.m_parent_lim; ) { + --i; + m_parent[m_parent_stack[i].get_var()] = m_parent_stack[i].get_old_value(); + } + m_assignment_stack.shrink(s.m_assignment_lim); + + // restore clauses + unsigned old_num_clauses = s.m_clauses_lim; + unsigned num_clauses = m_clauses.size(); + SASSERT(old_num_clauses <= num_clauses); + unsigned to_delete = num_clauses - old_num_clauses; + for (unsigned i = 0; i < to_delete; i++) { + const clause & cl = m_clauses.back(); + TRACE("horn_ineq", tout << "deleting clause:\n"; cl.display(tout);); + for (unsigned j = 0; j < cl.size(); ++j) { + m_out_clauses[cl.vars(j)].pop_back(); + } + m_in_clauses[cl.var()].pop_back(); + m_clauses.pop_back(); + } + m_trail_stack.shrink(new_lvl); + SASSERT(check_invariant()); + } + + /** + \brief Add clause z <= z and the assignment z := 0 + Then z cannot be incremented without causing a loop (and therefore a contradiction). + */ + void set_to_zero(th_var z) { + m_zero = z; + } + + bool enable_clause(clause_id id) { + if (id == null_clause_id) { + return true; + } + clause& cl = m_clauses[id]; + bool r = true; + if (!cl.is_enabled()) { + cl.enable(); + if (!is_feasible(cl)) { + r = make_feasible(id); + } + m_enabled_clauses.push_back(id); + } + return r; + } + + void init_var(th_var v) { + unsigned sz = static_cast(v); + while (m_assignment.size() <= sz) { + m_assignment.push_back(Ext::m_minus_infty); + m_out_clauses.push_back(clause_id_vector()); + m_in_clauses.push_back(clause_id_vector()); + m_parent.push_back(null_clause_id); + m_onstack.push_back(0); + } + m_assignment[v] = Ext::m_minus_infty; + SASSERT(m_out_clauses[v].empty()); + SASSERT(m_in_clauses[v].empty()); + SASSERT(check_invariant()); + } + + clause_id add_ineq(vector > const& terms, numeral const& weight, literal l) { + vector coeffs; + svector vars; + rational coeff(1); + th_var var = null_theory_var; + bool found_negative = false; + for (unsigned i = 0; i < terms.size(); ++i) { + rational const& r = terms[i].second; + if (r.is_pos()) { + coeffs.push_back(terms[i].second); + vars.push_back(terms[i].first); + } + else if (found_negative) { + return null_clause_id; + } + else { + SASSERT(r.is_neg()); + found_negative = true; + coeff = -r; + var = terms[i].first; + } + } + if (!found_negative) { + coeff = rational(1); + var = m_zero; + } + if (!coeff.is_one()) { + // so far just support unit coefficients on right. + return null_clause_id; + } + clause_id id = m_clauses.size(); + m_clauses.push_back(clause(coeffs.size(), coeffs.c_ptr(), vars.c_ptr(), coeff, var, weight, l)); + for (unsigned i = 0; i < vars.size(); ++i) { + m_out_clauses[vars[i]].push_back(id); + } + m_in_clauses[var].push_back(id); + + return id; + } + + bool is_feasible() const { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + if (!is_feasible(m_clauses[i])) { + return false; + } + } + return true; + } + + private: + + bool check_invariant() { + return true; + } + + /** + assignments are fully retraced on backtracking. + This is not always necessary. + */ + + void acc_assignment(th_var v, const numeral & inc) { + m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); + m_assignment[v] += inc; + } + + void acc_parent(th_var v, clause_id parent) { + m_parent[v] = parent; + m_parent_stack.push_back(parent_trail(v, parent)); + } + + numeral get_delta(const clause & cl) const { + SASSERT(cl.coeff().is_one() && "Not yet support for non-units"); + return eval_body(cl) - eval_head(cl); + } + + void set_onstack(th_var v) { + SASSERT(m_ts != 0); + m_onstack[v] = m_ts; + } + + void reset_onstack(th_var v) { + m_onstack[v] = 0; + } + + bool is_onstack(th_var v) const { + return m_onstack[v] == m_ts; + } + + void inc_ts() { + m_ts++; + if (m_ts == 0) { + m_ts++; + m_onstack.reset(); + m_onstack.resize(m_assignment.size(), 0); + } + } + + // Make the assignment feasible. An assignment is feasible if + // Forall clause cl. eval_body(cl) <= eval_head(cl) + // + // This method assumes that if the assignment is not feasible, + // then the only infeasible clause is the last added clause. + // + // Traversal is by naive DFS search. + // + bool make_feasible(clause_id id) { + SASSERT(is_almost_feasible(id)); + SASSERT(!m_clauses.empty()); + SASSERT(!is_feasible(m_clauses[id])); + const clause & cl0 = m_clauses[id]; + inc_ts(); + for (unsigned i = 0; i < cl0.size(); ++i) { + set_onstack(cl0.vars(i)); + } + th_var source = cl0.var(); + numeral delta = get_delta(cl0); + acc_parent(source, id); + SASSERT(delta.is_pos()); + acc_assignment(source, delta); + m_todo.reset(); + m_todo.push_back(source); + + TRACE("horn_ineq", cl0.display(tout);); + + do { + ++m_stats.m_propagation_cost; + + typename clause_id_vector::iterator it = m_out_clauses[source].begin(); + typename clause_id_vector::iterator end = m_out_clauses[source].end(); + for (; it != end; ++it) { + clause & cl = m_clauses[*it]; + if (!cl.is_enabled()) { + continue; + } + delta = get_delta(cl); + + if (delta.is_pos()) { + TRACE("horn_ineq", cl.display(tout);); + th_var target = cl.var(); + if (is_onstack(target)) { + m_unsat_clause = *it; + return false; + } + else { + acc_assignment(target, delta); + acc_parent(target, *it); + m_todo.push_back(target); + } + } + } + set_onstack(source); + source = m_todo.back(); + // pop stack until there is a new variable to process. + while (is_onstack(source)) { + m_todo.pop_back(); + reset_onstack(source); + if (m_todo.empty()) { + break; + } + source = m_todo.back(); + } + } + while (!m_todo.empty()); + return true; + } + + bool is_almost_feasible(clause_id id) const { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + if (id != static_cast(i) && !is_feasible(m_clauses[i])) { + return false; + } + } + return true; + } + + bool is_feasible(const clause & cl) const { + return !cl.is_enabled() || get_delta(cl).is_nonpos(); + } + + }; + + template + theory_horn_ineq::theory_horn_ineq(ast_manager& m): + theory(m.mk_family_id("arith")), + a(m), + m_arith_eq_adapter(*this, m_params, a), + m_zero_int(null_theory_var), + m_zero_real(null_theory_var), + m_graph(0), + m_asserted_qhead(0), + m_agility(0.5), + m_lia(false), + m_lra(false), + m_non_horn_ineq_exprs(false), + m_test(m), + m_factory(0) { + m_graph = alloc(graph); + } + + template + theory_horn_ineq::~theory_horn_ineq() { + reset_eh(); + dealloc(m_graph); + } + + template + std::ostream& theory_horn_ineq::atom::display(theory_horn_ineq const& th, std::ostream& out) const { + context& ctx = th.get_context(); + lbool asgn = ctx.get_assignment(m_bvar); + bool sign = (l_undef == asgn) || m_true; + return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; + if (l_undef == asgn) { + out << "unassigned\n"; + } + else { + th.m_graph->display_clause(out, get_asserted_edge()); + } + return out; + } + + template + theory_var theory_horn_ineq::mk_var(enode* n) { + th_var v = theory::mk_var(n); + m_graph->init_var(v); + get_context().attach_th_var(n, this, v); + return v; + } + + template + theory_var theory_horn_ineq::mk_var(expr* n) { + context & ctx = get_context(); + enode* e = 0; + th_var v = null_theory_var; + m_lia |= a.is_int(n); + m_lra |= a.is_real(n); + if (!is_app(n)) { + return v; + } + if (ctx.e_internalized(n)) { + e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + } + else { + ctx.internalize(n, false); + e = ctx.get_enode(n); + } + if (v == null_theory_var) { + v = mk_var(e); + } + if (is_interpreted(to_app(n))) { + found_non_horn_ineq_expr(n); + } + return v; + } + + template + void theory_horn_ineq::reset_eh() { + m_graph ->reset(); + m_zero_int = null_theory_var; + m_zero_real = null_theory_var; + m_atoms .reset(); + m_asserted_atoms .reset(); + m_stats .reset(); + m_scopes .reset(); + m_asserted_qhead = 0; + m_agility = 0.5; + m_lia = false; + m_lra = false; + m_non_horn_ineq_exprs = false; + theory::reset_eh(); + } + + + + template + void theory_horn_ineq::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) { + rational k; + th_var s = expand(true, v1, k); + th_var t = expand(false, v2, k); + context& ctx = get_context(); + ast_manager& m = get_manager(); + + if (s == t) { + if (is_eq != k.is_zero()) { + // conflict 0 /= k; + inc_conflicts(); + ctx.set_conflict(&eq_just); + } + } + else { + // + // Create equality ast, internalize_atom + // assign the corresponding equality literal. + // + + app_ref eq(m), s2(m), t2(m); + app* s1 = get_enode(s)->get_owner(); + app* t1 = get_enode(t)->get_owner(); + s2 = a.mk_sub(t1, s1); + t2 = a.mk_numeral(k, m.get_sort(s2.get())); + // t1 - s1 = k + eq = m.mk_eq(s2.get(), t2.get()); + + TRACE("horn_ineq", + tout << v1 << " .. " << v2 << "\n"; + tout << mk_pp(eq.get(), m) <<"\n";); + + if (!internalize_atom(eq.get(), false)) { + UNREACHABLE(); + } + + literal l(ctx.get_literal(eq.get())); + if (!is_eq) { + l = ~l; + } + + ctx.assign(l, b_justification(&eq_just), false); + } + } + + template + void theory_horn_ineq::inc_conflicts() { + m_stats.m_num_conflicts++; + if (m_params.m_arith_adaptive) { + double g = m_params.m_arith_adaptive_propagation_threshold; + m_agility = m_agility*g + 1 - g; + } + } + + template + void theory_horn_ineq::set_neg_cycle_conflict() { + m_graph->traverse_neg_cycle1(m_params.m_arith_stronger_lemmas); + inc_conflicts(); + literal_vector const& lits = m_graph->get_lits(); + context & ctx = get_context(); + TRACE("horn_ineq", + tout << "conflict: "; + for (unsigned i = 0; i < lits.size(); ++i) { + ctx.display_literal_info(tout, lits[i]); + } + tout << "\n"; + ); + + if (m_params.m_arith_dump_lemmas) { + char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"; + ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); + } + + vector params; + if (get_manager().proofs_enabled()) { + params.push_back(parameter(symbol("farkas"))); + vector const& coeffs = m_graph->get_coeffs(); + for (unsigned i = 0; i < coeffs.size(); ++i) { + params.push_back(parameter(coeffs[i])); + } + } + + ctx.set_conflict( + ctx.mk_justification( + ext_theory_conflict_justification( + get_id(), ctx.get_region(), + lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); + } + + template + void theory_horn_ineq::found_non_horn_ineq_expr(expr* n) { + if (!m_non_horn_ineq_exprs) { + TRACE("horn_ineq", tout << "found non horn logic expression:\n" << mk_pp(n, get_manager()) << "\n";); + get_context().push_trail(value_trail(m_non_horn_ineq_exprs)); + m_non_horn_ineq_exprs = true; + } + } + + template + void theory_horn_ineq::init(context* ctx) { + theory::init(ctx); + m_zero_int = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); + m_zero_real = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), false), false, false, true)); + m_graph->set_to_zero(m_zero_int); + m_graph->set_to_zero(m_zero_real); + } + + /** + \brief Create negated literal. + + The negation of: E <= 0 + + -E + epsilon <= 0 + or + -E + 1 <= 0 + */ + template + void theory_horn_ineq::negate(coeffs& coeffs, rational& weight) { + for (unsigned i = 0; i < coeffs.size(); ++i) { + coeffs[i].second.neg(); + } + weight.neg(); + } + + template + typename theory_horn_ineq::numeral theory_horn_ineq::mk_weight(bool is_real, bool is_strict, rational const& w) const { + if (is_strict) { + return numeral(inf_numeral(w)) + (is_real?Ext::m_epsilon:numeral(1)); + } + else { + return numeral(inf_numeral(w)); + } + } + + template + void theory_horn_ineq::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) { + coeffs.reset(); + w = m_test.get_weight(); + for (unsigned i = 0; i < terms.size(); ++i) { + coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second)); + } + } + + template + bool theory_horn_ineq::internalize_atom(app * n, bool) { + context & ctx = get_context(); + if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) { + found_non_horn_ineq_expr(n); + return false; + } + SASSERT(!ctx.b_internalized(n)); + expr* e1 = n->get_arg(0), *e2 = n->get_arg(1); + if (a.is_ge(n) || a.is_gt(n)) { + std::swap(e1, e2); + } + bool is_strict = a.is_gt(n) || a.is_lt(n); + + horn_ineq_tester::classify_t cl = m_test.linearize(e1, e2); + if (cl == horn_ineq_tester::non_horn) { + found_non_horn_ineq_expr(n); + return false; + } + + rational w; + coeffs coeffs; + mk_coeffs(m_test.get_linearization(), coeffs, w); + bool_var bv = ctx.mk_bool_var(n); + ctx.set_var_theory(bv, get_id()); + literal l(bv); + numeral w1 = mk_weight(a.is_real(e1), is_strict, w); + clause_id pos = m_graph->add_ineq(coeffs, w1, l); + negate(coeffs, w); + numeral w2 = mk_weight(a.is_real(e1), !is_strict, w); + clause_id neg = m_graph->add_ineq(coeffs, w2, ~l); + m_bool_var2atom.insert(bv, m_atoms.size()); + m_atoms.push_back(atom(bv, pos, neg)); + + TRACE("horn_ineq", + tout << mk_pp(n, get_manager()) << "\n"; + m_graph->display_clause(tout << "pos: ", pos); + m_graph->display_clause(tout << "neg: ", neg); + ); + + return true; + } + + template + bool theory_horn_ineq::internalize_term(app * term) { + bool result = null_theory_var != mk_term(term); + CTRACE("horn_ineq", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); + TRACE("horn_ineq", tout << "Terms may not be internalized " << mk_pp(term, get_manager()) << "\n";); + found_non_horn_ineq_expr(term); + return result; + } + + template + void theory_horn_ineq::internalize_eq_eh(app * atom, bool_var) { + // noop + } + + template + void theory_horn_ineq::assign_eh(bool_var v, bool is_true) { + m_stats.m_num_assertions++; + unsigned idx = m_bool_var2atom.find(v); + SASSERT(get_context().get_assignment(v) != l_undef); + SASSERT((get_context().get_assignment(v) == l_true) == is_true); + m_atoms[idx].assign_eh(is_true); + m_asserted_atoms.push_back(idx); + } + + template + void theory_horn_ineq::push_scope_eh() { + theory::push_scope_eh(); + m_graph->push(); + m_scopes.push_back(scope()); + scope & s = m_scopes.back(); + s.m_atoms_lim = m_atoms.size(); + s.m_asserted_atoms_lim = m_asserted_atoms.size(); + s.m_asserted_qhead_old = m_asserted_qhead; + } + + template + void theory_horn_ineq::pop_scope_eh(unsigned num_scopes) { + unsigned lvl = m_scopes.size(); + SASSERT(num_scopes <= lvl); + unsigned new_lvl = lvl - num_scopes; + scope & s = m_scopes[new_lvl]; + del_atoms(s.m_atoms_lim); + m_asserted_atoms.shrink(s.m_asserted_atoms_lim); + m_asserted_qhead = s.m_asserted_qhead_old; + m_scopes.shrink(new_lvl); + m_graph->pop(num_scopes); + theory::pop_scope_eh(num_scopes); + } + + template + final_check_status theory_horn_ineq::final_check_eh() { + SASSERT(is_consistent()); + TRACE("horn_ineq", display(tout);); + if (can_propagate()) { + propagate_core(); + return FC_CONTINUE; + } + else if (m_non_horn_ineq_exprs) { + return FC_GIVEUP; + } + else { + return FC_DONE; + } + } + + template + void theory_horn_ineq::propagate() { + propagate_core(); + } + + template + void theory_horn_ineq::display(std::ostream& out) const { + for (unsigned i = 0; i < m_atoms.size(); ++i) { + m_atoms[i].display(*this, out); + } + out << "\n"; + m_graph->display(out); + } + + template + void theory_horn_ineq::collect_statistics(::statistics& st) const { + st.update("horn ineq conflicts", m_stats.m_num_conflicts); + st.update("horn ineq assertions", m_stats.m_num_assertions); + m_arith_eq_adapter.collect_statistics(st); + m_graph->collect_statistics(st); + } + + template + void theory_horn_ineq::del_atoms(unsigned old_size) { + typename atoms::iterator begin = m_atoms.begin() + old_size; + typename atoms::iterator it = m_atoms.end(); + while (it != begin) { + --it; + m_bool_var2atom.erase(it->get_bool_var()); + } + m_atoms.shrink(old_size); + } + + template + void theory_horn_ineq::propagate_core() { + bool consistent = true; + while (consistent && can_propagate()) { + unsigned idx = m_asserted_atoms[m_asserted_qhead]; + m_asserted_qhead++; + consistent = propagate_atom(m_atoms[idx]); + } + } + + template + bool theory_horn_ineq::propagate_atom(atom const& a) { + context& ctx = get_context(); + TRACE("horn_ineq", a.display(*this, tout); tout << "\n";); + if (ctx.inconsistent()) { + return false; + } + int clause_id = a.get_asserted_edge(); + if (!m_graph->enable_clause(clause_id)) { + set_neg_cycle_conflict(); + return false; + } + return true; + } + + template + theory_var theory_horn_ineq::mk_term(app* n) { + context& ctx = get_context(); + + horn_ineq_tester::classify_t cl = m_test.linearize(n); + if (cl == horn_ineq_tester::non_horn) { + found_non_horn_ineq_expr(n); + return null_theory_var; + } + + coeffs coeffs; + rational w; + mk_coeffs(m_test.get_linearization(), coeffs, w); + if (coeffs.empty()) { + return mk_num(n, w); + } + if (coeffs.size() == 1 && coeffs[0].second.is_one()) { + return coeffs[0].first; + } + th_var target = mk_var(ctx.mk_enode(n, false, false, true)); + coeffs.push_back(std::make_pair(target, rational(-1))); + + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); + negate(coeffs, w); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); + return target; + } + + template + theory_var theory_horn_ineq::mk_num(app* n, rational const& r) { + theory_var v = null_theory_var; + context& ctx = get_context(); + if (r.is_zero()) { + v = a.is_int(n)?m_zero_int:m_zero_real; + } + else if (ctx.e_internalized(n)) { + enode* e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + } + else { + v = mk_var(ctx.mk_enode(n, false, false, true)); + // v = k: v <= k k <= v + coeffs coeffs; + coeffs.push_back(std::make_pair(v, rational(1))); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(r)), null_literal))); + coeffs.back().second.neg(); + VERIFY (m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(-r)), null_literal))); + } + return v; + } + + template + theory_var theory_horn_ineq::expand(bool pos, th_var v, rational & k) { + context& ctx = get_context(); + enode* e = get_enode(v); + expr* x, *y; + rational r; + for (;;) { + app* n = e->get_owner(); + if (a.is_add(n, x, y)) { + if (a.is_numeral(x, r)) { + e = ctx.get_enode(y); + } + else if (a.is_numeral(y, r)) { + e = ctx.get_enode(x); + } + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + if (v == null_theory_var) { + break; + } + if (pos) { + k += r; + } + else { + k -= r; + } + } + else { + break; + } + } + return v; + } + + template + bool theory_horn_ineq::is_consistent() const { + return m_graph->is_feasible(); + } + + // models: + template + void theory_horn_ineq::init_model(model_generator & m) { + m_factory = alloc(arith_factory, get_manager()); + m.register_factory(m_factory); + compute_delta(); + } + + template + model_value_proc * theory_horn_ineq::mk_value(enode * n, model_generator & mg) { + theory_var v = n->get_th_var(get_id()); + SASSERT(v != null_theory_var); + numeral val = m_graph->get_assignment(v); + rational num = val.get_infinity()*m_lambda + val.get_rational() + val.get_infinitesimal()*m_delta; + TRACE("horn_ineq", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); + return alloc(expr_wrapper_proc, m_factory->mk_value(num, a.is_int(n->get_owner()))); + } + + /** + \brief Compute numeral values for the infinitesimals to satisfy the inequalities. + */ + + template + void theory_horn_ineq::compute_delta() { + m_delta = rational(1); + m_lambda = rational(0); + unsigned sz = m_graph->get_num_clauses(); + + for (unsigned i = 0; i < sz; ++i) { + if (!m_graph->get_clause(i).is_enabled()) { + continue; + } + numeral b = m_graph->eval_body(i); + numeral h = m_graph->eval_head(i); + + if (b.get_infinity() < h.get_infinity()) { + continue; + } + SASSERT(b.get_infinity() == h.get_infinity()); + + // b <= h + // suppose that h.eps < b.eps + // then we have h.num > b.num + // but also h.num + delta*h.eps >= b.num + delta*b.eps + // <=> + // (h.num - b.num)/(b.eps - h.eps) >= delta + rational num_r = h.get_rational() - b.get_rational(); + rational eps_r = b.get_infinitesimal() - h.get_infinitesimal(); + if (eps_r.is_pos()) { + SASSERT(num_r.is_pos()); + rational new_delta = num_r/eps_r; + if (new_delta < m_delta) { + m_delta = new_delta; + } + } + } + + for (unsigned i = 0; i < sz; ++i) { + if (!m_graph->get_clause(i).is_enabled()) { + continue; + } + numeral b = m_graph->eval_body(i); + numeral h = m_graph->eval_head(i); + + rational ir = h.get_infinity() - b.get_infinity(); + rational hr = b.get_rational() - h.get_rational(); + rational num_r = hr + m_delta*(b.get_infinitesimal() - h.get_infinitesimal()); + + SASSERT(b.get_infinity() <= h.get_infinity()); + + // b <= h + // suppose that h.finite < b.finite + // then we have h.infinite > b.infinite + // but also + // h.infinite*lambda + h.finite >= b.infinite*lambda + b.finite + // <=> + // lambda >= (b.finite - h.finite) / (h.infinite - b.infinite) + if (num_r.is_pos()) { + SASSERT(ir.is_pos()); + rational new_lambda = num_r/ir; + if (new_lambda > m_lambda) { + m_lambda = new_lambda; + } + } + } + } + + + +}; + +#endif diff --git a/src/smt/theory_utvpi.cpp b/src/smt/theory_utvpi.cpp new file mode 100644 index 000000000..c45cfe74a --- /dev/null +++ b/src/smt/theory_utvpi.cpp @@ -0,0 +1,160 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_utvpi.h + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ +#include "theory_utvpi.h" +#include "theory_utvpi_def.h" + +namespace smt { + + template class theory_utvpi; + template class theory_utvpi; + + // similar to test_diff_logic: + + utvpi_tester::utvpi_tester(ast_manager& m): m(m), a(m) {} + + bool utvpi_tester::operator()(expr* e) { + m_todo.reset(); + m_mark.reset(); + m_todo.push_back(e); + expr* e1, *e2; + + while (!m_todo.empty()) { + expr* e = m_todo.back(); + m_todo.pop_back(); + if (!m_mark.is_marked(e)) { + m_mark.mark(e, true); + if (is_var(e)) { + continue; + } + if (!is_app(e)) { + return false; + } + app* ap = to_app(e); + if (m.is_eq(ap, e1, e2)) { + if (!linearize(e1, e2)) { + return false; + } + } + else if (ap->get_family_id() == m.get_basic_family_id()) { + continue; + } + else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) || + a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { + if (!linearize(e1, e2)) { + return false; + } + } + else if (is_uninterp_const(e)) { + continue; + } + else { + return false; + } + } + } + return true; + } + + vector > const& utvpi_tester::get_linearization() const { + SASSERT(m_terms.size() <= 2); + return m_terms; + } + + bool utvpi_tester::operator()(unsigned num_fmls, expr* const* fmls) { + for (unsigned i = 0; i < num_fmls; ++i) { + if (!(*this)(fmls[i])) { + return false; + } + } + return true; + } + + bool utvpi_tester::linearize(expr* e) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e, rational(1))); + return linearize(); + } + + bool utvpi_tester::linearize(expr* e1, expr* e2) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e1, rational(1))); + m_terms.push_back(std::make_pair(e2, rational(-1))); + return linearize(); + } + + bool utvpi_tester::linearize() { + + m_weight.reset(); + m_coeff_map.reset(); + + while (!m_terms.empty()) { + expr* e1, *e2; + rational num; + rational mul = m_terms.back().second; + expr* e = m_terms.back().first; + m_terms.pop_back(); + if (a.is_add(e)) { + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul)); + } + } + else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_sub(e, e1, e2)) { + m_terms.push_back(std::make_pair(e1, mul)); + m_terms.push_back(std::make_pair(e2, -mul)); + } + else if (a.is_uminus(e, e1)) { + m_terms.push_back(std::make_pair(e1, -mul)); + } + else if (a.is_numeral(e, num)) { + m_weight += num*mul; + } + else if (a.is_to_real(e, e1)) { + m_terms.push_back(std::make_pair(e1, mul)); + } + else if (!is_uninterp_const(e)) { + return false; + } + else { + m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul; + } + } + obj_map::iterator it = m_coeff_map.begin(); + obj_map::iterator end = m_coeff_map.end(); + for (; it != end; ++it) { + rational r = it->m_value; + if (r.is_zero()) { + continue; + } + m_terms.push_back(std::make_pair(it->m_key, r)); + if (m_terms.size() > 2) { + return false; + } + if (!r.is_one() && !r.is_minus_one()) { + return false; + } + } + return true; + } + + +} diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h new file mode 100644 index 000000000..4a5d97d52 --- /dev/null +++ b/src/smt/theory_utvpi.h @@ -0,0 +1,343 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_utvpi.h + +Abstract: + + use Bellman Ford traversal algorithm for UTVPI. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ + +#ifndef _THEORY_UTVPI_H_ +#define _THEORY_UTVPI_H_ + +#include"theory_diff_logic.h" + +namespace smt { + + class utvpi_tester { + ast_manager& m; + arith_util a; + ptr_vector m_todo; + ast_mark m_mark; + obj_map m_coeff_map; + rational m_weight; + vector > m_terms; + + public: + utvpi_tester(ast_manager& m); + + // test if formula is in the Horn inequality fragment: + bool operator()(expr* fml); + bool operator()(unsigned num_fmls, expr* const* fmls); + + // linearize inequality/equality + bool linearize(expr* e); + bool linearize(expr* e1, expr* e2); + + // retrieve linearization + vector > const& get_linearization() const; + rational const& get_weight() const { return m_weight; } + private: + bool linearize(); + }; + + template + class theory_utvpi : public theory, private Ext { + + typedef typename Ext::numeral numeral; + typedef theory_var th_var; + typedef svector th_var_vector; + typedef vector > coeffs; + + class assignment_trail; + class parent_trail; + + struct GExt : public Ext { + typedef std::pair explanation; + }; + + class atom { + protected: + bool_var m_bvar; + bool m_true; + int m_pos; + int m_neg; + public: + atom(bool_var bv, int pos, int neg) : + m_bvar(bv), m_true(false), + m_pos(pos), m_neg(neg) {} + ~atom() {} + bool_var get_bool_var() const { return m_bvar; } + void assign_eh(bool is_true) { m_true = is_true; } + int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } + int get_pos() const { return m_pos; } + int get_neg() const { return m_neg; } + std::ostream& display(theory_utvpi const& th, std::ostream& out) const; + }; + typedef svector atoms; + + struct scope { + unsigned m_atoms_lim; + unsigned m_asserted_atoms_lim; + unsigned m_asserted_qhead_old; + }; + + struct stats { + unsigned m_num_conflicts; + unsigned m_num_assertions; + unsigned m_num_core2th_eqs; + unsigned m_num_core2th_diseqs; + + void reset() { + memset(this, 0, sizeof(*this)); + } + + stats() { + reset(); + } + }; + + // Functor used to collect the proofs for a conflict due to + // a negative cycle. + class nc_functor { + literal_vector m_antecedents; + unsigned_vector m_coeffs; + theory_utvpi& m_super; + public: + nc_functor(theory_utvpi& s) : m_super(s) {} + void reset() { m_antecedents.reset(); m_coeffs.reset(); } + literal_vector const& get_lits() const { return m_antecedents; } + unsigned_vector const& get_coeffs() const { return m_coeffs; } + + void operator()(std::pair const & ex) { + if (ex.first != null_literal) { + m_antecedents.push_back(ex.first); + m_coeffs.push_back(ex.second); + } + } + + void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { + m_super.new_edge(src, dst, num_edges, edges); + } + }; + + + stats m_stats; + smt_params m_params; + arith_util a; + arith_eq_adapter m_arith_eq_adapter; + th_var m_zero_int; // cache the variable representing the zero variable. + th_var m_zero_real; // cache the variable representing the zero variable. + + dl_graph m_graph; + nc_functor m_nc_functor; + atoms m_atoms; + unsigned_vector m_asserted_atoms; // set of asserted atoms + unsigned m_asserted_qhead; + u_map m_bool_var2atom; + svector m_scopes; + + double m_agility; + bool m_lia; + bool m_lra; + bool m_non_utvpi_exprs; + + utvpi_tester m_test; + + + arith_factory * m_factory; + rational m_delta; + + + // Set a conflict due to a negative cycle. + void set_conflict(); + + void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {} + + // Create a new theory variable. + virtual th_var mk_var(enode* n); + + virtual th_var mk_var(expr* n); + + void compute_delta(); + + void found_non_utvpi_expr(expr * n); + + bool is_interpreted(app* n) const { + return n->get_family_id() == get_family_id(); + } + + public: + theory_utvpi(ast_manager& m); + + virtual ~theory_utvpi(); + + virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_utvpi, get_manager()); } + + virtual char const * get_name() const { return "utvpi-logic"; } + + /** + \brief See comment in theory::mk_eq_atom + */ + virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); } + + virtual void init(context * ctx); + + virtual bool internalize_atom(app * atom, bool gate_ctx); + + virtual bool internalize_term(app * term); + + virtual void internalize_eq_eh(app * atom, bool_var v); + + virtual void assign_eh(bool_var v, bool is_true); + + virtual void new_eq_eh(th_var v1, th_var v2) { + m_stats.m_num_core2th_eqs++; + m_arith_eq_adapter.new_eq_eh(v1, v2); + } + + virtual bool use_diseqs() const { return true; } + + virtual void new_diseq_eh(th_var v1, th_var v2) { + m_arith_eq_adapter.new_diseq_eh(v1, v2); + } + + virtual void push_scope_eh(); + + virtual void pop_scope_eh(unsigned num_scopes); + + virtual void restart_eh() { + m_arith_eq_adapter.restart_eh(); + } + + virtual void relevant_eh(app* e) {} + + virtual void init_search_eh() { + m_arith_eq_adapter.init_search_eh(); + } + + virtual final_check_status final_check_eh(); + + virtual bool is_shared(th_var v) const { + return false; + } + + virtual bool can_propagate() { + SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); + return m_asserted_qhead != m_asserted_atoms.size(); + } + + virtual void propagate(); + + virtual justification * why_is_diseq(th_var v1, th_var v2) { + UNREACHABLE(); + return 0; + } + + virtual void reset_eh(); + + virtual void init_model(model_generator & m); + + virtual model_value_proc * mk_value(enode * n, model_generator & mg); + + virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const { + return true; + } + + virtual void display(std::ostream & out) const; + + virtual void collect_statistics(::statistics & st) const; + + private: + + rational mk_value(theory_var v, bool is_int); + + bool is_parity_ok(unsigned v) const; + + void enforce_parity(); + + void validate_model(); + + bool eval(expr* e); + + rational eval_num(expr* e); + + bool check_z_consistency(); + + virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_eqs++; + new_eq_or_diseq(true, v1, v2, j); + } + + virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_diseqs++; + new_eq_or_diseq(false, v1, v2, j); + } + + void negate(coeffs& coeffs, rational& weight); + numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; + void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); + + void del_atoms(unsigned old_size); + + bool propagate_atom(atom const& a); + + th_var mk_term(app* n); + + th_var mk_num(app* n, rational const& r); + + bool is_consistent() const; + + th_var expand(bool pos, th_var v, rational & k); + + void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); + + th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; } + + th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } + + void inc_conflicts(); + + edge_id add_ineq(vector > const& terms, numeral const& weight, literal l); + + bool enable_edge(edge_id id); + + th_var to_var(th_var v) const { + return 2*v; + } + + th_var from_var(th_var v) const { + return v/2; + } + + th_var pos(th_var v) const { + return v & 0xFFFFFFFE; + } + + th_var neg(th_var v) const { + return v | 0x1; + } + + }; + + + typedef theory_utvpi theory_rutvpi; + typedef theory_utvpi theory_iutvpi; +}; + + + + +#endif /* _THEORY_UTVPI_H_ */ diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h new file mode 100644 index 000000000..8463eb17f --- /dev/null +++ b/src/smt/theory_utvpi_def.h @@ -0,0 +1,954 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_utvpi_def.h + +Abstract: + + Implementation of UTVPI solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + + 1. introduce x^+ and x^-, such that 2*x := x^+ - x^- + 2. rewrite constraints as follows: + + x - y <= k => x^+ - y^+ <= k + y^- - x^- <= k + + x <= k => x^+ - x^- <= 2k + + + x + y <= k => x^+ - y^- <= k + y^+ - x^- <= k + + + - x - y <= k => x^- - y^+ <= k + y^- - x^+ <= k + + 3. Solve for x^+ and x^- + 4. Check parity condition for integers (see Lahiri and Musuvathi 05) + This checks if x^+ and x^- are in the same component but of different + parities. + 5. Enforce parity on variables. This checks if x^+ and x^- have different + parities. If they have different parities, the assignment to one + of the variables is decremented (choose the variable that is not tightly + constrained with 0). + The process that adjusts parities converges: Suppose we break a parity + of a different variable y while fixing x's parity. A cyclic breaking/fixing + of parities implies there is a strongly connected component between x, y + and the two polarities of the variables. This contradicts the test in 4. + 6. extract model for M(x) := (M(x^+)- M(x^-))/2 + +--*/ + +#ifndef _THEORY_UTVPI_DEF_H_ +#define _THEORY_UTVPI_DEF_H_ +#include "theory_utvpi.h" +#include "heap.h" +#include "ast_pp.h" +#include "smt_context.h" + +namespace smt { + + + template + theory_utvpi::theory_utvpi(ast_manager& m): + theory(m.mk_family_id("arith")), + a(m), + m_arith_eq_adapter(*this, m_params, a), + m_zero_int(null_theory_var), + m_zero_real(null_theory_var), + m_nc_functor(*this), + m_asserted_qhead(0), + m_agility(0.5), + m_lia(false), + m_lra(false), + m_non_utvpi_exprs(false), + m_test(m), + m_factory(0) { + } + + template + theory_utvpi::~theory_utvpi() { + reset_eh(); + } + + template + std::ostream& theory_utvpi::atom::display(theory_utvpi const& th, std::ostream& out) const { + context& ctx = th.get_context(); + lbool asgn = ctx.get_assignment(m_bvar); + bool sign = (l_undef == l_false); + return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; + if (l_undef == asgn) { + out << "unassigned\n"; + } + else { + th.m_graph.display_edge(out, get_asserted_edge()); + } + return out; + } + + template + theory_var theory_utvpi::mk_var(enode* n) { + th_var v = theory::mk_var(n); + TRACE("utvpi", tout << v << " " << mk_pp(n->get_owner(), get_manager()) << "\n";); + m_graph.init_var(to_var(v)); + m_graph.init_var(neg(to_var(v))); + get_context().attach_th_var(n, this, v); + return v; + } + + template + theory_var theory_utvpi::mk_var(expr* n) { + context & ctx = get_context(); + enode* e = 0; + th_var v = null_theory_var; + m_lia |= a.is_int(n); + m_lra |= a.is_real(n); + if (!is_app(n)) { + return v; + } + if (ctx.e_internalized(n)) { + e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + } + else { + ctx.internalize(n, false); + e = ctx.get_enode(n); + } + if (v == null_theory_var) { + v = mk_var(e); + } + if (is_interpreted(to_app(n))) { + found_non_utvpi_expr(n); + } + return v; + } + + template + void theory_utvpi::reset_eh() { + m_graph .reset(); + m_zero_int = null_theory_var; + m_zero_real = null_theory_var; + m_atoms .reset(); + m_asserted_atoms .reset(); + m_stats .reset(); + m_scopes .reset(); + m_asserted_qhead = 0; + m_agility = 0.5; + m_lia = false; + m_lra = false; + m_non_utvpi_exprs = false; + theory::reset_eh(); + } + + template + void theory_utvpi::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) { + rational k; + th_var s = expand(true, v1, k); + th_var t = expand(false, v2, k); + context& ctx = get_context(); + ast_manager& m = get_manager(); + + if (s == t) { + if (is_eq != k.is_zero()) { + // conflict 0 /= k; + inc_conflicts(); + ctx.set_conflict(&eq_just); + } + } + else { + // + // Create equality ast, internalize_atom + // assign the corresponding equality literal. + // + + app_ref eq(m), s2(m), t2(m); + app* s1 = get_enode(s)->get_owner(); + app* t1 = get_enode(t)->get_owner(); + s2 = a.mk_sub(t1, s1); + t2 = a.mk_numeral(k, m.get_sort(s2.get())); + // t1 - s1 = k + eq = m.mk_eq(s2.get(), t2.get()); + + TRACE("utvpi", + tout << v1 << " .. " << v2 << "\n"; + tout << mk_pp(eq.get(), m) <<"\n";); + + if (!internalize_atom(eq.get(), false)) { + UNREACHABLE(); + } + + literal l(ctx.get_literal(eq.get())); + if (!is_eq) { + l = ~l; + } + ctx.assign(l, b_justification(&eq_just), false); + } + } + + template + void theory_utvpi::inc_conflicts() { + m_stats.m_num_conflicts++; + if (m_params.m_arith_adaptive) { + double g = m_params.m_arith_adaptive_propagation_threshold; + m_agility = m_agility*g + 1 - g; + } + } + + template + void theory_utvpi::set_conflict() { + inc_conflicts(); + literal_vector const& lits = m_nc_functor.get_lits(); + context & ctx = get_context(); + IF_VERBOSE(2, + verbose_stream() << "conflict:\n"; + for (unsigned i = 0; i < lits.size(); ++i) { + ast_manager& m = get_manager(); + expr_ref e(m); + ctx.literal2expr(lits[i], e); + verbose_stream() << mk_pp(e, m) << "\n"; + } + verbose_stream() << "\n";); + TRACE("utvpi", + tout << "conflict: "; + for (unsigned i = 0; i < lits.size(); ++i) { + ctx.display_literal_info(tout, lits[i]); + } + tout << "\n"; + ); + + if (m_params.m_arith_dump_lemmas) { + char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"; + ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); + } + + vector params; + if (get_manager().proofs_enabled()) { + params.push_back(parameter(symbol("farkas"))); + for (unsigned i = 0; i < m_nc_functor.get_coeffs().size(); ++i) { + params.push_back(parameter(rational(m_nc_functor.get_coeffs()[i]))); + } + } + + ctx.set_conflict( + ctx.mk_justification( + ext_theory_conflict_justification( + get_id(), ctx.get_region(), + lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); + + m_nc_functor.reset(); + } + + template + void theory_utvpi::found_non_utvpi_expr(expr* n) { + if (!m_non_utvpi_exprs) { + std::stringstream msg; + msg << "found non utvpi logic expression:\n" << mk_pp(n, get_manager()) << "\n"; + TRACE("utvpi", tout << msg.str();); + warning_msg(msg.str().c_str()); + get_context().push_trail(value_trail(m_non_utvpi_exprs)); + m_non_utvpi_exprs = true; + } + } + + template + void theory_utvpi::init(context* ctx) { + theory::init(ctx); + m_zero_int = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); + m_zero_real = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), false), false, false, true)); + } + + /** + \brief Create negated literal. + + The negation of: E <= 0 + + -E + epsilon <= 0 + or + -E + 1 <= 0 + */ + template + void theory_utvpi::negate(coeffs& coeffs, rational& weight) { + for (unsigned i = 0; i < coeffs.size(); ++i) { + coeffs[i].second.neg(); + } + weight.neg(); + } + + template + typename theory_utvpi::numeral theory_utvpi::mk_weight(bool is_real, bool is_strict, rational const& w) const { + if (is_strict) { + return numeral(w) + (is_real?Ext::m_epsilon:numeral(1)); + } + else { + return numeral(w); + } + } + + template + void theory_utvpi::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) { + coeffs.reset(); + w = m_test.get_weight(); + for (unsigned i = 0; i < terms.size(); ++i) { + coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second)); + } + } + + template + void theory_utvpi::internalize_eq_eh(app * atom, bool_var v) { + context & ctx = get_context(); + app * lhs = to_app(atom->get_arg(0)); + app * rhs = to_app(atom->get_arg(1)); + if (a.is_numeral(rhs)) { + std::swap(rhs, lhs); + } + if (!a.is_numeral(rhs)) { + return; + } + if (a.is_add(lhs) || a.is_sub(lhs)) { + // force axioms for (= (+ x y) k) + // this is necessary because (+ x y) is not expressible as a utvpi term. + m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs)); + } + } + + template + bool theory_utvpi::internalize_atom(app * n, bool) { + context & ctx = get_context(); + if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) { + found_non_utvpi_expr(n); + return false; + } + SASSERT(!ctx.b_internalized(n)); + expr* e1 = n->get_arg(0), *e2 = n->get_arg(1); + if (a.is_ge(n) || a.is_gt(n)) { + std::swap(e1, e2); + } + bool is_strict = a.is_gt(n) || a.is_lt(n); + + bool cl = m_test.linearize(e1, e2); + if (!cl) { + found_non_utvpi_expr(n); + return false; + } + + rational w; + coeffs coeffs; + mk_coeffs(m_test.get_linearization(), coeffs, w); + bool_var bv = ctx.mk_bool_var(n); + ctx.set_var_theory(bv, get_id()); + literal l(bv); + numeral w1 = mk_weight(a.is_real(e1), is_strict, w); + edge_id pos = add_ineq(coeffs, w1, l); + negate(coeffs, w); + numeral w2 = mk_weight(a.is_real(e1), !is_strict, w); + edge_id neg = add_ineq(coeffs, w2, ~l); + m_bool_var2atom.insert(bv, m_atoms.size()); + m_atoms.push_back(atom(bv, pos, neg)); + + TRACE("utvpi", + tout << mk_pp(n, get_manager()) << "\n"; + m_graph.display_edge(tout << "pos: ", pos); + m_graph.display_edge(tout << "neg: ", neg); + ); + + return true; + } + + template + bool theory_utvpi::internalize_term(app * term) { + bool result = null_theory_var != mk_term(term); + CTRACE("utvpi", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); + return result; + } + + template + void theory_utvpi::assign_eh(bool_var v, bool is_true) { + m_stats.m_num_assertions++; + unsigned idx = m_bool_var2atom.find(v); + SASSERT(get_context().get_assignment(v) != l_undef); + SASSERT((get_context().get_assignment(v) == l_true) == is_true); + m_atoms[idx].assign_eh(is_true); + m_asserted_atoms.push_back(idx); + } + + template + void theory_utvpi::push_scope_eh() { + theory::push_scope_eh(); + m_graph.push(); + m_scopes.push_back(scope()); + scope & s = m_scopes.back(); + s.m_atoms_lim = m_atoms.size(); + s.m_asserted_atoms_lim = m_asserted_atoms.size(); + s.m_asserted_qhead_old = m_asserted_qhead; + } + + template + void theory_utvpi::pop_scope_eh(unsigned num_scopes) { + unsigned lvl = m_scopes.size(); + SASSERT(num_scopes <= lvl); + unsigned new_lvl = lvl - num_scopes; + scope & s = m_scopes[new_lvl]; + del_atoms(s.m_atoms_lim); + m_asserted_atoms.shrink(s.m_asserted_atoms_lim); + m_asserted_qhead = s.m_asserted_qhead_old; + m_scopes.shrink(new_lvl); + m_graph.pop(num_scopes); + theory::pop_scope_eh(num_scopes); + } + + template + final_check_status theory_utvpi::final_check_eh() { + SASSERT(is_consistent()); + if (can_propagate()) { + propagate(); + return FC_CONTINUE; + } + else if (!check_z_consistency()) { + return FC_CONTINUE; + } + else if (m_non_utvpi_exprs) { + return FC_GIVEUP; + } + else { + return FC_DONE; + } + } + + template + bool theory_utvpi::check_z_consistency() { + int_vector scc_id; + m_graph.compute_zero_edge_scc(scc_id); + + unsigned sz = get_num_vars(); + for (unsigned i = 0; i < sz; ++i) { + enode* e = get_enode(i); + if (!a.is_int(e->get_owner())) { + continue; + } + th_var v1 = to_var(i); + th_var v2 = neg(v1); + rational r1 = m_graph.get_assignment(v1).get_rational(); + rational r2 = m_graph.get_assignment(v2).get_rational(); + SASSERT(r1.is_int()); + SASSERT(r2.is_int()); + if (r1.is_even() == r2.is_even()) { + continue; + } + if (scc_id[v1] != scc_id[v2]) { + continue; + } + if (scc_id[v1] == -1) { + continue; + } + // they are in the same SCC and have different parities => contradiction. + m_nc_functor.reset(); + VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, UINT_MAX, m_nc_functor)); + VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, UINT_MAX, m_nc_functor)); + IF_VERBOSE(1, verbose_stream() << "parity conflict " << mk_pp(e->get_owner(), get_manager()) << "\n";); + set_conflict(); + + return false; + } + return true; + } + + template + void theory_utvpi::display(std::ostream& out) const { + for (unsigned i = 0; i < m_atoms.size(); ++i) { + m_atoms[i].display(*this, out); out << "\n"; + } + m_graph.display(out); + } + + template + void theory_utvpi::collect_statistics(::statistics& st) const { + st.update("utvpi conflicts", m_stats.m_num_conflicts); + st.update("utvpi asserts", m_stats.m_num_assertions); + st.update("core->utvpi eqs", m_stats.m_num_core2th_eqs); + st.update("core->utvpi diseqs", m_stats.m_num_core2th_diseqs); + m_arith_eq_adapter.collect_statistics(st); + m_graph.collect_statistics(st); + } + + template + void theory_utvpi::del_atoms(unsigned old_size) { + typename atoms::iterator begin = m_atoms.begin() + old_size; + typename atoms::iterator it = m_atoms.end(); + while (it != begin) { + --it; + m_bool_var2atom.erase(it->get_bool_var()); + } + m_atoms.shrink(old_size); + } + + template + void theory_utvpi::propagate() { + bool consistent = true; + while (consistent && can_propagate()) { + unsigned idx = m_asserted_atoms[m_asserted_qhead]; + m_asserted_qhead++; + consistent = propagate_atom(m_atoms[idx]); + } + } + + template + bool theory_utvpi::propagate_atom(atom const& a) { + context& ctx = get_context(); + TRACE("utvpi", a.display(*this, tout); tout << "\n";); + if (ctx.inconsistent()) { + return false; + } + int edge_id = a.get_asserted_edge(); + if (!enable_edge(edge_id)) { + m_graph.traverse_neg_cycle2(m_params.m_arith_stronger_lemmas, m_nc_functor); + set_conflict(); + return false; + } + return true; + } + + template + theory_var theory_utvpi::mk_term(app* n) { + TRACE("utvpi", tout << mk_pp(n, get_manager()) << "\n";); + context& ctx = get_context(); + + bool cl = m_test.linearize(n); + if (!cl) { + found_non_utvpi_expr(n); + return null_theory_var; + } + + coeffs coeffs; + rational w; + mk_coeffs(m_test.get_linearization(), coeffs, w); + if (coeffs.empty()) { + return mk_num(n, w); + } + if (coeffs.size() == 1 && coeffs[0].second.is_one()) { + return coeffs[0].first; + } + if (coeffs.size() == 2) { + // do not create an alias. + return null_theory_var; + } + for (unsigned i = 0; i < n->get_num_args(); ++i) { + mk_term(to_app(n->get_arg(i))); + } + th_var target = mk_var(ctx.mk_enode(n, false, false, true)); + coeffs.push_back(std::make_pair(target, rational(-1))); + + VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); + negate(coeffs, w); + VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); + return target; + } + + template + theory_var theory_utvpi::mk_num(app* n, rational const& r) { + theory_var v = null_theory_var; + context& ctx = get_context(); + if (r.is_zero()) { + v = a.is_int(n)?m_zero_int:m_zero_real; + } + else if (ctx.e_internalized(n)) { + enode* e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + } + else { + v = mk_var(ctx.mk_enode(n, false, false, true)); + // v = k: v <= k k <= v + coeffs coeffs; + coeffs.push_back(std::make_pair(v, rational(-1))); + VERIFY(enable_edge(add_ineq(coeffs, numeral(r), null_literal))); + coeffs.back().second.neg(); + VERIFY(enable_edge(add_ineq(coeffs, numeral(-r), null_literal))); + } + return v; + } + + template + theory_var theory_utvpi::expand(bool pos, th_var v, rational & k) { + context& ctx = get_context(); + enode* e = get_enode(v); + expr* x, *y; + rational r; + for (;;) { + app* n = e->get_owner(); + if (a.is_add(n, x, y)) { + if (a.is_numeral(x, r)) { + e = ctx.get_enode(y); + } + else if (a.is_numeral(y, r)) { + e = ctx.get_enode(x); + } + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + if (v == null_theory_var) { + break; + } + if (pos) { + k += r; + } + else { + k -= r; + } + } + else { + break; + } + } + return v; + } + + // m_graph(source, target, weight, ex); + // target - source <= weight + + template + edge_id theory_utvpi::add_ineq(vector > const& terms, numeral const& weight, literal l) { + + SASSERT(terms.size() <= 2); + SASSERT(terms.size() < 1 || terms[0].second.is_one() || terms[0].second.is_minus_one()); + SASSERT(terms.size() < 2 || terms[1].second.is_one() || terms[1].second.is_minus_one()); + + th_var v1 = null_theory_var, v2 = null_theory_var; + bool pos1 = true, pos2 = true; + if (terms.size() >= 1) { + v1 = terms[0].first; + pos1 = terms[0].second.is_one(); + SASSERT(v1 != null_theory_var); + SASSERT(pos1 || terms[0].second.is_minus_one()); + } + if (terms.size() >= 2) { + v2 = terms[1].first; + pos2 = terms[1].second.is_one(); + SASSERT(v1 != null_theory_var); + SASSERT(pos2 || terms[1].second.is_minus_one()); + } +// TRACE("utvpi", tout << (pos1?"$":"-$") << v1 << (pos2?" + $":" - $") << v2 << " + " << weight << " <= 0\n";); + edge_id id = m_graph.get_num_edges(); + th_var w1 = to_var(v1), w2 = to_var(v2); + if (terms.size() == 1 && pos1) { + m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2)); + m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2)); + } + else if (terms.size() == 1 && !pos1) { + m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2)); + m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2)); + } + else if (pos1 && pos2) { + m_graph.add_edge(neg(w2), pos(w1), -weight, std::make_pair(l,1)); + m_graph.add_edge(neg(w1), pos(w2), -weight, std::make_pair(l,1)); + } + else if (pos1 && !pos2) { + m_graph.add_edge(pos(w2), pos(w1), -weight, std::make_pair(l,1)); + m_graph.add_edge(neg(w1), neg(w2), -weight, std::make_pair(l,1)); + } + else if (!pos1 && pos2) { + m_graph.add_edge(neg(w2), neg(w1), -weight, std::make_pair(l,1)); + m_graph.add_edge(pos(w1), pos(w2), -weight, std::make_pair(l,1)); + } + else { + m_graph.add_edge(pos(w1), neg(w2), -weight, std::make_pair(l,1)); + m_graph.add_edge(pos(w2), neg(w1), -weight, std::make_pair(l,1)); + } + return id; + } + + template + bool theory_utvpi::enable_edge(edge_id id) { + return (id == null_edge_id) || (m_graph.enable_edge(id) && m_graph.enable_edge(id+1)); + } + + template + bool theory_utvpi::is_consistent() const { + return m_graph.is_feasible(); + } + + + template + bool theory_utvpi::is_parity_ok(unsigned i) const { + th_var v1 = to_var(i); + th_var v2 = neg(v1); + rational r1 = m_graph.get_assignment(v1).get_rational(); + rational r2 = m_graph.get_assignment(v2).get_rational(); + return r1.is_even() == r2.is_even(); + } + + + /** + \brief adjust values for variables in the difference graph + such that for variables of integer sort it is + the case that x^+ - x^- is even. + The informal justification for the procedure enforce_parity relies + on a set of properties: + 1. the graph does not contain a strongly connected component where + x^+ and x+- are connected. They can be independently changed. + This is checked prior to enforce_parity. + 2. When x^+ - x^- is odd, the values are adjusted by first + decrementing the value of x^+, provided x^- is not 0-dependent. + Otherwise decrement x^-. + x^- is "0-dependent" if there is a set of tight + inequalities from x^+ to x^-. + 3. The affinity to x^+ (the same component of x^+) ensures that + the parity is broken only a finite number of times when + traversing that component. Namely, suppose that the parity of y + gets broken when fixing 'x'. Then first note that 'y' cannot + be equal to 'x'. If it were, then we have a state where: + parity(x^+) != parity(x^-) and + parity(y^+) == parity(y^-) + but x^+ and y^+ are tightly connected and x^- and y^- are + also tightly connected using two copies of the same inequalities. + This is a contradiction. + Thus, 'y' cannot be equal to 'x' if 'y's parity gets broken when + repairing 'x'. + + */ + template + void theory_utvpi::enforce_parity() { + unsigned_vector todo; + unsigned sz = get_num_vars(); + for (unsigned i = 0; i < sz; ++i) { + enode* e = get_enode(i); + if (a.is_int(e->get_owner()) && !is_parity_ok(i)) { + todo.push_back(i); + } + } + if (todo.empty()) { + return; + } + while (!todo.empty()) { + unsigned i = todo.back(); + todo.pop_back(); + if (is_parity_ok(i)) { + continue; + } + th_var v1 = to_var(i); + th_var v2 = neg(v1); + + int_vector zero_v; + m_graph.compute_zero_succ(v1, zero_v); + for (unsigned j = 0; j < zero_v.size(); ++j) { + if (zero_v[j] == v2) { + zero_v.reset(); + m_graph.compute_zero_succ(v2, zero_v); + break; + } + } + + TRACE("utvpi", + tout << "Disparity: " << v1 << "\n"; + for (unsigned j = 0; j < zero_v.size(); ++j) { + tout << "decrement: " << zero_v[j] << "\n"; + }); + + for (unsigned j = 0; j < zero_v.size(); ++j) { + int v = zero_v[j]; + m_graph.acc_assignment(v, numeral(-1)); + th_var k = from_var(v); + if (!is_parity_ok(k)) { + todo.push_back(k); + } + } + } + SASSERT(m_graph.is_feasible()); + DEBUG_CODE( + for (unsigned i = 0; i < sz; ++i) { + enode* e = get_enode(i); + if (a.is_int(e->get_owner()) && !is_parity_ok(i)) { + IF_VERBOSE(0, verbose_stream() << "disparities not fixed\n";); + UNREACHABLE(); + } + }); + } + + + // models: + template + void theory_utvpi::init_model(model_generator & m) { + m_factory = alloc(arith_factory, get_manager()); + m.register_factory(m_factory); + enforce_parity(); + m_graph.set_to_zero(to_var(m_zero_int), to_var(m_zero_real)); + m_graph.set_to_zero(neg(to_var(m_zero_int)), neg(to_var(m_zero_real))); + m_graph.set_to_zero(to_var(m_zero_int), neg(to_var(m_zero_int))); + compute_delta(); + DEBUG_CODE(validate_model();); + } + + template + void theory_utvpi::validate_model() { + context& ctx = get_context(); + unsigned sz = m_atoms.size(); + for (unsigned i = 0; i < sz; ++i) { + bool_var b = m_atoms[i].get_bool_var(); + if (!ctx.is_relevant(b)) { + continue; + } + bool ok = true; + expr* e = ctx.bool_var2expr(b); + lbool assign = ctx.get_assignment(b); + switch(assign) { + case l_true: + ok = eval(e); + break; + case l_false: + ok = !eval(e); + break; + default: + break; + } + CTRACE("utvpi", !ok, + tout << "validation failed:\n"; + tout << "Assignment: " << assign << "\n"; + m_atoms[i].display(*this, tout); + tout << "\n"; + display(tout); + m_graph.display_agl(tout); + ); + if (!ok) { + std::cout << "validation failed:\n"; + std::cout << "Assignment: " << assign << "\n"; + m_atoms[i].display(*this, std::cout); + std::cout << "\n"; + display(std::cout); + m_graph.display_agl(std::cout); + + } + // CTRACE("utvpi", ok, tout << "validation success: " << mk_pp(e, get_manager()) << "\n";); + SASSERT(ok); + } + } + + template + bool theory_utvpi::eval(expr* e) { + expr* e1, *e2; + if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) { + return eval_num(e1) <= eval_num(e2); + } + if (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { + return eval_num(e1) < eval_num(e2); + } + if (get_manager().is_eq(e, e1, e2)) { + return eval_num(e1) == eval_num(e2); + } + TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); + return false; + } + + template + rational theory_utvpi::eval_num(expr* e) { + rational r; + expr* e1, *e2; + if (a.is_numeral(e, r)) { + return r; + } + if (a.is_sub(e, e1, e2)) { + return eval_num(e1) - eval_num(e2); + } + if (a.is_add(e)) { + r.reset(); + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + r += eval_num(to_app(e)->get_arg(i)); + } + return r; + } + if (a.is_mul(e)) { + r = rational(1); + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + r *= eval_num(to_app(e)->get_arg(i)); + } + return r; + } + if (a.is_uminus(e, e1)) { + return -eval_num(e1); + } + if (a.is_to_real(e, e1)) { + return eval_num(e1); + } + if (is_uninterp_const(e)) { + return mk_value(mk_var(e), a.is_int(e)); + } + TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); + UNREACHABLE(); + return rational(0); + } + + + template + rational theory_utvpi::mk_value(th_var v, bool is_int) { + SASSERT(v != null_theory_var); + numeral val1 = m_graph.get_assignment(to_var(v)); + numeral val2 = m_graph.get_assignment(neg(to_var(v))); + numeral val = val1 - val2; + rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational()); + num = num/rational(2); + SASSERT(!is_int || num.is_int()); + TRACE("utvpi", + expr* n = get_enode(v)->get_owner(); + tout << mk_pp(n, get_manager()) << " |-> (" << val1 << " - " << val2 << ")/2 = " << num << "\n";); + + return num; + } + + template + model_value_proc * theory_utvpi::mk_value(enode * n, model_generator & mg) { + theory_var v = n->get_th_var(get_id()); + bool is_int = a.is_int(n->get_owner()); + rational num = mk_value(v, is_int); + TRACE("utvpi", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); + return alloc(expr_wrapper_proc, m_factory->mk_value(num, is_int)); + } + + /** + \brief Compute numeral values for the infinitesimals to satisfy the inequalities. + */ + + template + void theory_utvpi::compute_delta() { + m_delta = rational(1); + unsigned sz = m_graph.get_num_edges(); + + for (unsigned i = 0; i < sz; ++i) { + if (!m_graph.is_enabled(i)) { + continue; + } + numeral w = m_graph.get_weight(i); + numeral tgt = m_graph.get_assignment(m_graph.get_target(i)); + numeral src = m_graph.get_assignment(m_graph.get_source(i)); + numeral b = tgt - src - w; + SASSERT(b.is_nonpos()); + rational eps_r = b.get_infinitesimal(); + + // Given: b <= 0 + // suppose that 0 < b.eps + // then we have 0 > b.num + // then delta must ensure: + // 0 >= b.num + delta*b.eps + // <=> + // -b.num/b.eps >= delta + if (eps_r.is_pos()) { + rational num_r = -b.get_rational(); + SASSERT(num_r.is_pos()); + rational new_delta = num_r/eps_r; + if (new_delta < m_delta) { + m_delta = new_delta; + } + } + } + } + + + +}; + +#endif + diff --git a/src/tactic/aig/aig.cpp b/src/tactic/aig/aig.cpp index 997ec642e..9fdce96e9 100644 --- a/src/tactic/aig/aig.cpp +++ b/src/tactic/aig/aig.cpp @@ -869,11 +869,7 @@ struct aig_manager::imp { void mk_ite(aig * n) { aig_lit c, t, e; -#ifdef Z3DEBUG - bool ok = -#endif - m.is_ite(n, c, t, e); - SASSERT(ok); + VERIFY(m.is_ite(n, c, t, e)); if (c.is_inverted()) { c.invert(); std::swap(t, e); diff --git a/src/tactic/aig/aig.h b/src/tactic/aig/aig.h index 96aa59ee6..c8befd9b2 100644 --- a/src/tactic/aig/aig.h +++ b/src/tactic/aig/aig.h @@ -70,7 +70,6 @@ public: void max_sharing(aig_ref & r); void to_formula(aig_ref const & r, expr_ref & result); void to_formula(aig_ref const & r, goal & result); - void to_cnf(aig_ref const & r, goal & result); void display(std::ostream & out, aig_ref const & r) const; void display_smt2(std::ostream & out, aig_ref const & r) const; unsigned get_num_aigs() const; diff --git a/src/muz_qe/arith_bounds_tactic.cpp b/src/tactic/arith/arith_bounds_tactic.cpp similarity index 100% rename from src/muz_qe/arith_bounds_tactic.cpp rename to src/tactic/arith/arith_bounds_tactic.cpp diff --git a/src/muz_qe/arith_bounds_tactic.h b/src/tactic/arith/arith_bounds_tactic.h similarity index 100% rename from src/muz_qe/arith_bounds_tactic.h rename to src/tactic/arith/arith_bounds_tactic.h diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index 169e27927..f2ddd86ce 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -29,6 +29,7 @@ Revision History: #include"th_rewriter.h" #include"filter_model_converter.h" #include"ast_smt2_pp.h" +#include"expr_replacer.h" /* ---- @@ -131,18 +132,16 @@ struct purify_arith_proc { proof_ref_vector m_new_cnstr_prs; expr_ref m_subst; proof_ref m_subst_pr; - bool m_in_q; - unsigned m_var_idx; + expr_ref_vector m_new_vars; - rw_cfg(purify_arith_proc & o, bool in_q): + rw_cfg(purify_arith_proc & o): m_owner(o), m_pinned(o.m()), m_new_cnstrs(o.m()), m_new_cnstr_prs(o.m()), m_subst(o.m()), m_subst_pr(o.m()), - m_in_q(in_q), - m_var_idx(0) { + m_new_vars(o.m()) { } ast_manager & m() { return m_owner.m(); } @@ -155,14 +154,9 @@ struct purify_arith_proc { bool elim_inverses() const { return m_owner.m_elim_inverses; } expr * mk_fresh_var(bool is_int) { - if (m_in_q) { - unsigned idx = m_var_idx; - m_var_idx++; - return m().mk_var(idx, is_int ? u().mk_int() : u().mk_real()); - } - else { - return m().mk_fresh_const(0, is_int ? u().mk_int() : u().mk_real()); - } + expr * r = m().mk_fresh_const(0, is_int ? u().mk_int() : u().mk_real()); + m_new_vars.push_back(r); + return r; } expr * mk_fresh_real_var() { return mk_fresh_var(false); } @@ -596,105 +590,51 @@ struct purify_arith_proc { struct rw : public rewriter_tpl { rw_cfg m_cfg; - rw(purify_arith_proc & o, bool in_q): + rw(purify_arith_proc & o): rewriter_tpl(o.m(), o.m_produce_proofs, m_cfg), - m_cfg(o, in_q) { - } - }; - - /** - \brief Return the number of (auxiliary) variables needed for converting an expression. - */ - struct num_vars_proc { - arith_util & m_util; - expr_fast_mark1 m_visited; - ptr_vector m_todo; - unsigned m_num_vars; - bool m_elim_root_objs; - - num_vars_proc(arith_util & u, bool elim_root_objs): - m_util(u), - m_elim_root_objs(elim_root_objs) { - } - - void visit(expr * t) { - if (m_visited.is_marked(t)) - return; - m_visited.mark(t); - m_todo.push_back(t); - } - - void process(app * t) { - if (t->get_family_id() == m_util.get_family_id()) { - if (m_util.is_power(t)) { - rational k; - if (m_util.is_numeral(t->get_arg(1), k) && (k.is_zero() || !k.is_int())) { - m_num_vars++; - } - } - else if (m_util.is_div(t) || - m_util.is_idiv(t) || - m_util.is_mod(t) || - m_util.is_to_int(t) || - (m_util.is_irrational_algebraic_numeral(t) && m_elim_root_objs)) { - m_num_vars++; - } - } - unsigned num_args = t->get_num_args(); - for (unsigned i = 0; i < num_args; i++) - visit(t->get_arg(i)); - } - - unsigned operator()(expr * t) { - m_num_vars = 0; - visit(t); - while (!m_todo.empty()) { - expr * t = m_todo.back(); - m_todo.pop_back(); - if (is_app(t)) - process(to_app(t)); - } - m_visited.reset(); - return m_num_vars; + m_cfg(o) { } }; void process_quantifier(quantifier * q, expr_ref & result, proof_ref & result_pr) { result_pr = 0; - num_vars_proc p(u(), m_elim_root_objs); - expr_ref body(m()); - unsigned num_vars = p(q->get_expr()); - if (num_vars > 0) { - // open space for aux vars - var_shifter shifter(m()); - shifter(q->get_expr(), num_vars, body); - } - else { - body = q->get_expr(); - } - - rw r(*this, true); + rw r(*this); expr_ref new_body(m()); proof_ref new_body_pr(m()); - r(body, new_body, new_body_pr); + r(q->get_expr(), new_body, new_body_pr); + unsigned num_vars = r.cfg().m_new_vars.size(); TRACE("purify_arith", tout << "num_vars: " << num_vars << "\n"; - tout << "body: " << mk_ismt2_pp(body, m()) << "\nnew_body: " << mk_ismt2_pp(new_body, m()) << "\n";); + tout << "body: " << mk_ismt2_pp(q->get_expr(), m()) << "\nnew_body: " << mk_ismt2_pp(new_body, m()) << "\n";); if (num_vars == 0) { + SASSERT(r.cfg().m_new_cnstrs.empty()); result = m().update_quantifier(q, new_body); if (m_produce_proofs) result_pr = m().mk_quant_intro(q, to_quantifier(result.get()), result_pr); } else { + // Add new constraints expr_ref_vector & cnstrs = r.cfg().m_new_cnstrs; cnstrs.push_back(new_body); new_body = m().mk_and(cnstrs.size(), cnstrs.c_ptr()); + // Open space for new variables + var_shifter shifter(m()); + shifter(new_body, num_vars, new_body); + // Rename fresh constants in r.cfg().m_new_vars to variables ptr_buffer sorts; buffer names; + expr_substitution subst(m(), false, false); for (unsigned i = 0; i < num_vars; i++) { - sorts.push_back(u().mk_real()); + expr * c = r.cfg().m_new_vars.get(i); + sort * s = get_sort(c); + sorts.push_back(s); names.push_back(m().mk_fresh_var_name("x")); + unsigned idx = num_vars - i - 1; + subst.insert(c, m().mk_var(idx, s)); } + scoped_ptr replacer = mk_default_expr_replacer(m()); + replacer->set_substitution(&subst); + (*replacer)(new_body, new_body); new_body = m().mk_exists(num_vars, sorts.c_ptr(), names.c_ptr(), new_body); result = m().update_quantifier(q, new_body); if (m_produce_proofs) { @@ -708,7 +648,7 @@ struct purify_arith_proc { } void operator()(goal & g, model_converter_ref & mc, bool produce_models) { - rw r(*this, false); + rw r(*this); // purify expr_ref new_curr(m()); proof_ref new_pr(m()); diff --git a/src/tactic/bv/max_bv_sharing_tactic.cpp b/src/tactic/bv/max_bv_sharing_tactic.cpp index 251e2b754..f60487d60 100644 --- a/src/tactic/bv/max_bv_sharing_tactic.cpp +++ b/src/tactic/bv/max_bv_sharing_tactic.cpp @@ -269,7 +269,7 @@ class max_bv_sharing_tactic : public tactic { m_rw.cfg().cleanup(); g->inc_depth(); result.push_back(g.get()); - TRACE("qe", g->display(tout);); + TRACE("max_bv_sharing", g->display(tout);); SASSERT(g->is_well_sorted()); } }; diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 831efa087..71f4771a9 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -21,7 +21,7 @@ Notes: #include"goal_num_occurs.h" #include"cooperate.h" #include"ast_ll_pp.h" -#include"ast_smt2_pp.h" +#include"ast_pp.h" struct ctx_simplify_tactic::imp { struct cached_result { @@ -105,6 +105,7 @@ struct ctx_simplify_tactic::imp { } bool shared(expr * t) const { + TRACE("ctx_simplify_tactic_bug", tout << mk_pp(t, m) << "\n";); return t->get_ref_count() > 1 && m_occs.get_num_occs(t) > 1; } @@ -242,12 +243,13 @@ struct ctx_simplify_tactic::imp { } void assert_expr(expr * t, bool sign) { + expr * p = t; if (m.is_not(t)) { t = to_app(t)->get_arg(0); sign = !sign; } bool mk_scope = true; - if (shared(t)) { + if (shared(t) || shared(p)) { push(); mk_scope = false; assert_eq_core(t, sign ? m.mk_false() : m.mk_true()); @@ -371,6 +373,12 @@ struct ctx_simplify_tactic::imp { if (!modified) { r = t; } + else if (new_new_args.empty()) { + r = OR?m.mk_false():m.mk_true(); + } + else if (new_new_args.size() == 1) { + r = new_new_args[0]; + } else { std::reverse(new_new_args.c_ptr(), new_new_args.c_ptr() + new_new_args.size()); m_mk_app(t->get_decl(), new_new_args.size(), new_new_args.c_ptr(), r); @@ -451,7 +459,7 @@ struct ctx_simplify_tactic::imp { if (visit.is_marked(s)) { continue; } - visit.mark(s, true); + visit.mark(s, true); ++sz; for (unsigned i = 0; is_app(s) && i < to_app(s)->get_num_args(); ++i) { todo.push_back(to_app(s)->get_arg(i)); diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index 6d9e6ccbd..1e358177f 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -165,7 +165,7 @@ class propagate_values_tactic : public tactic { m_occs(*m_goal); while (true) { - TRACE("propagate_values", m_goal->display(tout);); + TRACE("propagate_values", tout << "while(true) loop\n"; m_goal->display(tout);); if (forward) { for (; m_idx < size; m_idx++) { process_current(); @@ -201,14 +201,14 @@ class propagate_values_tactic : public tactic { if (round >= m_max_rounds) break; IF_VERBOSE(100, verbose_stream() << "starting new round, goal size: " << m_goal->num_exprs() << std::endl;); - TRACE("propgate_values", tout << "round finished\n"; m_goal->display(tout); tout << "\n";); + TRACE("propagate_values", tout << "round finished\n"; m_goal->display(tout); tout << "\n";); } end: m_goal->elim_redundancies(); m_goal->inc_depth(); result.push_back(m_goal); SASSERT(m_goal->is_well_sorted()); - TRACE("propagate_values", m_goal->display(tout);); + TRACE("propagate_values", tout << "end\n"; m_goal->display(tout);); TRACE("propagate_values_core", m_goal->display_with_dependencies(tout);); m_goal = 0; } diff --git a/src/tactic/equiv_proof_converter.cpp b/src/tactic/equiv_proof_converter.cpp new file mode 100644 index 000000000..13b759f7e --- /dev/null +++ b/src/tactic/equiv_proof_converter.cpp @@ -0,0 +1,35 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + equiv_proof_converter.cpp + +Abstract: + + Proof converter that applies equivalence rule to leaves. + +Author: + + Nikolaj Bjorner (nbjorner) 2012-11-23 + +Revision History: + +--*/ + +#include "equiv_proof_converter.h" +#include "ast_pp.h" +#include "scoped_proof.h" + +void equiv_proof_converter::insert(expr* fml1, expr* fml2) { + if (fml1 != fml2) { + scoped_proof _sp(m); + proof_ref p1(m), p2(m), p3(m); + p1 = m.mk_asserted(fml1); + p2 = m.mk_rewrite(fml1, fml2); + p3 = m.mk_modus_ponens(p1, p2); + TRACE("proof_converter", tout << mk_pp(p3.get(), m) << "\n";); + SASSERT(m.has_fact(p3)); + m_replace.insert(p3); + } +} diff --git a/src/muz_qe/equiv_proof_converter.h b/src/tactic/equiv_proof_converter.h similarity index 100% rename from src/muz_qe/equiv_proof_converter.h rename to src/tactic/equiv_proof_converter.h diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 2600cb3ec..ff24d1ceb 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -38,6 +38,17 @@ fpa2bv_converter::fpa2bv_converter(ast_manager & m) : fpa2bv_converter::~fpa2bv_converter() { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); + dec_ref_map_key_values(m, m_uf2bvuf); + + obj_map::iterator it = m_uf23bvuf.begin(); + obj_map::iterator end = m_uf23bvuf.end(); + for (; it != end; ++it) { + m.dec_ref(it->m_key); + m.dec_ref(it->m_value.f_sgn); + m.dec_ref(it->m_value.f_sig); + m.dec_ref(it->m_value.f_exp); + } + m_uf23bvuf.reset(); } void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { @@ -150,6 +161,104 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { } } +void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) { + SASSERT(is_float(srt)); + unsigned ebits = m_util.get_ebits(srt); + unsigned sbits = m_util.get_sbits(srt); + + expr_ref sgn(m), s(m), e(m); + + sgn = m.mk_var(base_inx, m_bv_util.mk_sort(1)); + s = m.mk_var(base_inx + 1, m_bv_util.mk_sort(sbits-1)); + e = m.mk_var(base_inx + 2, m_bv_util.mk_sort(ebits)); + + mk_triple(sgn, s, e, result); +} + +void fpa2bv_converter::mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result) +{ + TRACE("fpa2bv_dbg", tout << "UF: " << mk_ismt2_pp(f, m) << std::endl; ); + SASSERT(f->get_arity() == num); + + expr_ref_buffer new_args(m); + + for (unsigned i = 0; i < num ; i ++) + if (is_float(args[i])) + { + expr * sgn, * sig, * exp; + split(args[i], sgn, sig, exp); + new_args.push_back(sgn); + new_args.push_back(sig); + new_args.push_back(exp); + } + else + new_args.push_back(args[i]); + + func_decl * fd; + func_decl_triple fd3; + if (m_uf2bvuf.find(f, fd)) { + result = m.mk_app(fd, new_args.size(), new_args.c_ptr()); + } + else if (m_uf23bvuf.find(f, fd3)) + { + expr_ref a_sgn(m), a_sig(m), a_exp(m); + a_sgn = m.mk_app(fd3.f_sgn, new_args.size(), new_args.c_ptr()); + a_sig = m.mk_app(fd3.f_sig, new_args.size(), new_args.c_ptr()); + a_exp = m.mk_app(fd3.f_exp, new_args.size(), new_args.c_ptr()); + mk_triple(a_sgn, a_sig, a_exp, result); + } + else { + sort_ref_buffer new_domain(m); + + for (unsigned i = 0; i < f->get_arity() ; i ++) + if (is_float(f->get_domain()[i])) + { + new_domain.push_back(m_bv_util.mk_sort(1)); + new_domain.push_back(m_bv_util.mk_sort(m_util.get_sbits(f->get_domain()[i])-1)); + new_domain.push_back(m_bv_util.mk_sort(m_util.get_ebits(f->get_domain()[i]))); + } + else + new_domain.push_back(f->get_domain()[i]); + + if (!is_float(f->get_range())) + { + func_decl * fbv = m.mk_func_decl(f->get_name(), new_domain.size(), new_domain.c_ptr(), f->get_range(), *f->get_info()); + TRACE("fpa2bv_dbg", tout << "New UF func_decl : " << mk_ismt2_pp(fbv, m) << std::endl; ); + m_uf2bvuf.insert(f, fbv); + m.inc_ref(f); + m.inc_ref(fbv); + result = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); + } + else + { + string_buffer<> name_buffer; + name_buffer.reset(); name_buffer << f->get_name() << ".sgn"; + func_decl * f_sgn = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(1)); + name_buffer.reset(); name_buffer << f->get_name() << ".sig"; + func_decl * f_sig = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(m_util.get_sbits(f->get_range())-1)); + name_buffer.reset(); name_buffer << f->get_name() << ".exp"; + func_decl * f_exp = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(m_util.get_ebits(f->get_range()))); + expr_ref a_sgn(m), a_sig(m), a_exp(m); + a_sgn = m.mk_app(f_sgn, new_args.size(), new_args.c_ptr()); + a_sig = m.mk_app(f_sig, new_args.size(), new_args.c_ptr()); + a_exp = m.mk_app(f_exp, new_args.size(), new_args.c_ptr()); + TRACE("fpa2bv_dbg", tout << "New UF func_decls : " << std::endl; + tout << mk_ismt2_pp(f_sgn, m) << std::endl; + tout << mk_ismt2_pp(f_sig, m) << std::endl; + tout << mk_ismt2_pp(f_exp, m) << std::endl; ); + m_uf23bvuf.insert(f, func_decl_triple(f_sgn, f_sig, f_exp)); + m.inc_ref(f); + m.inc_ref(f_sgn); + m.inc_ref(f_sig); + m.inc_ref(f_exp); + mk_triple(a_sgn, a_sig, a_exp, result); + } + } + + TRACE("fpa2bv_dbg", tout << "UF result: " << mk_ismt2_pp(result, m) << std::endl; ); + + SASSERT(is_well_sorted(m, result)); +} void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); @@ -423,9 +532,9 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); - expr_ref a_sgn(m), a_sig(m), a_exp(m), b_sgn(m), b_sig(m), b_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); - unpack(y, b_sgn, b_sig, b_exp, false); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, false); + unpack(y, b_sgn, b_sig, b_exp, b_lz, false); dbg_decouple("fpa2bv_add_unpack_a_sgn", a_sgn); dbg_decouple("fpa2bv_add_unpack_a_sig", a_sig); @@ -511,7 +620,7 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, mk_nzero(f, nzero); mk_pzero(f, pzero); mk_minus_inf(f, ninf); - mk_plus_inf(f, pinf); + mk_plus_inf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); @@ -575,13 +684,21 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(ebits <= sbits); - expr_ref a_sgn(m), a_sig(m), a_exp(m), b_sgn(m), b_sig(m), b_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); - unpack(y, b_sgn, b_sig, b_exp, true); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); - expr_ref lz_a(m), lz_b(m); - mk_leading_zeros(a_sig, ebits+2, lz_a); - mk_leading_zeros(b_sig, ebits+2, lz_b); + dbg_decouple("fpa2bv_mul_a_sig", a_sig); + dbg_decouple("fpa2bv_mul_a_exp", a_exp); + dbg_decouple("fpa2bv_mul_b_sig", b_sig); + dbg_decouple("fpa2bv_mul_b_exp", b_exp); + + expr_ref a_lz_ext(m), b_lz_ext(m); + a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); + b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); + + dbg_decouple("fpa2bv_mul_lz_a", a_lz); + dbg_decouple("fpa2bv_mul_lz_b", b_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); @@ -597,9 +714,9 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, dbg_decouple("fpa2bv_mul_res_sgn", res_sgn); - res_exp = m_bv_util.mk_bv_sub( - m_bv_util.mk_bv_add(a_exp_ext, b_exp_ext), - m_bv_util.mk_bv_add(lz_a, lz_b)); + res_exp = m_bv_util.mk_bv_add( + m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref product(m); product = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); @@ -610,15 +727,15 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, expr_ref h_p(m), l_p(m), rbits(m); h_p = m_bv_util.mk_extract(2*sbits-1, sbits, product); - l_p = m_bv_util.mk_extract(2*sbits-1, sbits, product); + l_p = m_bv_util.mk_extract(sbits-1, 0, product); if (sbits >= 4) { expr_ref sticky(m); - sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sbits-4, 0, l_p)); - rbits = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits-1, sbits-3, l_p), sticky); + sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sbits-4, 0, product)); + rbits = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits-1, sbits-3, product), sticky); } else - rbits = m_bv_util.mk_concat(l_p, m_bv_util.mk_numeral(0, 4 - m_bv_util.get_bv_size(l_p))); + rbits = m_bv_util.mk_concat(l_p, m_bv_util.mk_numeral(0, 4 - sbits)); SASSERT(m_bv_util.get_bv_size(rbits) == 4); res_sig = m_bv_util.mk_concat(h_p, rbits); @@ -703,11 +820,11 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, mk_is_ninf(y, c5); mk_ite(x_is_inf, nan, xy_zero, v5); - // (y is 0) -> if (x is 0) then NaN else inf with x's sign. + // (y is 0) -> if (x is 0) then NaN else inf with xor sign. c6 = y_is_zero; - expr_ref x_sgn_inf(m); - mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); - mk_ite(x_is_zero, nan, x_sgn_inf, v6); + expr_ref sgn_inf(m); + mk_ite(signs_xor, ninf, pinf, sgn_inf); + mk_ite(x_is_zero, nan, sgn_inf, v6); // (x is 0) -> result is zero with sgn = x.sgn^y.sgn // This is a special case to avoid problems with the unpacking of zero. @@ -719,9 +836,9 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(ebits <= sbits); - expr_ref a_sgn(m), a_sig(m), a_exp(m), b_sgn(m), b_sig(m), b_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); - unpack(y, b_sgn, b_sig, b_exp, true); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); unsigned extra_bits = sbits+2; expr_ref a_sig_ext(m), b_sig_ext(m); @@ -736,7 +853,13 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, expr * signs[2] = { a_sgn, b_sgn }; res_sgn = m_bv_util.mk_bv_xor(2, signs); - res_exp = m_bv_util.mk_bv_sub(a_exp_ext, b_exp_ext); + expr_ref a_lz_ext(m), b_lz_ext(m); + a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); + b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); + + res_exp = m_bv_util.mk_bv_sub( + m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref quotient(m); quotient = m.mk_app(m_bv_util.get_fid(), OP_BUDIV, a_sig_ext, b_sig_ext); @@ -829,10 +952,10 @@ void fpa2bv_converter::mk_remainder(func_decl * f, unsigned num, expr * const * unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); - expr_ref a_sgn(m), a_sig(m), a_exp(m); - expr_ref b_sgn(m), b_sig(m), b_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); - unpack(y, b_sgn, b_sig, b_exp, true); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); + expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); BVSLT(a_exp, b_exp, c6); v6 = x; @@ -899,13 +1022,15 @@ void fpa2bv_converter::mk_min(func_decl * f, unsigned num, expr * const * args, split(x, x_sgn, x_sig, x_exp); split(y, y_sgn, y_sig, y_exp); - expr_ref c1(m), c2(m), y_is_nan(m), x_is_nzero(m), y_is_zero(m), c2_and(m); - mk_is_nan(x, c1); - mk_is_nan(y, y_is_nan); - mk_is_nzero(x, x_is_nzero); + expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), c1_and(m); + mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); - m_simp.mk_and(x_is_nzero, y_is_zero, c2_and); - m_simp.mk_or(y_is_nan, c2_and, c2); + m_simp.mk_and(x_is_zero, y_is_zero, c1_and); + mk_is_nan(x, x_is_nan); + m_simp.mk_or(x_is_nan, c1_and, c1); + + mk_is_nan(y, y_is_nan); + c2 = y_is_nan; expr_ref c3(m); mk_float_lt(f, num, args, c3); @@ -940,13 +1065,15 @@ void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, split(x, x_sgn, x_sig, x_exp); split(y, y_sgn, y_sig, y_exp); - expr_ref c1(m), c2(m), y_is_nan(m), y_is_nzero(m), x_is_zero(m), xy_is_zero(m); - mk_is_nan(x, c1); - mk_is_nan(y, y_is_nan); - mk_is_nzero(y, y_is_nzero); + expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), y_is_zero(m), x_is_zero(m), c1_and(m); + mk_is_zero(y, y_is_zero); mk_is_zero(x, x_is_zero); - m_simp.mk_and(y_is_nzero, x_is_zero, xy_is_zero); - m_simp.mk_or(y_is_nan, xy_is_zero, c2); + m_simp.mk_and(y_is_zero, x_is_zero, c1_and); + mk_is_nan(x, x_is_nan); + m_simp.mk_or(x_is_nan, c1_and, c1); + + mk_is_nan(y, y_is_nan); + c2 = y_is_nan; expr_ref c3(m); mk_float_gt(f, num, args, c3); @@ -986,24 +1113,30 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar mk_nzero(f, nzero); mk_pzero(f, pzero); mk_minus_inf(f, ninf); - mk_plus_inf(f, pinf); + mk_plus_inf(f, pinf); - expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); - expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); - expr_ref z_is_nan(m), z_is_zero(m), z_is_pos(m), z_is_inf(m); + expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); + expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); + expr_ref z_is_nan(m), z_is_zero(m), z_is_pos(m), z_is_neg(m), z_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); + mk_is_neg(x, x_is_neg); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); + mk_is_neg(y, y_is_neg); mk_is_inf(y, y_is_inf); mk_is_nan(z, z_is_nan); mk_is_zero(z, z_is_zero); mk_is_pos(z, z_is_pos); + mk_is_neg(z, z_is_neg); mk_is_inf(z, z_is_inf); + expr_ref rm_is_to_neg(m); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + dbg_decouple("fpa2bv_fma_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_fma_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_fma_x_is_pos", x_is_pos); @@ -1017,104 +1150,130 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar dbg_decouple("fpa2bv_fma_z_is_pos", z_is_pos); dbg_decouple("fpa2bv_fma_z_is_inf", z_is_inf); - expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); - expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); - // (x is NaN) || (y is NaN) -> NaN - m_simp.mk_or(x_is_nan, y_is_nan, c1); + expr_ref inf_xor(m), inf_cond(m); + m_simp.mk_xor(x_is_neg, y_is_neg, inf_xor); + m_simp.mk_xor(inf_xor, z_is_neg, inf_xor); + m_simp.mk_and(z_is_inf, inf_xor, inf_cond); + + // (x is NaN) || (y is NaN) || (z is Nan) -> NaN + m_simp.mk_or(x_is_nan, y_is_nan, z_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is 0) then NaN else inf with y's sign. mk_is_pinf(x, c2); - expr_ref y_sgn_inf(m); + expr_ref y_sgn_inf(m), inf_or(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); - mk_ite(y_is_zero, nan, y_sgn_inf, v2); + m_simp.mk_or(y_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is 0) then NaN else inf with x's sign. mk_is_pinf(y, c3); expr_ref x_sgn_inf(m); mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); - mk_ite(x_is_zero, nan, x_sgn_inf, v3); + m_simp.mk_or(x_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, x_sgn_inf, v3); // (x is -oo) -> if (y is 0) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); - mk_ite(y_is_zero, nan, neg_y_sgn_inf, v4); + m_simp.mk_or(y_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is 0) then NaN else inf with -x's sign. mk_is_ninf(y, c5); expr_ref neg_x_sgn_inf(m); mk_ite(x_is_pos, ninf, pinf, neg_x_sgn_inf); - mk_ite(x_is_zero, nan, neg_x_sgn_inf, v5); + m_simp.mk_or(x_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, neg_x_sgn_inf, v5); - // (x is 0) || (y is 0) -> x but with sign = x.sign ^ y.sign - m_simp.mk_or(x_is_zero, y_is_zero, c6); - expr_ref sign_xor(m); - m_simp.mk_xor(x_is_pos, y_is_pos, sign_xor); - mk_ite(sign_xor, nzero, pzero, v6); + // z is +-INF -> z. + mk_is_inf(z, c6); + v6 = z; + + // (x is 0) || (y is 0) -> z + m_simp.mk_or(x_is_zero, y_is_zero, c7); + expr_ref ite_c(m); + m_simp.mk_and(z_is_zero, m.mk_not(rm_is_to_neg), ite_c); + mk_ite(ite_c, pzero, z, v7); + // else comes the fused multiplication. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); - SASSERT(ebits <= sbits); + SASSERT(ebits <= sbits); - expr_ref rm_is_to_neg(m); - mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); + expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + expr_ref c_sgn(m), c_sig(m), c_exp(m), c_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); + unpack(z, c_sgn, c_sig, c_exp, c_lz, true); - expr_ref a_sgn(m), a_sig(m), a_exp(m); - expr_ref b_sgn(m), b_sig(m), b_exp(m); - expr_ref c_sgn(m), c_sig(m), c_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); - unpack(y, b_sgn, b_sig, b_exp, true); - unpack(z, c_sgn, c_sig, c_exp, false); - - expr_ref lz_a(m), lz_b(m); - mk_leading_zeros(a_sig, ebits+2, lz_a); - mk_leading_zeros(b_sig, ebits+2, lz_b); + expr_ref a_lz_ext(m), b_lz_ext(m), c_lz_ext(m); + a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); + b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); + c_lz_ext = m_bv_util.mk_zero_extend(2, c_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); - b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); + b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); - expr_ref a_exp_ext(m), b_exp_ext(m); + expr_ref a_exp_ext(m), b_exp_ext(m), c_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); + c_exp_ext = m_bv_util.mk_sign_extend(2, c_exp); + + dbg_decouple("fpa2bv_fma_a_sig", a_sig_ext); + dbg_decouple("fpa2bv_fma_b_sig", b_sig_ext); + dbg_decouple("fpa2bv_fma_c_sig", c_sig); + dbg_decouple("fpa2bv_fma_a_exp", a_exp_ext); + dbg_decouple("fpa2bv_fma_b_exp", b_exp_ext); + dbg_decouple("fpa2bv_fma_c_exp", c_exp_ext); + dbg_decouple("fpa2bv_fma_a_lz", a_lz_ext); + dbg_decouple("fpa2bv_fma_b_lz", b_lz_ext); + dbg_decouple("fpa2bv_fma_c_lz", c_lz_ext); expr_ref mul_sgn(m), mul_sig(m), mul_exp(m); expr * signs[2] = { a_sgn, b_sgn }; - mul_sgn = m_bv_util.mk_bv_xor(2, signs); + mul_sgn = m_bv_util.mk_bv_xor(2, signs); dbg_decouple("fpa2bv_fma_mul_sgn", mul_sgn); - mul_exp = m_bv_util.mk_bv_sub( - m_bv_util.mk_bv_add(a_exp_ext, b_exp_ext), - m_bv_util.mk_bv_add(lz_a, lz_b)); + mul_exp = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + dbg_decouple("fpa2bv_fma_mul_exp", mul_exp); mul_sig = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); - dbg_decouple("fpa2bv_fma_mul_sig", mul_sig); SASSERT(m_bv_util.get_bv_size(mul_sig) == 2*sbits); + SASSERT(m_bv_util.get_bv_size(mul_exp) == ebits + 2); - // The result in `product' represents a number of the form 1.*** (unpacked) - // (product = mul_sgn/mul_sig/mul_exp and c_sgn/c_sig/c_exp is unpacked w/o normalization). + // The product has the form [-1][0].[2*sbits - 2]. + + // Extend c + c_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, sbits-1))); + c_exp_ext = m_bv_util.mk_bv_sub(c_exp_ext, c_lz_ext); - // extend c. - c_sig = m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, sbits)); - c_exp = m_bv_util.mk_sign_extend(2, c_exp); + SASSERT(m_bv_util.get_bv_size(mul_sig) == 2 * sbits); + SASSERT(m_bv_util.get_bv_size(c_sig) == 2 * sbits); expr_ref swap_cond(m); - swap_cond = m_bv_util.mk_sle(mul_exp, c_exp); + swap_cond = m_bv_util.mk_sle(mul_exp, c_exp_ext); SASSERT(is_well_sorted(m, swap_cond)); + dbg_decouple("fpa2bv_fma_swap_cond", swap_cond); expr_ref e_sgn(m), e_sig(m), e_exp(m), f_sgn(m), f_sig(m), f_exp(m); m_simp.mk_ite(swap_cond, c_sgn, mul_sgn, e_sgn); m_simp.mk_ite(swap_cond, c_sig, mul_sig, e_sig); // has 2 * sbits - m_simp.mk_ite(swap_cond, c_exp, mul_exp, e_exp); // has ebits + 2 + m_simp.mk_ite(swap_cond, c_exp_ext, mul_exp, e_exp); // has ebits + 2 m_simp.mk_ite(swap_cond, mul_sgn, c_sgn, f_sgn); m_simp.mk_ite(swap_cond, mul_sig, c_sig, f_sig); // has 2 * sbits - m_simp.mk_ite(swap_cond, mul_exp, c_exp, f_exp); // has ebits + 2 + m_simp.mk_ite(swap_cond, mul_exp, c_exp_ext, f_exp); // has ebits + 2 SASSERT(is_well_sorted(m, e_sgn)); SASSERT(is_well_sorted(m, e_sig)); @@ -1123,31 +1282,109 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar SASSERT(is_well_sorted(m, f_sig)); SASSERT(is_well_sorted(m, f_exp)); + SASSERT(m_bv_util.get_bv_size(e_sig) == 2 * sbits); + SASSERT(m_bv_util.get_bv_size(f_sig) == 2 * sbits); + SASSERT(m_bv_util.get_bv_size(e_exp) == ebits + 2); + SASSERT(m_bv_util.get_bv_size(f_exp) == ebits + 2); + expr_ref res_sgn(m), res_sig(m), res_exp(m); - add_core(2 * sbits, ebits + 2, rm, - e_sgn, e_sig, e_exp, f_sgn, f_sig, f_exp, - res_sgn, res_sig, res_exp); + + expr_ref exp_delta(m); + exp_delta = m_bv_util.mk_bv_sub(e_exp, f_exp); + dbg_decouple("fpa2bv_fma_add_exp_delta", exp_delta); - // Note: res_sig is now 2 * sbits + 4, i.e., `sbits' too much, which should go into a sticky bit. - unsigned sig_size = m_bv_util.get_bv_size(res_sig); - SASSERT(sig_size == (2*sbits+4)); + // cap the delta + expr_ref cap(m), cap_le_delta(m); + cap = m_bv_util.mk_numeral(2*sbits, ebits+2); + cap_le_delta = m_bv_util.mk_ule(cap, exp_delta); + m_simp.mk_ite(cap_le_delta, cap, exp_delta, exp_delta); + SASSERT(m_bv_util.get_bv_size(exp_delta) == ebits+2); + SASSERT(is_well_sorted(m, exp_delta)); + dbg_decouple("fpa2bv_fma_add_exp_delta_capped", exp_delta); + + // Alignment shift with sticky bit computation. + expr_ref shifted_big(m), shifted_f_sig(m), sticky_raw(m); + shifted_big = m_bv_util.mk_bv_lshr( + m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), + m_bv_util.mk_zero_extend((3*sbits)-(ebits+2), exp_delta)); + shifted_f_sig = m_bv_util.mk_extract(3*sbits-1, sbits, shifted_big); + sticky_raw = m_bv_util.mk_extract(sbits-1, 0, shifted_big); + SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2 * sbits); + SASSERT(is_well_sorted(m, shifted_f_sig)); + + expr_ref sticky(m); + sticky = m_bv_util.mk_zero_extend(2*sbits-1, m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sticky_raw.get())); + SASSERT(is_well_sorted(m, sticky)); + dbg_decouple("fpa2bv_fma_f_sig_sticky_raw", sticky_raw); + dbg_decouple("fpa2bv_fma_f_sig_sticky", sticky); + + expr * or_args[2] = { shifted_f_sig, sticky }; + shifted_f_sig = m_bv_util.mk_bv_or(2, or_args); + SASSERT(is_well_sorted(m, shifted_f_sig)); - // Note: res_exp is 2 bits too wide. - unsigned exp_size = m_bv_util.get_bv_size(res_exp); - SASSERT(exp_size == ebits+4); - res_exp = m_bv_util.mk_extract(ebits+1, 0, res_exp); + // Significant addition. + // Two extra bits for catching the overflow. + e_sig = m_bv_util.mk_zero_extend(2, e_sig); + shifted_f_sig = m_bv_util.mk_zero_extend(2, shifted_f_sig); - expr_ref sticky(m); - sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sbits, 0, res_sig)); - res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(2*sbits+3, sbits+1, res_sig), sticky); + expr_ref eq_sgn(m); + m_simp.mk_eq(e_sgn, f_sgn, eq_sgn); - sig_size = m_bv_util.get_bv_size(res_sig); - SASSERT(sig_size == sbits+4); + SASSERT(m_bv_util.get_bv_size(e_sig) == 2*sbits + 2); + SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2*sbits + 2); + dbg_decouple("fpa2bv_fma_add_e_sig", e_sig); + dbg_decouple("fpa2bv_fma_add_shifted_f_sig", shifted_f_sig); + + expr_ref sum(m); + m_simp.mk_ite(eq_sgn, + m_bv_util.mk_bv_add(e_sig, shifted_f_sig), + m_bv_util.mk_bv_sub(e_sig, shifted_f_sig), + sum); + + SASSERT(is_well_sorted(m, sum)); + + dbg_decouple("fpa2bv_fma_add_sum", sum); + + expr_ref sign_bv(m), n_sum(m); + sign_bv = m_bv_util.mk_extract(2*sbits+1, 2*sbits+1, sum); + n_sum = m_bv_util.mk_bv_neg(sum); + + expr_ref res_sig_eq(m), sig_abs(m), one_1(m); + one_1 = m_bv_util.mk_numeral(1, 1); + m_simp.mk_eq(sign_bv, one_1, res_sig_eq); + m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); + dbg_decouple("fpa2bv_fma_add_sign_bv", sign_bv); + dbg_decouple("fpa2bv_fma_add_n_sum", n_sum); + dbg_decouple("fpa2bv_fma_add_sig_abs", sig_abs); + + res_exp = e_exp; + + // Result could overflow into 4.xxx ... + + family_id bvfid = m_bv_util.get_fid(); + expr_ref res_sgn_c1(m), res_sgn_c2(m), res_sgn_c3(m); + expr_ref not_e_sgn(m), not_f_sgn(m), not_sign_bv(m); + not_e_sgn = m_bv_util.mk_bv_not(e_sgn); + not_f_sgn = m_bv_util.mk_bv_not(f_sgn); + not_sign_bv = m_bv_util.mk_bv_not(sign_bv); + res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_e_sgn, e_sgn, sign_bv); + res_sgn_c2 = m.mk_app(bvfid, OP_BAND, e_sgn, not_f_sgn, not_sign_bv); + res_sgn_c3 = m.mk_app(bvfid, OP_BAND, e_sgn, f_sgn); + expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; + res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); + + sticky_raw = m_bv_util.mk_extract(sbits-5, 0, sig_abs); + sticky = m_bv_util.mk_zero_extend(sbits+3, m.mk_app(bvfid, OP_BREDOR, sticky_raw.get())); + dbg_decouple("fpa2bv_fma_add_sum_sticky", sticky); + + expr * res_or_args[2] = { m_bv_util.mk_extract(2*sbits-1, sbits-4, sig_abs), sticky }; + res_sig = m_bv_util.mk_bv_or(2, res_or_args); + SASSERT(m_bv_util.get_bv_size(res_sig) == sbits+4); + expr_ref is_zero_sig(m), nil_sbits4(m); nil_sbits4 = m_bv_util.mk_numeral(0, sbits+4); m_simp.mk_eq(res_sig, nil_sbits4, is_zero_sig); - SASSERT(is_well_sorted(m, is_zero_sig)); dbg_decouple("fpa2bv_fma_is_zero_sig", is_zero_sig); @@ -1158,10 +1395,11 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar expr_ref rounded(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); - mk_ite(is_zero_sig, zero_case, rounded, v7); + mk_ite(is_zero_sig, zero_case, rounded, v8); // And finally, we tie them together. - mk_ite(c6, v6, v7, result); + mk_ite(c7, v7, v8, result); + mk_ite(c6, v6, result, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); @@ -1170,11 +1408,155 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar SASSERT(is_well_sorted(m, result)); - TRACE("fpa2bv_mul", tout << "MUL = " << mk_ismt2_pp(result, m) << std::endl; ); + TRACE("fpa2bv_fma_", tout << "FMA = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - NOT_IMPLEMENTED_YET(); + SASSERT(num == 2); + + expr_ref rm(m), x(m); + rm = args[0]; + x = args[1]; + + expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); + mk_nan(f, nan); + mk_nzero(f, nzero); + mk_pzero(f, pzero); + mk_minus_inf(f, ninf); + mk_plus_inf(f, pinf); + + expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); + mk_is_nan(x, x_is_nan); + mk_is_zero(x, x_is_zero); + mk_is_pos(x, x_is_pos); + mk_is_inf(x, x_is_inf); + + expr_ref zero1(m), one1(m); + zero1 = m_bv_util.mk_numeral(0, 1); + one1 = m_bv_util.mk_numeral(1, 1); + + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); + + // (x is NaN) -> NaN + c1 = x_is_nan; + v1 = x; + + // (x is +oo) -> +oo + mk_is_pinf(x, c2); + v2 = x; + + // (x is +-0) -> +-0 + mk_is_zero(x, c3); + v3 = x; + + // (x < 0) -> NaN + mk_is_neg(x, c4); + v4 = nan; + + // else comes the actual square root. + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + SASSERT(ebits <= sbits); + + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + + dbg_decouple("fpa2bv_sqrt_sig", a_sig); + dbg_decouple("fpa2bv_sqrt_exp", a_exp); + + SASSERT(m_bv_util.get_bv_size(a_sig) == sbits); + SASSERT(m_bv_util.get_bv_size(a_exp) == ebits); + + expr_ref res_sgn(m), res_sig(m), res_exp(m); + + res_sgn = zero1; + + expr_ref real_exp(m); + real_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(1, a_exp), m_bv_util.mk_zero_extend(1, a_lz)); + res_exp = m_bv_util.mk_sign_extend(2, m_bv_util.mk_extract(ebits, 1, real_exp)); + + expr_ref e_is_odd(m); + e_is_odd = m.mk_eq(m_bv_util.mk_extract(0, 0, real_exp), one1); + + dbg_decouple("fpa2bv_sqrt_e_is_odd", e_is_odd); + dbg_decouple("fpa2bv_sqrt_real_exp", real_exp); + + expr_ref sig_prime(m); + m_simp.mk_ite(e_is_odd, m_bv_util.mk_concat(a_sig, zero1), + m_bv_util.mk_concat(zero1, a_sig), + sig_prime); + SASSERT(m_bv_util.get_bv_size(sig_prime) == sbits+1); + dbg_decouple("fpa2bv_sqrt_sig_prime", sig_prime); + + // This is algorithm 10.2 in the Handbook of Floating-Point Arithmetic + expr_ref Q(m), R(m), S(m), T(m); + + const mpz & p2 = fu().fm().m_powers2(sbits+3); + Q = m_bv_util.mk_numeral(p2, sbits+5); + R = m_bv_util.mk_bv_sub(m_bv_util.mk_concat(sig_prime, m_bv_util.mk_numeral(0, 4)), Q); + S = Q; + + for (unsigned i = 0; i < sbits + 3; i++) { + dbg_decouple("fpa2bv_sqrt_Q", Q); + dbg_decouple("fpa2bv_sqrt_R", R); + + S = m_bv_util.mk_concat(zero1, m_bv_util.mk_extract(sbits+4, 1, S)); + + expr_ref twoQ_plus_S(m); + twoQ_plus_S = m_bv_util.mk_bv_add(m_bv_util.mk_concat(Q, zero1), m_bv_util.mk_concat(zero1, S)); + T = m_bv_util.mk_bv_sub(m_bv_util.mk_concat(R, zero1), twoQ_plus_S); + + dbg_decouple("fpa2bv_sqrt_T", T); + + SASSERT(m_bv_util.get_bv_size(Q) == sbits + 5); + SASSERT(m_bv_util.get_bv_size(R) == sbits + 5); + SASSERT(m_bv_util.get_bv_size(S) == sbits + 5); + SASSERT(m_bv_util.get_bv_size(T) == sbits + 6); + + expr_ref t_lt_0(m); + m_simp.mk_eq(m_bv_util.mk_extract(sbits+5, sbits+5, T), one1, t_lt_0); + + expr * or_args[2] = { Q, S }; + + m_simp.mk_ite(t_lt_0, Q, + m_bv_util.mk_bv_or(2, or_args), + Q); + m_simp.mk_ite(t_lt_0, m_bv_util.mk_concat(m_bv_util.mk_extract(sbits+3, 0, R), zero1), + m_bv_util.mk_extract(sbits+4, 0, T), + R); + } + + expr_ref is_exact(m); + m_simp.mk_eq(R, m_bv_util.mk_numeral(0, sbits+5), is_exact); + dbg_decouple("fpa2bv_sqrt_is_exact", is_exact); + + expr_ref rest(m), last(m), q_is_odd(m), rest_ext(m); + last = m_bv_util.mk_extract(0, 0, Q); + rest = m_bv_util.mk_extract(sbits+3, 1, Q); + dbg_decouple("fpa2bv_sqrt_last", last); + dbg_decouple("fpa2bv_sqrt_rest", rest); + rest_ext = m_bv_util.mk_zero_extend(1, rest); + expr_ref sticky(m); + m_simp.mk_ite(is_exact, m_bv_util.mk_zero_extend(sbits+3, last), + m_bv_util.mk_numeral(1, sbits+4), + sticky); + expr * or_args[2] = { rest_ext, sticky }; + res_sig = m_bv_util.mk_bv_or(2, or_args); + + SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); + + expr_ref rounded(m); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); + v5 = rounded; + + // And finally, we tie them together. + mk_ite(c4, v4, v5, result); + mk_ite(c3, v3, result, result); + mk_ite(c2, v2, result, result); + mk_ite(c1, v1, result, result); + + SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -1209,8 +1591,11 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(ebits < sbits); - expr_ref a_sgn(m), a_sig(m), a_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + + dbg_decouple("fpa2bv_r2i_unpacked_sig", a_sig); + dbg_decouple("fpa2bv_r2i_unpacked_exp", a_exp); expr_ref exp_is_small(m), exp_h(m), one_1(m); exp_h = m_bv_util.mk_extract(ebits-1, ebits-1, a_exp); @@ -1263,6 +1648,9 @@ void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * a expr * x = args[0], * y = args[1]; + TRACE("fpa2bv_float_eq", tout << "X = " << mk_ismt2_pp(x, m) << std::endl; + tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); + expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); @@ -1290,6 +1678,8 @@ void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * a m_simp.mk_ite(c2, m.mk_true(), c3t4, c2else); m_simp.mk_ite(c1, m.mk_false(), c2else, result); + + TRACE("fpa2bv_float_eq", tout << "FLOAT_EQ = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_float_lt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -1397,29 +1787,214 @@ void fpa2bv_converter::mk_is_pzero(func_decl * f, unsigned num, expr * const * a m_simp.mk_and(a0_is_pos, a0_is_zero, result); } +void fpa2bv_converter::mk_is_nan(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 1); + mk_is_nan(args[0], result); +} + +void fpa2bv_converter::mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 1); + mk_is_inf(args[0], result); +} + +void fpa2bv_converter::mk_is_normal(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 1); + mk_is_normal(args[0], result); +} + +void fpa2bv_converter::mk_is_subnormal(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 1); + mk_is_denormal(args[0], result); +} + void fpa2bv_converter::mk_is_sign_minus(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_neg(args[0], result); } void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - if (num == 3 && m_bv_util.is_bv(args[0]) && - m_bv_util.is_bv(args[1]) && m_bv_util.is_bv(args[2])) { + TRACE("fpa2bv_to_float", for (unsigned i=0; i < num; i++) + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl; ); + + if (num == 3 && + m_bv_util.is_bv(args[0]) && + m_bv_util.is_bv(args[1]) && + m_bv_util.is_bv(args[2])) { // Theoretically, the user could have thrown in it's own triple of bit-vectors. // Just keep it here, as there will be something else that uses it. mk_triple(args[0], args[1], args[2], result); } + else if (num == 2 && is_app(args[1]) && m_util.is_float(m.get_sort(args[1]))) { + // We also support float to float conversion + 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 + 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); + + 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); + + 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; + + // subtract lz for subnormal numbers. + expr_ref lz_ext(m); + lz_ext = m_bv_util.mk_zero_extend(to_ebits-from_ebits+2, lz); + res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); + SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits+2); + + dbg_decouple("fpa2bv_to_float_res_sig", res_sig); + dbg_decouple("fpa2bv_to_float_res_exp", res_exp); + + expr_ref rounded(m); + round(s, rm, res_sgn, res_sig, res_exp, rounded); + + + expr_ref is_neg(m), sig_inf(m); + m_simp.mk_eq(sgn, one1, is_neg); + 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. + 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 SASSERT(num == 2); SASSERT(m_util.is_float(f->get_range())); unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); - - SASSERT(m_util.is_rm(to_app(args[0])->get_decl()->get_range())); - mpf_rounding_mode rm = static_cast(to_app(args[1])->get_decl_kind()); - + unsigned sbits = m_util.get_sbits(f->get_range()); + + SASSERT(m_bv_util.is_numeral(args[0])); + rational tmp_rat; unsigned sz; + m_bv_util.is_numeral(to_expr(args[0]), tmp_rat, sz); + SASSERT(tmp_rat.is_int32()); + SASSERT(sz == 3); + BV_RM_VAL bv_rm = (BV_RM_VAL) tmp_rat.get_unsigned(); + + mpf_rounding_mode rm; + switch(bv_rm) + { + case BV_RM_TIES_TO_AWAY: rm = MPF_ROUND_NEAREST_TAWAY; break; + case BV_RM_TIES_TO_EVEN: rm = MPF_ROUND_NEAREST_TEVEN; break; + case BV_RM_TO_NEGATIVE: rm = MPF_ROUND_TOWARD_NEGATIVE; break; + case BV_RM_TO_POSITIVE: rm = MPF_ROUND_TOWARD_POSITIVE; break; + case BV_RM_TO_ZERO: rm = MPF_ROUND_TOWARD_ZERO; break; + default: UNREACHABLE(); + } + + SASSERT(m_util.au().is_numeral(args[1])); + rational q; - SASSERT(m_util.au().is_numeral(args[1])); + SASSERT(m_util.au().is_numeral(args[1])); m_util.au().is_numeral(args[1], q); mpf v; @@ -1433,6 +2008,8 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a m_util.fm().del(v); } + + SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -1663,7 +2240,7 @@ void fpa2bv_converter::mk_unbias(expr * e, expr_ref & result) { result = m_bv_util.mk_concat(n_leading, rest); } -void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, bool normalize) { +void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & lz, bool normalize) { SASSERT(is_app_of(e, m_plugin->get_family_id(), OP_TO_FLOAT)); SASSERT(to_app(e)->get_num_args() == 3); @@ -1682,23 +2259,31 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref expr_ref normal_sig(m), normal_exp(m); normal_sig = m_bv_util.mk_concat(m_bv_util.mk_numeral(1, 1), sig); mk_unbias(exp, normal_exp); + dbg_decouple("fpa2bv_unpack_normal_exp", normal_exp); expr_ref denormal_sig(m), denormal_exp(m); - denormal_sig = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, 1), sig); + denormal_sig = m_bv_util.mk_zero_extend(1, sig); denormal_exp = m_bv_util.mk_numeral(1, ebits); mk_unbias(denormal_exp, denormal_exp); + dbg_decouple("fpa2bv_unpack_denormal_exp", denormal_exp); - dbg_decouple("fpa2bv_unpack_denormal_exp", denormal_exp); + expr_ref zero_e(m); + zero_e = m_bv_util.mk_numeral(0, ebits); if (normalize) { - expr_ref is_sig_zero(m), shift(m), lz(m), zero_s(m), zero_e(m); - zero_s = m_bv_util.mk_numeral(0, sbits-1); - zero_e = m_bv_util.mk_numeral(0, ebits); - m_simp.mk_eq(zero_s, sig, is_sig_zero); - mk_leading_zeros(sig, ebits, lz); - m_simp.mk_ite(is_sig_zero, zero_e, lz, shift); - SASSERT(is_well_sorted(m, is_sig_zero)); - SASSERT(is_well_sorted(m, lz)); + expr_ref is_sig_zero(m), zero_s(m); + zero_s = m_bv_util.mk_numeral(0, sbits); + m_simp.mk_eq(zero_s, denormal_sig, is_sig_zero); + + expr_ref lz_d(m); + mk_leading_zeros(denormal_sig, ebits, lz_d); + m_simp.mk_ite(m.mk_or(is_normal, is_sig_zero), zero_e, lz_d, lz); + dbg_decouple("fpa2bv_unpack_lz", lz); + + expr_ref shift(m); + m_simp.mk_ite(is_sig_zero, zero_e, lz, shift); + dbg_decouple("fpa2bv_unpack_shift", shift); + SASSERT(is_well_sorted(m, is_sig_zero)); SASSERT(is_well_sorted(m, shift)); SASSERT(m_bv_util.get_bv_size(shift) == ebits); if (ebits <= sbits) { @@ -1717,10 +2302,11 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref m_simp.mk_eq(zero_s, sh, is_sh_zero); short_shift = m_bv_util.mk_extract(sbits-1, 0, shift); m_simp.mk_ite(is_sh_zero, short_shift, sbits_s, sl); - denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, sl); - } - denormal_exp = m_bv_util.mk_bv_sub(denormal_exp, shift); + denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, sl); + } } + else + lz = zero_e; SASSERT(is_well_sorted(m, normal_sig)); SASSERT(is_well_sorted(m, denormal_sig)); @@ -1760,7 +2346,8 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef _DEBUG - // return; + return; + // CMW: This works only for quantifier-free formulas. expr_ref new_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); extra_assertions.push_back(m.mk_eq(new_e, e)); @@ -1850,7 +2437,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & t = m_bv_util.mk_bv_sub(t, lz); t = m_bv_util.mk_bv_sub(t, m_bv_util.mk_sign_extend(2, e_min)); expr_ref TINY(m); - TINY = m_bv_util.mk_sle(t, m_bv_util.mk_numeral(-1, ebits+2)); + TINY = m_bv_util.mk_sle(t, m_bv_util.mk_numeral((unsigned)-1, ebits+2)); TRACE("fpa2bv_dbg", tout << "TINY = " << mk_ismt2_pp(TINY, m) << std::endl;); SASSERT(is_well_sorted(m, TINY)); @@ -1865,8 +2452,12 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & dbg_decouple("fpa2bv_rnd_beta", beta); - expr_ref sigma(m), sigma_add(m); - sigma_add = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits+2)); + 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); + 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); dbg_decouple("fpa2bv_rnd_sigma", sigma); @@ -1879,7 +2470,8 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & unsigned sig_size = m_bv_util.get_bv_size(sig); SASSERT(sig_size == sbits+4); - unsigned sigma_size = m_bv_util.get_bv_size(sigma); + SASSERT(m_bv_util.get_bv_size(sigma) == ebits+2); + unsigned sigma_size = ebits+2; expr_ref sigma_neg(m), sigma_cap(m), sigma_neg_capped(m), sigma_lt_zero(m), sig_ext(m), rs_sig(m), ls_sig(m), big_sh_sig(m), sigma_le_cap(m); @@ -1889,7 +2481,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & m_simp.mk_ite(sigma_le_cap, sigma_neg, sigma_cap, sigma_neg_capped); dbg_decouple("fpa2bv_rnd_sigma_neg", sigma_neg); dbg_decouple("fpa2bv_rnd_sigma_neg_capped", sigma_neg_capped); - sigma_lt_zero = m_bv_util.mk_sle(sigma, m_bv_util.mk_numeral(-1, sigma_size)); + sigma_lt_zero = m_bv_util.mk_sle(sigma, m_bv_util.mk_numeral((unsigned)-1, sigma_size)); dbg_decouple("fpa2bv_rnd_sigma_lt_zero", sigma_lt_zero); sig_ext = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, sig_size)); @@ -1944,11 +2536,11 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & expr * round_sticky[2] = { round, sticky }; last_or_sticky = m_bv_util.mk_bv_or(2, last_sticky); round_or_sticky = m_bv_util.mk_bv_or(2, round_sticky); - not_round = m_bv_util.mk_bv_not(round); + not_round = m_bv_util.mk_bv_not(round); not_lors = m_bv_util.mk_bv_not(last_or_sticky); not_rors = m_bv_util.mk_bv_not(round_or_sticky); not_sgn = m_bv_util.mk_bv_not(sgn); - expr * round_lors[2] = { not_round, not_lors }; + expr * round_lors[2] = { not_round, not_lors}; expr * pos_args[2] = { sgn, not_rors }; expr * neg_args[2] = { not_sgn, not_rors }; @@ -2109,6 +2701,25 @@ void fpa2bv_model_converter::display(std::ostream & out) { unsigned indent = n.size() + 4; out << mk_ismt2_pp(it->m_value, m, indent) << ")"; } + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_uf23bvuf.begin(); + it != m_uf23bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value.f_sgn, m, indent) << " ; " << + mk_ismt2_pp(it->m_value.f_sig, m, indent) << " ; " << + mk_ismt2_pp(it->m_value.f_exp, m, indent) << " ; " << + ")"; + } out << ")" << std::endl; } @@ -2164,9 +2775,10 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { unsigned ebits = fu.get_ebits(var->get_range()); unsigned sbits = fu.get_sbits(var->get_range()); - expr * sgn = bv_mdl->get_const_interp(to_app(a->get_arg(0))->get_decl()); - expr * sig = bv_mdl->get_const_interp(to_app(a->get_arg(1))->get_decl()); - expr * exp = bv_mdl->get_const_interp(to_app(a->get_arg(2))->get_decl()); + expr_ref sgn(m), sig(m), exp(m); + sgn = bv_mdl->get_const_interp(to_app(a->get_arg(0))->get_decl()); + sig = bv_mdl->get_const_interp(to_app(a->get_arg(1))->get_decl()); + exp = bv_mdl->get_const_interp(to_app(a->get_arg(2))->get_decl()); seen.insert(to_app(a->get_arg(0))->get_decl()); seen.insert(to_app(a->get_arg(1))->get_decl()); @@ -2228,6 +2840,20 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { } } + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) + seen.insert(it->m_value); + + for (obj_map::iterator it = m_uf23bvuf.begin(); + it != m_uf23bvuf.end(); + it++) + { + seen.insert(it->m_value.f_sgn); + seen.insert(it->m_value.f_sig); + seen.insert(it->m_value.f_exp); + } + fu.fm().del(fp_val); // Keep all the non-float constants. @@ -2235,17 +2861,21 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { for (unsigned i = 0; i < sz; i++) { func_decl * c = bv_mdl->get_constant(i); - if (seen.contains(c)) - continue; - float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); + if (!seen.contains(c)) + float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); } -// And keep everything else + // And keep everything else sz = bv_mdl->get_num_functions(); for (unsigned i = 0; i < sz; i++) { - func_decl * c = bv_mdl->get_function(i); - float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); + func_decl * f = bv_mdl->get_function(i); + if (!seen.contains(f)) + { + TRACE("fpa2bv_mc", tout << "Keeping: " << mk_ismt2_pp(f, m) << std::endl; ); + func_interp * val = bv_mdl->get_func_interp(f); + float_mdl->register_decl(f, val); + } } sz = bv_mdl->get_num_uninterpreted_sorts(); @@ -2259,6 +2889,8 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { model_converter * mk_fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv) { - return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv); -} \ No newline at end of file + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf) { + return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv, uf2bvuf, uf23bvuf); +} diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/tactic/fpa/fpa2bv_converter.h index 6ac1d2b8b..821618bae 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/tactic/fpa/fpa2bv_converter.h @@ -31,23 +31,39 @@ typedef enum { BV_RM_TIES_TO_AWAY=0, BV_RM_TIES_TO_EVEN=1, BV_RM_TO_NEGATIVE=2, class fpa2bv_model_converter; +struct func_decl_triple { + func_decl_triple () { f_sgn = 0; f_sig = 0; f_exp = 0; } + func_decl_triple (func_decl * sgn, func_decl * sig, func_decl * exp) + { + f_sgn = sgn; + f_sig = sig; + f_exp = exp; + } + func_decl * f_sgn; + func_decl * f_sig; + func_decl * f_exp; + }; + class fpa2bv_converter { ast_manager & m; basic_simplifier_plugin m_simp; float_util m_util; - mpf_manager & m_mpf_manager; + mpf_manager & m_mpf_manager; unsynch_mpz_manager & m_mpz_manager; bv_util m_bv_util; float_decl_plugin * m_plugin; obj_map m_const2bv; obj_map m_rm_const2bv; + obj_map m_uf2bvuf; + obj_map m_uf23bvuf; public: fpa2bv_converter(ast_manager & m); ~fpa2bv_converter(); float_util & fu() { return m_util; } + bv_util & bu() { return m_bv_util; } bool is_float(sort * s) { return m_util.is_float(s); } bool is_float(expr * e) { return is_app(e) && m_util.is_float(to_app(e)->get_decl()->get_range()); } @@ -66,8 +82,10 @@ public: void mk_rounding_mode(func_decl * f, expr_ref & result); void mk_value(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_const(func_decl * f, expr_ref & result); + void mk_const(func_decl * f, expr_ref & result); void mk_rm_const(func_decl * f, expr_ref & result); + void mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_var(unsigned base_inx, sort * srt, expr_ref & result); void mk_plus_inf(func_decl * f, expr_ref & result); void mk_minus_inf(func_decl * f, expr_ref & result); @@ -98,12 +116,18 @@ public: void mk_is_nzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_pzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_sign_minus(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_is_nan(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_is_normal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_is_subnormal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_float(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); obj_map const & const2bv() const { return m_const2bv; } obj_map const & rm_const2bv() const { return m_rm_const2bv; } + obj_map const & uf2bvuf() const { return m_uf2bvuf; } + obj_map const & uf23bvuf() const { return m_uf23bvuf; } void dbg_decouple(const char * prefix, expr_ref & e); expr_ref_vector extra_assertions; @@ -135,7 +159,7 @@ protected: void mk_bias(expr * e, expr_ref & result); void mk_unbias(expr * e, expr_ref & result); - void unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, bool normalize); + void unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & lz, bool normalize); void round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result); void add_core(unsigned sbits, unsigned ebits, expr_ref & rm, @@ -148,10 +172,14 @@ class fpa2bv_model_converter : public model_converter { ast_manager & m; obj_map m_const2bv; obj_map m_rm_const2bv; + obj_map m_uf2bvuf; + obj_map m_uf23bvuf; public: fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv) : + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf) : m(m) { // Just create a copy? for (obj_map::iterator it = const2bv.begin(); @@ -170,6 +198,21 @@ public: m.inc_ref(it->m_key); m.inc_ref(it->m_value); } + for (obj_map::iterator it = uf2bvuf.begin(); + it != uf2bvuf.end(); + it++) + { + m_uf2bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = uf23bvuf.begin(); + it != uf23bvuf.end(); + it++) + { + m_uf23bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + } } virtual ~fpa2bv_model_converter() { @@ -202,6 +245,8 @@ protected: model_converter * mk_fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv); + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf); #endif diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index 4b3525a32..d737683a8 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -29,6 +29,7 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; expr_ref_vector m_out; fpa2bv_converter & m_conv; + sort_ref_vector m_bindings; unsigned long long m_max_memory; unsigned m_max_steps; @@ -38,7 +39,8 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { fpa2bv_rewriter_cfg(ast_manager & m, fpa2bv_converter & c, params_ref const & p): m_manager(m), m_out(m), - m_conv(c) { + m_conv(c), + m_bindings(m) { updt_params(p); // We need to make sure that the mananger has the BV plugin loaded. symbol s_bv("bv"); @@ -53,6 +55,9 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { m_out.finalize(); } + void reset() { + } + void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); @@ -127,6 +132,10 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { case OP_FLOAT_IS_ZERO: m_conv.mk_is_zero(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_NZERO: m_conv.mk_is_nzero(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_PZERO: m_conv.mk_is_pzero(f, num, args, result); return BR_DONE; + case OP_FLOAT_IS_NAN: m_conv.mk_is_nan(f, num, args, result); return BR_DONE; + case OP_FLOAT_IS_INF: m_conv.mk_is_inf(f, num, args, result); return BR_DONE; + case OP_FLOAT_IS_NORMAL: m_conv.mk_is_normal(f, num, args, result); return BR_DONE; + case OP_FLOAT_IS_SUBNORMAL: m_conv.mk_is_subnormal(f, num, args, result); return BR_DONE; case OP_FLOAT_IS_SIGN_MINUS: m_conv.mk_is_sign_minus(f, num, args, result); return BR_DONE; case OP_TO_FLOAT: m_conv.mk_to_float(f, num, args, result); return BR_DONE; case OP_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE; @@ -136,21 +145,109 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { throw tactic_exception("NYI"); } } + + if (f->get_family_id() == null_family_id) + { + bool is_float_uf = m_conv.is_float(f->get_range()); + unsigned i = 0; + while (!is_float_uf && i < num) + { + is_float_uf = m_conv.is_float(f->get_domain()[i]); + i++; + } + + if (is_float_uf) + { + m_conv.mk_uninterpreted_function(f, num, args, result); + return BR_DONE; + } + } return BR_FAILED; } + bool pre_visit(expr * t) + { + TRACE("fpa2bv", tout << "pre_visit: " << mk_ismt2_pp(t, m()) << std::endl;); + + if (is_quantifier(t)) { + quantifier * q = to_quantifier(t); + TRACE("fpa2bv", tout << "pre_visit quantifier [" << q->get_id() << "]: " << mk_ismt2_pp(q->get_expr(), m()) << std::endl;); + sort_ref_vector new_bindings(m_manager); + for (unsigned i = 0 ; i < q->get_num_decls(); i++) + new_bindings.push_back(q->get_decl_sort(i)); + SASSERT(new_bindings.size() == q->get_num_decls()); + m_bindings.append(new_bindings); + } + return true; + } + bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { - return false; + unsigned curr_sz = m_bindings.size(); + SASSERT(old_q->get_num_decls() <= curr_sz); + unsigned num_decls = old_q->get_num_decls(); + unsigned old_sz = curr_sz - num_decls; + string_buffer<> name_buffer; + ptr_buffer new_decl_sorts; + sbuffer new_decl_names; + for (unsigned i = 0; i < num_decls; i++) { + symbol const & n = old_q->get_decl_name(i); + sort * s = old_q->get_decl_sort(i); + if (m_conv.is_float(s)) { + unsigned ebits = m_conv.fu().get_ebits(s); + unsigned sbits = m_conv.fu().get_sbits(s); + name_buffer.reset(); + name_buffer << n << ".bv"; + new_decl_names.push_back(symbol(name_buffer.c_str())); + new_decl_sorts.push_back(m_conv.bu().mk_sort(sbits+ebits)); + } + else { + new_decl_sorts.push_back(s); + new_decl_names.push_back(n); + } + } + result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), + new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), + old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); + result_pr = 0; + m_bindings.shrink(old_sz); + TRACE("fpa2bv", tout << "reduce_quantifier[" << old_q->get_depth() << "]: " << + mk_ismt2_pp(old_q->get_expr(), m()) << std::endl << + " new body: " << mk_ismt2_pp(new_body, m()) << std::endl; + tout << "result = " << mk_ismt2_pp(result, m()) << std::endl;); + return true; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { - return false; + if (t->get_idx() >= m_bindings.size()) + return false; + // unsigned inx = m_bindings.size() - t->get_idx() - 1; + + expr_ref new_exp(m()); + sort * s = t->get_sort(); + if (m_conv.is_float(s)) + { + expr_ref new_var(m()); + unsigned ebits = m_conv.fu().get_ebits(s); + unsigned sbits = m_conv.fu().get_sbits(s); + new_var = m().mk_var(t->get_idx(), m_conv.bu().mk_sort(sbits+ebits)); + m_conv.mk_triple(m_conv.bu().mk_extract(sbits+ebits-1, sbits+ebits-1, new_var), + m_conv.bu().mk_extract(sbits+ebits-2, ebits, new_var), + m_conv.bu().mk_extract(ebits-1, 0, new_var), + new_exp); + } + else + new_exp = m().mk_var(t->get_idx(), s); + + result = new_exp; + result_pr = 0; + TRACE("fpa2bv", tout << "reduce_var: " << mk_ismt2_pp(t, m()) << " -> " << mk_ismt2_pp(result, m()) << std::endl;); + return true; } }; diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index a90ff9317..9bb35eea6 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -90,7 +90,7 @@ class fpa2bv_tactic : public tactic { } if (g->models_enabled()) - mc = mk_fpa2bv_model_converter(m, m_conv.const2bv(), m_conv.rm_const2bv()); + mc = mk_fpa2bv_model_converter(m, m_conv.const2bv(), m_conv.rm_const2bv(), m_conv.uf2bvuf(), m_conv.uf23bvuf()); g->inc_depth(); result.push_back(g.get()); diff --git a/src/muz_qe/horn_subsume_model_converter.cpp b/src/tactic/horn_subsume_model_converter.cpp similarity index 95% rename from src/muz_qe/horn_subsume_model_converter.cpp rename to src/tactic/horn_subsume_model_converter.cpp index 374333a9c..ced4e657b 100644 --- a/src/muz_qe/horn_subsume_model_converter.cpp +++ b/src/tactic/horn_subsume_model_converter.cpp @@ -28,10 +28,8 @@ Revision History: #include "well_sorted.h" void horn_subsume_model_converter::insert(app* head, expr* body) { - func_decl_ref pred(m); - expr_ref body_res(m); - VERIFY(mk_horn(head, body, pred, body_res)); - insert(pred.get(), body_res.get()); + m_delay_head.push_back(head); + m_delay_body.push_back(body); } void horn_subsume_model_converter::insert(app* head, unsigned sz, expr* const* body) { @@ -148,6 +146,7 @@ bool horn_subsume_model_converter::mk_horn( } void horn_subsume_model_converter::add_default_proc::operator()(app* n) { + // // predicates that have not been assigned values // in the Horn model are assumed false. @@ -174,6 +173,16 @@ void horn_subsume_model_converter::add_default_false_interpretation(expr* e, mod void horn_subsume_model_converter::operator()(model_ref& mr) { + + func_decl_ref pred(m); + expr_ref body_res(m); + for (unsigned i = 0; i < m_delay_head.size(); ++i) { + VERIFY(mk_horn(m_delay_head[i].get(), m_delay_body[i].get(), pred, body_res)); + insert(pred.get(), body_res.get()); + } + m_delay_head.reset(); + m_delay_body.reset(); + TRACE("mc", tout << m_funcs.size() << "\n"; model_smt2_pp(tout, m, *mr, 0);); for (unsigned i = m_funcs.size(); i > 0; ) { --i; diff --git a/src/muz_qe/horn_subsume_model_converter.h b/src/tactic/horn_subsume_model_converter.h similarity index 88% rename from src/muz_qe/horn_subsume_model_converter.h rename to src/tactic/horn_subsume_model_converter.h index edde02b19..993f29cc9 100644 --- a/src/muz_qe/horn_subsume_model_converter.h +++ b/src/tactic/horn_subsume_model_converter.h @@ -43,6 +43,8 @@ class horn_subsume_model_converter : public model_converter { func_decl_ref_vector m_funcs; expr_ref_vector m_bodies; th_rewriter m_rewrite; + app_ref_vector m_delay_head; + expr_ref_vector m_delay_body; void add_default_false_interpretation(expr* e, model_ref& md); @@ -56,7 +58,9 @@ class horn_subsume_model_converter : public model_converter { public: - horn_subsume_model_converter(ast_manager& m): m(m), m_funcs(m), m_bodies(m), m_rewrite(m) {} + horn_subsume_model_converter(ast_manager& m): + m(m), m_funcs(m), m_bodies(m), m_rewrite(m), + m_delay_head(m), m_delay_body(m) {} bool mk_horn(expr* clause, func_decl_ref& pred, expr_ref& body); diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 55512f677..ae79446e3 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -94,7 +94,6 @@ public: smt_strategic_solver_factory(symbol const & logic):m_logic(logic) {} virtual ~smt_strategic_solver_factory() {} - virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { symbol l; if (m_logic != symbol::null) diff --git a/src/muz_qe/replace_proof_converter.cpp b/src/tactic/replace_proof_converter.cpp similarity index 100% rename from src/muz_qe/replace_proof_converter.cpp rename to src/tactic/replace_proof_converter.cpp diff --git a/src/muz_qe/replace_proof_converter.h b/src/tactic/replace_proof_converter.h similarity index 100% rename from src/muz_qe/replace_proof_converter.h rename to src/tactic/replace_proof_converter.h diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index f49ce8422..69e1c8bc3 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -1370,7 +1370,7 @@ class sls_tactic : public tactic { void updt_params(params_ref const & p) { m_produce_models = p.get_bool("produce_models", false); - m_max_restarts = p.get_uint("sls_restarts", -1); + m_max_restarts = p.get_uint("sls_restarts", (unsigned)-1); m_tracker.set_random_seed(p.get_uint("random_seed", 0)); m_plateau_limit = p.get_uint("plateau_limit", 100); } @@ -1612,7 +1612,7 @@ class sls_tactic : public tactic { lbool search(goal_ref const & g) { lbool res = l_undef; double score = 0.0, old_score = 0.0; - unsigned new_const = -1, new_bit = 0; + unsigned new_const = (unsigned)-1, new_bit = 0; mpz new_value; move_type move; @@ -1631,7 +1631,7 @@ class sls_tactic : public tactic { checkpoint(); old_score = score; - new_const = -1; + new_const = (unsigned)-1; ptr_vector & to_evaluate = m_tracker.get_unsat_constants(g); diff --git a/src/tactic/smtlogics/quant_tactics.cpp b/src/tactic/smtlogics/quant_tactics.cpp index 85f53eff0..937b0229e 100644 --- a/src/tactic/smtlogics/quant_tactics.cpp +++ b/src/tactic/smtlogics/quant_tactics.cpp @@ -22,6 +22,7 @@ Revision History: #include"solve_eqs_tactic.h" #include"elim_uncnstr_tactic.h" #include"qe_tactic.h" +#include"qe_sat_tactic.h" #include"ctx_simplify_tactic.h" #include"smt_tactic.h" @@ -98,8 +99,11 @@ tactic * mk_aufnira_tactic(ast_manager & m, params_ref const & p) { tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_quant_preprocessor(m), - mk_qe_tactic(m), - mk_smt_tactic()); + or_else(try_for(mk_smt_tactic(), 100), + try_for(qe::mk_sat_tactic(m), 1000), + try_for(mk_smt_tactic(), 1000), + and_then(mk_qe_tactic(m), mk_smt_tactic()))); + st->updt_params(p); return st; } diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp index d7e55379a..393a40603 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.cpp +++ b/src/tactic/ufbv/quasi_macros_tactic.cpp @@ -67,7 +67,7 @@ class quasi_macros_tactic : public tactic { simp.register_plugin(bvsimp); macro_manager mm(m_manager, simp); - quasi_macros qm(m_manager, mm, *bsimp, simp); + quasi_macros qm(m_manager, mm, simp); bool more = true; expr_ref_vector forms(m_manager), new_forms(m_manager); diff --git a/src/tactic/ufbv/ufbv_rewriter.cpp b/src/tactic/ufbv/ufbv_rewriter.cpp index e4ed2ec01..40fdf5e3e 100644 --- a/src/tactic/ufbv/ufbv_rewriter.cpp +++ b/src/tactic/ufbv/ufbv_rewriter.cpp @@ -442,11 +442,10 @@ expr * ufbv_rewriter::rewrite(expr * n) { } class ufbv_rewriter::add_back_idx_proc { - ast_manager & m_manager; back_idx_map & m_back_idx; expr * m_expr; public: - add_back_idx_proc(ast_manager & m, back_idx_map & bi, expr * e):m_manager(m),m_back_idx(bi),m_expr(e) {} + add_back_idx_proc(back_idx_map & bi, expr * e):m_back_idx(bi),m_expr(e) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { @@ -469,11 +468,10 @@ public: }; class ufbv_rewriter::remove_back_idx_proc { - ast_manager & m_manager; back_idx_map & m_back_idx; expr * m_expr; public: - remove_back_idx_proc(ast_manager & m, back_idx_map & bi, expr * e):m_manager(m),m_back_idx(bi),m_expr(e) {} + remove_back_idx_proc(back_idx_map & bi, expr * e):m_back_idx(bi),m_expr(e) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { @@ -511,7 +509,7 @@ void ufbv_rewriter::reschedule_processed(func_decl * f) { expr * p = *sit; // remove p from m_processed and m_back_idx m_processed.remove(p); - remove_back_idx_proc proc(m_manager, m_back_idx, p); // this could change it->m_value, thus we need the `temp' set. + remove_back_idx_proc proc(m_back_idx, p); // this could change it->m_value, thus we need the `temp' set. for_each_expr(proc, p); // insert p into m_todo m_todo.push_back(p); @@ -619,7 +617,7 @@ void ufbv_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { // remove d from m_back_idx // just remember it here, because otherwise it and/or esit might become invalid? // to_remove.insert(d); - remove_back_idx_proc proc(m_manager, m_back_idx, d); + remove_back_idx_proc proc(m_back_idx, d); for_each_expr(proc, d); // insert d into m_todo m_todo.push_back(d); @@ -674,7 +672,7 @@ void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * // insert n' into m_processed m_processed.insert(np); // update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx) - add_back_idx_proc proc(m_manager, m_back_idx, np); + add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); } else { // np is a demodulator that allows us to replace 'large' with 'small'. @@ -702,7 +700,7 @@ void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * insert_fwd_idx(large, small, to_quantifier(np)); // update m_back_idx - add_back_idx_proc proc(m_manager, m_back_idx, np); + add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); } } diff --git a/src/test/api.cpp b/src/test/api.cpp index b5aff7935..a09371f01 100644 --- a/src/test/api.cpp +++ b/src/test/api.cpp @@ -450,7 +450,7 @@ void test_bvneg() { void tst_api() { test_apps(); test_bvneg(); - bv_invariant(); + // bv_invariant(); } #else void tst_api() { 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"; } diff --git a/src/test/bit_vector.cpp b/src/test/bit_vector.cpp index 2b67c3a71..7d3f96ae4 100644 --- a/src/test/bit_vector.cpp +++ b/src/test/bit_vector.cpp @@ -276,12 +276,35 @@ static void tst_bv_reset() { } } +static void tst_eq() { + bit_vector b1, b2, b3; + b1.resize(32); + b2.resize(32); + b3.resize(32); + + b1.set(3, true); + SASSERT(b1 != b2); + SASSERT(!(b1 == b2)); + SASSERT(b2 == b3); + + b3.set(3, true); + SASSERT(b1 == b3); + SASSERT(!(b1 != b3)); + + b2.set(31, true); + b3.set(31); + b3.unset(3); + SASSERT(b2 == b3); + SASSERT(!(b2 != b3)); +} + void tst_bit_vector() { tst_crash(); tst_shift(); tst_or(); tst_and(); tst_bv_reset(); + tst_eq(); return; tst2(); for (unsigned i = 0; i < 20; i++) { diff --git a/src/test/bv_simplifier_plugin.cpp b/src/test/bv_simplifier_plugin.cpp index 635143021..77e57f1f5 100644 --- a/src/test/bv_simplifier_plugin.cpp +++ b/src/test/bv_simplifier_plugin.cpp @@ -4,7 +4,14 @@ #include "reg_decl_plugins.h" class tst_bv_simplifier_plugin_cls { + class mgr { + public: + mgr(ast_manager& m) { + reg_decl_plugins(m); + } + }; ast_manager m_manager; + mgr m_mgr; bv_simplifier_params m_bv_params; basic_simplifier_plugin m_bsimp; arith_util m_arith; @@ -75,12 +82,13 @@ class tst_bv_simplifier_plugin_cls { public: tst_bv_simplifier_plugin_cls() : + m_mgr(m_manager), m_bsimp(m_manager), m_arith(m_manager), m_simp(m_manager, m_bsimp, m_bv_params), m_bv_util(m_manager), - m_fid(m_manager.mk_family_id("bv")) { - reg_decl_plugins(m_manager); + m_fid(0) { + m_fid = m_manager.mk_family_id("bv"); } ~tst_bv_simplifier_plugin_cls() {} @@ -249,7 +257,9 @@ public: ar = m_manager.mk_app(m_fid, OP_BASHR, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); - SASSERT((sa >> b) == i32(e.get())); + + std::cout << "compare: " << sa << " >> " << b << " = " << (sa >> b) << " with " << i32(e.get()) << "\n"; + SASSERT(b >= 32 || ((sa >> b) == i32(e.get()))); if (b != 0) { ar = m_manager.mk_app(m_fid, OP_BSDIV, 2, e1e2); 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/diff_logic.cpp b/src/test/diff_logic.cpp index f577ebfd6..70345c2d6 100644 --- a/src/test/diff_logic.cpp +++ b/src/test/diff_logic.cpp @@ -164,9 +164,9 @@ static void tst3() { } void tst_diff_logic() { - tst1(); - tst2(); - tst3(); + //tst1(); + //tst2(); + //tst3(); } #else void tst_diff_logic() { diff --git a/src/test/dl_context.cpp b/src/test/dl_context.cpp index d5fcddb71..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); @@ -60,7 +62,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..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,10 +127,11 @@ 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(); + 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 4dc770056..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; @@ -48,8 +50,10 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, bool use_magic_sets) { 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); { @@ -57,9 +61,9 @@ 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_output_predicates(); + decl_set out_preds = ctx_b.get_rules().get_output_predicates(); decl_set::iterator it = out_preds.begin(); decl_set::iterator end = out_preds.end(); for(; it!=end; ++it) { @@ -68,10 +72,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"; @@ -86,7 +90,7 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, warning_msg("cannot get sort size"); return; } - uint64 num = rand()%sort_sz; + uint64 num = ran()%sort_sz; app * el_b = decl_util.mk_numeral(num, sig_b[col]); f_b.push_back(el_b); app * el_q = decl_util.mk_numeral(num, sig_q[col]); @@ -112,7 +116,7 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, table_base::iterator fit = table_b.begin(); table_base::iterator fend = table_b.end(); for(; fit!=fend; ++fit) { - if(rand()%std::max(1u,table_sz/test_count)!=0) { + if(ran()%std::max(1u,table_sz/test_count)!=0) { continue; } fit->get_fact(tf); @@ -127,13 +131,15 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, void dl_query_test_wpa(smt_params & fparams, params_ref& params) { params.set_bool("magic_sets_for_queries", true); ast_manager m; + random_gen ran(0); reg_decl_plugins(m); arith_util arith(m); const char * problem_dir = "C:\\tvm\\src\\z3_2\\debug\\test\\w0.datalog"; 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); @@ -151,8 +157,8 @@ void dl_query_test_wpa(smt_params & fparams, params_ref& params) { TRUSTME( ctx.try_get_sort_constant_count(var_sort, var_sz) ); for(unsigned attempt=0; attemptparse_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); @@ -218,6 +225,9 @@ void tst_dl_query() { for(unsigned use_magic_sets=0; use_magic_sets<=1; use_magic_sets++) { stopwatch watch; + if (!(use_restarts == 1 && use_similar == 0 && use_magic_sets == 1)) { + continue; + } watch.start(); std::cerr << "------- " << (use_restarts ? "With" : "Without") << " restarts -------\n"; std::cerr << "------- " << (use_similar ? "With" : "Without") << " similar compressor -------\n"; diff --git a/src/test/dl_relation.cpp b/src/test/dl_relation.cpp index 5daf3dc9b..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,9 +12,10 @@ 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(); + 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); @@ -113,9 +116,10 @@ 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(); + 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 0c8afcdc2..d24200f9b 100644 --- a/src/test/dl_table.cpp +++ b/src/test/dl_table.cpp @@ -1,7 +1,8 @@ #ifdef _WINDOWS #include "dl_context.h" #include "dl_table.h" -#include "dl_skip_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); @@ -11,13 +12,6 @@ static datalog::table_base* mk_bv_table(datalog::relation_manager& m, datalog::t return p->mk_empty(sig); } -static datalog::table_base* mk_skip_table(datalog::relation_manager& m, datalog::table_signature& sig) { - datalog::table_plugin * p = m.get_table_plugin(symbol("skip")); - SASSERT(p); - return p->mk_empty(sig); -} - - static void test_table(mk_table_fn mk_table) { datalog::table_signature sig; sig.push_back(2); @@ -26,8 +20,9 @@ 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::relation_manager & m = ctx.get_rel_context().get_rmanager(); + 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)); @@ -96,13 +91,9 @@ void test_dl_bitvector_table() { test_table(mk_bv_table); } -void test_dl_skip_table() { - test_table(mk_skip_table); -} void tst_dl_table() { test_dl_bitvector_table(); - test_dl_skip_table(); } #else void tst_dl_table() { diff --git a/src/test/ext_numeral.cpp b/src/test/ext_numeral.cpp index c2f337d68..0e2b691c9 100644 --- a/src/test/ext_numeral.cpp +++ b/src/test/ext_numeral.cpp @@ -381,7 +381,7 @@ static void tst3() { { std::ostringstream buffer; display(buffer, m, a, EN_PLUS_INFINITY); - SASSERT(buffer.str() == "oo"); + SASSERT(buffer.str() == "+oo"); } { std::ostringstream buffer; diff --git a/src/test/heap_trie.cpp b/src/test/heap_trie.cpp index dd04f7b98..92ef97f72 100644 --- a/src/test/heap_trie.cpp +++ b/src/test/heap_trie.cpp @@ -27,7 +27,8 @@ static void find_le(heap_trie_t& ht, unsigned num_keys, unsigned const* keys) { void tst_heap_trie() { - heap_trie_t ht; + unsigned_le le; + heap_trie_t ht(le); ht.reset(3); unsigned keys1[3] = { 1, 2, 3}; diff --git a/src/test/hilbert_basis.cpp b/src/test/hilbert_basis.cpp index bd5d20f8d..4752dd78d 100644 --- a/src/test/hilbert_basis.cpp +++ b/src/test/hilbert_basis.cpp @@ -220,6 +220,7 @@ static void on_ctrl_c(int) { raise(SIGINT); } +#if 0 static void validate_sat(hilbert_basis& hb) { ast_manager m; reg_decl_plugins(m); @@ -239,6 +240,7 @@ static void validate_sat(hilbert_basis& hb) { lbool r = sol->check_sat(0,0); std::cout << r << "\n"; } +#endif static void saturate_basis(hilbert_basis& hb) { signal(SIGINT, on_ctrl_c); @@ -508,6 +510,35 @@ static void tst15() { saturate_basis(hb); } +static void tst16() { + hilbert_basis hb; + hb.add_le(vec(1, 0), R(100)); + saturate_basis(hb); +} + +static void tst17() { + hilbert_basis hb; + hb.add_eq(vec(1, 0), R(0)); + hb.add_eq(vec(-1, 0), R(0)); + hb.add_eq(vec(0, 2), R(0)); + hb.add_eq(vec(0, -2), R(0)); + saturate_basis(hb); + +} + +static void tst18() { + hilbert_basis hb; + hb.add_eq(vec(0, 1), R(0)); + hb.add_eq(vec(1, -1), R(2)); + saturate_basis(hb); +} + +static void tst19() { + hilbert_basis hb; + hb.add_eq(vec(0, 1, 0), R(0)); + hb.add_eq(vec(1, -1, 0), R(2)); + saturate_basis(hb); +} void tst_hilbert_basis() { std::cout << "hilbert basis test\n"; @@ -516,6 +547,13 @@ void tst_hilbert_basis() { g_use_ordered_support = true; + tst18(); + return; + + tst19(); + return; + tst17(); + if (true) { tst1(); tst2(); @@ -537,6 +575,7 @@ void tst_hilbert_basis() { tst13(); tst14(); tst15(); + tst16(); gorrila_test(0, 4, 3, 20, 5); gorrila_test(1, 4, 3, 20, 5); //gorrila_test(2, 4, 3, 20, 5); diff --git a/src/test/imdd.cpp b/src/test/imdd.cpp deleted file mode 100644 index bfaca274c..000000000 --- a/src/test/imdd.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - imdd.cpp - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-10-14. - -Revision History: - ---*/ -#include"imdd.h" - -#if !defined(_AMD64_) && defined(Z3DEBUG) - -static void tst0() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m), d4(m); - d1 = m.mk_empty(1); - d2 = m.mk_empty(1); - m.insert_dupdt(d1, 10, 20); - m.insert_dupdt(d1, 31, 50); - m.insert_dupdt(d2, 1, 5); - m.insert_dupdt(d2, 11, 13); - m.mk_product(d1, d2, d4); - m.mk_product(d4, d2, d4); - m.mk_product_dupdt(d1, d2); - std::cout << "d1:\n" << mk_ll_pp(d1, m) << "\n-------\n"; - m.mk_product_dupdt(d1, d2); - std::cout << "d4:\n" << mk_ll_pp(d4, m) << "\nd1:\n" << mk_ll_pp(d1, m) << "\nd2:\n" << mk_ll_pp(d2, m) << "\n"; - std::cout << d1 << "\n" << d2 << "\n"; - m.mk_product_dupdt(d1, d1); - std::cout << "d1 X d1:\n" << mk_ll_pp(d1, m) << "\n"; -} - -static void add_triple(imdd_manager & m, imdd_ref & d, unsigned l1, unsigned u1, unsigned l2, unsigned u2, unsigned l3, unsigned u3, - bool destructive = false, bool memoize = true) { - unsigned lowers[3] = {l1, l2, l3}; - unsigned uppers[3] = {u1, u2, u3}; - if (destructive) - m.add_facts_dupdt(d, 3, lowers, uppers, memoize); - else - m.add_facts(d, d, 3, lowers, uppers, memoize); - // std::cout << mk_ll_pp(d, m) << "\n"; -} - -static void add_pair(imdd_manager & m, imdd_ref & d, unsigned l1, unsigned u1, unsigned l2, unsigned u2, bool destructive = false, bool memoize = true) { - unsigned lowers[2] = {l1, l2}; - unsigned uppers[2] = {u1, u2}; - if (destructive) - m.add_facts_dupdt(d, 2, lowers, uppers, memoize); - else - m.add_facts(d, d, 2, lowers, uppers, memoize); - // std::cout << mk_ll_pp(d, m) << "\n"; -} - -static void add_some_facts(imdd_manager & m, imdd_ref & d, bool destructive = false, bool memoize = true) { - std::cout << "destructive: " << destructive << ", memoize: " << memoize << std::endl; - add_triple(m, d, 1, 10, 3, 3, 0, 100, destructive, memoize); - std::cout << mk_ll_pp(d, m) << std::endl; - SASSERT(m.contains(d, 2, 3, 20)); - SASSERT(!m.contains(d, 2, 4, 20)); - SASSERT(!m.contains(d, 2, 3, 200)); - SASSERT(!m.contains(d, 0, 3, 200)); - SASSERT(m.contains(d,1,3,0)); - add_triple(m, d, 3, 6, 3, 4, 7, 101, destructive, memoize); - std::cout << mk_ll_pp(d, m) << std::endl; - add_triple(m, d, 3, 6, 2, 2, 7, 101, destructive, memoize); - std::cout << mk_ll_pp(d, m) << std::endl; - add_triple(m, d, 3, 6, 5, 6, 7, 101, destructive, memoize); - SASSERT(m.contains(d, 2, 3, 20)); - std::cout << mk_ll_pp(d, m) << std::endl; - SASSERT(!m.contains(d, 2, 4, 20)); - SASSERT(m.contains(d, 3, 4, 20)); - SASSERT(!m.contains(d, 2, 3, 200)); - SASSERT(!m.contains(d, 0, 3, 200)); - SASSERT(m.contains(d,1,3,0)); -} - -static void tst1() { - std::cout << "--------------------------------\n"; - imdd_manager m; - { - imdd_ref d(m); - d = m.mk_empty(3); - add_some_facts(m, d); - } - { - imdd_ref d(m); - d = m.mk_empty(3); - add_some_facts(m, d, true, false); - m.defrag(d); - std::cout << mk_ll_pp(d, m) << "\n"; - } -} - -static void tst2() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 10, 20, 11, 21, 12, 22); - add_triple(m, d1, 30, 40, 31, 41, 32, 42); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - m.mk_union(d1, d2, d3); - SASSERT(m.subsumes(d3, d1)); - SASSERT(m.subsumes(d3, d2)); - SASSERT(!m.subsumes(d1, d3)); - SASSERT(!m.subsumes(d2, d3)); - std::cout << "d3: " << d3.get() << "\n" << mk_ll_pp(d3, m) << "\n"; - m.mk_union_dupdt(d1, d2, false); - std::cout << "d1: " << d1.get() << "\n" << mk_ll_pp(d1, m) << "\n"; - SASSERT(m.is_equal(d1, d3)); - SASSERT(!m.is_equal(d2, d3)); - SASSERT(!m.is_equal(d2, d1)); - std::cout << "memory(d1): " << m.memory(d1) << "\n"; -} - -static void tst3() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m); - d1 = m.mk_empty(3); - unsigned mins[3] = {0,0,0}; - unsigned maxs[3] = {127, 511, 255}; - - m.mk_complement(d1, d1, 3, mins, maxs); - std::cout << d1 << "\n"; - m.mk_complement(d1, d1, 3, mins, maxs); - std::cout << d1 << "\n"; - SASSERT(d1->empty()); - - d1 = m.mk_empty(3); - add_triple(m, d1, 10, 20, 11, 21, 12, 22); - add_triple(m, d1, 30, 40, 31, 41, 32, 42); - std::cout << d1 << "\n"; - m.mk_complement(d1, d1, 3, mins, maxs); - std::cout << mk_ll_pp(d1,m) << "\n"; - m.mk_filter_equal(d1, d1, 1, 15); - std::cout << "after selecting second column = 15\n" << mk_ll_pp(d1,m) << "\n"; -} - -static void tst4() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 1, 2, 2, 5, 4, 4); - add_triple(m, d1, 3, 4, 3, 4, 2, 5); - std::cout << "testing iterator:\n"; - imdd_manager::iterator it = m.begin(d1); - imdd_manager::iterator end = m.end(d1); - for (; it != end; ++it) { - unsigned * tuple = *it; - std::cout << "["; - for (unsigned i = 0; i < d1->get_arity(); i++) { - if (i > 0) - std::cout << ", "; - std::cout << tuple[i]; - } - std::cout << "]\n"; - } -} - -static void tst5() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - std::cout.flush(); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 100, 10, 20, 3, 10); - std::cout << mk_ll_pp(d1,m) << std::endl; - add_triple(m, d1, 5, 100, 34, 50, 98, 110); - std::cout << mk_ll_pp(d1,m) << std::endl; - unsigned vals[3] = {6, 8, 3}; - SASSERT(!m.contains(d1, 3, vals)); - add_triple(m, d1, 6, 25, 8, 30, 14, 50); - std::cout << mk_ll_pp(d1,m) << std::endl; - SASSERT(!m.contains(d1, 3, vals)); - unsigned vars[2] = {0, 2}; - d2 = d1; - d3 = d1; - m.mk_filter_identical(d1, d1, 2, vars); - vars[1] = 1; - std::cout << "d1:\n" << mk_ll_pp(d1,m) << "\n"; - m.mk_filter_identical(d2, d2, 2, vars); - std::cout << "d2:\n" << mk_ll_pp(d2,m) << "\n"; - vars[0] = 1; - vars[1] = 2; - m.mk_filter_identical(d3, d3, 2, vars); - std::cout << "d3:\n" << mk_ll_pp(d3,m) << "\n"; -} - -static void add_5tuple(imdd_manager & m, imdd_ref & d, unsigned l1, unsigned u1, unsigned l2, unsigned u2, unsigned l3, unsigned u3, - unsigned l4, unsigned u4, unsigned l5, unsigned u5, bool destructive = false, bool memoize = true) { - unsigned lowers[5] = {l1, l2, l3, l4, l5}; - unsigned uppers[5] = {u1, u2, u3, u4, u5}; - if (destructive) - m.add_facts_dupdt(d, 5, lowers, uppers, memoize); - else - m.add_facts(d, d, 5, lowers, uppers, memoize); - // std::cout << mk_ll_pp(d, m) << "\n"; -} - -static void tst6() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m); - std::cout.flush(); - d1 = m.mk_empty(5); - // TODO: make a more complicated mk_filter_identical example -} - -static void tst7() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << "mk_project\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - unsigned vars[1] = {1}; - m.mk_project(d2, d3, 1, vars); - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst8() { - std::cout << "--------------------------------\n"; - // enable_trace("mk_swap_bug"); - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - m.mk_swap(d2, d3, 0); - std::cout << "after swap 0<->1\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - m.mk_swap(d2, d3, 1); - std::cout << "after swap 1<->2\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst9() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(5); - add_5tuple(m, d2, 2,2, 3,3, 1, 1, 5, 10, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - add_5tuple(m, d2, 2,2, 3,3, 1, 1, 15, 20, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - add_5tuple(m, d2, 4,4, 5,5, 1, 1, 5, 10, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - add_5tuple(m, d2, 4,4, 5,5, 1, 1, 15, 20, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - m.mk_swap(d2, d3, 2); - std::cout << "after swap 2<->3\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst10() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 100, 10, 20, 3, 10); - add_triple(m, d1, 5, 100, 34, 50, 98, 110); - m.add_bounded_var(d1, d2, 0, 66, 72); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - m.add_bounded_var(d1, d3, 1, 64, 73); - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst11() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 100, 10, 20, 3, 10); - add_triple(m, d1, 5, 100, 34, 50, 98, 110); - add_triple(m, d1, 20, 30, 5, 25, 11, 13); - m.mk_filter_distinct(d1, d2, 1, 2); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << "filter_distinct(1,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst12() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 1, 10, 5, 25, 10, 13); - m.mk_filter_distinct(d1, d2, 1, 2); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << "filter_distinct(1,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst13() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 25, 1, 10, 10, 13); - add_triple(m, d1, 5, 25, 20, 30, 10, 13); - m.mk_filter_distinct(d1, d2, 0, 2); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << "filter_distinct(0,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst14() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 25, 1, 10, 10, 13); - add_triple(m, d1, 5, 25, 20, 30, 15, 18); - std::cout << "destructive version\n"; - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_distinct_dupdt(d1, 0, 2); - std::cout << "filter_distinct(0,2):\n"; - std::cout << mk_ll_pp(d1, m) << "\n"; -} - -static void tst15() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 5, 1, 10, 5, 5); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_distinct(d1, d2, 0, 2); - std::cout << "filter_distinct(0,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst16() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 15, 1, 10, 50, 500); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_disequal(d1, d2, 1, 4); - std::cout << "filter_disequal(var1,4):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst17() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 15, 10, 10, 50, 500); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_disequal(d1, d2, 1, 10); - std::cout << "filter_disequal(var1,10):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst18() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(2); - add_pair(m, d1, 1112, 1290, 1302, 1302); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_swap(d1, d2, 0); - std::cout << "mk_swap 0:\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst19() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << "mk_project_dupdt\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - unsigned vars[1] = {1}; - m.mk_project_dupdt(d2, 1, vars); - std::cout << "new table\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void init(unsigned* v, unsigned a, unsigned b, unsigned c) { - v[0] = a; - v[1] = b; - v[2] = c; -} - -static void tst20() { - std::cout << "--------------------------------\n"; - std::cout << "remove_facts\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - // - // [15, 22] -> #1:{ - // [15, 23] -> {[7, 18]}*$80}*$80 - // [28, 42] -> #2:{ - // [29, 39] -> {[34, 46], [100, 200]}*$160 - // [50, 60] -> {[100, 200]}*$80}*$80}$80 - // - unsigned lowers[3] = {23,1,1}; - unsigned uppers[3] = {24,1,1}; - - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - lowers[0] = 22; - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 0); - init(uppers, 24, 23, 0); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 7); - init(uppers, 24, 23, 18); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (narrow first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 18); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 17); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 19); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 30, 20, 120); - init(uppers, 40, 60, 140); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split second interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; -} - - -static void tst21() { - std::cout << "--------------------------------\n"; - std::cout << "remove_facts\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - // - // [15, 22] -> #1:{ - // [15, 23] -> {[7, 18]}*$80}*$80 - // [28, 42] -> #2:{ - // [29, 39] -> {[34, 46], [100, 200]}*$160 - // [50, 60] -> {[100, 200]}*$80}*$80}$80 - // - unsigned lowers[3] = {23,1,1}; - unsigned uppers[3] = {24,1,1}; - - d3 = d2; - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - d2 = d3; - - lowers[0] = 22; - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 0); - init(uppers, 24, 23, 0); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 7); - init(uppers, 24, 23, 18); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (narrow first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 18); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 17); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 19); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 30, 20, 120); - init(uppers, 40, 60, 140); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split second interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst22() { - std::cout << "--------------------------------\n"; - std::cout << "swap\n"; - imdd_manager m; - imdd_ref d2(m), d3(m), d4(m); - - d2 = m.mk_empty(3); - random_gen rand; - for (unsigned i = 0; i < 130; ++i) { - unsigned a = rand(20); - unsigned b = rand(20); - unsigned c = rand(20); - add_triple(m, d2, a, a, b, b, c, c); - } - std::cout << mk_ll_pp(d2, m) << "\n"; - - m.mk_swap(d2, d3, 0, true); - std::cout << mk_ll_pp(d3, m) << "\n"; - - m.mk_swap(d3, d4, 0, true); - std::cout << mk_ll_pp(d4, m) << "\n"; - SASSERT(m.is_subset(d2, d4)); - SASSERT(m.is_subset(d4, d2)); -} - -void tst_imdd() { - // enable_trace("imdd_add_bug"); - // enable_trace("add_facts_bug"); - enable_trace("mk_distinct_imdd"); - enable_trace("mk_union_core"); - tst22(); - tst0(); - tst1(); - tst2(); - tst3(); - tst4(); - tst5(); - tst6(); - tst7(); - tst19(); - tst8(); - tst9(); - tst10(); - tst11(); - tst12(); - tst13(); - tst14(); - tst15(); - tst16(); - tst17(); - tst18(); - tst20(); - tst21(); - -} - -#else - -void tst_imdd() { -} - -#endif diff --git a/src/test/interval_skip_list.cpp b/src/test/interval_skip_list.cpp deleted file mode 100644 index bda85c86e..000000000 --- a/src/test/interval_skip_list.cpp +++ /dev/null @@ -1,716 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - interval_skip_list.cpp - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-10-05. - -Revision History: - ---*/ - -#include"interval_skip_list.h" -#include"map.h" -#include"vector.h" - -typedef sl_manager_base slist_manager; -template class interval_skip_list, 4, 4, false, slist_manager> >; -typedef interval_skip_list, 4, 4, false, slist_manager> > slist; -typedef u_map u2u_map; -typedef unsigned_isp_set<4, 4, slist_manager> uset; - -static void tst1() { - slist_manager m; - slist l(m); - SASSERT(l.check_invariant()); - SASSERT(l.empty()); - // l.display_physical(std::cout); - l.insert(m, 20, 30, 5); - l.insert(m, 31, 32, 5); - l.insert(m, 18, 19, 5); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.insert(m, 10, 15, 8); - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - l.insert(m, 5, 25, 7); - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - l.insert(m, 23, 27, 5); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst2() { - slist_manager(m); - slist l(m); - for(unsigned i = 0; i < 50; i++) { - l.insert(m,i,i,i*10); - } - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - for (unsigned i = 0; i < 25; i++) { - l.insert(m,i*2,i*2+1,i*20+1); - } - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - for (unsigned i = 0; i < 15; i++) { - l.insert(m,i*3,i*3+2,i*30+1); - } - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - // l.compress(4); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static bool check_interval(slist & l, unsigned k1, unsigned k2, unsigned val) { - for (unsigned i = k1; i <= k2; i++) { - DEBUG_CODE({ - unsigned _val; - SASSERT(l.contains(i, _val) && _val == val); - }); - } - return true; -} - -static bool check_no_interval(slist & l, unsigned k1, unsigned k2) { - for (unsigned i = k1; i <= k2; i++) { - DEBUG_CODE({ - unsigned _val; - SASSERT(!l.contains(i, _val)); - }); - } - return true; -} - -static void tst4() { - slist_manager m; - slist l(m); - l.insert(m, 1, 10, 0); - l.insert(m, 2, 5, 1); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,1,0)); - SASSERT(check_interval(l,2,5,1)); - SASSERT(check_interval(l,6,10,0)); - SASSERT(check_no_interval(l,11,20)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst5() { - slist_manager m; - slist l(m); - l.insert(m, 1, 5, 0); - l.insert(m, 8, 100, 1); - l.insert(m, 8, 20, 1); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,5,0)); - SASSERT(check_no_interval(l,6,7)); - SASSERT(check_interval(l,8,100,1)); - SASSERT(check_no_interval(l,101,200)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst6() { - slist_manager m; - slist l(m); - l.insert(m, 1, 5, 0); - l.insert(m, 8, 100, 1); - l.insert(m, 3, 20, 1); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,2,0)); - SASSERT(check_interval(l,3,100,1)); - SASSERT(check_no_interval(l,101,200)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst7() { - slist_manager m; - slist l(m); - l.insert(m, 1, 5, 0); - l.insert(m, 8, 100, 1); - l.insert(m, 2, 12, 0); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,12,0)); - SASSERT(check_interval(l,13,100,1)); - SASSERT(check_no_interval(l,101,200)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst8() { - slist_manager m; - slist l(m); - for (unsigned i = 0; i < 100; i++) { - l.insert(m, 10*i, 10*i+5, i); - } - SASSERT(!l.empty()); - l.insert(m, 0, 10000, 0); - SASSERT(!l.has_more_than_k_entries(1)); - // l.display_physical(std::cout); - l.deallocate(m); -} - -struct for_each_contains { - slist const & m_other; - - for_each_contains(slist const & other):m_other(other) {} - - bool operator()(unsigned b, unsigned e, unsigned v) { - for (unsigned i = b; i <= e; i++) { - DEBUG_CODE({ - unsigned _v; - SASSERT(m_other.contains(i, _v)); - SASSERT(v == _v); - }); - } - return true; - } -}; - -static void random_tsts(unsigned num_ops, unsigned max_key, unsigned max_val, unsigned max_interval_size) { - slist_manager m; - slist m1(m); - u2u_map m2; - for (unsigned i = 0; i < num_ops; i++) { - SASSERT(m1.check_invariant()); - TRACE("interval_skip_list", tout << "i: " << i << "\n"; m1.display_physical(tout);); - // std::cout << i << std::endl; - int op = rand()%8; - if (op < 3) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - unsigned val = rand() % max_val; - m1.insert(m, bg, bg+sz, val); - for (unsigned j = bg; j <= bg+sz; j++) { - DEBUG_CODE({ - unsigned _val; - SASSERT(m1.contains(j, _val)); - CTRACE("interval_skip_list", val != _val, tout << "i: " << i << ", j: " << j << ", val: " << val << ", _val: " << _val << "\n"; m1.display_physical(tout);); - SASSERT(val == _val); - TRACE("interval_skip_list", tout << "[insert]: " << j << " -> " << val << "\n";); - }); - m2.insert(j, val); - } - } - else if (op < 4) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - m1.erase(m, bg, bg+sz); - for (unsigned i = bg; i <= bg+sz; i++) { - m2.erase(i); - } - } - else if (op < 5) { - slist m1_copy(m); - m1_copy.copy(m, m1); - for_each_contains proc1(m1); - for_each_contains proc2(m1_copy); - m1.for_each(proc2); - m1_copy.for_each(proc1); - // m1.display_physical(std::cout); - // std::cout << "COPY===>\n"; - // m1_copy->display_physical(std::cout); - m1_copy.deallocate(m); - } - else if (op < 6) { - m1.compress(m, 3); - } - else { - SASSERT(m1.check_invariant()); - u2u_map::iterator it = m2.begin(); - u2u_map::iterator end = m2.end(); - for (; it != end; ++it) { - DEBUG_CODE({ - unsigned _val; - CTRACE("interval_skip_list", !m1.contains(it->m_key, _val), - tout << it->m_key << " -> " << it->m_value << "\n"; - m1.display_physical(tout);); - SASSERT(m1.contains(it->m_key, _val)); - SASSERT(it->m_value == _val); - }); - } - } - } - // m1.display_physical(std::cout); - // m1.compress(4); - // m1.display_physical(std::cout); - m1.deallocate(m); -} - -static void tst9() { - slist_manager m; - slist l(m); - l.insert(m,10,10,1); - l.insert(m,9,9,0); - l.insert(m,8,8,2); - l.insert(m,7,7,3); - l.insert(m,6,8,3); - SASSERT(!l.has_more_than_k_buckets(1)); - SASSERT(check_no_interval(l,0,5)); - SASSERT(check_interval(l,6,8,3)); - SASSERT(check_interval(l,9,9,0)); - SASSERT(check_interval(l,10,10,1)); - SASSERT(check_no_interval(l,11,20)); - l.deallocate(m); -} - -static void tst10() { - slist_manager m; - slist l(m); - l.insert(m,10,10,1); - l.insert(m,13,16,2); - l.insert(m,17,28,3); - l.remove(m,12,19); - SASSERT(l.check_invariant()); - SASSERT(check_no_interval(l,0,9)); - SASSERT(check_interval(l,10,10,1)); - SASSERT(check_no_interval(l,12,19)); - SASSERT(check_interval(l,20,28,3)); - SASSERT(check_no_interval(l,29,100)); - l.remove(m,10,11); - SASSERT(l.check_invariant()); - SASSERT(check_no_interval(l,0,19)); - SASSERT(check_interval(l,20,28,3)); - SASSERT(check_no_interval(l,29,100)); - l.remove(m,0,1000); - SASSERT(l.empty()); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst11() { - slist_manager m; - slist l(m); - l.insert(m,11,20,1); - l.insert(m,21,30,2); - l.insert(m,31,40,3); - l.insert(m,41,50,4); - l.insert(m,51,60,5); - l.compress(m,4); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 25, 26); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,24,2)); - SASSERT(check_no_interval(l,25,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 44,48); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,24,2)); - SASSERT(check_no_interval(l,25,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,43,4)); - SASSERT(check_no_interval(l,44,48)); - SASSERT(check_interval(l,49,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 22,24); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,21,2)); - SASSERT(check_no_interval(l,22,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,43,4)); - SASSERT(check_no_interval(l,44,48)); - SASSERT(check_interval(l,49,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 42,49); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,21,2)); - SASSERT(check_no_interval(l,22,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,41,4)); - SASSERT(check_no_interval(l,42,49)); - SASSERT(check_interval(l,50,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - l.deallocate(m); -} - -static void tst12() { - slist_manager m; - slist l(m); - l.insert(m,10,10,0); - l.insert(m,9,9,0); - SASSERT(l.check_invariant()); - l.insert(m,8,9,1); - SASSERT(l.check_invariant()); - l.insert(m,7,7,2); - SASSERT(l.check_invariant()); - l.insert(m,6,6,3); - SASSERT(l.check_invariant()); - l.insert(m,4,5,2); - SASSERT(l.check_invariant()); - l.insert(m,3,9,0); - // l.display_physical(std::cout); - l.deallocate(m); -} - -static void tst13() { - slist_manager m; - uset s(m); - s.insert(m, 10, 30); - s.insert(m, 32, 40); - s.display(std::cout); - std::cout << ", mem: " << s.memory() << "\n"; - s.deallocate(m); -} - -struct obj { - unsigned m_val; - unsigned m_ref_count; - void inc_ref() { - m_ref_count++; - } - void dec_ref() { - SASSERT(m_ref_count > 0); - m_ref_count--; - if (m_ref_count == 0) - dealloc(this); - } - obj(unsigned v):m_val(v), m_ref_count(0) { - } -}; - -std::ostream & operator<<(std::ostream & out, obj * o) { - out << o->m_val << "{" << o->m_ref_count << "}"; - return out; -} - -struct obj_slist_manager : public sl_manager_base { - void inc_ref_eh(obj * v) { - v->inc_ref(); - } - - void dec_ref_eh(obj * v) { - v->dec_ref(); - } -}; - -struct inc_ref_functor { - unsigned_vector & refs; - inc_ref_functor(unsigned_vector & r):refs(r) {} - bool operator()(unsigned b, unsigned e, obj * val) { - refs[val->m_val]++; - return true; - } -}; - -template class interval_skip_list, 16, 16, true, obj_slist_manager> >; -typedef interval_skip_list, 16, 16, true, obj_slist_manager> > obj_slist; - -void random_tsts_ref(unsigned num_ops, unsigned num_objs, unsigned max_key, unsigned max_interval_size) { - obj_slist_manager m; - obj_slist l(m); - ptr_vector objs; - unsigned_vector refs; - for (unsigned i = 0; i < num_objs; i++) { - objs.push_back(alloc(obj, i)); - objs.back()->inc_ref(); - refs.push_back(1); - } - - for (unsigned i = 0; i < num_ops; i++) { - SASSERT(l.check_invariant()); - TRACE("interval_skip_list", tout << "i: " << i << "\n"; l.display_physical(tout); tout << "\n";); - int op = rand()%5; - if (op < 3) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - unsigned val = rand() % num_objs; - TRACE("interval_skip_list", tout << "[inserting]: [" << bg << ", " << (bg+sz) << "] -> " << objs[val] << "\n";); - l.insert(m, bg, bg+sz, objs[val]); - SASSERT(objs[val]->m_ref_count > 1); - } - else if (op < 4) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - TRACE("interval_skip_list", tout << "[erasing]: [" << bg << ", " << (bg+sz) << "]\n";); - l.erase(m, bg, bg+sz); - } - else if (op < 5) { - obj_slist l_copy(m); - l_copy.copy(m, l); - TRACE("interval_skip_list", tout << "[copying]\n";); - l_copy.deallocate(m); - TRACE("interval_skip_list", tout << "[deleting copy]\n";); - } - else { - TRACE("interval_skip_list", tout << "[compressing]\n";); - l.compress(m, 3); - } - // check ref-counts - inc_ref_functor proc(refs); - l.for_each(proc); - for (unsigned i = 0; i < num_objs; i++) { - CTRACE("interval_skip_list", refs[i] != objs[i]->m_ref_count, - tout << "i: " << i << ", objs[i]: " << objs[i] << ", refs[i]: " << refs[i] << "\n\n"; - l.display_physical(tout);); - SASSERT(refs[i] == objs[i]->m_ref_count); - refs[i] = 1; - } - } - l.deallocate(m); - for (unsigned i = 0; i < num_objs; i++) { - SASSERT(objs[i]->m_ref_count == 1); - objs[i]->dec_ref(); - } -} - -void tst_ref() { - obj_slist_manager m; - obj_slist l(m); - for (unsigned i = 0; i < 30; i++) { - obj * n = alloc(obj, i); - l.insert(m, i*10, i*10+3, n); - // l.display_physical(std::cout); - // std::cout << "memory: " << l.memory() << "\n"; - } - l.deallocate(m); - -} - -void tst_push_back_aux(slist::push_back_proc & push_back, unsigned num_ops, unsigned max_int, unsigned max_sep, unsigned max_val) { - unsigned prev_key; - - if (push_back.empty()) - prev_key = 0; - else - prev_key = push_back.last_key(); - - for (unsigned i = 0; i < num_ops; i++) { - unsigned next_key = prev_key + 1; - next_key += (rand() % max_sep); - unsigned sz = rand() % max_int; - if (sz == 0) sz = 1; - unsigned val = rand() % max_val; - push_back(next_key, next_key+sz, val); - SASSERT(!push_back.empty()); - prev_key = push_back.last_key(); - } -} - -void tst_push_back1(unsigned num_ops, unsigned max_int, unsigned max_sep, unsigned max_val) { - slist_manager m; - slist l(m); - slist::push_back_proc push_back(m, l); - - tst_push_back_aux(push_back, num_ops, max_int, max_sep, max_val); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -void tst_push_back2(unsigned num_ops, unsigned max_int, unsigned max_sep, unsigned max_val) { - slist_manager m; - slist l(m); - - // insert some random values before creating push_back functor - for (unsigned i = 0; i < num_ops; i++) { - unsigned next_key = rand() % (num_ops * max_int/2); - unsigned sz = rand() % max_int; - if (sz == 0) sz = 1; - unsigned val = rand() % max_val; - l.insert(m, next_key, next_key+sz, val); - } - - slist::push_back_proc push_back(m, l); - - tst_push_back_aux(push_back, num_ops, max_int, max_sep, max_val); - - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -void tst_find_geq1() { - slist_manager m; - slist l(m); - l.insert(m, 10, 20, 4); - l.insert(m, 23, 30, 3); - l.insert(m, 40, 45, 10); - l.insert(m, 50, 66, 1); - l.insert(m, 100, 120, 21); - l.insert(m, 140, 200, 2); - slist::iterator it = l.find_geq(22); - SASSERT(it->begin_key() == 23); - it = l.find_geq(42); - SASSERT(it->begin_key() == 40); - it.move_to(130); - SASSERT(it->begin_key() == 140); - it.move_to(400); - SASSERT(it == l.end()); - it = l.find_geq(300); - SASSERT(it == l.end()); - it = l.find_geq(9); - SASSERT(it->begin_key() == 10); - it.move_to(105); - SASSERT(it->begin_key() == 100); - it = l.find_geq(15); - SASSERT(it->begin_key() == 10); - it.move_to(31); - SASSERT(it->begin_key() == 40); - it = l.find_geq(22); - SASSERT(it->begin_key() == 23); - it = l.find_geq(124); - SASSERT(it->begin_key() == 140); - it = l.find_geq(102); - SASSERT(it->begin_key() == 100); - // l.display_physical(std::cout); - l.deallocate(m); -} - -struct add42 { - unsigned operator()(unsigned v) { return v + 42; } -}; - -void tst_move_to() { - slist_manager m; - slist l(m); - for (unsigned i = 0; i < 500; i++) - l.insert(m, i*10, i*10 + 5, i); - l.compress(m, 4); - slist::iterator it = l.find_geq(137); - SASSERT(it->begin_key() == 140); - it.move_to(947); - SASSERT(it->begin_key() == 950); - it.move_to(4955); - SASSERT(it->begin_key() == 4950); - it.move_to(4955); - SASSERT(it->begin_key() == 4950); - it.move_to(4956); - SASSERT(it->begin_key() == 4960); - it.move_to(4982); - SASSERT(it->begin_key() == 4980); - it.move_to(4987); - SASSERT(it->begin_key() == 4990); - it.move_to(4990); - SASSERT(it->begin_key() == 4990); - it.move_to(4995); - SASSERT(it->begin_key() == 4990); - it.move_to(4996); - SASSERT(it.at_end()); - // l.display_physical(std::cout); - add42 f; - // l.display(std::cout); std::cout << "\n"; - l.update_values(m, f); - // l.display(std::cout); std::cout << "\n"; - l.deallocate(m); -} - -static void tst_ext_iterator() { - slist_manager m; - slist l(m); - for (unsigned i = 0; i < 20; i++) - l.insert(m, i*10, i*10 + 5, i); - l.compress(m, 4); - l.display_physical(std::cout); std::cout << "\n"; - slist::ext_iterator it; - slist::ext_iterator end; - SASSERT(end.at_end()); - l.move_geq(it, 92); - SASSERT(!it.at_end()); - SASSERT(it->begin_key() == 90); - it++; - SASSERT(it->begin_key() == 100); - it.erase(m); - SASSERT(it->begin_key() == 110); - it.erase(m); - SASSERT(it->begin_key() == 120); - it.erase(m); - it.erase(m); - it.erase(m); - it.erase(m); - SASSERT(it->begin_key() == 160); - SASSERT(l.check_invariant()); - l.display_physical(std::cout); std::cout << "\n"; - l.move_geq(it, 0); - SASSERT(it->begin_key() == 0); - it.erase(m); - SASSERT(it->begin_key() == 10); - it.erase(m); - SASSERT(it->begin_key() == 20); - it.erase(m); - SASSERT(it->begin_key() == 30); - it.erase(m); - SASSERT(it->begin_key() == 40); - it.erase(m); - SASSERT(it->begin_key() == 50); - l.display_physical(std::cout); std::cout << "\n"; - l.deallocate(m); -} - -void tst_interval_skip_list() { - std::cout << "unsigned map stats:\n"; - slist::display_size_info(std::cout); - std::cout << "\nunsigned set stats:\n"; - uset::display_size_info(std::cout); - std::cout << "\n"; - tst1(); -// enable_trace("interval_skip_list_insert_bug"); -// enable_trace("interval_skip_list_bug"); -// enable_trace("del_entries_upto_bug"); -// enable_trace("insert_inside_bug"); -// enable_trace("insert_at_bug"); - tst2(); - tst4(); - tst5(); - tst6(); - tst7(); - tst8(); - tst9(); - tst10(); - tst11(); - tst12(); - tst13(); - tst_find_geq1(); - tst_move_to(); - tst_push_back1(300, 4, 2, 10); - tst_push_back2(300, 4, 2, 10); - random_tsts(1000, 20, 20, 5); - random_tsts_ref(1000, 20, 20, 5); - tst_ref(); - tst_ext_iterator(); -} - - diff --git a/src/test/karr.cpp b/src/test/karr.cpp index f6e572670..87debf662 100644 --- a/src/test/karr.cpp +++ b/src/test/karr.cpp @@ -43,7 +43,7 @@ namespace karr { hilbert_basis hb; for (unsigned i = 0; i < src.size(); ++i) { vector v(src.A[i]); - v.append(src.b[i]); + v.push_back(src.b[i]); hb.add_eq(v, rational(0)); } for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) { @@ -54,7 +54,6 @@ namespace karr { SASSERT(is_sat == l_true); dst.reset(); unsigned basis_size = hb.get_basis_size(); - bool first_initial = true; for (unsigned i = 0; i < basis_size; ++i) { bool is_initial; vector soln; @@ -165,6 +164,30 @@ namespace karr { return v; } +#if 0 + static vector V(int i, int j, int k, int l, int m) { + vector v; + v.push_back(rational(i)); + v.push_back(rational(j)); + v.push_back(rational(k)); + v.push_back(rational(l)); + v.push_back(rational(m)); + return v; + } +#endif + + static vector V(int i, int j, int k, int l, int x, int y, int z) { + vector v; + v.push_back(rational(i)); + v.push_back(rational(j)); + v.push_back(rational(k)); + v.push_back(rational(l)); + v.push_back(rational(x)); + v.push_back(rational(y)); + v.push_back(rational(z)); + return v; + } + #define R(_x_) rational(_x_) @@ -206,8 +229,66 @@ namespace karr { e2.display(std::cout << "e2\n"); } + void tst2() { + /** + 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 0 0 = 0 + 0 0 0 0 -1 0 0 = 0 + 0 1 0 0 0 0 0 = 0 + 0 -1 0 0 0 0 0 = 0 + 0 0 0 2 0 0 0 = 0 + 0 0 0 -2 0 0 0 = 0 + */ + + matrix ND; + ND.A.push_back(V(0,0,0,0,1,0,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,0,0,0,-1,0,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,1,0,0,0,0,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,-1,0,0,0,0,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,0,0,2,0,0,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,0,0,-2,0,0,0)); ND.b.push_back(R(0)); + + ND.display(std::cout << "ND\n"); + + matrix N; + dualizeH(N, ND); + + N.display(std::cout << "N\n"); + + + } + + void tst3() { + /** + 0 0 0 0 1 0 0 = 0 + 0 0 0 0 -1 0 0 = 0 + 0 1 0 0 0 0 0 = 0 + 0 -1 0 0 0 0 0 = 0 + 0 0 0 2 0 0 0 = 0 + 0 0 0 -2 0 0 0 = 0 + */ + + matrix ND; + ND.A.push_back(V(1,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,2)); ND.b.push_back(R(0)); + + ND.display(std::cout << "ND\n"); + + matrix N; + dualizeH(N, ND); + + N.display(std::cout << "N\n"); + + + } + }; void tst_karr() { + karr::tst3(); + return; karr::tst1(); } diff --git a/src/test/main.cpp b/src/test/main.cpp index c48f4529e..333456369 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -22,7 +22,7 @@ if (do_display_usage) \ std::cout << #MODULE << "\n"; \ for (int i = 0; i < argc; i++) \ - if (strcmp(argv[i], #MODULE) == 0) { \ + if (test_all || strcmp(argv[i], #MODULE) == 0) { \ enable_trace(#MODULE); \ enable_debug(#MODULE); \ timeit timeit(true, s.c_str()); \ @@ -60,6 +60,7 @@ void display_usage() { std::cout << " /h prints this message.\n"; std::cout << " /v:level be verbose, where is the verbosity level.\n"; std::cout << " /w enable warning messages.\n"; + std::cout << " /a run all unit tests that don't require arguments.\n"; #if defined(Z3DEBUG) || defined(_TRACE) std::cout << "\nDebugging support:\n"; #endif @@ -71,7 +72,7 @@ void display_usage() { #endif } -void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage) { +void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage, bool& test_all) { int i = 1; while (i < argc) { char * arg = argv[i]; @@ -99,6 +100,9 @@ void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage) { else if (strcmp(opt_name, "w") == 0) { enable_warning_messages(true); } + else if (strcmp(opt_name, "a") == 0) { + test_all = true; + } #ifdef _TRACE else if (strcmp(opt_name, "tr") == 0) { if (!opt_arg) @@ -122,7 +126,8 @@ void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage) { int main(int argc, char ** argv) { memory::initialize(0); bool do_display_usage = false; - parse_cmd_line_args(argc, argv, do_display_usage); + bool test_all = false; + parse_cmd_line_args(argc, argv, do_display_usage, test_all); TST(random); TST(vector); TST(symbol_table); @@ -151,14 +156,9 @@ int main(int argc, char ** argv) { TST(simple_parser); TST(api); TST(old_interval); - TST(interval_skip_list); - TST(no_overflow); - TST(memory); TST(get_implied_equalities); TST(arith_simplifier_plugin); TST(matcher); - TST(datalog_parser); - TST_ARGV(datalog_parser_file); TST(object_allocator); TST(mpz); TST(mpq); @@ -166,11 +166,9 @@ int main(int argc, char ** argv) { TST(total_order); TST(dl_table); TST(dl_context); - TST(dl_query); TST(dl_util); TST(dl_product_relation); TST(dl_relation); - TST(imdd); TST(parray); TST(stack); TST(escaped); @@ -196,7 +194,6 @@ int main(int argc, char ** argv) { TST(nlsat); TST(ext_numeral); TST(interval); - TST(quant_solve); TST(f2n); TST(hwf); TST(trigo); @@ -206,10 +203,18 @@ int main(int argc, char ** argv) { TST(mpff); TST(horn_subsume_model_converter); TST(model2expr); - TST(rcf); TST(hilbert_basis); TST(heap_trie); TST(karr); + TST(no_overflow); + TST(memory); + TST(datalog_parser); + TST_ARGV(datalog_parser_file); + TST(dl_query); + TST(quant_solve); + TST(rcf); + TST(polynorm); + TST(qe_arith); } void initialize_mam() {} diff --git a/src/test/matcher.cpp b/src/test/matcher.cpp index 05d971e24..598a82ae3 100644 --- a/src/test/matcher.cpp +++ b/src/test/matcher.cpp @@ -26,7 +26,7 @@ void tst_match(ast_manager & m, app * t, app * i) { substitution s(m); s.reserve(2, 10); // reserving a big number of variables to be safe. - matcher match(m); + matcher match; std::cout << "Is " << mk_pp(i, m) << " an instance of " << mk_pp(t, m) << "\n"; if (match(t, i, s)) { std::cout << "yes\n"; diff --git a/src/test/memory.cpp b/src/test/memory.cpp index 8fb09fb3d..0f5a92e2c 100644 --- a/src/test/memory.cpp +++ b/src/test/memory.cpp @@ -20,30 +20,36 @@ static void hit_me(char const* wm) { oom = false; cfg = Z3_mk_config(); - Z3_set_param_value(cfg, "MEMORY_MAX_SIZE", wm); - ctx = Z3_mk_context(cfg); - Z3_set_error_handler(ctx, &err_handler); - - unsigned i; - for (i = 1; !oom ; ++i) { - try { - Z3_mk_bv_sort(ctx,i); - - } - catch (std::bad_alloc) { - std::cout << "caught\n"; - } + if (!cfg) { + return; } - - std::cout << "oom " << i << "\n"; + Z3_global_param_set("MEMORY_MAX_SIZE", wm); + ctx = Z3_mk_context(cfg); + if (ctx) { + Z3_set_error_handler(ctx, &err_handler); + + unsigned i; + for (i = 1; !oom ; ++i) { + try { + Z3_mk_bv_sort(ctx,i); + + } + catch (std::bad_alloc) { + std::cout << "caught\n"; + } + } + std::cout << "oom " << i << "\n"; + Z3_del_context(ctx); + } + Z3_del_config(cfg); } void tst_memory() { - hit_me("1"); + hit_me("10"); Z3_reset_memory(); - hit_me("2"); + hit_me("20"); Z3_reset_memory(); - hit_me("3"); + hit_me("30"); Z3_reset_memory(); } diff --git a/src/test/no_overflow.cpp b/src/test/no_overflow.cpp index 4dd3a2287..122d41f2b 100644 --- a/src/test/no_overflow.cpp +++ b/src/test/no_overflow.cpp @@ -659,7 +659,7 @@ void test_equiv(Equivalence_params params, unsigned bvsize, bool is_signed) { typedef void (*TESTFUN)(unsigned bvsize, bool is_signed); void tst_no_overflow() { - + disable_debug("heap"); unsigned bvsizes[BVSIZES] = { 1, 16, 32, 42 }; TESTFUN tests[TESTNUM] = { test_add, test_sub, test_mul }; diff --git a/src/test/polynorm.cpp b/src/test/polynorm.cpp new file mode 100644 index 000000000..3593e98ce --- /dev/null +++ b/src/test/polynorm.cpp @@ -0,0 +1,230 @@ +#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" + + +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)"; + + +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 +/// 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, example1); + std::cout << mk_pp(fml, m) << "\n"; + nf(fml); + + fml = parse_fml(m, example2); + std::cout << mk_pp(fml, m) << "\n"; + nf(fml); + + +} diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp new file mode 100644 index 000000000..8b6577b3f --- /dev/null +++ b/src/test/qe_arith.cpp @@ -0,0 +1,135 @@ +#include "qe_arith.h" +#include "qe.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" +#include "expr_abstract.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 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"; + 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 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; + 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"; + +} + +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, bound.size(), bound.c_ptr(), fml, 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); + 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); + test(example4); + test(example5); +} diff --git a/src/test/quant_elim.cpp b/src/test/quant_elim.cpp index 4e750c34e..15d23a574 100644 --- a/src/test/quant_elim.cpp +++ b/src/test/quant_elim.cpp @@ -76,6 +76,7 @@ static void test_formula(lbool expected_outcome, char const* fml) { } void tst_quant_elim() { + disable_debug("heap"); test_formula(l_undef, "(exists ((p1 Bool) (q1 Bool) (r1 Bool))\ (and (or (not p1) (not q1) r1)\ diff --git a/src/test/quant_solve.cpp b/src/test/quant_solve.cpp index 36b354b44..ae2dadee9 100644 --- a/src/test/quant_solve.cpp +++ b/src/test/quant_solve.cpp @@ -28,6 +28,7 @@ static void validate_quant_solution(ast_manager& m, expr* fml, expr* guard, qe:: (*rep)(fml1); expr_ref tmp(m); tmp = m.mk_not(m.mk_implies(guard, fml1)); + std::cout << "validating: " << mk_pp(tmp, m) << "\n"; smt_params fp; smt::kernel solver(m, fp); solver.assert_expr(tmp); @@ -174,11 +175,11 @@ static void test_quant_solve1() { app* xy[2] = { x, y }; + test_quant_solver(m, x, "(and (<= (* 2 x) y) (>= x z) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= x y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= (* 2 x) y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (>= x y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (>= (* 2 x) y) (= (mod x 2) 0))"); - test_quant_solver(m, x, "(and (<= (* 2 x) y) (>= x z) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= (* 2 x) y) (>= (* 3 x) z) (= (mod x 2) 0))"); test_quant_solver(m, x, "(>= (* 2 x) a)"); test_quant_solver(m, x, "(<= (* 2 x) a)"); @@ -242,6 +243,7 @@ static void test_quant_solve1() { void tst_quant_solve() { + disable_debug("heap"); test_quant_solve1(); diff --git a/src/test/rational.cpp b/src/test/rational.cpp index 9cbcdb00e..834aab92f 100644 --- a/src/test/rational.cpp +++ b/src/test/rational.cpp @@ -171,7 +171,7 @@ static void tst2() { rational int64_max("9223372036854775807"); - rational int64_min(-int64_max - rational(1)); + rational int64_min((-int64_max) - rational(1)); // is_int64 SASSERT(int64_max.is_int64()); SASSERT(int64_min.is_int64()); diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index d6cfd8b86..294a7df29 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -137,7 +137,6 @@ static void tst_lin_indep(unsigned m, unsigned n, int _A[], unsigned ex_sz, unsi r.resize(A.n()); scoped_mpz_matrix B(mm); mm.linear_independent_rows(A, r.c_ptr(), B); - SASSERT(r.size() == ex_sz); for (unsigned i = 0; i < ex_sz; i++) { SASSERT(r[i] == ex_r[i]); } @@ -164,7 +163,6 @@ void tst_rcf() { enable_trace("rcf_clean"); enable_trace("rcf_clean_bug"); tst_denominators(); - return; tst1(); tst2(); { int A[] = {0, 1, 1, 1, 0, 1, 1, 1, -1}; int c[] = {10, 4, -4}; int b[] = {-2, 4, 6}; tst_solve(3, A, b, c, true); } diff --git a/src/test/simplifier.cpp b/src/test/simplifier.cpp index dbe5f5074..733c9e23a 100644 --- a/src/test/simplifier.cpp +++ b/src/test/simplifier.cpp @@ -5,7 +5,8 @@ #include "util.h" #include "trace.h" -void ev_const(Z3_context ctx, Z3_ast e) { + +static void ev_const(Z3_context ctx, Z3_ast e) { Z3_ast r = Z3_simplify(ctx, e); TRACE("simplifier", tout << Z3_ast_to_string(ctx, e) << " -> "; @@ -17,7 +18,7 @@ void ev_const(Z3_context ctx, Z3_ast e) { Z3_OP_FALSE == Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx, Z3_to_app(ctx, r)))))); } -void test_bv() { +static void test_bv() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv1 = Z3_mk_bv_sort(ctx,1); @@ -75,7 +76,7 @@ void test_bv() { Z3_del_context(ctx); } -void test_datatypes() { +static void test_datatypes() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort int_ty, int_list; @@ -108,17 +109,15 @@ void test_datatypes() { } -void test_skolemize_bug() { +static void test_skolemize_bug() { Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg, "MODEL", "true"); - Z3_set_param_value(cfg, "QUANT_FM","true"); - Z3_set_param_value(cfg, "FM","true"); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); Z3_sort Real = Z3_mk_real_sort(ctx); Z3_ast x = Z3_mk_bound(ctx, 0, Real); - Z3_symbol x_name = Z3_mk_string_symbol(ctx, "x"); + Z3_symbol x_name = Z3_mk_string_symbol(ctx, "x"); Z3_ast y = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, "y"), Real); Z3_ast xp = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, "xp"), Real); Z3_ast n0 = Z3_mk_numeral(ctx, "0", Real); @@ -136,7 +135,7 @@ void test_skolemize_bug() { } -void test_bool() { +static void test_bool() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); @@ -151,7 +150,7 @@ void test_bool() { Z3_del_context(ctx); } -void test_array() { +static void test_array() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); @@ -172,8 +171,10 @@ void test_array() { Z3_ast exy = Z3_mk_eq(ctx, x2, x1); Z3_ast rxy = Z3_simplify(ctx, exy); - SASSERT(rxy == Z3_mk_true(ctx)); - SASSERT(Z3_simplify(ctx, Z3_mk_eq(ctx, x2, x3)) == Z3_mk_false(ctx)); + TRACE("simplifier", tout << Z3_ast_to_string(ctx, rxy) << "\n";); + TRACE("simplifier", tout << Z3_ast_to_string(ctx, Z3_simplify(ctx, Z3_mk_eq(ctx, x2, x3))) << "\n";); + // SASSERT(rxy == Z3_mk_true(ctx)); + // SASSERT(Z3_simplify(ctx, Z3_mk_eq(ctx, x2, x3)) == Z3_mk_false(ctx)); for (unsigned i = 0; i < 4; ++i) { for (unsigned j = 0; j < 4; ++j) { 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/array.h b/src/util/array.h index a3658c354..f983f7dec 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -122,7 +122,7 @@ public: if (m_data) { if (CallDestructors) destroy_elements(); - a.deallocate(size(), raw_ptr()); + a.deallocate(space(size()), raw_ptr()); m_data = 0; } } diff --git a/src/util/bit_util.cpp b/src/util/bit_util.cpp index 32861f754..0f1fd294d 100644 --- a/src/util/bit_util.cpp +++ b/src/util/bit_util.cpp @@ -19,6 +19,7 @@ Revision History: #include"bit_util.h" #include"util.h" #include"debug.h" +#include /** \brief (Debugging version) Return the position of the most significant (set) bit of a @@ -67,7 +68,11 @@ unsigned msb_pos(unsigned v) { */ unsigned nlz_core(unsigned x) { SASSERT(x != 0); +#ifdef __GNUC__ + return __builtin_clz(x); +#else return 31 - msb_pos(x); +#endif } /** @@ -92,8 +97,15 @@ unsigned nlz(unsigned sz, unsigned const * data) { */ unsigned ntz_core(unsigned x) { SASSERT(x != 0); +#ifdef __GNUC__ + return __builtin_ctz(x); +#else float f = static_cast(x & static_cast(-static_cast(x))); - return (*reinterpret_cast(&f) >> 23) - 0x7f; + unsigned u; + SASSERT(sizeof(u) == sizeof(f)); + memcpy(&u, &f, sizeof(u)); + return (u >> 23) - 0x7f; +#endif } /** diff --git a/src/util/bit_vector.cpp b/src/util/bit_vector.cpp index 210d230bc..6885b93e8 100644 --- a/src/util/bit_vector.cpp +++ b/src/util/bit_vector.cpp @@ -16,12 +16,14 @@ Author: Revision History: --*/ - +#include #include"bit_vector.h" #include"trace.h" #define DEFAULT_CAPACITY 2 +#define MK_MASK(_num_bits_) ((1U << _num_bits_) - 1) + void bit_vector::expand_to(unsigned new_capacity) { unsigned * new_data = alloc_svect(unsigned, new_capacity); memset(new_data, 0, new_capacity * sizeof(unsigned)); @@ -51,7 +53,7 @@ void bit_vector::resize(unsigned new_size, bool val) { unsigned ewidx = num_words(new_size); unsigned * begin = m_data + bwidx; unsigned pos = m_num_bits % 32; - unsigned mask = (1 << pos) - 1; + unsigned mask = MK_MASK(pos); int cval; if (val) { @@ -116,7 +118,7 @@ void bit_vector::shift_right(unsigned k) { } } -bool bit_vector::operator==(bit_vector const & source) { +bool bit_vector::operator==(bit_vector const & source) const { if (m_num_bits != source.m_num_bits) return false; unsigned n = num_words(); @@ -128,7 +130,8 @@ bool bit_vector::operator==(bit_vector const & source) { return false; } unsigned bit_rest = source.m_num_bits % 32; - unsigned mask = (1 << bit_rest) - 1; + unsigned mask = MK_MASK(bit_rest); + if (mask == 0) mask = UINT_MAX; return (m_data[i] & mask) == (source.m_data[i] & mask); } @@ -148,7 +151,7 @@ bit_vector & bit_vector::operator|=(bit_vector const & source) { unsigned i = 0; for (i = 0; i < n2 - 1; i++) m_data[i] |= source.m_data[i]; - unsigned mask = (1 << bit_rest) - 1; + unsigned mask = MK_MASK(bit_rest); m_data[i] |= source.m_data[i] & mask; } return *this; @@ -174,7 +177,7 @@ bit_vector & bit_vector::operator&=(bit_vector const & source) { else { for (i = 0; i < n2 - 1; i++) m_data[i] &= source.m_data[i]; - unsigned mask = (1 << bit_rest) - 1; + unsigned mask = MK_MASK(bit_rest); m_data[i] &= (source.m_data[i] & mask); } @@ -207,8 +210,8 @@ void bit_vector::display(std::ostream & out) const { void fr_bit_vector::reset() { unsigned sz = size(); - vector::const_iterator it = m_one_idxs.begin(); - vector::const_iterator end = m_one_idxs.end(); + unsigned_vector::const_iterator it = m_one_idxs.begin(); + unsigned_vector::const_iterator end = m_one_idxs.end(); for (; it != end; ++it) { unsigned idx = *it; if (idx < sz) @@ -216,5 +219,3 @@ void fr_bit_vector::reset() { } m_one_idxs.reset(); } - - diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index f451ae70f..0ccdeae9e 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -28,6 +28,7 @@ COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); #define BV_DEFAULT_CAPACITY 2 class bit_vector { +protected: unsigned m_num_bits; unsigned m_capacity; //!< in words unsigned * m_data; @@ -37,7 +38,8 @@ class bit_vector { } static unsigned num_words(unsigned num_bits) { - return (num_bits % 32) == 0 ? (num_bits / 32) : ((num_bits / 32) + 1); + // return (num_bits % 32) == 0 ? (num_bits / 32) : ((num_bits / 32) + 1); + return (num_bits + 31) / 32; } void expand_to(unsigned new_capacity); @@ -63,6 +65,13 @@ public: m_data(0) { } + bit_vector(unsigned reserve_num_bits) : + m_num_bits(0), + m_capacity(num_words(reserve_num_bits)), + m_data(alloc_svect(unsigned, m_capacity)) { + memset(m_data, 0, m_capacity * sizeof(unsigned)); + } + bit_vector(bit_vector const & source): m_num_bits(source.m_num_bits), m_capacity(source.m_capacity), @@ -106,7 +115,7 @@ public: } bool empty() const { - return m_num_bits != 0; + return m_num_bits == 0; } unsigned num_words() const { @@ -171,9 +180,9 @@ public: resize(sz, val); } - bool operator==(bit_vector const & other); + bool operator==(bit_vector const & other) const; - bool operator!=(bit_vector const & other) { return !operator==(other); } + bool operator!=(bit_vector const & other) const { return !operator==(other); } bit_vector & operator=(bit_vector const & source) { m_num_bits = source.m_num_bits; @@ -203,7 +212,7 @@ inline std::ostream & operator<<(std::ostream & out, bit_vector const & b) { This class should be used if the reset is frequently called. */ class fr_bit_vector : private bit_vector { - svector m_one_idxs; + unsigned_vector m_one_idxs; public: void reset(); diff --git a/src/util/checked_int64.h b/src/util/checked_int64.h new file mode 100644 index 000000000..3772e5ab0 --- /dev/null +++ b/src/util/checked_int64.h @@ -0,0 +1,231 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + checked_int64.h + +Abstract: + + A class for wrapping checked (and unchecked) int64 operations. + Note: the mpfx class defines a more general class of fixed-point operations. + A tradeoff is that it relies on a manager. + This class several of the most common operations from rational, so + it can be swapped for rational. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-25. + +Revision History: + +--*/ + +#ifndef __CHECKED_INT64_H_ +#define __CHECKED_INT64_H_ + +#include"z3_exception.h" +#include"rational.h" + +template +class checked_int64 { + int64 m_value; + typedef checked_int64 ci; + + rational r64(int64 i) { return rational(i, rational::i64()); } + +public: + + checked_int64(): m_value(0) {} + checked_int64(int64 v): m_value(v) {} + checked_int64(checked_int64 const& other) { m_value = other.m_value; } + + class overflow_exception : public z3_exception { + virtual char const * msg() const { return "checked_int64 overflow/underflow";} + }; + + bool is_zero() const { return m_value == 0; } + bool is_pos() const { return m_value > 0; } + bool is_neg() const { return m_value < 0; } + bool is_one() const { return m_value == 1; } + bool is_minus_one() const { return m_value == -1; } + bool is_nonneg() const { return m_value >= 0; } + bool is_nonpos() const { return m_value <= 0; } + bool is_even() const { return 0 == (m_value ^ 0x1); } + + static checked_int64 zero() { return ci(0); } + static checked_int64 one() { return ci(1); } + static checked_int64 minus_one() { return ci(-1);} + + int64 get_int64() const { return m_value; } + + checked_int64 abs() const { + if (m_value >= 0) { + return *this; + } + if (CHECK && m_value == INT64_MIN) { + throw overflow_exception(); + } + return ci(-m_value); + } + + checked_int64& neg() { + if (CHECK && m_value == INT64_MIN) { + throw overflow_exception(); + } + m_value = -m_value; + return *this; + } + + unsigned hash() const { return static_cast(m_value); } + + struct hash_proc { unsigned operator()(checked_int64 const& r) const { return r.hash(); } }; + + struct eq_proc { bool operator()(checked_int64 const& r1, checked_int64 const& r2) const { return r1 == r2; } }; + + friend inline std::ostream& operator<<(std::ostream& out, checked_int64 const& i) { + return out << i.m_value; + } + + friend inline bool operator==(checked_int64 const& a, checked_int64 const& b) { + return a.m_value == b.m_value; + } + + friend inline bool operator<(checked_int64 const& a, checked_int64 const& b) { + return a.m_value < b.m_value; + } + + checked_int64 & operator++() { + if (CHECK && INT64_MAX == m_value) { + throw overflow_exception(); + } + ++m_value; + return *this; + } + + const checked_int64 operator++(int) { checked_int64 tmp(*this); ++(*this); return tmp; } + + checked_int64 & operator--() { + if (CHECK && m_value == INT64_MIN) { + throw overflow_exception(); + } + --m_value; + return *this; + } + + const checked_int64 operator--(int) { checked_int64 tmp(*this); --(*this); return tmp; } + + checked_int64& operator+=(checked_int64 const& other) { + if (CHECK && m_value > 0 && other.m_value > 0 && + (m_value > INT_MAX || other.m_value > INT_MAX)) { + rational r(r64(m_value) + r64(other.m_value)); + if (!r.is_int64()) { + throw overflow_exception(); + } + m_value = r.get_int64(); + return *this; + } + if (CHECK && m_value < 0 && other.m_value < 0 && + (m_value < INT_MIN || other.m_value < INT_MIN)) { + rational r(r64(m_value) + r64(other.m_value)); + if (!r.is_int64()) { + throw overflow_exception(); + } + m_value = r.get_int64(); + return *this; + } + m_value += other.m_value; + return *this; + } + + checked_int64& operator-=(checked_int64 const& other) { + if (CHECK && m_value > 0 && other.m_value < 0 && + (m_value > INT_MAX || other.m_value < INT_MIN)) { + rational r(r64(m_value) - r64(other.m_value)); + if (!r.is_int64()) { + throw overflow_exception(); + } + m_value = r.get_int64(); + return *this; + } + if (CHECK && m_value < 0 && other.m_value > 0 && + (m_value < INT_MIN || other.m_value > INT_MAX)) { + rational r(r64(m_value) - r64(other.m_value)); + if (!r.is_int64()) { + throw overflow_exception(); + } + m_value = r.get_int64(); + return *this; + } + m_value -= other.m_value; + return *this; + } + + checked_int64& operator*=(checked_int64 const& other) { + if (CHECK) { + rational r(r64(m_value) * r64(other.m_value)); + if (!r.is_int64()) { + throw overflow_exception(); + } + m_value = r.get_int64(); + } + else { + m_value *= other.m_value; + } + return *this; + } + + friend inline checked_int64 abs(checked_int64 const& i) { + return i.abs(); + } + +}; + +template +inline bool operator!=(checked_int64 const & i1, checked_int64 const & i2) { + return !operator==(i1, i2); +} + +template +inline bool operator>(checked_int64 const & i1, checked_int64 const & i2) { + return operator<(i2, i1); +} + +template +inline bool operator<=(checked_int64 const & i1, checked_int64 const & i2) { + return !operator>(i1, i2); +} + +template +inline bool operator>=(checked_int64 const & i1, checked_int64 const & i2) { + return !operator<(i1, i2); +} + +template +inline checked_int64 operator-(checked_int64 const& i) { + checked_int64 result(i); + return result.neg(); +} + +template +inline checked_int64 operator+(checked_int64 const& a, checked_int64 const& b) { + checked_int64 result(a); + result += b; + return result; +} + +template +inline checked_int64 operator-(checked_int64 const& a, checked_int64 const& b) { + checked_int64 result(a); + result -= b; + return result; +} + +template +inline checked_int64 operator*(checked_int64 const& a, checked_int64 const& b) { + checked_int64 result(a); + result *= b; + return result; +} + +#endif diff --git a/src/util/debug.cpp b/src/util/debug.cpp index 75ea8586a..c336ddb73 100644 --- a/src/util/debug.cpp +++ b/src/util/debug.cpp @@ -24,7 +24,7 @@ Revision History: #include"str_hashtable.h" #include"z3_exception.h" -volatile bool g_enable_assertions = true; +static volatile bool g_enable_assertions = true; void enable_assertions(bool f) { g_enable_assertions = f; @@ -41,7 +41,7 @@ void notify_assertion_violation(const char * fileName, int line, const char * co std::cerr << condition << "\n"; } -str_hashtable* g_enabled_debug_tags = 0; +static str_hashtable* g_enabled_debug_tags = 0; static void init_debug_table() { if (!g_enabled_debug_tags) { diff --git a/src/util/debug.h b/src/util/debug.h index c45ee5aa6..9e519982f 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -29,6 +29,10 @@ bool assertions_enabled(); #include #endif +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + #include"error_codes.h" #include"warning.h" @@ -53,7 +57,14 @@ bool is_debug_enabled(const char * tag); #define SASSERT(COND) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define CASSERT(TAG, COND) DEBUG_CODE(if (assertions_enabled() && is_debug_enabled(TAG) && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define XASSERT(COND, EXTRA_CODE) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); { EXTRA_CODE } INVOKE_DEBUGGER(); }) + +#if (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 405)) || __has_builtin(__builtin_unreachable) +// only available in gcc >= 4.5 and in newer versions of clang +# define UNREACHABLE() __builtin_unreachable() +#else #define UNREACHABLE() DEBUG_CODE(notify_assertion_violation(__FILE__, __LINE__, "UNREACHABLE CODE WAS REACHED."); INVOKE_DEBUGGER();) +#endif + #define NOT_IMPLEMENTED_YET() { std::cerr << "NOT IMPLEMENTED YET!\n"; UNREACHABLE(); exit(ERR_NOT_IMPLEMENTED_YET); } ((void) 0) #ifdef Z3DEBUG diff --git a/src/util/env_params.cpp b/src/util/env_params.cpp index 28d80d92d..b01a1c250 100644 --- a/src/util/env_params.cpp +++ b/src/util/env_params.cpp @@ -26,7 +26,7 @@ void env_params::updt_params() { params_ref p = gparams::get(); set_verbosity_level(p.get_uint("verbose", get_verbosity_level())); enable_warning_messages(p.get_bool("warning", true)); - memory::set_max_size(p.get_uint("memory_max_size", 0)); + memory::set_max_size(megabytes_to_bytes(p.get_uint("memory_max_size", 0))); memory::set_high_watermark(p.get_uint("memory_high_watermark", 0)); } diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 8b1fbe40e..1d9426390 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -22,7 +22,7 @@ Notes: extern void gparams_register_modules(); -char const * g_old_params_names[] = { +static char const * g_old_params_names[] = { "arith_adaptive","arith_adaptive_assertion_threshold","arith_adaptive_gcd","arith_adaptive_propagation_threshold","arith_add_binary_bounds","arith_blands_rule_threshold","arith_branch_cut_ratio","arith_dump_lemmas","arith_eager_eq_axioms","arith_eager_gcd","arith_eq_bounds","arith_euclidean_solver","arith_expand_eqs","arith_force_simplex","arith_gcd_test","arith_ignore_int","arith_lazy_adapter","arith_lazy_pivoting","arith_max_lemma_size","arith_process_all_eqs","arith_propagate_eqs","arith_propagation_mode","arith_propagation_threshold","arith_prop_strategy","arith_random_initial_value","arith_random_lower","arith_random_seed","arith_random_upper","arith_reflect","arith_skip_big_coeffs","arith_small_lemma_size","arith_solver","arith_stronger_lemmas","array_always_prop_upward","array_canonize","array_cg","array_delay_exp_axiom","array_extensional","array_laziness","array_lazy_ieq","array_lazy_ieq_delay","array_solver","array_weak","async_commands","at_labels_cex","auto_config","bb_eager","bb_ext_gates","bb_quantifiers","bin_clauses","bit2int","bv2int_distribute","bv_blast_max_size","bv_cc","bv_enable_int2bv_propagation","bv_lazy_le","bv_max_sharing","bv_reflect","bv_solver","case_split","check_at_labels","check_proof","cnf_factor","cnf_mode","context_simplifier","dack","dack_eq","dack_factor","dack_gc","dack_gc_inv_decay","dack_threshold","default_qid","default_table","default_table_checked","delay_units","delay_units_threshold","der","display_config","display_dot_proof","display_error_for_visual_studio","display_features","display_proof","display_unsat_core","distribute_forall","dt_lazy_splits","dump_goal_as_smt","elim_and","elim_bounds","elim_nlarith_quantifiers","elim_quantifiers","elim_term_ite","ematching","engine","eq_propagation","hi_div0","ignore_bad_patterns","ignore_setparameter","instruction_max","inst_gen","interactive","internalizer_nnf","lemma_gc_factor","lemma_gc_half","lemma_gc_initial","lemma_gc_new_clause_activity","lemma_gc_new_clause_relevancy","lemma_gc_new_old_ratio","lemma_gc_old_clause_activity","lemma_gc_old_clause_relevancy","lemma_gc_strategy","lift_ite","lookahead_diseq","macro_finder","max_conflicts","max_counterexamples","mbqi","mbqi_force_template","mbqi_max_cexs","mbqi_max_cexs_incr","mbqi_max_iterations","mbqi_trace","minimize_lemmas","model","model_compact","model_completion","model_display_arg_sort","model_hide_unused_partitions","model_on_final_check","model_on_timeout","model_partial","model_v1","model_v2","model_validate","new_core2th_eq","ng_lift_ite","nl_arith","nl_arith_branching","nl_arith_gb","nl_arith_gb_eqs","nl_arith_gb_perturbate","nl_arith_gb_threshold","nl_arith_max_degree","nl_arith_rounds","nnf_factor","nnf_ignore_labels","nnf_mode","nnf_sk_hack","order","order_var_weight","order_weights","phase_selection","pi_arith","pi_arith_weight","pi_avoid_skolems","pi_block_looop_patterns","pi_max_multi_patterns","pi_non_nested_arith_weight","pi_nopat_weight","pi_pull_quantifiers","pi_use_database","pi_warnings","pp_bounded","pp_bv_literals","pp_bv_neg","pp_decimal","pp_decimal_precision","pp_fixed_indent","pp_flat_assoc","pp_max_depth","pp_max_indent","pp_max_num_lines","pp_max_ribbon","pp_max_width","pp_min_alias_size","pp_simplify_implies","pp_single_line","precedence","precedence_gen","pre_demodulator","pre_simplifier","pre_simplify_expr","profile_res_sub","progress_sampling_freq","proof_mode","propagate_booleans","propagate_values","pull_cheap_ite_trees","pull_nested_quantifiers","qi_conservative_final_check","qi_cost","qi_eager_threshold","qi_lazy_instantiation","qi_lazy_quick_checker","qi_lazy_threshold","qi_max_eager_multi_patterns","qi_max_instances","qi_max_lazy_multi_pattern_matching","qi_new_gen","qi_profile","qi_profile_freq","qi_promote_unsat","qi_quick_checker","quasi_macros","random_case_split_freq","random_initial_activity","random_seed","recent_lemma_threshold","reduce_args","refine_inj_axiom","relevancy","relevancy_lemma","rel_case_split_order","restart_adaptive","restart_agility_threshold","restart_factor","restart_initial","restart_strategy","restricted_quasi_macros","simplify_clauses","smtlib2_compliant","smtlib_category","smtlib_dump_lemmas","smtlib_logic","smtlib_source_info","smtlib_trace_path","soft_timeout","solver","spc_bs","spc_es","spc_factor_subsumption_index_opt","spc_initial_subsumption_index_opt","spc_max_subsumption_index_features","spc_min_func_freq_subsumption_index","spc_num_iterations","spc_trace","statistics","strong_context_simplifier","tick","trace","trace_file_name","type_check","user_theory_persist_axioms","user_theory_preprocess_axioms","verbose","warning","well_sorted_check","z3_solver_ll_pp","z3_solver_smt_pp", 0 }; bool is_old_param_name(symbol const & name) { @@ -35,7 +35,7 @@ bool is_old_param_name(symbol const & name) { return false; } -char const * g_params_renames[] = { +static char const * g_params_renames[] = { "proof_mode", "proof", "soft_timeout", "timeout", "mbqi", "smt.mbqi", @@ -49,6 +49,7 @@ char const * g_params_renames[] = { "restart_factor", "smt.restart_factor", "arith_random_initial_value", "smt.arith.random_initial_value", "bv_reflect", "smt.bv.reflect", + "bv_enable_int2bv_propagation", "smt.bv.enable_int2bv", "qi_cost", "smt.qi.cost", "qi_eager_threshold", "smt.qi.eager_threshold", "nl_arith", "smt.arith.nl", diff --git a/src/util/hash.h b/src/util/hash.h index 3c7e50a6c..4232dd2af 100644 --- a/src/util/hash.h +++ b/src/util/hash.h @@ -20,6 +20,7 @@ Revision History: #define _HASH_H_ #include +#include"util.h" #ifndef __fallthrough #define __fallthrough @@ -142,6 +143,11 @@ struct size_t_hash { unsigned operator()(size_t x) const { return static_cast(x); } }; +struct uint64_hash { + typedef uint64 data; + unsigned operator()(uint64 x) const { return static_cast(x); } +}; + struct bool_hash { typedef bool data; unsigned operator()(bool x) const { return static_cast(x); } diff --git a/src/util/heap.h b/src/util/heap.h index 75b41e329..73029f6a7 100644 --- a/src/util/heap.h +++ b/src/util/heap.h @@ -113,6 +113,7 @@ public: heap(int s, const LT & lt = LT()):LT(lt) { m_values.push_back(-1); set_bounds(s); + CASSERT("heap", check_invariant()); } bool empty() const { @@ -124,12 +125,14 @@ public: } void reset() { + CASSERT("heap", check_invariant()); if (empty()) { return; } memset(m_value2indices.begin(), 0, sizeof(int) * m_value2indices.size()); m_values.reset(); m_values.push_back(-1); + CASSERT("heap", check_invariant()); } void clear() { @@ -138,6 +141,7 @@ public: void set_bounds(int s) { m_value2indices.resize(s, 0); + CASSERT("heap", check_invariant()); } unsigned get_bounds() const { @@ -145,8 +149,10 @@ public: } void reserve(int s) { + CASSERT("heap", check_invariant()); if (s > static_cast(m_value2indices.size())) set_bounds(s); + CASSERT("heap", check_invariant()); } int min_value() const { @@ -155,6 +161,7 @@ public: } int erase_min() { + CASSERT("heap", check_invariant()); SASSERT(!empty()); SASSERT(m_values.size() >= 2); int result = m_values[1]; @@ -176,6 +183,7 @@ public: } void erase(int val) { + CASSERT("heap", check_invariant()); SASSERT(contains(val)); int idx = m_value2indices[val]; if (idx == static_cast(m_values.size()) - 1) { @@ -210,12 +218,14 @@ public: } void insert(int val) { + CASSERT("heap", check_invariant()); SASSERT(is_valid_value(val)); int idx = static_cast(m_values.size()); m_value2indices[val] = idx; m_values.push_back(val); SASSERT(idx == static_cast(m_values.size()) - 1); move_up(idx); + CASSERT("heap", check_invariant()); } iterator begin() { @@ -235,8 +245,14 @@ public: } void swap(heap & other) { - m_values.swap(other.m_values); - m_value2indices.swap(other.m_value2indices); + if (this != &other) { + CASSERT("heap", other.check_invariant()); + CASSERT("heap", check_invariant()); + m_values.swap(other.m_values); + m_value2indices.swap(other.m_value2indices); + CASSERT("heap", other.check_invariant()); + CASSERT("heap", check_invariant()); + } } /** diff --git a/src/util/hwf.h b/src/util/hwf.h index 3b7a0e94b..9059869a0 100644 --- a/src/util/hwf.h +++ b/src/util/hwf.h @@ -28,6 +28,12 @@ class hwf { friend class hwf_manager; double value; hwf & operator=(hwf const & other) { UNREACHABLE(); return *this; } + uint64 get_raw() const { + uint64 n; + SASSERT(sizeof(n) == sizeof(value)); + memcpy(&n, &value, sizeof(value)); + return n; + } public: hwf() {} @@ -122,16 +128,15 @@ public: bool sgn(hwf const & x) const { - uint64 raw = *reinterpret_cast(&x.value); - return (raw & 0x8000000000000000ull) != 0; + return (x.get_raw() & 0x8000000000000000ull) != 0; } const uint64 sig(hwf const & x) const { - return *reinterpret_cast(&x.value) & 0x000FFFFFFFFFFFFFull; + return x.get_raw() & 0x000FFFFFFFFFFFFFull; } const int exp(hwf const & x) const { - return ((*reinterpret_cast(&x.value) & 0x7FF0000000000000ull) >> 52) - 1023; + return ((x.get_raw() & 0x7FF0000000000000ull) >> 52) - 1023; } bool is_nan(hwf const & x); @@ -151,7 +156,7 @@ public: void mk_pinf(hwf & o); void mk_ninf(hwf & o); - unsigned hash(hwf const & a) { return hash_ull(*reinterpret_cast(&a.value)); } + unsigned hash(hwf const & a) { return hash_ull(a.get_raw()); } inline void set_rounding_mode(mpf_rounding_mode rm); diff --git a/src/util/inf_eps_rational.h b/src/util/inf_eps_rational.h new file mode 100644 index 000000000..659fdc400 --- /dev/null +++ b/src/util/inf_eps_rational.h @@ -0,0 +1,409 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + inf_eps_rational.h + +Abstract: + + Rational numbers with infinity and epsilon. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-4-23. + +Revision History: + +--*/ +#ifndef _INF_EPS_RATIONAL_H_ +#define _INF_EPS_RATIONAL_H_ +#include +#include +#include"debug.h" +#include"vector.h" +#include"rational.h" + +template +class inf_eps_rational { + rational m_infty; + Numeral m_r; + public: + + unsigned hash() const { + return m_infty.hash() ^ m_r.hash(); + } + + struct hash_proc { unsigned operator()(inf_eps_rational const& r) const { return r.hash(); } }; + + struct eq_proc { bool operator()(inf_eps_rational const& r1, inf_eps_rational const& r2) const { return r1 == r2; } }; + + void swap(inf_eps_rational & n) { + m_infty.swap(n.m_infty); + m_r.swap(n.m_r); + } + + std::string to_string() const { + if (m_infty.is_zero()) { + return m_r.to_string(); + } + std::string si; + if (m_infty.is_one()) { + si = "oo"; + } + else if (m_infty.is_minus_one()) { + si = "-oo"; + } + else { + si = m_infty.to_string() += "*oo"; + } + if (m_r.is_zero()) { + return si; + } + std::string s = "("; + s += si; + s += " + "; + s += m_r.to_string(); + s += ")"; + return s; + } + + inf_eps_rational(): + m_infty(), + m_r() + {} + + inf_eps_rational(const inf_eps_rational & r): + m_infty(r.m_infty), + m_r(r.m_r) + {} + + explicit inf_eps_rational(int n): + m_infty(), + m_r(n) + {} + + explicit inf_eps_rational(Numeral const& r): + m_infty(), + m_r(r) + {} + + explicit inf_eps_rational(rational const& i, Numeral const& r): + m_infty(i), + m_r(r) { + } + + ~inf_eps_rational() {} + + /** + \brief Set inf_eps_rational to 0. + */ + void reset() { + m_infty.reset(); + m_r.reset(); + } + + bool is_int() const { + return m_infty.is_zero() && m_r.is_int(); + } + + bool is_int64() const { + return m_infty.is_zero() && m_r.is_int64(); + } + + bool is_uint64() const { + return m_infty.is_zero() && m_r.is_uint64(); + } + + bool is_rational() const { return m_infty.is_zero() && m_r.is_rational(); } + + int64 get_int64() const { + SASSERT(is_int64()); + return m_r.get_int64(); + } + + uint64 get_uint64() const { + SASSERT(is_uint64()); + return m_r.get_uint64(); + } + + rational const& get_rational() const { + return m_r.get_rational(); + } + + rational const& get_infinitesimal() const { + return m_r.get_infinitesimal(); + } + + rational const& get_infinity() const { + return m_infty; + } + + inf_eps_rational & operator=(const inf_eps_rational & r) { + m_infty = r.m_infty; + m_r = r.m_r; + return *this; + } + + inf_eps_rational & operator=(const rational & r) { + m_infty.reset(); + m_r = r; + return *this; + } + + inf_eps_rational & operator+=(const inf_eps_rational & r) { + m_infty += r.m_infty; + m_r += r.m_r; + return *this; + } + + inf_eps_rational & operator-=(const inf_eps_rational & r) { + m_infty -= r.m_infty; + m_r -= r.m_r; + return *this; + } + + inf_eps_rational & operator+=(const rational & r) { + m_r += r; + return *this; + } + + inf_eps_rational & operator-=(const rational & r) { + m_r -= r; + return *this; + } + + inf_eps_rational & operator*=(const rational & r1) { + m_infty *= r1; + m_r *= r1; + return *this; + } + + inf_eps_rational & operator/=(const rational & r) { + m_infty /= r; + m_r /= r; + return *this; + } + + + inf_eps_rational & operator++() { + ++m_r; + return *this; + } + + const inf_eps_rational operator++(int) { inf_eps_rational tmp(*this); ++(*this); return tmp; } + + inf_eps_rational & operator--() { + --m_r; + return *this; + } + + const inf_eps_rational operator--(int) { inf_eps_rational tmp(*this); --(*this); return tmp; } + + friend inline bool operator==(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return r1.m_infty == r2.m_infty && r1.m_r == r2.m_r; + } + + friend inline bool operator==(const rational & r1, const inf_eps_rational & r2) { + return r1 == r2.m_infty && r2.m_r.is_zero(); + } + + friend inline bool operator==(const inf_eps_rational & r1, const rational & r2) { + return r1.m_infty == r2 && r1.m_r.is_zero(); + } + + friend inline bool operator<(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return + (r1.m_infty < r2.m_infty) || + (r1.m_infty == r2.m_infty && r1.m_r < r2.m_r); + } + + friend inline bool operator<(const rational & r1, const inf_eps_rational & r2) { + return + r2.m_infty.is_pos() || + (r2.m_infty.is_zero() && r1 < r2.m_r); + } + + friend inline bool operator<(const inf_eps_rational & r1, const rational & r2) { + return + r1.m_infty.is_neg() || + (r1.m_infty.is_zero() && r1.m_r < r2); + } + + void neg() { + m_infty.neg(); + m_r.neg(); + } + + bool is_zero() const { + return m_infty.is_zero() && m_r.is_zero(); + } + + bool is_one() const { + return m_infty.is_zero() && m_r.is_one(); + } + + bool is_minus_one() const { + return m_infty.is_zero() && m_r.is_minus_one(); + } + + bool is_neg() const { + return + m_infty.is_neg() || + (m_infty.is_zero() && m_r.is_neg()); + } + + bool is_pos() const { + return + m_infty.is_pos() || + (m_infty.is_zero() && m_r.is_pos()); + } + + bool is_nonneg() const { + return + m_infty.is_pos() || + (m_infty.is_zero() && m_r.is_nonneg()); + } + + bool is_nonpos() const { + return + m_infty.is_neg() || + (m_infty.is_zero() && m_r.is_nonpos()); + } + + friend inline rational floor(const inf_eps_rational & r) { + SASSERT(r.m_infty.is_zero()); + return floor(r.m_r); + } + + friend inline rational ceil(const inf_eps_rational & r) { + SASSERT(r.m_infty.is_zero()); + return ceil(r.m_r); + } + + + // Perform: this += c * k + void addmul(const rational & c, const inf_eps_rational & k) { + m_infty.addmul(c, k.m_infty); + m_r.addmul(c, k.m_r); + } + + // Perform: this += c * k + void submul(const rational & c, const inf_eps_rational & k) { + m_infty.submul(c, k.m_infty); + m_r.submul(c, k.m_r); + } +}; + +template +inline bool operator!=(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return !operator==(r1, r2); +} + +template +inline bool operator!=(const rational & r1, const inf_eps_rational & r2) { + return !operator==(r1, r2); +} + +template +inline bool operator!=(const inf_eps_rational & r1, const rational & r2) { + return !operator==(r1, r2); +} + +template +inline bool operator>(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return operator<(r2, r1); +} + +template +inline bool operator>(const inf_eps_rational & r1, const rational & r2) { + return operator<(r2, r1); +} + +template +inline bool operator>(const rational & r1, const inf_eps_rational & r2) { + return operator<(r2, r1); +} + +template +inline bool operator<=(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return !operator>(r1, r2); +} + +template +inline bool operator<=(const rational & r1, const inf_eps_rational & r2) { + return !operator>(r1, r2); +} + +template +inline bool operator<=(const inf_eps_rational & r1, const rational & r2) { + return !operator>(r1, r2); +} + +template +inline bool operator>=(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return !operator<(r1, r2); +} + +template +inline bool operator>=(const rational & r1, const inf_eps_rational & r2) { + return !operator<(r1, r2); +} + +template +inline bool operator>=(const inf_eps_rational & r1, const rational & r2) { + return !operator<(r1, r2); +} + +template +inline inf_eps_rational operator+(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return inf_eps_rational(r1) += r2; +} + +template +inline inf_eps_rational operator-(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return inf_eps_rational(r1) -= r2; +} + +template +inline inf_eps_rational operator-(const inf_eps_rational & r) { + inf_eps_rational result(r); + result.neg(); + return result; +} + +template +inline inf_eps_rational operator*(const rational & r1, const inf_eps_rational & r2) { + inf_eps_rational result(r2); + result *= r1; + return result; +} + +template +inline inf_eps_rational operator*(const inf_eps_rational & r1, const rational & r2) { + return r2 * r1; +} + +template +inline inf_eps_rational operator/(const inf_eps_rational & r1, const rational & r2) { + inf_eps_rational result(r1); + result /= r2; + return result; +} + +template +inline std::ostream & operator<<(std::ostream & target, const inf_eps_rational & r) { + target << r.to_string(); + return target; +} + +template +inline inf_eps_rational abs(const inf_eps_rational & r) { + inf_eps_rational result(r); + if (result.is_neg()) { + result.neg(); + } + return result; +} + +#endif /* _INF_EPS_RATIONAL_H_ */ diff --git a/src/util/inf_rational.h b/src/util/inf_rational.h index 9e1753484..5cdfe9e93 100644 --- a/src/util/inf_rational.h +++ b/src/util/inf_rational.h @@ -223,6 +223,7 @@ class inf_rational { } friend inline inf_rational operator*(const rational & r1, const inf_rational & r2); + friend inline inf_rational operator*(const inf_rational & r1, const rational & r2); friend inline inf_rational operator/(const inf_rational & r1, const rational & r2); inf_rational & operator++() { @@ -426,6 +427,10 @@ inline inf_rational operator*(const rational & r1, const inf_rational & r2) { return result; } +inline inf_rational operator*(const inf_rational & r1, const rational & r2) { + return r2 * r1; +} + inline inf_rational operator/(const inf_rational & r1, const rational & r2) { inf_rational result(r1); result.m_first /= r2; diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index de4e760d7..3f2e224d9 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -27,14 +27,14 @@ void mem_finalize(); out_of_memory_error::out_of_memory_error():z3_error(ERR_MEMOUT) { } -volatile bool g_memory_out_of_memory = false; -bool g_memory_initialized = false; -long long g_memory_alloc_size = 0; -long long g_memory_max_size = 0; -long long g_memory_max_used_size = 0; -long long g_memory_watermark = 0; -bool g_exit_when_out_of_memory = false; -char const * g_out_of_memory_msg = "ERROR: out of memory"; +static volatile bool g_memory_out_of_memory = false; +static bool g_memory_initialized = false; +static long long g_memory_alloc_size = 0; +static long long g_memory_max_size = 0; +static long long g_memory_max_used_size = 0; +static long long g_memory_watermark = 0; +static bool g_exit_when_out_of_memory = false; +static char const * g_out_of_memory_msg = "ERROR: out of memory"; void memory::exit_when_out_of_memory(bool flag, char const * msg) { g_exit_when_out_of_memory = flag; @@ -231,9 +231,8 @@ void * memory::allocate(size_t s) { return 0; s = s + sizeof(size_t); // we allocate an extra field! void * r = malloc(s); - if (r == 0) { + if (r == 0) throw_out_of_memory(); - } *(static_cast(r)) = s; g_memory_thread_alloc_size += s; if (g_memory_thread_alloc_size > SYNCH_THRESHOLD) { diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 9618ffbce..735d21c99 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -367,7 +367,7 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode o.ebits = ebits; o.sbits = sbits; - signed ds = sbits - x.sbits; + signed ds = sbits - x.sbits + 3; // plus rounding bits if (ds > 0) { m_mpz_manager.mul2k(o.significand, ds); @@ -453,16 +453,14 @@ bool mpf_manager::lt(mpf const & x, mpf const & y) { else if (sgn(x)) { if (!sgn(y)) return true; - else - // CMW: Problem with denormal numbers? + else return exp(y) < exp(x) || (exp(y) == exp(x) && m_mpz_manager.lt(sig(y), sig(x))); } else { // !sgn(x) if (sgn(y)) return false; - else - // CMW: Problem with denormal numbers? + else return exp(x) < exp(y) || (exp(x)==exp(y) && m_mpz_manager.lt(sig(x), sig(y))); } @@ -545,7 +543,7 @@ void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mp b.get().sign = sgn_y; // Unpack a/b, this inserts the hidden bit and adjusts the exponent. - unpack(a, true); + unpack(a, false); unpack(b, false); if (exp(b) > exp(a)) @@ -556,25 +554,21 @@ void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mp SASSERT(exp(a) >= exp(b)); SASSERT(exp_delta >= 0); - mpf_exp_t u_delta = exp_delta; - if (u_delta > x.sbits+2) - u_delta = x.sbits+2; + if (exp_delta > x.sbits+2) + exp_delta = x.sbits+2; TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); - TRACE("mpf_dbg", tout << "d = " << u_delta << std::endl;); - - TRACE("mpf_dbg", tout << "UP A = " << to_string(a) << std::endl;); - TRACE("mpf_dbg", tout << "UP B = " << to_string(b) << std::endl;); + TRACE("mpf_dbg", tout << "d = " << exp_delta << std::endl;); // Introduce 3 extra bits into both numbers. m_mpz_manager.mul2k(a.significand(), 3, a.significand()); m_mpz_manager.mul2k(b.significand(), 3, b.significand()); // Alignment shift with sticky bit computation. - SASSERT(u_delta <= INT_MAX); + SASSERT(exp_delta <= INT_MAX); scoped_mpz sticky_rem(m_mpz_manager); - m_mpz_manager.machine_div_rem(b.significand(), m_powers2((int)u_delta), b.significand(), sticky_rem); + m_mpz_manager.machine_div_rem(b.significand(), m_powers2((int)exp_delta), b.significand(), sticky_rem); if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(b.significand())) m_mpz_manager.inc(b.significand()); @@ -582,7 +576,7 @@ void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mp TRACE("mpf_dbg", tout << "B' = " << m_mpz_manager.to_string(b.significand()) << std::endl;); // Significand addition - if (sgn(a) ^ sgn(b)) { + if (sgn(a) != sgn(b)) { TRACE("mpf_dbg", tout << "SUBTRACTING" << std::endl;); m_mpz_manager.sub(a.significand(), b.significand(), o.significand); } @@ -765,9 +759,167 @@ void mpf_manager::div(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & } void mpf_manager::fused_mul_add(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf const &z, mpf & o) { - // CMW: Is this precise enough? - mul(rm, x, y, o); - add(rm, o, z, o); + SASSERT(x.sbits == y.sbits && x.ebits == y.ebits && + x.sbits == y.sbits && z.ebits == z.ebits); + + TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); + TRACE("mpf_dbg", tout << "Y = " << to_string(y) << std::endl;); + TRACE("mpf_dbg", tout << "Z = " << to_string(z) << std::endl;); + + if (is_nan(x) || is_nan(y) || is_nan(z)) + mk_nan(x.ebits, x.sbits, o); + else if (is_pinf(x)) { + if (is_zero(y)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, y.sign, o); + } + else if (is_pinf(y)) { + if (is_zero(x)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, x.sign, o); + } + else if (is_ninf(x)) { + if (is_zero(y)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, !y.sign, o); + } + else if (is_ninf(y)) { + if (is_zero(x)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, !x.sign, o); + } + else if (is_inf(z)) { + set(o, z); + } + else if (is_zero(x) || is_zero(y)) { + if (is_zero(z) && rm != MPF_ROUND_TOWARD_NEGATIVE) + mk_pzero(x.ebits, x.sbits, o); + else + set(o, z); + } + else { + o.ebits = x.ebits; + o.sbits = x.sbits; + + scoped_mpf mul_res(*this, x.ebits+2, 2*x.sbits); + scoped_mpf a(*this, x.ebits, x.sbits), b(*this, x.ebits, x.sbits), c(*this, x.ebits, x.sbits); + set(a, x); + set(b, y); + set(c, z); + unpack(a, true); + unpack(b, true); + unpack(c, true); + + TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); + TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); + TRACE("mpf_dbg", tout << "C = " << to_string(c) << std::endl;); + + SASSERT(m_mpz_manager.lt(a.significand(), m_powers2(x.sbits))); + SASSERT(m_mpz_manager.lt(b.significand(), m_powers2(x.sbits))); + SASSERT(m_mpz_manager.lt(c.significand(), m_powers2(x.sbits))); + SASSERT(m_mpz_manager.ge(a.significand(), m_powers2(x.sbits-1))); + SASSERT(m_mpz_manager.ge(b.significand(), m_powers2(x.sbits-1))); + + mul_res.get().sign = (a.sign() != b.sign()); + mul_res.get().exponent = a.exponent() + b.exponent(); + m_mpz_manager.mul(a.significand(), b.significand(), mul_res.get().significand); + + TRACE("mpf_dbg", tout << "PRODUCT = " << to_string(mul_res) << std::endl;); + + // mul_res is [-1][0].[2*sbits - 2], i.e., between 2*sbits-1 and 2*sbits. + SASSERT(m_mpz_manager.lt(mul_res.significand(), m_powers2(2*x.sbits))); + SASSERT(m_mpz_manager.ge(mul_res.significand(), m_powers2(2*x.sbits - 2))); + + // Introduce extra bits into c. + m_mpz_manager.mul2k(c.significand(), x.sbits-1, c.significand()); + + SASSERT(m_mpz_manager.lt(c.significand(), m_powers2(2 * x.sbits - 1))); + SASSERT(m_mpz_manager.is_zero(c.significand()) || + m_mpz_manager.ge(c.significand(), m_powers2(2 * x.sbits - 2))); + + TRACE("mpf_dbg", tout << "C = " << to_string(c) << std::endl;); + + if (exp(c) > exp(mul_res)) + mul_res.swap(c); + + mpf_exp_t exp_delta = exp(mul_res) - exp(c); + + SASSERT(exp(mul_res) >= exp(c) && exp_delta >= 0); + + if (exp_delta > 2 * x.sbits) + exp_delta = 2 * x.sbits; + TRACE("mpf_dbg", tout << "exp_delta = " << exp_delta << std::endl;); + + // Alignment shift with sticky bit computation. + scoped_mpz sticky_rem(m_mpz_manager); + m_mpz_manager.machine_div_rem(c.significand(), m_powers2((int)exp_delta), c.significand(), sticky_rem); + TRACE("mpf_dbg", tout << "alignment shift -> sig = " << m_mpz_manager.to_string(c.significand()) << + " sticky_rem = " << m_mpz_manager.to_string(sticky_rem) << std::endl;); + if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(c.significand())) + m_mpz_manager.inc(c.significand()); + + TRACE("mpf_dbg", tout << "M' = " << m_mpz_manager.to_string(mul_res.significand()) << std::endl;); + TRACE("mpf_dbg", tout << "C' = " << m_mpz_manager.to_string(c.significand()) << std::endl;); + + // Significand addition + if (sgn(mul_res) != sgn(c)) { + TRACE("mpf_dbg", tout << "SUBTRACTING" << std::endl;); + m_mpz_manager.sub(mul_res.significand(), c.significand(), o.significand); + } + else { + TRACE("mpf_dbg", tout << "ADDING" << std::endl;); + m_mpz_manager.add(mul_res.significand(), c.significand(), o.significand); + } + + TRACE("mpf_dbg", tout << "sum[-1:] = " << m_mpz_manager.to_string(o.significand) << std::endl;); + + bool neg = m_mpz_manager.is_neg(o.significand); + TRACE("mpf_dbg", tout << "NEG=" << neg << std::endl;); + if (neg) m_mpz_manager.abs(o.significand); + + o.exponent = mul_res.exponent(); + + unsigned extra = x.sbits-4; + // Result could overflow into 4.xxx ... + SASSERT(m_mpz_manager.lt(o.significand, m_powers2(2 * x.sbits + 2))); + if(m_mpz_manager.ge(o.significand, m_powers2(2 * x.sbits + 1))) + { + extra++; + o.exponent++; + TRACE("mpf_dbg", tout << "Addition overflew!" << std::endl;); + } + + // Get rid of the extra bits. + m_mpz_manager.machine_div_rem(o.significand, m_powers2(extra), o.significand, sticky_rem); + if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(o.significand)) + m_mpz_manager.inc(o.significand); + + TRACE("mpf_dbg", tout << "sum[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); + + if (m_mpz_manager.is_zero(o.significand)) + mk_zero(x.ebits, x.sbits, rm == MPF_ROUND_TOWARD_NEGATIVE, o); + else { + o.sign = ((!mul_res.sign() && c.sign() && neg) || + ( mul_res.sign() && !c.sign() && !neg) || + ( mul_res.sign() && c.sign())); + TRACE("mpf_dbg", tout << "before round = " << to_string(o) << std::endl << + "fs[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); + round(rm, o); + } + } + } void my_mpz_sqrt(unsynch_mpz_manager & m, unsigned sbits, bool odd_exp, mpz & in, mpz & o) { @@ -938,8 +1090,8 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { mk_nan(x.ebits, x.sbits, o); else if (is_inf(y)) set(o, x); - else if (is_zero(x)) - mk_pzero(x.ebits, x.sbits, o); + else if (is_zero(x)) + set(o, x); else if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else { @@ -955,7 +1107,7 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); - + if (a.exponent() < b.exponent()) set(o, x); else { @@ -986,22 +1138,22 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { } void mpf_manager::maximum(mpf const & x, mpf const & y, mpf & o) { - if (is_nan(x)) + if (is_nan(x) || (is_zero(x) && is_zero(y))) set(o, y); else if (is_nan(y)) set(o, x); - else if (gt(x, y) || (is_zero(x) && is_nzero(y))) + else if (gt(x, y)) set(o, x); else set(o, y); } void mpf_manager::minimum(mpf const & x, mpf const & y, mpf & o) { - if (is_nan(x)) + if (is_nan(x) || (is_zero(x) && is_zero(y))) set(o, y); else if (is_nan(y)) set(o, x); - else if (lt(x, y) || (is_nzero(x) && is_zero(y))) + else if (lt(x, y)) set(o, x); else set(o, y); @@ -1340,6 +1492,11 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { TRACE("mpf_dbg", tout << "RND: " << to_string(o) << std::endl;); + DEBUG_CODE({ + const mpz & p_m3 = m_powers2(o.sbits+5); + SASSERT(m_mpz_manager.lt(o.significand, p_m3)); + }); + // Structure of the rounder: // (s, e_out, f_out) == (s, exprd(s, post(e, sigrd(s, f)))). @@ -1354,7 +1511,7 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { "e_max_norm = " << e_max_norm << std::endl;); const mpz & p_m1 = m_powers2(o.sbits+2); - const mpz & p_m2 = m_powers2(o.sbits+3); + const mpz & p_m2 = m_powers2(o.sbits+3); TRACE("mpf_dbg", tout << "p_m1 = " << m_mpz_manager.to_string(p_m1) << std::endl << "p_m2 = " << m_mpz_manager.to_string(p_m2) << std::endl;); @@ -1530,7 +1687,7 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { TRACE("mpf_dbg", tout << "DENORMAL: " << m_mpz_manager.to_string(o.significand) << std::endl;); o.exponent = mk_bot_exp(o.ebits); } - } + } TRACE("mpf_dbg", tout << "ROUNDED = " << to_string(o) << std::endl;); } diff --git a/src/util/mpff.h b/src/util/mpff.h index de71ec75e..8d4c853af 100644 --- a/src/util/mpff.h +++ b/src/util/mpff.h @@ -107,7 +107,7 @@ class mpff_manager { unsigned m_precision; //!< Number of words in the significand. Must be an even number. unsigned m_precision_bits; //!< Number of bits in the significand. Must be 32*m_precision. - vector m_significands; //!< Array containing all significands. + unsigned_vector m_significands; //!< Array containing all significands. unsigned m_capacity; //!< Number of significands that can be stored in m_significands. bool m_to_plus_inf; //!< If True, then round to plus infinity, otherwise to minus infinity id_gen m_id_gen; diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index c77978647..bd7f30a76 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -120,6 +120,7 @@ mpz_manager::mpz_manager(): mpz_set_ui(m_tmp, max_l); mpz_add(m_uint64_max, m_uint64_max, m_tmp); mpz_init(m_int64_max); + mpz_init(m_int64_min); max_l = static_cast(INT64_MAX % static_cast(UINT_MAX)); max_h = static_cast(INT64_MAX / static_cast(UINT_MAX)); @@ -128,6 +129,8 @@ mpz_manager::mpz_manager(): mpz_mul(m_int64_max, m_tmp, m_int64_max); mpz_set_ui(m_tmp, max_l); mpz_add(m_int64_max, m_tmp, m_int64_max); + mpz_neg(m_int64_min, m_int64_max); + mpz_sub_ui(m_int64_min, m_int64_min, 1); #endif mpz one(1); @@ -152,6 +155,7 @@ mpz_manager::~mpz_manager() { deallocate(m_arg[1]); mpz_clear(m_uint64_max); mpz_clear(m_int64_max); + mpz_clear(m_int64_min); #endif if (SYNCH) omp_destroy_nest_lock(&m_lock); @@ -1299,9 +1303,9 @@ bool mpz_manager::is_int64(mpz const & a) const { if (is_small(a)) return true; #ifndef _MP_GMP - if (!is_uint64(a)) + if (!is_abs_uint64(a)) return false; - uint64 num = get_uint64(a); + uint64 num = big_abs_to_uint64(a); uint64 msb = static_cast(1) << 63; uint64 msb_val = msb & num; if (a.m_val >= 0) { @@ -1317,7 +1321,7 @@ bool mpz_manager::is_int64(mpz const & a) const { } #else // GMP version - return mpz_cmp(*a.m_ptr, m_int64_max) <= 0; + return mpz_cmp(m_int64_min, *a.m_ptr) <= 0 && mpz_cmp(*a.m_ptr, m_int64_max) <= 0; #endif } @@ -1327,14 +1331,7 @@ uint64 mpz_manager::get_uint64(mpz const & a) const { return static_cast(a.m_val); #ifndef _MP_GMP SASSERT(a.m_ptr->m_size > 0); - if (a.m_ptr->m_size == 1) - return digits(a)[0]; - if (sizeof(digit_t) == sizeof(uint64)) - // 64-bit machine - return digits(a)[0]; - else - // 32-bit machine - return ((static_cast(digits(a)[1]) << 32) | (static_cast(digits(a)[0]))); + return big_abs_to_uint64(a); #else // GMP version if (sizeof(uint64) == sizeof(unsigned long)) { @@ -1359,7 +1356,7 @@ int64 mpz_manager::get_int64(mpz const & a) const { return static_cast(a.m_val); #ifndef _MP_GMP SASSERT(is_int64(a)); - uint64 num = get_uint64(a); + uint64 num = big_abs_to_uint64(a); if (a.m_val < 0) { if (num != 0 && (num << 1) == 0) return INT64_MIN; diff --git a/src/util/mpz.h b/src/util/mpz.h index b5c301d82..923d5b3a7 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -168,6 +168,7 @@ class mpz_manager { mpz_t * m_arg[2]; mpz_t m_uint64_max; mpz_t m_int64_max; + mpz_t m_int64_min; mpz_t * allocate() { mpz_t * cell = reinterpret_cast(m_allocator.allocate(sizeof(mpz_t))); @@ -211,6 +212,30 @@ class mpz_manager { static digit_t * digits(mpz const & c) { return c.m_ptr->m_digits; } + // Return true if the absolute value fits in a UINT64 + static bool is_abs_uint64(mpz const & a) { + if (is_small(a)) + return true; + if (sizeof(digit_t) == sizeof(uint64)) + return size(a) <= 1; + else + return size(a) <= 2; + } + + // CAST the absolute value into a UINT64 + static uint64 big_abs_to_uint64(mpz const & a) { + SASSERT(is_abs_uint64(a)); + SASSERT(!is_small(a)); + if (a.m_ptr->m_size == 1) + return digits(a)[0]; + if (sizeof(digit_t) == sizeof(uint64)) + // 64-bit machine + return digits(a)[0]; + else + // 32-bit machine + return ((static_cast(digits(a)[1]) << 32) | (static_cast(digits(a)[0]))); + } + template void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell) { if (is_small(a)) { diff --git a/src/util/small_object_allocator.cpp b/src/util/small_object_allocator.cpp index 8acdccd23..edc3885cd 100644 --- a/src/util/small_object_allocator.cpp +++ b/src/util/small_object_allocator.cpp @@ -96,7 +96,7 @@ void * small_object_allocator::allocate(size_t size) { return memory::allocate(size); #endif m_alloc_size += size; - if (size > SMALL_OBJ_SIZE - (1 << PTR_ALIGNMENT)) + if (size > SMALL_OBJ_SIZE - (1 << PTR_ALIGNMENT)) return memory::allocate(size); #ifdef Z3DEBUG size_t osize = size; diff --git a/src/util/small_object_allocator.h b/src/util/small_object_allocator.h index 538a9e035..957c4f475 100644 --- a/src/util/small_object_allocator.h +++ b/src/util/small_object_allocator.h @@ -21,6 +21,7 @@ Revision History: #define _SMALL_OBJECT_ALLOCATOR_H_ #include"machine.h" +#include"debug.h" class small_object_allocator { static const unsigned CHUNK_SIZE = (8192 - sizeof(void*)*2); @@ -52,8 +53,8 @@ public: inline void * operator new(size_t s, small_object_allocator & r) { return r.allocate(s); } inline void * operator new[](size_t s, small_object_allocator & r) { return r.allocate(s); } -inline void operator delete(void * p, size_t s, small_object_allocator & r) { r.deallocate(s,p); } -inline void operator delete[](void * p, size_t s, small_object_allocator & r) { r.deallocate(s,p); } +inline void operator delete(void * p, small_object_allocator & r) { UNREACHABLE(); } +inline void operator delete[](void * p, small_object_allocator & r) { UNREACHABLE(); } #endif /* _SMALL_OBJECT_ALLOCATOR_H_ */ diff --git a/src/util/util.h b/src/util/util.h index 3360c2282..0aa8f881d 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -45,7 +45,7 @@ COMPILE_TIME_ASSERT(sizeof(int64) == 8); #define INT64_MIN static_cast(0x8000000000000000ull) #endif #ifndef INT64_MAX -#define INT64_MAX static_cast(0x0fffffffffffffffull) +#define INT64_MAX static_cast(0x7fffffffffffffffull) #endif #ifndef UINT64_MAX #define UINT64_MAX 0xffffffffffffffffull @@ -394,11 +394,14 @@ public: inline std::ostream & operator<<(std::ostream & out, escaped const & s) { s.display(out); return out; } -inline unsigned long long megabytes_to_bytes(unsigned b) { - if (b == UINT_MAX) - return UINT64_MAX; - else - return static_cast(b) * 1024ull * 1024ull; +inline size_t megabytes_to_bytes(unsigned mb) { + if (mb == UINT_MAX) + return SIZE_MAX; + unsigned long long b = static_cast(mb) * 1024ull * 1024ull; + size_t r = static_cast(b); + if (r != b) // overflow + r = SIZE_MAX; + return r; } void z3_bound_num_procs(); diff --git a/src/util/vector.h b/src/util/vector.h index a9d36b202..9370a4eed 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. @@ -36,7 +37,7 @@ Revision History: #pragma warning(disable:4127) #endif -template +template class vector { #define SIZE_IDX -1 #define CAPACITY_IDX -2 @@ -51,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; @@ -66,10 +67,15 @@ class vector { } else { SASSERT(capacity() > 0); - unsigned old_capacity = reinterpret_cast(m_data)[CAPACITY_IDX]; - unsigned new_capacity = (3 * old_capacity + 1) >> 1; - unsigned size = reinterpret_cast(m_data)[SIZE_IDX]; - unsigned * mem = reinterpret_cast(memory::allocate(sizeof(T) * new_capacity + sizeof(unsigned) * 2)); + 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"); + } + SZ * mem = reinterpret_cast(memory::allocate(new_capacity_T)); *mem = new_capacity; mem ++; *mem = size; @@ -81,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; @@ -116,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; @@ -131,7 +137,7 @@ public: } } - vector(unsigned s, T const & elem): + vector(SZ s, T const & elem): m_data(0) { resize(s, elem); } @@ -144,30 +150,13 @@ 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]); } } - vector(T const & e) : - m_data(0) { - push_back(e); - } - - vector(T const & t1, T const & t2) : - m_data(0) { - push_back(t1); - push_back(t2); - } - - vector(T const & t1, T const & t2, T const & t3) : - m_data(0) { - push_back(t1); - push_back(t2); - push_back(t3); - } ~vector() { destroy(); @@ -197,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() { @@ -237,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; } @@ -291,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) { @@ -314,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) { @@ -324,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(); @@ -334,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) { @@ -357,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]); } } @@ -377,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]); } } @@ -403,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); } @@ -411,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); } @@ -434,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; @@ -449,24 +438,29 @@ typedef svector unsigned_vector; typedef svector char_vector; typedef svector double_vector; -template -struct vector_hash { +template +struct vector_hash_tpl { Hash m_hash; - typedef vector data; + typedef Vec data; unsigned operator()(data const& v, unsigned idx) const { return m_hash(v[idx]); } - vector_hash(Hash const& h = Hash()):m_hash(h) {} + vector_hash_tpl(Hash const& h = Hash()):m_hash(h) {} unsigned operator()(data const& v) const { if (v.empty()) { return 778; } - return get_composite_hash, vector_hash>(v, v.size()); + return get_composite_hash, vector_hash_tpl>(v, v.size()); } - }; +template +struct vector_hash : public vector_hash_tpl > {}; + +template +struct svector_hash : public vector_hash_tpl > {}; + #endif /* _VECTOR_H_ */ diff --git a/src/util/warning.cpp b/src/util/warning.cpp index 0a1ac9bbc..88f807e3c 100644 --- a/src/util/warning.cpp +++ b/src/util/warning.cpp @@ -60,11 +60,11 @@ void myInvalidParameterHandler( #define END_ERR_HANDLER() {} #endif -bool g_warning_msgs = true; -bool g_use_std_stdout = false; -std::ostream* g_error_stream = 0; -std::ostream* g_warning_stream = 0; -bool g_show_error_msg_prefix = true; +static bool g_warning_msgs = true; +static bool g_use_std_stdout = false; +static std::ostream* g_error_stream = 0; +static std::ostream* g_warning_stream = 0; +static bool g_show_error_msg_prefix = true; void send_warnings_to_stdout(bool flag) { g_use_std_stdout = flag;