From 338193548b62cb8168dff0de89e2abc60b876c2f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 10 Mar 2017 22:52:55 +0100 Subject: [PATCH 001/105] fixing build break, adding fixedpoint object to C++ API Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/rewriter.h | 2 ++ src/ast/rewriter/rewriter_def.h | 44 ++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index fc596cabd..2b4f4b14e 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -315,6 +315,8 @@ protected: template void process_app(app * t, frame & fr); + bool constant_fold(app* t, frame& fr); + template void process_quantifier(quantifier * q, frame & fr); diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index dddb02dfd..aecc1c93a 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -174,6 +174,38 @@ bool rewriter_tpl::visit(expr * t, unsigned max_depth) { } } +template +bool rewriter_tpl::constant_fold(app * t, frame & fr) { + if (fr.m_i == 1 && m().is_ite(t)) { + expr * cond = result_stack()[fr.m_spos].get(); + expr* arg = 0; + if (m().is_true(cond)) { + arg = t->get_arg(1); + } + else if (m().is_false(cond)) { + arg = t->get_arg(2); + } + if (arg) { + result_stack().shrink(fr.m_spos); + result_stack().push_back(arg); + fr.m_state = REWRITE_BUILTIN; + unsigned max_depth = fr.m_max_depth; + if (visit(arg, fr.m_max_depth)) { + m_r = result_stack().back(); + result_stack().pop_back(); + result_stack().pop_back(); + result_stack().push_back(m_r); + cache_result(t, m_r, m_pr, fr.m_cache_result); + frame_stack().pop_back(); + set_new_child_flag(t); + } + m_r = 0; + return true; + } + } + return false; +} + template template void rewriter_tpl::process_app(app * t, frame & fr) { @@ -183,16 +215,10 @@ void rewriter_tpl::process_app(app * t, frame & fr) { case PROCESS_CHILDREN: { unsigned num_args = t->get_num_args(); while (fr.m_i < num_args) { - expr * arg = t->get_arg(fr.m_i); - if (fr.m_i >= 1 && m().is_ite(t) && !ProofGen) { - expr * cond = result_stack()[fr.m_spos].get(); - if (m().is_true(cond)) { - arg = t->get_arg(1); - } - else if (m().is_false(cond)) { - arg = t->get_arg(2); - } + if (!ProofGen && constant_fold(t, fr)) { + return; } + expr * arg = t->get_arg(fr.m_i); fr.m_i++; if (!visit(arg, fr.m_max_depth)) return; From 509f7409badc0e7834968511ca3c6b6f97fdaed6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 10 Mar 2017 23:01:43 +0100 Subject: [PATCH 002/105] adding fixedpoint object to C++ API Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 21a3fedf4..bfd4eb2c4 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -2175,6 +2175,50 @@ namespace z3 { }; inline std::ostream & operator<<(std::ostream & out, optimize const & s) { out << Z3_optimize_to_string(s.ctx(), s.m_opt); return out; } + class fixedpoint : public object { + Z3_fixedpoint m_fp; + public: + fixedpoint(context& c):object(c) { m_fp = Z3_mk_fixedpoint(c); Z3_fixedpoint_inc_ref(c, m_fp); } + ~fixedpoint() { Z3_fixedpoint_dec_ref(ctx(), m_fp); } + operator Z3_fixedpoint() const { return m_fp; } + void from_string(char const* s) { Z3_fixedpoint_from_string(ctx(), m_fp, s); check_error(); } + void from_file(char const* s) { Z3_fixedpoint_from_file(ctx(), m_fp, s); check_error(); } + void add_rule(expr& rule, symbol const& name) { Z3_fixedpoint_add_rule(ctx(), m_fp, rule, name); check_error(); } + void add_fact(func_decl& f, unsigned * args) { Z3_fixedpoint_add_fact(ctx(), m_fp, f, f.arity(), args); check_error(); } + check_result query(expr& q) { Z3_lbool r = Z3_fixedpoint_query(ctx(), m_fp, q); check_error(); to_check_result(r); } + check_result query(func_decl_vector& relations) { + array rs(relations); + Z3_lbool r = Z3_fixedpoint_query_relations(ctx(), m_fp, rs.size(), rs.ptr()); + check_error(); + return to_check_result(r); + } + expr get_answer() { Z3_ast r = Z3_fixedpoint_get_answer(ctx(), m_fp); check_error(); return expr(ctx(), r); } + std::string reason_unknown() { return Z3_fixedpoint_get_reason_unknown(ctx(), m_fp); } + void update_rule(expr& rule, symbol const& name) { Z3_fixedpoint_update_rule(ctx(), m_fp, rule, name); check_error(); } + unsigned get_num_levels(func_decl& p) { unsigned r = Z3_fixedpoint_get_num_levels(ctx(), m_fp, p); check_error(); return r; } + expr get_cover_delta(int level, func_decl& p) { + Z3_ast r = Z3_fixedpoint_get_cover_delta(ctx(), m_fp, level, p); + check_error(); + return expr(ctx(), r); + } + void add_cover(int level, func_decl& p, expr& property) { Z3_fixedpoint_add_cover(ctx(), m_fp, level, p, property); check_error(); } + stats statistics() const { Z3_stats r = Z3_fixedpoint_get_statistics(ctx(), m_fp); check_error(); return stats(ctx(), r); } + void register_relation(func_decl& p) { Z3_fixedpoint_register_relation(ctx(), m_fp, p); } + expr_vector assertions() const { Z3_ast_vector r = Z3_fixedpoint_get_assertions(ctx(), m_fp); check_error(); return expr_vector(ctx(), r); } + expr_vector rules() const { Z3_ast_vector r = Z3_fixedpoint_get_rules(ctx(), m_fp); check_error(); return expr_vector(ctx(), r); } + void set(params const & p) { Z3_fixedpoint_set_params(ctx(), m_fp, p); check_error(); } + std::string help() const { return Z3_fixedpoint_get_help(ctx(), m_fp); } + param_descrs get_param_descrs() { return param_descrs(ctx(), Z3_fixedpoint_get_param_descrs(ctx(), m_fp)); } + std::string to_string() { return Z3_fixedpoint_to_string(ctx(), m_fp, 0, 0); } + std::string to_string(expr_vector const& queries) { + array qs(queries); + return Z3_fixedpoint_to_string(ctx(), m_fp, qs.size(), qs.ptr()); + } + void push() { Z3_fixedpoint_push(ctx(), m_fp); check_error(); } + void pop() { Z3_fixedpoint_pop(ctx(), m_fp); check_error(); } + }; + inline std::ostream & operator<<(std::ostream & out, fixedpoint const & f) { return out << Z3_fixedpoint_to_string(f.ctx(), f, 0, 0); } + inline tactic fail_if(probe const & p) { Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); p.check_error(); From 228111511c8293c7a4b35743b141693d4a3b6415 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Mar 2017 18:41:36 +0100 Subject: [PATCH 003/105] fixing build break, addressing #935 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 1 + src/smt/mam.cpp | 2 +- src/smt/smt_context.cpp | 40 +++++++++++++++++++++++++++++---- src/smt/smt_context.h | 8 +++++++ src/smt/smt_quantifier.cpp | 26 ++++++++++++++------- 5 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 67b1df50c..2551f0aa0 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1551,6 +1551,7 @@ void cmd_context::validate_model() { p.set_uint("sort_store", true); p.set_bool("completion", true); model_evaluator evaluator(*(md.get()), p); + evaluator.set_expand_array_equalities(false); contains_array_op_proc contains_array(m()); { scoped_rlimit _rlimit(m().limit(), 0); diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 5d03a3563..ba9f970a2 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -3942,7 +3942,7 @@ namespace smt { #endif virtual void on_match(quantifier * qa, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, ptr_vector & used_enodes) { - TRACE("trigger_bug", tout << "found match\n";); + TRACE("trigger_bug", tout << "found match " << mk_pp(qa, m_ast_manager) << "\n";); #ifdef Z3DEBUG if (m_check_missing_instances) { if (!m_context.slow_contains_instance(qa, num_bindings, bindings)) { diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index e816f5c73..3fca7d04b 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -304,7 +304,6 @@ namespace smt { TRACE("assign_core", tout << (decision?"decision: ":"propagating: ") << l << " "; display_literal_verbose(tout, l); tout << " level: " << m_scope_lvl << "\n"; display(tout, j);); - SASSERT(l.var() < static_cast(m_b_internalized_stack.size())); m_assigned_literals.push_back(l); m_assignment[l.index()] = l_true; m_assignment[(~l).index()] = l_false; @@ -319,14 +318,23 @@ namespace smt { d.m_phase_available = true; d.m_phase = !l.sign(); TRACE("phase_selection", tout << "saving phase, is_pos: " << d.m_phase << " l: " << l << "\n";); + TRACE("relevancy", - tout << "is_atom: " << d.is_atom() << " is relevant: " << is_relevant_core(bool_var2expr(l.var())) << "\n";); - if (d.is_atom() && (m_fparams.m_relevancy_lvl == 0 || (m_fparams.m_relevancy_lvl == 1 && !d.is_quantifier()) || is_relevant_core(bool_var2expr(l.var())))) + tout << "is_atom: " << d.is_atom() << " is relevant: " << is_relevant_core(l) << "\n";); + if (d.is_atom() && (m_fparams.m_relevancy_lvl == 0 || (m_fparams.m_relevancy_lvl == 1 && !d.is_quantifier()) || is_relevant_core(l))) m_atom_propagation_queue.push_back(l); if (m_manager.has_trace_stream()) trace_assign(l, j, decision); m_case_split_queue->assign_lit_eh(l); + + // a unit is asserted at search level. Mark it as relevant. + // this addresses bug... where a literal becomes fixed to true (false) + // as a conflict gets assigned misses relevancy (and quantifier instantiation). + // + if (false && !decision && relevancy() && at_search_level() && !is_relevant_core(l)) { + mark_as_relevant(l); + } } bool context::bcp() { @@ -1634,7 +1642,7 @@ namespace smt { m_atom_propagation_queue.push_back(literal(v, val == l_false)); } } - TRACE("propagate_relevancy", tout << "marking as relevant:\n" << mk_bounded_pp(n, m_manager) << "\n";); + TRACE("propagate_relevancy", tout << "marking as relevant:\n" << mk_bounded_pp(n, m_manager) << " " << m_scope_lvl << "\n";); #ifndef SMTCOMP m_case_split_queue->relevant_eh(n); #endif @@ -3737,7 +3745,9 @@ namespace smt { // I invoke pop_scope_core instead of pop_scope because I don't want // to reset cached generations... I need them to rebuild the literals // of the new conflict clause. + if (relevancy()) record_relevancy(num_lits, lits); unsigned num_bool_vars = pop_scope_core(m_scope_lvl - new_lvl); + if (relevancy()) restore_relevancy(num_lits, lits); SASSERT(m_scope_lvl == new_lvl); // the logical context may still be in conflict after // clauses are reinitialized in pop_scope. @@ -3850,6 +3860,28 @@ namespace smt { } return false; } + + /* + \brief we record and restore relevancy information for literals in conflict clauses. + A literal may have been marked relevant within the scope that gets popped during + conflict resolution. In this case, the literal is no longer marked as relevant after + the pop. This can cause quantifier instantiation to miss relevant triggers and thereby + cause incmpleteness. + */ + void context::record_relevancy(unsigned n, literal const* lits) { + m_relevant_conflict_literals.reset(); + for (unsigned i = 0; i < n; ++i) { + m_relevant_conflict_literals.push_back(is_relevant(lits[i])); + } + } + + void context::restore_relevancy(unsigned n, literal const* lits) { + for (unsigned i = 0; i < n; ++i) { + if (m_relevant_conflict_literals[i] && !is_relevant(lits[i])) { + mark_as_relevant(lits[i]); + } + } + } void context::get_relevant_labels(expr* cnstr, buffer & result) { if (m_fparams.m_check_at_labels) { diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index c1c684fec..2a555e6b5 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1103,6 +1103,10 @@ namespace smt { bool is_relevant_core(expr * n) const { return m_relevancy_propagator->is_relevant(n); } + svector m_relevant_conflict_literals; + void record_relevancy(unsigned n, literal const* lits); + void restore_relevancy(unsigned n, literal const* lits); + public: // event handler for relevancy_propagator class void relevant_eh(expr * n); @@ -1124,6 +1128,10 @@ namespace smt { return is_relevant(l.var()); } + bool is_relevant_core(literal l) const { + return is_relevant_core(bool_var2expr(l.var())); + } + void mark_as_relevant(expr * n) { m_relevancy_propagator->mark_as_relevant(n); m_relevancy_propagator->propagate(); } void mark_as_relevant(enode * n) { mark_as_relevant(n->get_owner()); } diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index 2a56168f2..bad788f5d 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -52,8 +52,9 @@ namespace smt { m_qi_queue.setup(); } - bool has_trace_stream() const { return m_context.get_manager().has_trace_stream(); } - std::ostream & trace_stream() { return m_context.get_manager().trace_stream(); } + ast_manager& m() const { return m_context.get_manager(); } + bool has_trace_stream() const { return m().has_trace_stream(); } + std::ostream & trace_stream() { return m().trace_stream(); } quantifier_stat * get_stat(quantifier * q) const { return m_quantifier_stat.find(q); @@ -110,8 +111,9 @@ namespace smt { unsigned max_top_generation, ptr_vector & used_enodes) { max_generation = std::max(max_generation, get_generation(q)); - if (m_num_instances > m_params.m_qi_max_instances) + if (m_num_instances > m_params.m_qi_max_instances) { return false; + } get_stat(q)->update_max_generation(max_generation); fingerprint * f = m_context.add_fingerprint(q, q->get_id(), num_bindings, bindings); if (f) { @@ -132,9 +134,17 @@ namespace smt { } m_qi_queue.insert(f, pat, max_generation, min_top_generation, max_top_generation); // TODO m_num_instances++; - return true; } - return false; + TRACE("quantifier", + tout << mk_pp(q, m()) << " "; + for (unsigned i = 0; i < num_bindings; ++i) { + tout << mk_pp(bindings[i]->get_owner(), m()) << " "; + } + tout << "\n"; + tout << "inserted: " << (f != 0) << "\n"; + ); + + return f != 0; } void init_search_eh() { @@ -186,7 +196,7 @@ namespace smt { } bool check_quantifier(quantifier* q) { - return m_context.is_relevant(q) && m_context.get_assignment(q) == l_true; // && !m_context.get_manager().is_rec_fun_def(q); + return m_context.is_relevant(q) && m_context.get_assignment(q) == l_true; // && !m().is_rec_fun_def(q); } bool quick_check_quantifiers() { @@ -501,13 +511,13 @@ namespace smt { SASSERT(m_context->get_manager().is_pattern(mp)); bool unary = (mp->get_num_args() == 1); if (!unary && j >= num_eager_multi_patterns) { - TRACE("assign_quantifier", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n" + TRACE("quantifier", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n" << "j: " << j << " unary: " << unary << " m_params.m_qi_max_eager_multipatterns: " << m_fparams->m_qi_max_eager_multipatterns << " num_eager_multi_patterns: " << num_eager_multi_patterns << "\n";); m_lazy_mam->add_pattern(q, mp); } else { - TRACE("assign_quantifier", tout << "adding:\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n";); + TRACE("quantifier", tout << "adding:\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n";); m_mam->add_pattern(q, mp); } if (!unary) From 559c5e5ae63e13a0fbb18b7b6589d8a0b111debe Mon Sep 17 00:00:00 2001 From: James Bornholt Date: Sat, 11 Mar 2017 16:03:09 -0800 Subject: [PATCH 004/105] z3py: With tactical should not try to use context as a parameter --- src/api/python/z3/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index a0aba95d6..16d7fbb5f 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -7204,7 +7204,7 @@ def With(t, *args, **keys): >>> t((x + 1)*(y + 2) == 0) [[2*x + y + x*y == -2]] """ - ctx = keys.get('ctx', None) + ctx = keys.pop('ctx', None) t = _to_tactic(t, ctx) p = args2params(args, keys, t.ctx) return Tactic(Z3_tactic_using_params(t.ctx.ref(), t.tactic, p.params), t.ctx) From 8bec1e25a83e8e3f8c9b48f1641b68b6df841f08 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 12 Mar 2017 08:32:06 +0100 Subject: [PATCH 005/105] move restore relevancy until after literals have been replayed Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 3fca7d04b..9336322f7 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3747,7 +3747,6 @@ namespace smt { // of the new conflict clause. if (relevancy()) record_relevancy(num_lits, lits); unsigned num_bool_vars = pop_scope_core(m_scope_lvl - new_lvl); - if (relevancy()) restore_relevancy(num_lits, lits); SASSERT(m_scope_lvl == new_lvl); // the logical context may still be in conflict after // clauses are reinitialized in pop_scope. @@ -3778,6 +3777,7 @@ namespace smt { } } } + if (relevancy()) restore_relevancy(num_lits, lits); // Resetting the cache manually because I did not invoke pop_scope, but pop_scope_core reset_cache_generation(); TRACE("resolve_conflict_bug", From 2cb4223979cc94e2ebc4e49a9e83adbdcd2b6979 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 12 Mar 2017 12:29:26 +0100 Subject: [PATCH 006/105] [CMake] Support including Git hash and description into the build. CMake will automatically pick up changes in git's HEAD so that the necessary code is rebuilt when the build system is invoked. Two new options `INCLUDE_GIT_HASH` and `INCLUDE_GIT_DESCRIBE` have been added that enable/disable including the git hash and the output of `git describe` respectively. By default if the source tree is a git repository both options are on, otherwise they are false by default. To support the `Z3GITHASH` macro a different implementation is used from the old build system. In that build system the define is passed on the command line. This would not work well for CMake because CMake conservatively (and correctly) rebuilds *everything* if the flags given to the compiler change. This would result in the entire project being rebuilt everytime git's `HEAD` changed. Instead in this implementation a CMake specific version of `version.h.in` (named `version.h.cmake.in`) is added that uses the `#cmakedefine` feature of CMake's `configure_file()` command to define `Z3GITHASH` if it is available and not define it otherwise. This way only object files that depend on `version.h` get re-built rather than the whole project. It is unfortunate that the build systems now have different `version.h` file templates. However they are very simple and I don't want to modify how templates are handled in the python/Makefile build system. --- CMakeLists.txt | 60 ++++++++- README-CMake.md | 2 + contrib/cmake/cmake/git_utils.cmake | 173 ++++++++++++++++++++++++++ contrib/cmake/src/util/CMakeLists.txt | 4 +- src/util/version.h.cmake.in | 8 ++ 5 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 contrib/cmake/cmake/git_utils.cmake create mode 100644 src/util/version.h.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 87bd07f31..8788cdaf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,8 +35,8 @@ set(Z3_VERSION_MAJOR 4) set(Z3_VERSION_MINOR 5) set(Z3_VERSION_PATCH 1) set(Z3_VERSION_TWEAK 0) -set(Z3_FULL_VERSION 0) set(Z3_VERSION "${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}.${Z3_VERSION_PATCH}.${Z3_VERSION_TWEAK}") +set(Z3_FULL_VERSION_STR "${Z3_VERSION}") # Note this might be modified message(STATUS "Z3 version ${Z3_VERSION}") ################################################################################ @@ -75,6 +75,64 @@ endif() ################################################################################ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") +################################################################################ +# Handle git hash and description +################################################################################ +include(${CMAKE_SOURCE_DIR}/cmake/git_utils.cmake) +macro(disable_git_describe) + message(WARNING "Disabling INCLUDE_GIT_DESCRIBE") + set(INCLUDE_GIT_DESCRIBE OFF CACHE BOOL "Include git describe output in version output" FORCE) +endmacro() +macro(disable_git_hash) + message(WARNING "Disabling INCLUDE_GIT_HASH") + set(INCLUDE_GIT_HASH OFF CACHE BOOL "Include git hash in version output" FORCE) + unset(Z3GITHASH) # Used in configure_file() +endmacro() +option(INCLUDE_GIT_HASH "Include git hash in version output" ON) +option(INCLUDE_GIT_DESCRIBE "Include git describe output in version output" ON) + +set(GIT_DIR "${CMAKE_SOURCE_DIR}/.git") +if (EXISTS "${GIT_DIR}") + # Try to make CMake configure depend on the current git HEAD so that + # a re-configure is triggered when the HEAD changes. + add_git_dir_dependency("${GIT_DIR}" ADD_GIT_DEP_SUCCESS) + if (ADD_GIT_DEP_SUCCESS) + if (INCLUDE_GIT_HASH) + get_git_head_hash("${GIT_DIR}" Z3GITHASH) + if (NOT Z3GITHASH) + message(WARNING "Failed to get Git hash") + disable_git_hash() + endif() + message(STATUS "Using Git hash in version output: ${Z3GITHASH}") + # This mimics the behaviour of the old build system. + string(APPEND Z3_FULL_VERSION_STR " ${Z3GITHASH}") + else() + message(STATUS "Not using Git hash in version output") + unset(Z3GITHASH) # Used in configure_file() + endif() + if (INCLUDE_GIT_DESCRIBE) + get_git_head_describe("${GIT_DIR}" Z3_GIT_DESCRIPTION) + if (NOT Z3_GIT_DESCRIPTION) + message(WARNING "Failed to get Git description") + disable_git_describe() + endif() + message(STATUS "Using Git description in version output: ${Z3_GIT_DESCRIPTION}") + # This mimics the behaviour of the old build system. + string(APPEND Z3_FULL_VERSION_STR " ${Z3_GIT_DESCRIPTION}") + else() + message(STATUS "Not including git descrption in version") + endif() + else() + message(WARNING "Failed to add git dependency.") + disable_git_describe() + disable_git_hash() + endif() +else() + message(STATUS "Failed to find git directory.") + disable_git_describe() + disable_git_hash() +endif() + ################################################################################ # Useful CMake functions/Macros ################################################################################ diff --git a/README-CMake.md b/README-CMake.md index 0943e7a7d..2a8317890 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -283,6 +283,8 @@ The following useful options can be passed to CMake whilst configuring. * ``INSTALL_JAVA_BINDINGS`` - BOOL. If set to ``TRUE`` and ``BUILD_JAVA_BINDINGS`` is ``TRUE`` then running the ``install`` target will install Z3's Java bindings. * ``Z3_JAVA_JAR_INSTALLDIR`` - STRING. The path to directory to install the Z3 Java ``.jar`` file. This path should be relative to ``CMAKE_INSTALL_PREFIX``. * ``Z3_JAVA_JNI_LIB_INSTALLDIRR`` - STRING. The path to directory to install the Z3 Java JNI bridge library. This path should be relative to ``CMAKE_INSTALL_PREFIX``. +* ``INCLUDE_GIT_DESCRIBE`` - BOOL. If set to ``TRUE`` and the source tree of Z3 is a git repository then the output of ``git describe`` will be included in the build. +* ``INCLUDE_GIT_HASH`` - BOOL. If set to ``TRUE`` and the source tree of Z3 is a git repository then the git hash will be included in the build. On the command line these can be passed to ``cmake`` using the ``-D`` option. In ``ccmake`` and ``cmake-gui`` these can be set in the user interface. diff --git a/contrib/cmake/cmake/git_utils.cmake b/contrib/cmake/cmake/git_utils.cmake new file mode 100644 index 000000000..aa7f38825 --- /dev/null +++ b/contrib/cmake/cmake/git_utils.cmake @@ -0,0 +1,173 @@ +# add_git_dir_dependency(GIT_DIR SUCCESS_VAR) +# +# Adds a configure time dependency on the git directory such that if the HEAD +# of the git directory changes CMake will be forced to re-run. This useful +# for fetching the current git hash and including it in the build. +# +# `GIT_DIR` is the path to the git directory (i.e. the `.git` directory) +# `SUCCESS_VAR` is the name of the variable to set. It will be set to TRUE +# if the dependency was successfully added and FALSE otherwise. +function(add_git_dir_dependency GIT_DIR SUCCESS_VAR) + if (NOT "${ARGC}" EQUAL 2) + message(FATAL_ERROR "Invalid number (${ARGC}) of arguments") + endif() + + if (NOT IS_ABSOLUTE "${GIT_DIR}") + message(FATAL_ERROR "GIT_DIR (\"${GIT_DIR}\") is not an absolute path") + endif() + + if (NOT IS_DIRECTORY "${GIT_DIR}") + message(FATAL_ERROR "GIT_DIR (\"${GIT_DIR}\") is not a directory") + endif() + + set(GIT_HEAD_FILE "${GIT_DIR}/HEAD") + if (NOT EXISTS "${GIT_HEAD_FILE}") + message(AUTHOR_WARNING "Git head file \"${GIT_HEAD_FILE}\" cannot be found") + set(${SUCCESS_VAR} FALSE PARENT_SCOPE) + return() + endif() + + # List of files in the git tree that CMake configuration should depend on + set(GIT_FILE_DEPS "${GIT_HEAD_FILE}") + + # Examine the HEAD and workout what additional dependencies there are. + file(READ "${GIT_HEAD_FILE}" GIT_HEAD_DATA LIMIT 128) + string(STRIP "${GIT_HEAD_DATA}" GIT_HEAD_DATA_STRIPPED) + + if ("${GIT_HEAD_DATA_STRIPPED}" MATCHES "^ref:[ ]*(.+)$") + # HEAD points at a reference. + set(GIT_REF "${CMAKE_MATCH_1}") + if (EXISTS "${GIT_DIR}/${GIT_REF}") + # Unpacked reference. The file contains the commit hash + # so add a dependency on this file so that if we stay on this + # reference (i.e. branch) but change commit CMake will be forced + # to reconfigure. + list(APPEND GIT_FILE_DEPS "${GIT_DIR}/${GIT_REF}") + elseif(EXISTS "${GIT_DIR}/packed-refs") + # The ref must be packed (see `man git-pack-refs`). + list(APPEND GIT_FILE_DEPS "${GIT_DIR}/packed-refs") + else() + # Fail + message(AUTHOR_WARNING "Unhandled git reference") + set(${SUCCESS_VAR} FALSE PARENT_SCOPE) + return() + endif() + else() + # Detached HEAD. + # No other dependencies needed + endif() + + # FIXME: + # This is the directory we will copy (via `configure_file()`) git files + # into. This is a hack. It would be better to use the + # `CMAKE_CONFIGURE_DEPENDS` directory property but that feature is not + # available in CMake 2.8.12. So we use `configure_file()` to effectively + # do the same thing. When the source file to `configure_file()` changes + # it will trigger a re-run of CMake. + set(GIT_CMAKE_FILES_DIR "${CMAKE_CURRENT_BINARY_DIR}/git_cmake_files") + file(MAKE_DIRECTORY "${GIT_CMAKE_FILES_DIR}") + + foreach (git_dependency ${GIT_FILE_DEPS}) + message(STATUS "Adding git dependency \"${git_dependency}\"") + configure_file( + "${git_dependency}" + "${GIT_CMAKE_FILES_DIR}" + COPYONLY + ) + endforeach() + + set(${SUCCESS_VAR} TRUE PARENT_SCOPE) +endfunction() + +# get_git_head_hash(GIT_DIR OUTPUT_VAR) +# +# Retrieve the current commit hash for a git working directory where `GIT_DIR` +# is the `.git` directory in the root of the git working directory. +# +# `OUTPUT_VAR` should be the name of the variable to put the result in. If this +# function fails then either a fatal error will be raised or `OUTPUT_VAR` will +# contain a string with the suffix `NOTFOUND` which can be used in CMake `if()` +# commands. +function(get_git_head_hash GIT_DIR OUTPUT_VAR) + if (NOT "${ARGC}" EQUAL 2) + message(FATAL_ERROR "Invalid number of arguments") + endif() + if (NOT IS_DIRECTORY "${GIT_DIR}") + message(FATAL_ERROR "\"${GIT_DIR}\" is not a directory") + endif() + if (NOT IS_ABSOLUTE "${GIT_DIR}") + message(FATAL_ERROR \""${GIT_DIR}\" is not an absolute path") + endif() + find_package(Git) + if (NOT Git_FOUND) + set(${OUTPUT_VAR} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + get_filename_component(GIT_WORKING_DIR "${GIT_DIR}" DIRECTORY) + execute_process( + COMMAND + "${GIT_EXECUTABLE}" + "rev-parse" + "-q" # Quiet + "HEAD" + WORKING_DIRECTORY + "${GIT_WORKING_DIR}" + RESULT_VARIABLE + GIT_EXIT_CODE + OUTPUT_VARIABLE + Z3_GIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if (NOT "${GIT_EXIT_CODE}" EQUAL 0) + message(WARNING "Failed to execute git") + set(${OUTPUT_VAR} NOTFOUND PARENT_SCOPE) + return() + endif() + set(${OUTPUT_VAR} "${Z3_GIT_HASH}" PARENT_SCOPE) +endfunction() + +# get_git_head_describe(GIT_DIR OUTPUT_VAR) +# +# Retrieve the output of `git describe` for a git working directory where +# `GIT_DIR` is the `.git` directory in the root of the git working directory. +# +# `OUTPUT_VAR` should be the name of the variable to put the result in. If this +# function fails then either a fatal error will be raised or `OUTPUT_VAR` will +# contain a string with the suffix `NOTFOUND` which can be used in CMake `if()` +# commands. +function(get_git_head_describe GIT_DIR OUTPUT_VAR) + if (NOT "${ARGC}" EQUAL 2) + message(FATAL_ERROR "Invalid number of arguments") + endif() + if (NOT IS_DIRECTORY "${GIT_DIR}") + message(FATAL_ERROR "\"${GIT_DIR}\" is not a directory") + endif() + if (NOT IS_ABSOLUTE "${GIT_DIR}") + message(FATAL_ERROR \""${GIT_DIR}\" is not an absolute path") + endif() + find_package(Git) + if (NOT Git_FOUND) + set(${OUTPUT_VAR} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + get_filename_component(GIT_WORKING_DIR "${GIT_DIR}" DIRECTORY) + execute_process( + COMMAND + "${GIT_EXECUTABLE}" + "describe" + "--long" + WORKING_DIRECTORY + "${GIT_WORKING_DIR}" + RESULT_VARIABLE + GIT_EXIT_CODE + OUTPUT_VARIABLE + Z3_GIT_DESCRIPTION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if (NOT "${GIT_EXIT_CODE}" EQUAL 0) + message(WARNING "Failed to execute git") + set(${OUTPUT_VAR} NOTFOUND PARENT_SCOPE) + return() + endif() + set(${OUTPUT_VAR} "${Z3_GIT_DESCRIPTION}" PARENT_SCOPE) +endfunction() diff --git a/contrib/cmake/src/util/CMakeLists.txt b/contrib/cmake/src/util/CMakeLists.txt index c85076531..b76f909d0 100644 --- a/contrib/cmake/src/util/CMakeLists.txt +++ b/contrib/cmake/src/util/CMakeLists.txt @@ -3,7 +3,9 @@ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/version.h") ${z3_polluted_tree_msg} ) endif() -configure_file(version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h @ONLY) + +set(Z3_FULL_VERSION "\"${Z3_FULL_VERSION_STR}\"") +configure_file(version.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) z3_add_component(util SOURCES diff --git a/src/util/version.h.cmake.in b/src/util/version.h.cmake.in new file mode 100644 index 000000000..af3a652a6 --- /dev/null +++ b/src/util/version.h.cmake.in @@ -0,0 +1,8 @@ +// automatically generated file. +#define Z3_MAJOR_VERSION @Z3_VERSION_MAJOR@ +#define Z3_MINOR_VERSION @Z3_VERSION_MINOR@ +#define Z3_BUILD_NUMBER @Z3_VERSION_PATCH@ +#define Z3_REVISION_NUMBER @Z3_VERSION_TWEAK@ + +#define Z3_FULL_VERSION @Z3_FULL_VERSION@ +#cmakedefine Z3GITHASH @Z3GITHASH@ From 73614abf37feab15d4558cf65d0ab682ecd2e7f7 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 3 Mar 2017 19:39:53 +0000 Subject: [PATCH 007/105] [CMake] Implement generation of `Z3Config.cmake` and `Z3Target.cmake` file for the build and install tree. These files allow users of CMake to use Z3 via a CMake config package. Clients can do `find_package(Z3 CONFIG)` to get use the package from their projects. When generating the files for the install tree we try to generate the files so that they are relocatable so that it shouldn't matter if the installed files aren't in the CMAKE_INSTALL_PREFIX when a user consumes them. As long as the relative locations of the files aren't changed things should still work. A new CMake cache variable `CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR` has been added so that the install location of the Z3 CMake package files can be controlled. This addresses #915 . --- CMakeLists.txt | 78 +++++++++++++++++++++++++++ README-CMake.md | 1 + contrib/cmake/cmake/Z3Config.cmake.in | 30 +++++++++++ contrib/cmake/src/CMakeLists.txt | 1 + 4 files changed, 110 insertions(+) create mode 100644 contrib/cmake/cmake/Z3Config.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 8788cdaf4..72ea1143d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -398,10 +398,18 @@ set(CMAKE_INSTALL_PKGCONFIGDIR PATH "Directory to install pkgconfig files" ) + +set(CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR + "${CMAKE_INSTALL_LIBDIR}/cmake/z3" + CACHE + PATH + "Directory to install Z3 CMake package files" +) message(STATUS "CMAKE_INSTALL_LIBDIR: \"${CMAKE_INSTALL_LIBDIR}\"") message(STATUS "CMAKE_INSTALL_BINDIR: \"${CMAKE_INSTALL_BINDIR}\"") message(STATUS "CMAKE_INSTALL_INCLUDEDIR: \"${CMAKE_INSTALL_INCLUDEDIR}\"") message(STATUS "CMAKE_INSTALL_PKGCONFIGDIR: \"${CMAKE_INSTALL_PKGCONFIGDIR}\"") +message(STATUS "CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR: \"${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}\"") ################################################################################ # Uninstall rule @@ -449,6 +457,76 @@ include(${CMAKE_SOURCE_DIR}/cmake/z3_add_component.cmake) include(${CMAKE_SOURCE_DIR}/cmake/z3_append_linker_flag_list_to_target.cmake) add_subdirectory(src) +################################################################################ +# Create `Z3Config.cmake` and related files for the build tree so clients can +# use Z3 via CMake. +################################################################################ +include(CMakePackageConfigHelpers) +export(EXPORT Z3_EXPORTED_TARGETS + NAMESPACE z3:: + FILE "${CMAKE_BINARY_DIR}/Z3Targets.cmake" +) +set(Z3_FIRST_PACKAGE_INCLUDE_DIR "${CMAKE_BINARY_DIR}/src/api") +set(Z3_SECOND_PACKAGE_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/src/api") +set(Z3_CXX_PACKAGE_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/src/api/c++") +set(AUTO_GEN_MSG "Automatically generated. DO NOT EDIT") +set(CONFIG_FILE_TYPE "build tree") +configure_package_config_file("${CMAKE_SOURCE_DIR}/cmake/Z3Config.cmake.in" + "Z3Config.cmake" + INSTALL_DESTINATION "${CMAKE_BINARY_DIR}" + PATH_VARS + Z3_FIRST_PACKAGE_INCLUDE_DIR + Z3_SECOND_PACKAGE_INCLUDE_DIR + Z3_CXX_PACKAGE_INCLUDE_DIR + INSTALL_PREFIX "${CMAKE_BINARY_DIR}" +) +unset(Z3_FIRST_PACKAGE_INCLUDE_DIR) +unset(Z3_SECOND_PACKAGE_INCLUDE_DIR) +unset(Z3_CXX_PACKAGE_INCLUDE_DIR) +unset(AUTO_GEN_MSG) +unset(CONFIG_FILE_TYPE) +# TODO: Provide a `Z3Version.cmake` file so that clients can specify the version +# of Z3 they want. + +################################################################################ +# Create `Z3Config.cmake` and related files for install tree so clients can use +# Z3 via CMake. +################################################################################ +install(EXPORT + Z3_EXPORTED_TARGETS + FILE "Z3Targets.cmake" + NAMESPACE z3:: + DESTINATION "${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}" +) +set(Z3_INSTALL_TREE_CMAKE_CONFIG_FILE "${CMAKE_BINARY_DIR}/cmake/Z3Config.cmake") +set(Z3_FIRST_PACKAGE_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}") +set(Z3_SECOND_INCLUDE_DIR "") +set(Z3_CXX_PACKAGE_INCLUDE_DIR "") +set(AUTO_GEN_MSG "Automatically generated. DO NOT EDIT") +set(CONFIG_FILE_TYPE "install tree") +# We use `configure_package_config_file()` to try and create CMake files +# that are re-locatable so that it doesn't matter if the files aren't placed +# in the original install prefix. +configure_package_config_file("${CMAKE_SOURCE_DIR}/cmake/Z3Config.cmake.in" + "${Z3_INSTALL_TREE_CMAKE_CONFIG_FILE}" + INSTALL_DESTINATION "${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}" + PATH_VARS Z3_FIRST_PACKAGE_INCLUDE_DIR +) +unset(Z3_FIRST_PACKAGE_INCLUDE_DIR) +unset(Z3_SECOND_PACKAGE_INCLUDE_DIR) +unset(Z3_CXX_PACKAGE_INCLUDE_DIR) +unset(AUTO_GEN_MSG) +unset(CONFIG_FILE_TYPE) + +# Add install rule to install ${Z3_INSTALL_TREE_CMAKE_CONFIG_FILE} +install( + FILES "${Z3_INSTALL_TREE_CMAKE_CONFIG_FILE}" + DESTINATION "${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}" +) + +# TODO: Provide a `Z3Version.cmake` file so that clients can specify the version +# of Z3 they want. + ################################################################################ # Examples ################################################################################ diff --git a/README-CMake.md b/README-CMake.md index 2a8317890..6a78b5d4c 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -267,6 +267,7 @@ The following useful options can be passed to CMake whilst configuring. * ``CMAKE_INSTALL_PREFIX`` - STRING. The install prefix to use (e.g. ``/usr/local/``). * ``CMAKE_INSTALL_PKGCONFIGDIR`` - STRING. The path to install pkgconfig files. * ``CMAKE_INSTALL_PYTHON_PKG_DIR`` - STRING. The path to install the z3 python bindings. This can be relative (to ``CMAKE_INSTALL_PREFIX``) or absolute. +* ``CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR`` - STRING. The path to install CMake package files (e.g. ``/usr/lib/cmake/z3``). * ``ENABLE_TRACING_FOR_NON_DEBUG`` - BOOL. If set to ``TRUE`` enable tracing in non-debug builds, if set to ``FALSE`` disable tracing in non-debug builds. Note in debug builds tracing is always enabled. * ``BUILD_LIBZ3_SHARED`` - BOOL. If set to ``TRUE`` build libz3 as a shared library otherwise build as a static library. * ``ENABLE_EXAMPLE_TARGETS`` - BOOL. If set to ``TRUE`` add the build targets for building the API examples. diff --git a/contrib/cmake/cmake/Z3Config.cmake.in b/contrib/cmake/cmake/Z3Config.cmake.in new file mode 100644 index 000000000..e7f604591 --- /dev/null +++ b/contrib/cmake/cmake/Z3Config.cmake.in @@ -0,0 +1,30 @@ +################################################################################ +# @AUTO_GEN_MSG@ +# +# This file is intended to be consumed by clients who wish to use Z3 from CMake. +# It can be use by doing `find_package(Z3 config)` from within a +# `CMakeLists.txt` file. If CMake doesn't find this package automatically you +# can give it a hint by passing `-DZ3_DIR=` to the CMake invocation where +# `` is the path to the directory containing this file. +# +# This file was built for the @CONFIG_FILE_TYPE@. +################################################################################ + +# Exported targets +include("${CMAKE_CURRENT_LIST_DIR}/Z3Targets.cmake") + +@PACKAGE_INIT@ + +# Version information +set(Z3_VERSION_MAJOR @Z3_VERSION_MAJOR@) +set(Z3_VERSION_MINOR @Z3_VERSION_MINOR@) +set(Z3_VERSION_PATCH @Z3_VERSION_PATCH@) +set(Z3_VERSION_TWEAK @Z3_VERSION_TWEAK@) +set(Z3_VERSION_STRING "${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}.${Z3_VERSION_PATCH}.${Z3_VERSION_TWEAK}") + +# NOTE: We can't use `set_and_check()` here because this a list of paths. +# List of include directories +set(Z3_C_INCLUDE_DIRS @PACKAGE_Z3_FIRST_PACKAGE_INCLUDE_DIR@ @PACKAGE_Z3_SECOND_PACKAGE_INCLUDE_DIR@) +set(Z3_CXX_INCLUDE_DIRS @PACKAGE_Z3_CXX_PACKAGE_INCLUDE_DIR@ ${Z3_C_INCLUDE_DIRS}) +# List of libraries to link against +set(Z3_LIBRARIES "z3::libz3") diff --git a/contrib/cmake/src/CMakeLists.txt b/contrib/cmake/src/CMakeLists.txt index 65eef8094..548b59053 100644 --- a/contrib/cmake/src/CMakeLists.txt +++ b/contrib/cmake/src/CMakeLists.txt @@ -168,6 +168,7 @@ foreach (header ${libz3_public_headers}) endforeach() install(TARGETS libz3 + EXPORT Z3_EXPORTED_TARGETS LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" # On Windows this installs ``libz3.lib`` which CMake calls the "corresponding import library". Do we want this installed? RUNTIME DESTINATION "${CMAKE_INSTALL_LIBDIR}" # For Windows. DLLs are runtime targets for CMake From d9617841e085f8d62bafb5a6994db96807aacdcd Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 4 Mar 2017 13:07:36 +0000 Subject: [PATCH 008/105] [CMake] Python examples should only be copied over if python bindings are being built. --- contrib/cmake/examples/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/cmake/examples/CMakeLists.txt b/contrib/cmake/examples/CMakeLists.txt index e596ed3dd..04df6e6b4 100644 --- a/contrib/cmake/examples/CMakeLists.txt +++ b/contrib/cmake/examples/CMakeLists.txt @@ -1,4 +1,6 @@ add_subdirectory(c) add_subdirectory(c++) add_subdirectory(tptp) -add_subdirectory(python) +if (BUILD_PYTHON_BINDINGS) + add_subdirectory(python) +endif() From db5520c71d1dca4354ad82bbed4a7afad240dca1 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 4 Mar 2017 16:45:37 +0000 Subject: [PATCH 009/105] [CMake] Build `c_example`, `cpp_example` and `z3_tptp5` as external projects. This works by giving each example it's own CMake build system and then consuming Z3 via the Z3 CMake config package from the build tree. --- contrib/cmake/examples/CMakeLists.txt | 64 +++++++++++++++++++++- contrib/cmake/examples/c++/CMakeLists.txt | 37 ++++++++++--- contrib/cmake/examples/c/CMakeLists.txt | 35 +++++++++--- contrib/cmake/examples/tptp/CMakeLists.txt | 32 +++++++++-- 4 files changed, 144 insertions(+), 24 deletions(-) diff --git a/contrib/cmake/examples/CMakeLists.txt b/contrib/cmake/examples/CMakeLists.txt index 04df6e6b4..3fa49f9e0 100644 --- a/contrib/cmake/examples/CMakeLists.txt +++ b/contrib/cmake/examples/CMakeLists.txt @@ -1,6 +1,64 @@ -add_subdirectory(c) -add_subdirectory(c++) -add_subdirectory(tptp) +include(ExternalProject) +# Unfortunately `BUILD_ALWAYS` only seems to be supported with the version of ExternalProject +# that shipped with CMake >= 3.1. +if (("${CMAKE_VERSION}" VERSION_EQUAL "3.1") OR ("${CMAKE_VERSION}" VERSION_GREATER "3.1")) + set(EXTERNAL_PROJECT_BUILD_ALWAYS_ARG BUILD_ALWAYS 1) +else() + set(EXTERNAL_PROJECT_BUILD_ALWAYS_ARG "") +endif() + +################################################################################ +# Build example project using libz3's C API as an external project +################################################################################ +ExternalProject_Add(c_example + DEPENDS libz3 + # Configure step + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/c" + CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + # Build step + ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/c_example_build_dir" + # Install Step + INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "" # Dummy command +) +set_target_properties(c_example PROPERTIES EXCLUDE_FROM_ALL TRUE) + + +################################################################################ +# Build example project using libz3's C++ API as an external project +################################################################################ +ExternalProject_Add(cpp_example + DEPENDS libz3 + # Configure step + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/c++" + CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + # Build step + ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/cpp_example_build_dir" + # Install Step + INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "" # Dummy command +) +set_target_properties(cpp_example PROPERTIES EXCLUDE_FROM_ALL TRUE) + +################################################################################ +# Build example tptp5 project using libz3's C++ API as an external project +################################################################################ +ExternalProject_Add(z3_tptp5 + DEPENDS libz3 + # Configure step + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tptp" + CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + # Build step + ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/tptp_build_dir" + # Install Step + INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "" # Dummy command +) +set_target_properties(z3_tptp5 PROPERTIES EXCLUDE_FROM_ALL TRUE) + +################################################################################ +# Build Python examples +################################################################################ if (BUILD_PYTHON_BINDINGS) add_subdirectory(python) endif() diff --git a/contrib/cmake/examples/c++/CMakeLists.txt b/contrib/cmake/examples/c++/CMakeLists.txt index fdc5cf387..b25bea533 100644 --- a/contrib/cmake/examples/c++/CMakeLists.txt +++ b/contrib/cmake/examples/c++/CMakeLists.txt @@ -1,9 +1,28 @@ -# FIXME: We should build this as an external project and consume -# Z3 as `find_package(z3 CONFIG)`. -add_executable(cpp_example EXCLUDE_FROM_ALL example.cpp) -target_link_libraries(cpp_example PRIVATE libz3) -target_include_directories(cpp_example PRIVATE "${CMAKE_SOURCE_DIR}/src/api") -target_include_directories(cpp_example PRIVATE "${CMAKE_SOURCE_DIR}/src/api/c++") -if (NOT BUILD_LIBZ3_SHARED) - z3_append_linker_flag_list_to_target(cpp_example ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) -endif() +################################################################################ +# Example C++ project +################################################################################ +project(Z3_C_EXAMPLE CXX) +cmake_minimum_required(VERSION 2.8.12) +find_package(Z3 + REQUIRED + CONFIG + # `NO_DEFAULT_PATH` is set so that -DZ3_DIR has to be passed to find Z3. + # This should prevent us from accidently picking up an installed + # copy of Z3. This is here to benefit Z3's build sytem when building + # this project. When making your own project you probably shouldn't + # use this option. + NO_DEFAULT_PATH +) +message(STATUS "Z3_FOUND: ${Z3_FOUND}") +message(STATUS "Found Z3 ${Z3_VERSION_STRING}") +message(STATUS "Z3_DIR: ${Z3_DIR}") + +add_executable(cpp_example example.cpp) +target_include_directories(cpp_example PRIVATE ${Z3_CXX_INCLUDE_DIRS}) +target_link_libraries(cpp_example PRIVATE ${Z3_LIBRARIES}) + +# FIXME: The Z3 package does not export information on the link flags +# This is needed for when libz3 is built as a static library +#if (NOT BUILD_LIBZ3_SHARED) +# z3_append_linker_flag_list_to_target(cpp_example ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) +#endif() diff --git a/contrib/cmake/examples/c/CMakeLists.txt b/contrib/cmake/examples/c/CMakeLists.txt index fc6eaee18..d51503e29 100644 --- a/contrib/cmake/examples/c/CMakeLists.txt +++ b/contrib/cmake/examples/c/CMakeLists.txt @@ -1,9 +1,28 @@ -# FIXME: We should build this as an external project and consume -# Z3 as `find_package(z3 CONFIG)`. -add_executable(c_example EXCLUDE_FROM_ALL test_capi.c) -target_link_libraries(c_example PRIVATE libz3) -target_include_directories(c_example PRIVATE "${CMAKE_SOURCE_DIR}/src/api") +################################################################################ +# Example C project +################################################################################ +project(Z3_C_EXAMPLE C) +cmake_minimum_required(VERSION 2.8.12) +find_package(Z3 + REQUIRED + CONFIG + # `NO_DEFAULT_PATH` is set so that -DZ3_DIR has to be passed to find Z3. + # This should prevent us from accidently picking up an installed + # copy of Z3. This is here to benefit Z3's build sytem when building + # this project. When making your own project you probably shouldn't + # use this option. + NO_DEFAULT_PATH +) +message(STATUS "Z3_FOUND: ${Z3_FOUND}") +message(STATUS "Found Z3 ${Z3_VERSION_STRING}") +message(STATUS "Z3_DIR: ${Z3_DIR}") + +add_executable(c_example test_capi.c) +target_include_directories(c_example PRIVATE ${Z3_C_INCLUDE_DIRS}) +target_link_libraries(c_example PRIVATE ${Z3_LIBRARIES}) + +# FIXME: The Z3 package does not export information on the link flags # This is needed for when libz3 is built as a static library -if (NOT BUILD_LIBZ3_SHARED) - z3_append_linker_flag_list_to_target(c_example ${Z3_DEPENDENT_EXTRA_C_LINK_FLAGS}) -endif() +#if (NOT BUILD_LIBZ3_SHARED) +# z3_append_linker_flag_list_to_target(c_example ${Z3_DEPENDENT_EXTRA_C_LINK_FLAGS}) +#endif() diff --git a/contrib/cmake/examples/tptp/CMakeLists.txt b/contrib/cmake/examples/tptp/CMakeLists.txt index 41fa9cc65..6a2858a9b 100644 --- a/contrib/cmake/examples/tptp/CMakeLists.txt +++ b/contrib/cmake/examples/tptp/CMakeLists.txt @@ -1,4 +1,28 @@ -add_executable(z3_tptp5 EXCLUDE_FROM_ALL tptp5.cpp tptp5.lex.cpp) -target_link_libraries(z3_tptp5 PRIVATE libz3) -target_include_directories(z3_tptp5 PRIVATE "${CMAKE_SOURCE_DIR}/src/api") -target_include_directories(z3_tptp5 PRIVATE "${CMAKE_SOURCE_DIR}/src/api/c++") +################################################################################ +# TPTP example +################################################################################ +project(Z3_TPTP5 CXX) +cmake_minimum_required(VERSION 2.8.12) +find_package(Z3 + REQUIRED + CONFIG + # `NO_DEFAULT_PATH` is set so that -DZ3_DIR has to be passed to find Z3. + # This should prevent us from accidently picking up an installed + # copy of Z3. This is here to benefit Z3's build sytem when building + # this project. When making your own project you probably shouldn't + # use this option. + NO_DEFAULT_PATH +) +message(STATUS "Z3_FOUND: ${Z3_FOUND}") +message(STATUS "Found Z3 ${Z3_VERSION_STRING}") +message(STATUS "Z3_DIR: ${Z3_DIR}") + +add_executable(z3_tptp5 tptp5.cpp tptp5.lex.cpp) +target_include_directories(z3_tptp5 PRIVATE ${Z3_CXX_INCLUDE_DIRS}) +target_link_libraries(z3_tptp5 PRIVATE ${Z3_LIBRARIES}) + +# FIXME: The Z3 package does not export information on the link flags +# This is needed for when libz3 is built as a static library +#if (NOT BUILD_LIBZ3_SHARED) +# z3_append_linker_flag_list_to_target(z3_tptp5 ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) +#endif() From b20bf5169a5f9d7c6e235199b081993f210084c6 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 4 Mar 2017 16:49:19 +0000 Subject: [PATCH 010/105] [CMake] Fix typo handling OpenMP flags. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 72ea1143d..6281f951b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -294,11 +294,11 @@ if (OPENMP_FOUND) # flag by MSVC and breaks the build if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")) - list(APPEND Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS ${OpenMP_C_FLAGS}) + list(APPEND Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS ${OpenMP_CXX_FLAGS}) endif() if (("${CMAKE_C_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU")) - list(APPEND Z3_DEPENDENT_EXTRA_C_LINK_FLAGS ${OpenMP_CXX_FLAGS}) + list(APPEND Z3_DEPENDENT_EXTRA_C_LINK_FLAGS ${OpenMP_C_FLAGS}) endif() unset(CMAKE_REQUIRED_FLAGS) message(STATUS "Using OpenMP") From ac85c68ccb437590c2e6669d892eef146ba87d65 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 4 Mar 2017 19:01:23 +0000 Subject: [PATCH 011/105] [CMake] Fix examples linking against libz3 when it is built as a static library on Linux. --- CMakeLists.txt | 6 ------ contrib/cmake/examples/c++/CMakeLists.txt | 6 ------ contrib/cmake/examples/c/CMakeLists.txt | 12 +++++------- contrib/cmake/examples/tptp/CMakeLists.txt | 6 ------ contrib/cmake/src/CMakeLists.txt | 8 +++++++- contrib/cmake/src/api/java/CMakeLists.txt | 1 - 6 files changed, 12 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6281f951b..cf46e7012 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,7 +147,6 @@ set(Z3_COMPONENT_CXX_FLAGS "") set(Z3_COMPONENT_EXTRA_INCLUDE_DIRS "") set(Z3_DEPENDENT_LIBS "") set(Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS "") -set(Z3_DEPENDENT_EXTRA_C_LINK_FLAGS "") ################################################################################ # Build type @@ -296,10 +295,6 @@ if (OPENMP_FOUND) ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")) list(APPEND Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS ${OpenMP_CXX_FLAGS}) endif() - if (("${CMAKE_C_COMPILER_ID}" MATCHES "Clang") OR - ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU")) - list(APPEND Z3_DEPENDENT_EXTRA_C_LINK_FLAGS ${OpenMP_C_FLAGS}) - endif() unset(CMAKE_REQUIRED_FLAGS) message(STATUS "Using OpenMP") else() @@ -386,7 +381,6 @@ message(STATUS "Z3_COMPONENT_CXX_FLAGS: ${Z3_COMPONENT_CXX_FLAGS}") message(STATUS "Z3_DEPENDENT_LIBS: ${Z3_DEPENDENT_LIBS}") message(STATUS "Z3_COMPONENT_EXTRA_INCLUDE_DIRS: ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}") message(STATUS "Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS: ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}") -message(STATUS "Z3_DEPENDENT_EXTRA_C_LINK_FLAGS: ${Z3_DEPENDENT_EXTRA_C_LINK_FLAGS}") ################################################################################ # Z3 installation locations diff --git a/contrib/cmake/examples/c++/CMakeLists.txt b/contrib/cmake/examples/c++/CMakeLists.txt index b25bea533..c37aa475d 100644 --- a/contrib/cmake/examples/c++/CMakeLists.txt +++ b/contrib/cmake/examples/c++/CMakeLists.txt @@ -20,9 +20,3 @@ message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(cpp_example example.cpp) target_include_directories(cpp_example PRIVATE ${Z3_CXX_INCLUDE_DIRS}) target_link_libraries(cpp_example PRIVATE ${Z3_LIBRARIES}) - -# FIXME: The Z3 package does not export information on the link flags -# This is needed for when libz3 is built as a static library -#if (NOT BUILD_LIBZ3_SHARED) -# z3_append_linker_flag_list_to_target(cpp_example ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) -#endif() diff --git a/contrib/cmake/examples/c/CMakeLists.txt b/contrib/cmake/examples/c/CMakeLists.txt index d51503e29..5ca985845 100644 --- a/contrib/cmake/examples/c/CMakeLists.txt +++ b/contrib/cmake/examples/c/CMakeLists.txt @@ -1,7 +1,11 @@ ################################################################################ # Example C project ################################################################################ -project(Z3_C_EXAMPLE C) +# NOTE: Even though this is a C project, libz3 uses C++. When using libz3 +# as a static library if we don't configure this project to also support +# C++ we will use the C linker rather than the C++ linker and will not link +# the C++ standard library in resulting in a link failure. +project(Z3_C_EXAMPLE C CXX) cmake_minimum_required(VERSION 2.8.12) find_package(Z3 REQUIRED @@ -20,9 +24,3 @@ message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(c_example test_capi.c) target_include_directories(c_example PRIVATE ${Z3_C_INCLUDE_DIRS}) target_link_libraries(c_example PRIVATE ${Z3_LIBRARIES}) - -# FIXME: The Z3 package does not export information on the link flags -# This is needed for when libz3 is built as a static library -#if (NOT BUILD_LIBZ3_SHARED) -# z3_append_linker_flag_list_to_target(c_example ${Z3_DEPENDENT_EXTRA_C_LINK_FLAGS}) -#endif() diff --git a/contrib/cmake/examples/tptp/CMakeLists.txt b/contrib/cmake/examples/tptp/CMakeLists.txt index 6a2858a9b..6cd814487 100644 --- a/contrib/cmake/examples/tptp/CMakeLists.txt +++ b/contrib/cmake/examples/tptp/CMakeLists.txt @@ -20,9 +20,3 @@ message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(z3_tptp5 tptp5.cpp tptp5.lex.cpp) target_include_directories(z3_tptp5 PRIVATE ${Z3_CXX_INCLUDE_DIRS}) target_link_libraries(z3_tptp5 PRIVATE ${Z3_LIBRARIES}) - -# FIXME: The Z3 package does not export information on the link flags -# This is needed for when libz3 is built as a static library -#if (NOT BUILD_LIBZ3_SHARED) -# z3_append_linker_flag_list_to_target(z3_tptp5 ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) -#endif() diff --git a/contrib/cmake/src/CMakeLists.txt b/contrib/cmake/src/CMakeLists.txt index 548b59053..66e34790a 100644 --- a/contrib/cmake/src/CMakeLists.txt +++ b/contrib/cmake/src/CMakeLists.txt @@ -143,7 +143,13 @@ endif() # so that if those are also shared libraries they are referenced by `libz3.so`. target_link_libraries(libz3 PRIVATE ${Z3_DEPENDENT_LIBS}) -z3_append_linker_flag_list_to_target(libz3 ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) +# This is currently only for the OpenMP flags. It needs to be set +# via `target_link_libraries()` rather than `z3_append_linker_flag_list_to_target()` +# because when building the `libz3` as a static library when the target is exported +# the link dependencies need to be exported too. +foreach (flag_name ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) + target_link_libraries(libz3 PRIVATE ${flag_name}) +endforeach() # Declare which header file are the public header files of libz3 # these will automatically installed when the libz3 target is installed diff --git a/contrib/cmake/src/api/java/CMakeLists.txt b/contrib/cmake/src/api/java/CMakeLists.txt index b34277266..dce2bc4ea 100644 --- a/contrib/cmake/src/api/java/CMakeLists.txt +++ b/contrib/cmake/src/api/java/CMakeLists.txt @@ -44,7 +44,6 @@ target_link_libraries(z3java PRIVATE libz3) # Z3's components to build ``Native.cpp`` lets do the same for now. target_compile_options(z3java PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) target_compile_definitions(z3java PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) -z3_append_linker_flag_list_to_target(z3java ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) target_include_directories(z3java PRIVATE "${CMAKE_SOURCE_DIR}/src/api" "${CMAKE_BINARY_DIR}/src/api" From 28493622c25e033a2db48bb99a4fba7d12e2fb7e Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 13 Mar 2017 12:37:29 +0000 Subject: [PATCH 012/105] [CMake] On Windows when building the examples copy the Z3 library into the directory of the example executable so that it works "out of the box". --- contrib/cmake/examples/c++/CMakeLists.txt | 16 ++++++++++++++++ contrib/cmake/examples/c/CMakeLists.txt | 16 ++++++++++++++++ contrib/cmake/examples/tptp/CMakeLists.txt | 17 +++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/contrib/cmake/examples/c++/CMakeLists.txt b/contrib/cmake/examples/c++/CMakeLists.txt index c37aa475d..d60604924 100644 --- a/contrib/cmake/examples/c++/CMakeLists.txt +++ b/contrib/cmake/examples/c++/CMakeLists.txt @@ -20,3 +20,19 @@ message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(cpp_example example.cpp) target_include_directories(cpp_example PRIVATE ${Z3_CXX_INCLUDE_DIRS}) target_link_libraries(cpp_example PRIVATE ${Z3_LIBRARIES}) + +if ("${CMAKE_SYSTEM_NAME}" MATCHES "[Ww]indows") + # On Windows we need to copy the Z3 libraries + # into the same directory as the executable + # so that they can be found. + foreach (z3_lib ${Z3_LIBRARIES}) + message(STATUS "Adding copy rule for ${z3_lib}") + add_custom_command(TARGET cpp_example + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy_if_different + $ + $ + ) + endforeach() +endif() diff --git a/contrib/cmake/examples/c/CMakeLists.txt b/contrib/cmake/examples/c/CMakeLists.txt index 5ca985845..dd8fa6328 100644 --- a/contrib/cmake/examples/c/CMakeLists.txt +++ b/contrib/cmake/examples/c/CMakeLists.txt @@ -24,3 +24,19 @@ message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(c_example test_capi.c) target_include_directories(c_example PRIVATE ${Z3_C_INCLUDE_DIRS}) target_link_libraries(c_example PRIVATE ${Z3_LIBRARIES}) + +if ("${CMAKE_SYSTEM_NAME}" MATCHES "[Ww]indows") + # On Windows we need to copy the Z3 libraries + # into the same directory as the executable + # so that they can be found. + foreach (z3_lib ${Z3_LIBRARIES}) + message(STATUS "Adding copy rule for ${z3_lib}") + add_custom_command(TARGET c_example + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy_if_different + $ + $ + ) + endforeach() +endif() diff --git a/contrib/cmake/examples/tptp/CMakeLists.txt b/contrib/cmake/examples/tptp/CMakeLists.txt index 6cd814487..8e8dfb8ea 100644 --- a/contrib/cmake/examples/tptp/CMakeLists.txt +++ b/contrib/cmake/examples/tptp/CMakeLists.txt @@ -20,3 +20,20 @@ message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(z3_tptp5 tptp5.cpp tptp5.lex.cpp) target_include_directories(z3_tptp5 PRIVATE ${Z3_CXX_INCLUDE_DIRS}) target_link_libraries(z3_tptp5 PRIVATE ${Z3_LIBRARIES}) + +if ("${CMAKE_SYSTEM_NAME}" MATCHES "[Ww]indows") + # On Windows we need to copy the Z3 libraries + # into the same directory as the executable + # so that they can be found. + foreach (z3_lib ${Z3_LIBRARIES}) + message(STATUS "Adding copy rule for ${z3_lib}") + add_custom_command(TARGET z3_tptp5 + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy_if_different + $ + $ + ) + endforeach() +endif() + From 5c9d7538a03c0e18b1e99df863058e5218cf0022 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 13 Mar 2017 14:39:12 -0400 Subject: [PATCH 013/105] add alternate str.at semantics check in seq_rewriter this rewrites to empty string if the index is negative or beyond the length of the string, which is consistent with CVC4's semantics for this term --- src/ast/rewriter/seq_rewriter.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 26c3e23e4..adb70c30c 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -598,14 +598,25 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { return BR_FAILED; } +/* + * (str.at s i), constants s/i, i < 0 or i >= |s| ==> (str.at s i) = "" + */ br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) { zstring c; rational r; - if (m_util.str.is_string(a, c) && m_autil.is_numeral(b, r) && r.is_unsigned()) { - unsigned j = r.get_unsigned(); - if (j < c.length()) { - result = m_util.str.mk_string(c.extract(j, 1)); + if (m_util.str.is_string(a, c) && m_autil.is_numeral(b, r)) { + if (r.is_neg()) { + result = m_util.str.mk_string(symbol("")); return BR_DONE; + } else if (r.is_unsigned()) { + unsigned j = r.get_unsigned(); + if (j < c.length()) { + result = m_util.str.mk_string(c.extract(j, 1)); + return BR_DONE; + } else { + result = m_util.str.mk_string(symbol("")); + return BR_DONE; + } } } return BR_FAILED; From 0b1d5645097d41eec4c43946407e08d57b41ad64 Mon Sep 17 00:00:00 2001 From: hume Date: Tue, 14 Mar 2017 18:11:00 +0800 Subject: [PATCH 014/105] added no exception support to z3++.h --- src/api/c++/z3++.h | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index bfd4eb2c4..ac0e2c84a 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -86,7 +86,13 @@ namespace z3 { }; inline std::ostream & operator<<(std::ostream & out, exception const & e) { out << e.msg(); return out; } - +#if !defined(Z3_THROW) +#if __cpp_exceptions || _CPPUNWIND +#define Z3_THROW(x) throw x +#else +#define Z3_THROW(x) {} +#endif +#endif // !defined(Z3_THROW) /** \brief Z3 global configuration object. @@ -165,7 +171,7 @@ namespace z3 { Z3_error_code check_error() const { Z3_error_code e = Z3_get_error_code(m_ctx); if (e != Z3_OK && enable_exceptions()) - throw exception(Z3_get_error_msg(m_ctx, e)); + Z3_THROW(exception(Z3_get_error_msg(m_ctx, e))); return e; } @@ -701,7 +707,7 @@ namespace z3 { if (!is_numeral_i(result)) { assert(ctx().enable_exceptions()); if (!ctx().enable_exceptions()) return 0; - throw exception("numeral does not fit in machine int"); + Z3_THROW(exception("numeral does not fit in machine int")); } return result; } @@ -721,7 +727,7 @@ namespace z3 { if (!is_numeral_u(result)) { assert(ctx().enable_exceptions()); if (!ctx().enable_exceptions()) return 0; - throw exception("numeral does not fit in machine uint"); + Z3_THROW(exception("numeral does not fit in machine uint")); } return result; } @@ -738,7 +744,7 @@ namespace z3 { if (!is_numeral_i64(result)) { assert(ctx().enable_exceptions()); if (!ctx().enable_exceptions()) return 0; - throw exception("numeral does not fit in machine __int64"); + Z3_THROW(exception("numeral does not fit in machine __int64")); } return result; } @@ -755,7 +761,7 @@ namespace z3 { if (!is_numeral_u64(result)) { assert(ctx().enable_exceptions()); if (!ctx().enable_exceptions()) return 0; - throw exception("numeral does not fit in machine __uint64"); + Z3_THROW(exception("numeral does not fit in machine __uint64")); } return result; } @@ -1699,7 +1705,7 @@ namespace z3 { Z3_bool status = Z3_model_eval(ctx(), m_model, n, model_completion, &r); check_error(); if (status == Z3_FALSE && ctx().enable_exceptions()) - throw exception("failed to evaluate expression"); + Z3_THROW(exception("failed to evaluate expression")); return expr(ctx(), r); } @@ -2023,7 +2029,7 @@ namespace z3 { } inline tactic par_or(unsigned n, tactic const* tactics) { if (n == 0) { - throw exception("a non-zero number of tactics need to be passed to par_or"); + Z3_THROW(exception("a non-zero number of tactics need to be passed to par_or")); } array buffer(n); for (unsigned i = 0; i < n; ++i) buffer[i] = tactics[i]; From 1dd2de61ecbe17ac9b66bd1f9df6a9dc4bb1b7c0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Mar 2017 07:43:26 -0700 Subject: [PATCH 015/105] add sum shorthand to C++. Issue #694 Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index bfd4eb2c4..8b523d91c 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -890,6 +890,7 @@ namespace z3 { friend expr operator+(expr const & a, expr const & b); friend expr operator+(expr const & a, int b); friend expr operator+(int a, expr const & b); + friend expr sum(expr_vector const& args); friend expr operator*(expr const & a, expr const & b); friend expr operator*(expr const & a, int b); @@ -1561,6 +1562,15 @@ namespace z3 { } + inline expr sum(expr_vector const& args) { + assert(args.size() > 0); + context& ctx = args[0].ctx(); + array _args(args); + Z3_ast r = Z3_mk_add(ctx, _args.size(), _args.ptr()); + ctx.check_error(); + return expr(ctx, r); + } + inline expr distinct(expr_vector const& args) { assert(args.size() > 0); context& ctx = args[0].ctx(); From 0668ba5f6c916e85b2efc48d4e473680f11bdf3d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Mar 2017 07:58:39 -0700 Subject: [PATCH 016/105] add pb shorthands to C++. Issue #694 Signed-off-by: Nikolaj Bjorner --- src/api/api_pb.cpp | 6 +++--- src/api/c++/z3++.h | 49 +++++++++++++++++++++++++++++++++++++++++++--- src/api/z3_api.h | 6 +++--- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/api/api_pb.cpp b/src/api/api_pb.cpp index f19fd8661..bb4a40c9a 100644 --- a/src/api/api_pb.cpp +++ b/src/api/api_pb.cpp @@ -52,7 +52,7 @@ extern "C" { } Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, - Z3_ast const args[], int _coeffs[], + Z3_ast const args[], int const _coeffs[], int k) { Z3_TRY; LOG_Z3_mk_pble(c, num_args, args, _coeffs, k); @@ -70,7 +70,7 @@ extern "C" { } Z3_ast Z3_API Z3_mk_pbge(Z3_context c, unsigned num_args, - Z3_ast const args[], int _coeffs[], + Z3_ast const args[], int const _coeffs[], int k) { Z3_TRY; LOG_Z3_mk_pble(c, num_args, args, _coeffs, k); @@ -88,7 +88,7 @@ extern "C" { } Z3_ast Z3_API Z3_mk_pbeq(Z3_context c, unsigned num_args, - Z3_ast const args[], int _coeffs[], + Z3_ast const args[], int const _coeffs[], int k) { Z3_TRY; LOG_Z3_mk_pble(c, num_args, args, _coeffs, k); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 6250f324b..b6157f3ff 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -935,7 +935,6 @@ namespace z3 { friend expr operator>=(expr const & a, expr const & b); - friend expr wasoperator(expr const & a, expr const & b); friend expr operator>=(expr const & a, int b); friend expr operator>=(int a, expr const & b); @@ -947,6 +946,12 @@ namespace z3 { friend expr operator>(expr const & a, int b); friend expr operator>(int a, expr const & b); + friend expr pble(expr_vector const& es, int const * coeffs, int bound); + friend expr pbge(expr_vector const& es, int const * coeffs, int bound); + friend expr pbeq(expr_vector const& es, int const * coeffs, int bound); + friend expr atmost(expr_vector const& es, unsigned bound); + friend expr atleast(expr_vector const& es, unsigned bound); + friend expr operator&(expr const & a, expr const & b); friend expr operator&(expr const & a, int b); friend expr operator&(int a, expr const & b); @@ -1566,8 +1571,46 @@ namespace z3 { array vars(xs); Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); } - - + inline expr pble(expr_vector const& es, int const* coeffs, int bound) { + assert(es.size() > 0); + context& ctx = es[0].ctx(); + array _es(es); + Z3_ast r = Z3_mk_pble(ctx, _es.size(), _es.ptr(), coeffs, bound); + ctx.check_error(); + return expr(ctx, r); + } + inline expr pbge(expr_vector const& es, int const* coeffs, int bound) { + assert(es.size() > 0); + context& ctx = es[0].ctx(); + array _es(es); + Z3_ast r = Z3_mk_pbge(ctx, _es.size(), _es.ptr(), coeffs, bound); + ctx.check_error(); + return expr(ctx, r); + } + inline expr pbeq(expr_vector const& es, int const* coeffs, int bound) { + assert(es.size() > 0); + context& ctx = es[0].ctx(); + array _es(es); + Z3_ast r = Z3_mk_pbeq(ctx, _es.size(), _es.ptr(), coeffs, bound); + ctx.check_error(); + return expr(ctx, r); + } + inline expr atmost(expr_vector const& es, unsigned bound) { + assert(es.size() > 0); + context& ctx = es[0].ctx(); + array _es(es); + Z3_ast r = Z3_mk_atmost(ctx, _es.size(), _es.ptr(), bound); + ctx.check_error(); + return expr(ctx, r); + } + inline expr atleast(expr_vector const& es, unsigned bound) { + assert(es.size() > 0); + context& ctx = es[0].ctx(); + array _es(es); + Z3_ast r = Z3_mk_atleast(ctx, _es.size(), _es.ptr(), bound); + ctx.check_error(); + return expr(ctx, r); + } inline expr sum(expr_vector const& args) { assert(args.size() > 0); context& ctx = args[0].ctx(); diff --git a/src/api/z3_api.h b/src/api/z3_api.h index ee35c002e..272c94dda 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -4006,7 +4006,7 @@ extern "C" { def_API('Z3_mk_pble', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT))) */ Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, - Z3_ast const args[], int coeffs[], + Z3_ast const args[], int const coeffs[], int k); /** @@ -4017,7 +4017,7 @@ extern "C" { def_API('Z3_mk_pbge', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT))) */ Z3_ast Z3_API Z3_mk_pbge(Z3_context c, unsigned num_args, - Z3_ast const args[], int coeffs[], + Z3_ast const args[], int const coeffs[], int k); /** @@ -4028,7 +4028,7 @@ extern "C" { def_API('Z3_mk_pbeq', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT))) */ Z3_ast Z3_API Z3_mk_pbeq(Z3_context c, unsigned num_args, - Z3_ast const args[], int coeffs[], + Z3_ast const args[], int const coeffs[], int k); /** From 34717a7b6e3048b1c0b6730361ac35594d603aaa Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 14 Mar 2017 14:14:46 -0400 Subject: [PATCH 017/105] str.extract semantics fix --- src/ast/rewriter/seq_rewriter.cpp | 35 ++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 26c3e23e4..4f99c6ae6 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -509,15 +509,40 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) { br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& result) { zstring s; rational pos, len; - if (m_util.str.is_string(a, s) && m_autil.is_numeral(b, pos) && m_autil.is_numeral(c, len) && - pos.is_unsigned() && len.is_unsigned() && pos.get_unsigned() + len.get_unsigned() <= s.length()) { - unsigned _pos = pos.get_unsigned(); - unsigned _len = len.get_unsigned(); - result = m_util.str.mk_string(s.extract(_pos, _len)); + + bool constantBase = m_util.str.is_string(a, s); + bool constantPos = m_autil.is_numeral(b, pos); + bool constantLen = m_autil.is_numeral(c, len); + + // case 1: pos<0 or len<0 + // rewrite to "" + if ( (constantPos && pos.is_neg()) || (constantLen && len.is_neg()) ) { + result = m_util.str.mk_string(symbol("")); return BR_DONE; } + // case 1.1: pos >= length(base) + // rewrite to "" + if (constantBase && constantPos && pos.get_unsigned() >= s.length()) { + result = m_util.str.mk_string(symbol("")); + return BR_DONE; + } + + if (constantBase && constantPos && constantLen) { + if (pos.get_unsigned() + len.get_unsigned() >= s.length()) { + // case 2: pos+len goes past the end of the string + unsigned _len = s.length() - pos.get_unsigned() + 1; + result = m_util.str.mk_string(s.extract(pos.get_unsigned(), _len)); + return BR_DONE; + } else { + // case 3: pos+len still within string + result = m_util.str.mk_string(s.extract(pos.get_unsigned(), len.get_unsigned())); + return BR_DONE; + } + } + return BR_FAILED; } + br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { zstring c, d; if (m_util.str.is_string(a, c) && m_util.str.is_string(b, d)) { From 05c267b8d8ad4cf3c00e104453f2049ecd665a72 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Mar 2017 15:37:47 -0700 Subject: [PATCH 018/105] make seq.at operations generic Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index adb70c30c..26918261e 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -604,20 +604,21 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) { zstring c; rational r; - if (m_util.str.is_string(a, c) && m_autil.is_numeral(b, r)) { + if (m_autil.is_numeral(b, r)) { if (r.is_neg()) { - result = m_util.str.mk_string(symbol("")); + result = m_util.str.mk_empty(m().get_sort(a)); + return BR_DONE; + } + unsigned len = 0; + bool bounded = min_length(1, &a, len); + if (bounded && r >= rational(len)) { + result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; - } else if (r.is_unsigned()) { - unsigned j = r.get_unsigned(); - if (j < c.length()) { - result = m_util.str.mk_string(c.extract(j, 1)); - return BR_DONE; - } else { - result = m_util.str.mk_string(symbol("")); - return BR_DONE; - } } + if (m_util.str.is_string(a, c) && r.is_unsigned() && r < rational(c.length())) { + result = m_util.str.mk_string(c.extract(r.get_unsigned(), 1)); + return BR_DONE; + } } return BR_FAILED; } From 72651e2e98b38a8eec25acffa41f1b5470a76fa8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 14 Mar 2017 19:35:11 -0700 Subject: [PATCH 019/105] fixing sources for double frees of clauses. #940 Signed-off-by: Nikolaj Bjorner --- src/sat/sat_asymm_branch.cpp | 2 +- src/sat/sat_simplifier.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sat/sat_asymm_branch.cpp b/src/sat/sat_asymm_branch.cpp index 8a761ea3a..782713d5c 100644 --- a/src/sat/sat_asymm_branch.cpp +++ b/src/sat/sat_asymm_branch.cpp @@ -188,8 +188,8 @@ namespace sat { case 1: TRACE("asymm_branch", tout << "produced unit clause: " << c[0] << "\n";); s.assign(c[0], justification()); - s.del_clause(c); s.propagate_core(false); + s.del_clause(c); 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: diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index bd975115b..e744bc007 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -279,7 +279,10 @@ namespace sat { unsigned sz = c.size(); if (sz == 0) { s.set_conflict(justification()); - return; + for (; it != end; ++it, ++it2) { + *it2 = *it; + } + break; } if (sz == 1) { s.assign(c[0], justification()); From a0237ed2a6e2c08483e39ae64daa0727e165e54c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 Mar 2017 18:56:43 -0700 Subject: [PATCH 020/105] fix crash reported in #946 Signed-off-by: Nikolaj Bjorner --- src/opt/opt_solver.cpp | 1 + src/smt/theory_dense_diff_logic_def.h | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp index 351141a3f..8ae4e467f 100644 --- a/src/opt/opt_solver.cpp +++ b/src/opt/opt_solver.cpp @@ -50,6 +50,7 @@ namespace opt { if (m_params.m_case_split_strategy == CS_ACTIVITY_DELAY_NEW) { m_params.m_relevancy_lvl = 0; } + // m_params.m_auto_config = false; } unsigned opt_solver::m_dump_count = 0; diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index 877d4f659..628eeea83 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -901,7 +901,7 @@ namespace smt { objective_term const& objective = m_objectives[v]; has_shared = false; - IF_VERBOSE(1, + IF_VERBOSE(4, for (unsigned i = 0; i < objective.size(); ++i) { verbose_stream() << objective[i].second << " * v" << objective[i].first << " "; @@ -991,9 +991,12 @@ namespace smt { if (num_nodes <= v && v < num_nodes + num_edges) { unsigned edge_id = v - num_nodes; literal lit = m_edges[edge_id].m_justification; - get_context().literal2expr(lit, tmp); - core.push_back(tmp); + if (lit != null_literal) { + get_context().literal2expr(lit, tmp); + core.push_back(tmp); + } } + TRACE("opt", tout << core << "\n";); } for (unsigned i = 0; i < num_nodes; ++i) { mpq_inf const& val = S.get_value(i); From d754aa2dc4221c56bcb2a240a5084c4b394e0e5b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 17 Mar 2017 10:12:32 -0700 Subject: [PATCH 021/105] disable ackerman reduction when head contains a non-constant/non-variable. #947 Signed-off-by: Nikolaj Bjorner --- src/muz/transforms/dl_mk_array_blast.cpp | 7 +++++++ src/muz/transforms/dl_mk_interp_tail_simplifier.cpp | 4 ---- src/smt/theory_arith_int.h | 6 ++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/muz/transforms/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp index 82d351113..031c5098e 100644 --- a/src/muz/transforms/dl_mk_array_blast.cpp +++ b/src/muz/transforms/dl_mk_array_blast.cpp @@ -126,6 +126,12 @@ namespace datalog { app* s; var* v; + // disable Ackerman reduction if head contains a non-variable or non-constant argument. + for (unsigned i = 0; i < to_app(head)->get_num_args(); ++i) { + expr* arg = to_app(head)->get_arg(i); + if (!is_var(arg) && !m.is_value(arg)) return false; + } + for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); if (is_select_eq_var(e, s, v)) { @@ -281,6 +287,7 @@ namespace datalog { m_rewriter(body); sub(head); m_rewriter(head); + TRACE("dl", tout << body << " => " << head << "\n";); change = ackermanize(r, body, head); if (!change) { rules.add_rule(&r); diff --git a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp index ea0e6c887..455b06d3d 100644 --- a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp @@ -394,10 +394,6 @@ namespace datalog { m_simp(a, simp1_res); (*m_rw)(simp1_res.get(), res); - /*if (simp1_res.get()!=res.get()) { - std::cout<<"pre norm:\n"<get_owner(), m_util.mk_numeral(_k, true)); + expr* e = get_enode(v)->get_owner(); + bound = m_util.mk_ge(e, m_util.mk_numeral(_k, m_util.is_int(e))); TRACE("arith_int", tout << mk_bounded_pp(bound, get_manager()) << "\n";); context & ctx = get_context(); ctx.internalize(bound, true); @@ -371,7 +372,7 @@ namespace smt { ctx.mk_th_axiom(get_id(), l1, l2); - TRACE("theory_arith_int", + TRACE("arith_int", tout << "cut: (or " << mk_pp(p1, get_manager()) << " " << mk_pp(p2, get_manager()) << ")\n"; ); @@ -1407,6 +1408,7 @@ namespace smt { if (m_params.m_arith_int_eq_branching && branch_infeasible_int_equality()) { return FC_CONTINUE; } + theory_var int_var = find_infeasible_int_base_var(); if (int_var != null_theory_var) { TRACE("arith_int", tout << "v" << int_var << " does not have an integer assignment: " << get_value(int_var) << "\n";); From d58018841e8bca4a85bbd8972fccb5d6cca645a7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 17 Mar 2017 10:52:16 -0700 Subject: [PATCH 022/105] remove code that causes infinite loop. Stackoverflow question from Dominik Wojtaszek Signed-off-by: Nikolaj Bjorner --- src/ast/ast_smt2_pp.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 98c3b7962..89af8bd3e 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -1002,14 +1002,6 @@ class smt2_printer { reset_stacks(); SASSERT(&(r.get_manager()) == &(fm())); m_soccs(n); - TRACE("smt2_pp_shared", - tout << "shared terms for:\n" << mk_pp(n, m()) << "\n"; - tout << "------>\n"; - shared_occs::iterator it = m_soccs.begin_shared(); - shared_occs::iterator end = m_soccs.end_shared(); - for (; it != end; ++it) { - tout << mk_pp(*it, m()) << "\n"; - }); m_root = n; push_frame(n, true); while (!m_frame_stack.empty()) { From 8ac060c549441e0968c8db5e0166ca61ef996f18 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 20 Mar 2017 09:12:41 +0000 Subject: [PATCH 023/105] fix build with VS 2017 --- src/util/hwf.cpp | 1 + src/util/hwf.h | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index bd8d4958d..f84f7fe40 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -52,6 +52,7 @@ Revision History: #ifdef USE_INTRINSICS #include +#include #endif hwf_manager::hwf_manager() : diff --git a/src/util/hwf.h b/src/util/hwf.h index cf0c9b7ea..8816e5b37 100644 --- a/src/util/hwf.h +++ b/src/util/hwf.h @@ -88,9 +88,6 @@ public: bool is_pzero(hwf const & x); bool is_one(hwf const & x); - - // structural eq - bool eq_core(hwf const & x, hwf const & y); bool eq(hwf const & x, hwf const & y); bool lt(hwf const & x, hwf const & y); From ca4ae171ea95d39fd836972789db09cdbbb7c82f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 21 Mar 2017 07:40:35 -0600 Subject: [PATCH 024/105] remove unsound simplification in prefix #949 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 17 +++++++++++------ src/smt/theory_seq.cpp | 7 +++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 26918261e..b7f99298c 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -676,6 +676,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { bool isc2 = m_util.str.is_string(b, s2); if (isc1 && isc2) { result = m().mk_bool_val(s1.prefixof(s2)); + TRACE("seq", tout << result << "\n";); return BR_DONE; } if (m_util.str.is_empty(a)) { @@ -689,6 +690,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { expr_ref_vector as(m()), bs(m()); if (a1 != b1 && isc1 && isc2) { + TRACE("seq", tout << s1 << " " << s2 << "\n";); if (s1.length() <= s2.length()) { if (s1.prefixof(s2)) { if (a == a1) { @@ -733,26 +735,27 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { m_util.str.get_concat(a, as); m_util.str.get_concat(b, bs); unsigned i = 0; - bool all_values = true; expr_ref_vector eqs(m()); for (; i < as.size() && i < bs.size(); ++i) { expr* a = as[i].get(), *b = bs[i].get(); if (a == b) { continue; } - all_values &= m().is_value(a) && m().is_value(b); - if (all_values) { - result = m().mk_false(); - return BR_DONE; - } if (m_util.str.is_unit(a) && m_util.str.is_unit(b)) { eqs.push_back(m().mk_eq(a, b)); continue; } + if (m().is_value(a) && m().is_value(b) && m_util.str.is_string(a) && m_util.str.is_string(b)) { + TRACE("seq", tout << mk_pp(a, m()) << " != " << mk_pp(b, m()) << "\n";); + result = m().mk_false(); + return BR_DONE; + } + break; } if (i == as.size()) { result = mk_and(eqs); + TRACE("seq", tout << result << "\n";); if (m().is_true(result)) { return BR_DONE; } @@ -764,6 +767,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { eqs.push_back(m().mk_eq(m_util.str.mk_empty(m().get_sort(a)), as[j].get())); } result = mk_and(eqs); + TRACE("seq", tout << result << "\n";); return BR_REWRITE3; } if (i > 0) { @@ -771,6 +775,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { a = m_util.str.mk_concat(as.size() - i, as.c_ptr() + i); b = m_util.str.mk_concat(bs.size() - i, bs.c_ptr() + i); result = m_util.str.mk_prefix(a, b); + TRACE("seq", tout << result << "\n";); return BR_DONE; } else { diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index d5251c56b..daf5e3702 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2719,7 +2719,9 @@ bool theory_seq::can_propagate() { expr_ref theory_seq::canonize(expr* e, dependency*& eqs) { expr_ref result = expand(e, eqs); + TRACE("seq", tout << mk_pp(e, m) << " expands to " << result << "\n";); m_rewrite(result); + TRACE("seq", tout << mk_pp(e, m) << " rewrites to " << result << "\n";); return result; } @@ -4469,10 +4471,11 @@ bool theory_seq::canonizes(bool sign, expr* e) { context& ctx = get_context(); dependency* deps = 0; expr_ref cont = canonize(e, deps); - TRACE("seq", tout << mk_pp(e, m) << " -> " << cont << "\n";); + TRACE("seq", tout << mk_pp(e, m) << " -> " << cont << "\n"; + if (deps) display_deps(tout, deps);); if ((m.is_true(cont) && !sign) || (m.is_false(cont) && sign)) { - TRACE("seq", display(tout);); + TRACE("seq", display(tout); tout << ctx.get_assignment(ctx.get_literal(e)) << "\n";); propagate_lit(deps, 0, 0, ctx.get_literal(e)); return true; } From 6804c88b66fbdbdc8bba55487dbd6366352361fc Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 21 Mar 2017 12:54:06 -0400 Subject: [PATCH 025/105] make seq.extract rewrite type-generic --- src/ast/rewriter/seq_rewriter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 4f99c6ae6..526d715dc 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -517,13 +517,13 @@ br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& resu // case 1: pos<0 or len<0 // rewrite to "" if ( (constantPos && pos.is_neg()) || (constantLen && len.is_neg()) ) { - result = m_util.str.mk_string(symbol("")); + result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } // case 1.1: pos >= length(base) // rewrite to "" if (constantBase && constantPos && pos.get_unsigned() >= s.length()) { - result = m_util.str.mk_string(symbol("")); + result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } From 25d839ed10b67353ed0ae065b87935f2b4d2d83c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 22 Mar 2017 10:55:55 -0700 Subject: [PATCH 026/105] fix bug in simplifier of bv2int over concatentations exposed by #948 Signed-off-by: Nikolaj Bjorner --- src/ast/simplifier/bv_simplifier_plugin.cpp | 25 +++++++++++++++------ src/smt/theory_arith_core.h | 7 +++--- src/smt/theory_bv.cpp | 5 +++-- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/ast/simplifier/bv_simplifier_plugin.cpp b/src/ast/simplifier/bv_simplifier_plugin.cpp index 11ed1b9e0..a72e7e117 100644 --- a/src/ast/simplifier/bv_simplifier_plugin.cpp +++ b/src/ast/simplifier/bv_simplifier_plugin.cpp @@ -1517,13 +1517,24 @@ void bv_simplifier_plugin::mk_bv2int(expr * arg, sort* range, expr_ref & result) result = m_arith.mk_add(tmp1, tmp2); } // commented out to reproduce bug in reduction of int2bv/bv2int - else if (m_util.is_concat(arg)) { - expr_ref tmp1(m_manager), tmp2(m_manager); - unsigned sz2 = get_bv_size(to_app(arg)->get_arg(1)); - mk_bv2int(to_app(arg)->get_arg(0), range, tmp1); - mk_bv2int(to_app(arg)->get_arg(1), range, tmp2); - tmp1 = m_arith.mk_mul(m_arith.mk_numeral(power(numeral(2), sz2), true), tmp1); - result = m_arith.mk_add(tmp1, tmp2); + else if (m_util.is_concat(arg) && to_app(arg)->get_num_args() > 0) { + expr_ref_vector args(m_manager); + unsigned num_args = to_app(arg)->get_num_args(); + for (unsigned i = 0; i < num_args; ++i) { + expr_ref tmp(m_manager); + mk_bv2int(to_app(arg)->get_arg(i), range, tmp); + args.push_back(tmp); + } + unsigned sz = get_bv_size(to_app(arg)->get_arg(num_args-1)); + for (unsigned i = num_args - 1; i > 0; ) { + expr_ref tmp(m_manager); + --i; + tmp = args[i].get(); + tmp = m_arith.mk_mul(m_arith.mk_numeral(power(numeral(2), sz), true), tmp); + args[i] = tmp; + sz += get_bv_size(to_app(arg)->get_arg(i)); + } + result = m_arith.mk_add(args.size(), args.c_ptr()); } else { parameter parameter(range); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 513cf36a4..785e0120f 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -40,7 +40,7 @@ namespace smt { template void theory_arith::found_underspecified_op(app * n) { if (!m_found_underspecified_op) { - TRACE("arith", tout << "found non underspecificed expression:\n" << mk_pp(n, get_manager()) << "\n";); + TRACE("arith", tout << "found underspecificed expression:\n" << mk_pp(n, get_manager()) << "\n";); get_context().push_trail(value_trail(m_found_underspecified_op)); m_found_underspecified_op = true; } @@ -395,6 +395,7 @@ namespace smt { template theory_var theory_arith::internalize_div(app * n) { + if (!m_util.is_numeral(n->get_arg(1))) found_underspecified_op(n); found_underspecified_op(n); theory_var s = mk_binary_op(n); context & ctx = get_context(); @@ -418,7 +419,7 @@ namespace smt { template theory_var theory_arith::internalize_mod(app * n) { TRACE("arith_mod", tout << "internalizing...\n" << mk_pp(n, get_manager()) << "\n";); - found_underspecified_op(n); + if (!m_util.is_numeral(n->get_arg(1))) found_underspecified_op(n); theory_var s = mk_binary_op(n); context & ctx = get_context(); if (!ctx.relevancy()) @@ -428,7 +429,7 @@ namespace smt { template theory_var theory_arith::internalize_rem(app * n) { - found_underspecified_op(n); + if (!m_util.is_numeral(n->get_arg(1))) found_underspecified_op(n); theory_var s = mk_binary_op(n); context & ctx = get_context(); if (!ctx.relevancy()) { diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index a886c8a1e..ae2aa95e2 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -607,12 +607,13 @@ namespace smt { } expr_ref sum(m); arith_simp().mk_add(sz, args.c_ptr(), sum); + literal l(mk_eq(n, sum, false)); TRACE("bv", tout << mk_pp(n, m) << "\n"; tout << mk_pp(sum, m) << "\n"; + ctx.display_literal_verbose(tout, l); + tout << "\n"; ); - - literal l(mk_eq(n, sum, false)); ctx.mark_as_relevant(l); ctx.mk_th_axiom(get_id(), 1, &l); From e47e8c67c093f2832fd862a7b7a80716dd9d7ee7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 22 Mar 2017 14:12:47 -0700 Subject: [PATCH 027/105] introducing scoped detacth/attach of clauses to enforce basic sat solver invariants. Part of investigating #939: Signed-off-by: Nikolaj Bjorner --- src/sat/sat_asymm_branch.cpp | 11 ++++------- src/sat/sat_elim_eqs.cpp | 2 +- src/sat/sat_solver.cpp | 24 ++++++++++++------------ src/sat/sat_solver.h | 27 +++++++++++++++++++++++---- src/util/small_object_allocator.cpp | 2 ++ 5 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/sat/sat_asymm_branch.cpp b/src/sat/sat_asymm_branch.cpp index 782713d5c..cca0c5c1b 100644 --- a/src/sat/sat_asymm_branch.cpp +++ b/src/sat/sat_asymm_branch.cpp @@ -96,7 +96,6 @@ namespace sat { if (!process(c)) continue; // clause was removed *it2 = *it; - // throw exception to test bug fix: if (it2 != it) throw solver_exception("trigger bug"); ++it2; } s.m_clauses.set_end(it2); @@ -129,14 +128,14 @@ namespace sat { // check if the clause is already satisfied for (i = 0; i < sz; i++) { if (s.value(c[i]) == l_true) { - s.dettach_clause(c); + s.detach_clause(c); s.del_clause(c); return false; } } // try asymmetric branching // clause must not be used for propagation - s.dettach_clause(c); + solver::scoped_detach scoped_d(s, c); s.push(); for (i = 0; i < sz - 1; i++) { literal l = c[i]; @@ -154,7 +153,6 @@ namespace sat { SASSERT(s.m_qhead == s.m_trail.size()); if (i == sz - 1) { // clause size can't be reduced. - s.attach_clause(c); return true; } // clause can be reduced @@ -189,18 +187,17 @@ namespace sat { TRACE("asymm_branch", tout << "produced unit clause: " << c[0] << "\n";); s.assign(c[0], justification()); s.propagate_core(false); - s.del_clause(c); + scoped_d.del_clause(); 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); + scoped_d.del_clause(); 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_elim_eqs.cpp b/src/sat/sat_elim_eqs.cpp index 6a7ca6280..b7f83df6c 100644 --- a/src/sat/sat_elim_eqs.cpp +++ b/src/sat/sat_elim_eqs.cpp @@ -94,7 +94,7 @@ namespace sat { continue; } if (!c.frozen()) - m_solver.dettach_clause(c); + m_solver.detach_clause(c); // apply substitution for (i = 0; i < sz; i++) { SASSERT(!m_solver.was_eliminated(c[i].var())); diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 57cdc2fb4..a66f82486 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -462,25 +462,25 @@ namespace sat { return simplify_clause_core(num_lits, lits); } - void solver::dettach_bin_clause(literal l1, literal l2, bool learned) { + void solver::detach_bin_clause(literal l1, literal l2, bool learned) { get_wlist(~l1).erase(watched(l2, learned)); get_wlist(~l2).erase(watched(l1, learned)); } - void solver::dettach_clause(clause & c) { + void solver::detach_clause(clause & c) { if (c.size() == 3) - dettach_ter_clause(c); + detach_ter_clause(c); else - dettach_nary_clause(c); + detach_nary_clause(c); } - void solver::dettach_nary_clause(clause & c) { + void solver::detach_nary_clause(clause & c) { clause_offset cls_off = get_offset(c); erase_clause_watch(get_wlist(~c[0]), cls_off); erase_clause_watch(get_wlist(~c[1]), cls_off); } - void solver::dettach_ter_clause(clause & c) { + void solver::detach_ter_clause(clause & c) { erase_ternary_watch(get_wlist(~c[0]), c[1], c[2]); erase_ternary_watch(get_wlist(~c[1]), c[0], c[2]); erase_ternary_watch(get_wlist(~c[2]), c[0], c[1]); @@ -1493,7 +1493,7 @@ namespace sat { for (unsigned i = new_sz; i < sz; i++) { clause & c = *(m_learned[i]); if (can_delete(c)) { - dettach_clause(c); + detach_clause(c); del_clause(c); } else { @@ -1551,7 +1551,7 @@ namespace sat { else { c.inc_inact_rounds(); if (c.inact_rounds() > m_config.m_gc_k) { - dettach_clause(c); + detach_clause(c); del_clause(c); m_stats.m_gc_clause++; deleted++; @@ -1562,7 +1562,7 @@ namespace sat { if (psm(c) > static_cast(c.size() * m_min_d_tk)) { // move to frozen; TRACE("sat_frozen", tout << "freezing size: " << c.size() << " psm: " << psm(c) << " " << c << "\n";); - dettach_clause(c); + detach_clause(c); c.reset_inact_rounds(); c.freeze(); m_num_frozen++; @@ -2595,7 +2595,7 @@ namespace sat { } else { clause & c = *(cw.get_clause()); - dettach_clause(c); + detach_clause(c); attach_clause(c, reinit); if (scope_lvl() > 0 && reinit) { // clause propagated literal, must keep it in the reinit stack. @@ -2628,7 +2628,7 @@ namespace sat { for (unsigned i = 0; i < clauses.size(); ++i) { clause & c = *(clauses[i]); if (c.contains(lit)) { - dettach_clause(c); + detach_clause(c); del_clause(c); } else { @@ -2646,7 +2646,7 @@ namespace sat { literal l1 = m_user_bin_clauses[i].first; literal l2 = m_user_bin_clauses[i].second; if (nlit == l1 || nlit == l2) { - dettach_bin_clause(l1, l2, learned); + detach_bin_clause(l1, l2, learned); } } } diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index f910e374f..6c91565aa 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -195,15 +195,34 @@ namespace sat { bool attach_nary_clause(clause & c); void attach_clause(clause & c, bool & reinit); void attach_clause(clause & c) { bool reinit; attach_clause(c, reinit); } + class scoped_detach { + solver& s; + clause& c; + bool m_deleted; + public: + scoped_detach(solver& s, clause& c): s(s), c(c), m_deleted(false) { + s.detach_clause(c); + } + ~scoped_detach() { + if (!m_deleted) s.attach_clause(c); + } + + void del_clause() { + if (!m_deleted) { + s.del_clause(c); + m_deleted = true; + } + } + }; unsigned select_watch_lit(clause const & cls, unsigned starting_at) const; unsigned select_learned_watch_lit(clause const & cls) const; bool simplify_clause(unsigned & num_lits, literal * lits) const; template bool simplify_clause_core(unsigned & num_lits, literal * lits) const; - void dettach_bin_clause(literal l1, literal l2, bool learned); - void dettach_clause(clause & c); - void dettach_nary_clause(clause & c); - void dettach_ter_clause(clause & c); + void detach_bin_clause(literal l1, literal l2, bool learned); + void detach_clause(clause & c); + void detach_nary_clause(clause & c); + void detach_ter_clause(clause & c); void push_reinit_stack(clause & c); // ----------------------- diff --git a/src/util/small_object_allocator.cpp b/src/util/small_object_allocator.cpp index aee84c1f0..60c85b660 100644 --- a/src/util/small_object_allocator.cpp +++ b/src/util/small_object_allocator.cpp @@ -70,6 +70,7 @@ void small_object_allocator::reset() { void small_object_allocator::deallocate(size_t size, void * p) { if (size == 0) return; + #if defined(Z3DEBUG) && !defined(_WINDOWS) // Valgrind friendly memory::deallocate(p); @@ -93,6 +94,7 @@ void small_object_allocator::deallocate(size_t size, void * p) { void * small_object_allocator::allocate(size_t size) { if (size == 0) return 0; + #if defined(Z3DEBUG) && !defined(_WINDOWS) // Valgrind friendly return memory::allocate(size); From 26ae3a5abb92a39ace36f3d28fd661283ec8e34f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 22 Mar 2017 19:06:59 -0700 Subject: [PATCH 028/105] making simplifier code exception friendlier. Towards getting a handle on #939 Signed-off-by: Nikolaj Bjorner --- src/sat/sat_simplifier.cpp | 17 ++++++++++------- src/sat/sat_simplifier.h | 11 +++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index e744bc007..007751220 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -168,14 +168,13 @@ namespace sat { m_need_cleanup = false; m_use_list.init(s.num_vars()); init_visited(); - bool learned_in_use_lists = false; + m_learned_in_use_lists = false; if (learned) { register_clauses(s.m_learned); - learned_in_use_lists = true; + m_learned_in_use_lists = true; } register_clauses(s.m_clauses); - if (!learned && (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls)) elim_blocked_clauses(); @@ -184,7 +183,9 @@ namespace sat { m_sub_counter = m_subsumption_limit; m_elim_counter = m_res_limit; - unsigned old_num_elim_vars = m_num_elim_vars; + m_old_num_elim_vars = m_num_elim_vars; + + scoped_finalize _scoped_finalize(*this); do { if (m_subsumption) @@ -199,20 +200,22 @@ namespace sat { break; } while (!m_sub_todo.empty()); + } - bool vars_eliminated = m_num_elim_vars > old_num_elim_vars; + void simplifier::scoped_finalize_fn() { + bool vars_eliminated = m_num_elim_vars > m_old_num_elim_vars; if (m_need_cleanup) { TRACE("after_simplifier", tout << "cleanning watches...\n";); cleanup_watches(); - cleanup_clauses(s.m_learned, true, vars_eliminated, learned_in_use_lists); + cleanup_clauses(s.m_learned, true, vars_eliminated, m_learned_in_use_lists); cleanup_clauses(s.m_clauses, false, vars_eliminated, true); } else { TRACE("after_simplifier", tout << "skipping cleanup...\n";); if (vars_eliminated) { // must remove learned clauses with eliminated variables - cleanup_clauses(s.m_learned, true, true, learned_in_use_lists); + cleanup_clauses(s.m_learned, true, true, m_learned_in_use_lists); } } CASSERT("sat_solver", s.check_invariant()); diff --git a/src/sat/sat_simplifier.h b/src/sat/sat_simplifier.h index 9ee239083..d26d0041f 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -91,6 +91,9 @@ namespace sat { unsigned m_num_sub_res; unsigned m_num_elim_lits; + bool m_learned_in_use_lists; + unsigned m_old_num_elim_vars; + struct size_lt { bool operator()(clause const * c1, clause const * c2) const { return c1->size() > c2->size(); } }; @@ -170,6 +173,14 @@ namespace sat { struct subsumption_report; struct elim_var_report; + class scoped_finalize { + simplifier& s; + public: + scoped_finalize(simplifier& s) : s(s) {} + ~scoped_finalize() { s.scoped_finalize_fn(); } + }; + void scoped_finalize_fn(); + public: simplifier(solver & s, params_ref const & p); ~simplifier(); From 1ab7ab9d744d5cddec94ae539d4d52233fedcb16 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 23 Mar 2017 11:09:36 -0700 Subject: [PATCH 029/105] fix double ownership of enode marking causing crash during tracing. Issue #952 Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 2 +- src/smt/smt_conflict_resolution.cpp | 6 +++--- src/smt/smt_context.cpp | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index b6157f3ff..104f3ae6c 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -140,7 +140,7 @@ namespace z3 { class context { bool m_enable_exceptions; Z3_context m_ctx; - static void error_handler(Z3_context /*c*/, Z3_error_code /*e*/) { /* do nothing */ } + static void error_handler(Z3_context c, Z3_error_code e) { std::cout << "ex\n"; Z3_THROW(exception(Z3_get_error_msg(c, e))); std::cout << "unreach\n"; } void init(config & c) { m_ctx = Z3_mk_context_rc(c); m_enable_exceptions = true; diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index 7dd9144fe..8d90f9583 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -59,9 +59,9 @@ namespace smt { SASSERT(n->trans_reaches(n->get_root())); while (n) { if (Set) - n->set_mark(); + n->set_mark2(); else - n->unset_mark(); + n->unset_mark2(); n = n->m_trans.m_target; } } @@ -84,7 +84,7 @@ namespace smt { mark_enodes_in_trans(n1); while (true) { SASSERT(n2); - if (n2->is_marked()) { + if (n2->is_marked2()) { mark_enodes_in_trans(n1); return n2; } diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 9336322f7..6bc5cc6ab 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -4214,6 +4214,7 @@ namespace smt { for (unsigned i = 0; i < m_asserted_formulas.get_num_formulas(); ++i) { expr* e = m_asserted_formulas.get_formula(i); if (is_quantifier(e)) { + TRACE("context", tout << mk_pp(e, m) << "\n";); quantifier* q = to_quantifier(e); if (!m.is_rec_fun_def(q)) continue; SASSERT(q->get_num_patterns() == 1); From 62e87d647474e539ef9274e244869dac8615fa8c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 23 Mar 2017 11:10:19 -0700 Subject: [PATCH 030/105] fix double ownership of enode marking causing crash during tracing. Issue #952 Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 104f3ae6c..b6157f3ff 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -140,7 +140,7 @@ namespace z3 { class context { bool m_enable_exceptions; Z3_context m_ctx; - static void error_handler(Z3_context c, Z3_error_code e) { std::cout << "ex\n"; Z3_THROW(exception(Z3_get_error_msg(c, e))); std::cout << "unreach\n"; } + static void error_handler(Z3_context /*c*/, Z3_error_code /*e*/) { /* do nothing */ } void init(config & c) { m_ctx = Z3_mk_context_rc(c); m_enable_exceptions = true; From 37167a8dd607d27fb666222b63bfeef188de5789 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 23 Mar 2017 19:53:23 +0000 Subject: [PATCH 031/105] Fixed excessive trace output --- src/smt/smt_context_pp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index ff45c5089..74c759510 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -606,7 +606,7 @@ namespace smt { case b_justification::CLAUSE: { clause * cls = j.get_clause(); out << "clause "; - if (cls) display_literals_verbose(out, cls->get_num_literals(), cls->begin_literals()); + if (cls) display_literals(out, cls->get_num_literals(), cls->begin_literals()); break; } case b_justification::JUSTIFICATION: { From c56c7fd649a41e5b99a1ae150d059d895cd3cf69 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 24 Mar 2017 01:31:00 -0700 Subject: [PATCH 032/105] add handlers for dense difference logic Signed-off-by: Nikolaj Bjorner --- src/opt/opt_solver.cpp | 27 ++++++++++++++++++++++++++- src/smt/theory_dense_diff_logic_def.h | 2 ++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp index 8ae4e467f..bc6462a18 100644 --- a/src/opt/opt_solver.cpp +++ b/src/opt/opt_solver.cpp @@ -358,6 +358,7 @@ namespace opt { } smt::theory_opt& opt = get_optimizer(); smt::theory_var v = m_objective_vars[var]; + TRACE("opt", tout << "v" << var << " " << val << "\n";); if (typeid(smt::theory_inf_arith) == typeid(opt)) { smt::theory_inf_arith& th = dynamic_cast(opt); @@ -387,8 +388,32 @@ namespace opt { smt::theory_rdl& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val); } + + if (typeid(smt::theory_dense_i) == typeid(opt) && + val.get_infinitesimal().is_zero()) { + smt::theory_dense_i& th = dynamic_cast(opt); + return th.mk_ge(m_fm, v, val); + } - // difference logic? + if (typeid(smt::theory_dense_mi) == typeid(opt) && + val.get_infinitesimal().is_zero()) { + smt::theory_dense_mi& th = dynamic_cast(opt); + return th.mk_ge(m_fm, v, val); + } + + if (typeid(smt::theory_dense_si) == typeid(opt) && + val.get_infinitesimal().is_zero()) { + smt::theory_dense_si& th = dynamic_cast(opt); + return th.mk_ge(m_fm, v, val); + } + + if (typeid(smt::theory_dense_smi) == typeid(opt) && + val.get_infinitesimal().is_zero()) { + smt::theory_dense_smi& th = dynamic_cast(opt); + return th.mk_ge(m_fm, v, val); + } + + IF_VERBOSE(0, verbose_stream() << "WARNING: unhandled theory " << typeid(opt).name() << "\n";); return expr_ref(m.mk_true(), m); } diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index 628eeea83..ed94ee62c 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -1019,6 +1019,7 @@ namespace smt { template theory_var theory_dense_diff_logic::add_objective(app* term) { + TRACE("opt", tout << mk_pp(term, get_manager()) << "\n";); objective_term objective; theory_var result = m_objectives.size(); rational q(1), r(0); @@ -1053,6 +1054,7 @@ namespace smt { ast_manager& m = get_manager(); objective_term const& t = m_objectives[v]; expr_ref e(m), f(m), f2(m); + TRACE("opt", tout << "mk_ineq " << v << " " << val << "\n";); if (t.size() == 1 && t[0].second.is_one()) { f = get_enode(t[0].first)->get_owner(); } From ec4770622690fc29b2eb0557b7d644651a92b90c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 24 Mar 2017 02:23:50 -0700 Subject: [PATCH 033/105] fix constant offset and handling of ite in difference logic optimizer code-path. Issue #946 Signed-off-by: Nikolaj Bjorner --- src/opt/opt_context.cpp | 7 ++++++- src/smt/theory_dense_diff_logic_def.h | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index cd40944b2..d1b7a489e 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -273,7 +273,8 @@ namespace opt { display_benchmark(); IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n";); lbool is_sat = s.check_sat(0,0); - TRACE("opt", tout << "initial search result: " << is_sat << "\n";); + TRACE("opt", tout << "initial search result: " << is_sat << "\n"; + s.display(tout);); if (is_sat != l_false) { s.get_model(m_model); s.get_labels(m_labels); @@ -1037,6 +1038,10 @@ namespace opt { TRACE("opt", tout << "Purifying " << term << "\n";); term = purify(fm, term); } + else if (m.is_ite(term)) { + TRACE("opt", tout << "Purifying " << term << "\n";); + term = purify(fm, term); + } if (fm) { m_model_converter = concat(m_model_converter.get(), fm.get()); } diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index ed94ee62c..addb5d92b 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -868,7 +868,8 @@ namespace smt { e = ctx.get_enode(to_app(n)); } else { - e = ctx.mk_enode(to_app(n), false, false, true); + ctx.internalize(n, false); + e = ctx.get_enode(n); } v = e->get_th_var(get_id()); if (v == null_theory_var) { @@ -1008,7 +1009,8 @@ namespace smt { inf_eps result(rational(0), r); blocker = mk_gt(v, result); IF_VERBOSE(10, verbose_stream() << blocker << "\n";); - return result; + r += m_objective_consts[v]; + return inf_eps(rational(0), r); } default: TRACE("opt", tout << "unbounded\n"; ); From 866035d786ca69860359d7833a6c15108f7c0740 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Mar 2017 09:40:18 +0000 Subject: [PATCH 034/105] Disabled debug output --- src/smt/smt_context_pp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 74c759510..73d822fb4 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -613,7 +613,7 @@ namespace smt { out << "justification "; literal_vector lits; const_cast(*m_conflict_resolution).justification2literals(j.get_justification(), lits); - display_literals_verbose(out, lits.size(), lits.c_ptr()); + display_literals(out, lits.size(), lits.c_ptr()); break; } default: From e9cd4d10570c7e035489841ebadc9ce08df8a1a9 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Mar 2017 11:51:36 +0000 Subject: [PATCH 035/105] Build fix for systems that don't come with SSE4.1 support by default --- src/util/hwf.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index f84f7fe40..ac39db71a 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -52,8 +52,10 @@ Revision History: #ifdef USE_INTRINSICS #include +#ifdef __SSE4_1__ #include #endif +#endif hwf_manager::hwf_manager() : m_mpz_manager(m_mpq_manager) From 7f9c37e19d4bab0eaeaed586513c812f271603f5 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Mar 2017 14:23:39 +0000 Subject: [PATCH 036/105] VS2017 SSE4 intrinsics build fix --- src/util/hwf.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index ac39db71a..f8e4ff69b 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -52,10 +52,8 @@ Revision History: #ifdef USE_INTRINSICS #include -#ifdef __SSE4_1__ #include #endif -#endif hwf_manager::hwf_manager() : m_mpz_manager(m_mpq_manager) @@ -306,7 +304,9 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o // According to the Intel Architecture manual, the x87-instrunction FRNDINT is the // same in 32-bit and 64-bit mode. The _mm_round_* intrinsics are SSE4 extensions. #ifdef _WINDOWS -#ifdef USE_INTRINSICS +#if defined(USE_INTRINSICS) && \ + (defined(_WINDOWS) && defined(__AVX__)) || \ + (!defined(_WINDOWS) && defined(__SSE4_1__) ) switch (rm) { case 0: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_NEAREST_INT)); break; case 2: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_POS_INF)); break; From d10dec2218657023d8adcfc0a1b5e6c4a61aba97 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Mar 2017 14:31:06 +0000 Subject: [PATCH 037/105] Removed unused variable --- src/ast/rewriter/rewriter_def.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index aecc1c93a..76f149df7 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -189,10 +189,9 @@ bool rewriter_tpl::constant_fold(app * t, frame & fr) { result_stack().shrink(fr.m_spos); result_stack().push_back(arg); fr.m_state = REWRITE_BUILTIN; - unsigned max_depth = fr.m_max_depth; if (visit(arg, fr.m_max_depth)) { m_r = result_stack().back(); - result_stack().pop_back(); + result_stack().pop_back(); result_stack().pop_back(); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); From 0399e5e2d339505a9b34a66358133ef872549077 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Mar 2017 14:49:24 +0000 Subject: [PATCH 038/105] Fixed variable initialization warning --- src/sat/sat_solver.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index a66f82486..9c858a29a 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -101,7 +101,7 @@ namespace sat { if (!it->is_binary_non_learned_clause()) continue; literal l2 = it->get_literal(); - if (l.index() > l2.index()) + if (l.index() > l2.index()) continue; mk_clause_core(l, l2); } @@ -223,7 +223,7 @@ namespace sat { if (propagate_bin_clause(l1, l2)) { if (scope_lvl() == 0) return; - if (!learned) + if (!learned) m_clauses_to_reinit.push_back(clause_wrapper(l1, l2)); } m_stats.m_mk_bin_clause++; @@ -248,7 +248,7 @@ namespace sat { void solver::push_reinit_stack(clause & c) { TRACE("sat_reinit", tout << "adding to reinit stack: " << c << "\n";); m_clauses_to_reinit.push_back(clause_wrapper(c)); - c.set_reinit_stack(true); + c.set_reinit_stack(true); } @@ -257,7 +257,7 @@ namespace sat { clause * r = m_cls_allocator.mk_clause(3, lits, learned); bool reinit = attach_ter_clause(*r); if (reinit && !learned) push_reinit_stack(*r); - + if (learned) m_learned.push_back(r); else @@ -806,22 +806,22 @@ namespace sat { m_params.set_uint("random_seed", m_rand()); if (i == 1 + num_threads/2) { m_params.set_sym("phase", symbol("random")); - } + } solvers[i] = alloc(sat::solver, m_params, rlims[i], 0); solvers[i]->copy(*this); solvers[i]->set_par(&par); - scoped_rlimit.push_child(&solvers[i]->rlimit()); + scoped_rlimit.push_child(&solvers[i]->rlimit()); } set_par(&par); m_params.set_sym("phase", saved_phase); int finished_id = -1; std::string ex_msg; - par_exception_kind ex_kind; + par_exception_kind ex_kind = DEFAULT_EX; unsigned error_code = 0; lbool result = l_undef; #pragma omp parallel for for (int i = 0; i < num_threads; ++i) { - try { + try { lbool r = l_undef; if (i < num_extra_solvers) { r = solvers[i]->check(num_lits, lits); @@ -851,7 +851,7 @@ namespace sat { rlims[j].cancel(); } } - } + } } catch (z3_error & err) { if (i == 0) { @@ -871,7 +871,7 @@ namespace sat { m_stats = solvers[finished_id]->m_stats; } - for (int i = 0; i < num_extra_solvers; ++i) { + for (int i = 0; i < num_extra_solvers; ++i) { dealloc(solvers[i]); } if (finished_id == -1) { @@ -1140,7 +1140,7 @@ namespace sat { for (unsigned i = 0; !inconsistent() && i < m_assumptions.size(); ++i) { assign(m_assumptions[i], justification()); } - TRACE("sat", + TRACE("sat", for (unsigned i = 0; i < m_assumptions.size(); ++i) { index_set s; if (m_antecedents.find(m_assumptions[i].var(), s)) { @@ -2037,7 +2037,7 @@ namespace sat { } } - literal consequent = m_not_l; + literal consequent = m_not_l; justification js = m_conflict; @@ -3115,7 +3115,7 @@ namespace sat { literal_pair p(l1, l2); if (!seen_bc.contains(p)) { seen_bc.insert(p); - mc.add_edge(l1.index(), l2.index()); + mc.add_edge(l1.index(), l2.index()); } } vector _mutexes; @@ -3168,7 +3168,7 @@ namespace sat { } void solver::fixup_consequence_core() { - index_set s; + index_set s; TRACE("sat", tout << m_core << "\n";); for (unsigned i = 0; i < m_core.size(); ++i) { TRACE("sat", tout << m_core[i] << ": "; display_index_set(tout, m_antecedents.find(m_core[i].var())) << "\n";); @@ -3218,20 +3218,20 @@ namespace sat { while (true) { ++num_iterations; SASSERT(!inconsistent()); - + lbool r = bounded_search(); if (r != l_undef) { fixup_consequence_core(); return r; } - + extract_fixed_consequences(num_units, asms, unfixed_vars, conseq); if (m_conflicts > m_config.m_max_conflicts) { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts << "\")\n";); return l_undef; } - + restart(); simplify_problem(); if (check_inconsistent()) { @@ -3239,11 +3239,11 @@ namespace sat { return l_false; } gc(); - + if (m_config.m_restart_max <= num_iterations) { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-restarts\")\n";); return l_undef; - } + } } } From fb105afac2ed8ec35e6f5c9706d8c596b95a06d7 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Mar 2017 15:22:33 +0000 Subject: [PATCH 039/105] Windows build fix --- src/util/hwf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index f8e4ff69b..5572ee252 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -305,8 +305,8 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o // same in 32-bit and 64-bit mode. The _mm_round_* intrinsics are SSE4 extensions. #ifdef _WINDOWS #if defined(USE_INTRINSICS) && \ - (defined(_WINDOWS) && defined(__AVX__)) || \ - (!defined(_WINDOWS) && defined(__SSE4_1__) ) + (defined(_WINDOWS) && (defined(__AVX__) || defined(_M_X64))) || \ + (!defined(_WINDOWS) && defined(__SSE4_1__)) switch (rm) { case 0: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_NEAREST_INT)); break; case 2: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_POS_INF)); break; From f8d022a18060ac6abe6429c7e6e79ecdaf15fc02 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Mar 2017 15:25:18 +0000 Subject: [PATCH 040/105] Non-windows build fix --- src/util/hwf.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index 5572ee252..e577e15df 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -52,8 +52,10 @@ Revision History: #ifdef USE_INTRINSICS #include +#if defined(_MSC_VER) || defined(__SSE4_1__) #include #endif +#endif hwf_manager::hwf_manager() : m_mpz_manager(m_mpq_manager) From 3bbe5eceeb0e0a51c6d11d7a2ba2d54fb6d52dbc Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 24 Mar 2017 15:53:46 +0000 Subject: [PATCH 041/105] fix for --get-describe --- scripts/mk_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 17ab8dea0..f05250ae7 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2685,8 +2685,8 @@ def get_full_version_string(major, minor, build, revision): if GIT_HASH: res += " " + GIT_HASH if GIT_DESCRIBE: - branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD', '--long']) - res += " master " + check_output(['git', 'describe']) + branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) + res += " " + branch + " " + check_output(['git', 'describe']) return '"' + res + '"' # Update files with the version number From e05cee757ba715aa3631a32ea4b8ee92cede2d7c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 24 Mar 2017 10:10:42 -0700 Subject: [PATCH 042/105] properly handle recursive function definitions #898 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 7 +++++-- src/sat/sat_simplifier.cpp | 1 + src/smt/smt_context.cpp | 32 +++++--------------------------- src/smt/smt_context.h | 2 -- src/smt/smt_model_checker.cpp | 2 +- 5 files changed, 12 insertions(+), 32 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 2551f0aa0..7060d79ad 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -739,8 +739,11 @@ void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, s lhs = m().mk_app(f, binding.size(), binding.c_ptr()); eq = m().mk_eq(lhs, e); if (!ids.empty()) { - expr* pat = m().mk_pattern(lhs); - eq = m().mk_forall(ids.size(), f->get_domain(), ids.c_ptr(), eq, 0, m().rec_fun_qid(), symbol::null, 1, &pat); + if (!is_app(e)) { + throw cmd_exception("Z3 only supports recursive definitions that are proper terms (not binders or variables)"); + } + expr* pats[2] = { m().mk_pattern(lhs), m().mk_pattern(to_app(e)) }; + eq = m().mk_forall(ids.size(), f->get_domain(), ids.c_ptr(), eq, 0, m().rec_fun_qid(), symbol::null, 2, pats); } // diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 007751220..8b753fb67 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -149,6 +149,7 @@ namespace sat { } void simplifier::operator()(bool learned) { + std::cout << s.rlimit().count() << "\n"; if (s.inconsistent()) return; if (!m_subsumption && !m_elim_blocked_clauses && !m_resolution) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 6bc5cc6ab..f1b043556 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -4217,40 +4217,18 @@ namespace smt { TRACE("context", tout << mk_pp(e, m) << "\n";); quantifier* q = to_quantifier(e); if (!m.is_rec_fun_def(q)) continue; - SASSERT(q->get_num_patterns() == 1); + SASSERT(q->get_num_patterns() == 2); expr* fn = to_app(q->get_pattern(0))->get_arg(0); + expr* body = to_app(q->get_pattern(1))->get_arg(0); SASSERT(is_app(fn)); func_decl* f = to_app(fn)->get_decl(); - expr* eq = q->get_expr(); - expr_ref body(m); - if (is_fun_def(fn, q->get_expr(), body)) { - func_interp* fi = alloc(func_interp, m, f->get_arity()); - fi->set_else(body); - m_model->register_decl(f, fi); - } + func_interp* fi = alloc(func_interp, m, f->get_arity()); + fi->set_else(body); + m_model->register_decl(f, fi); } } } - bool context::is_fun_def(expr* f, expr* body, expr_ref& result) { - expr* t1, *t2, *t3; - if (m_manager.is_eq(body, t1, t2) || m_manager.is_iff(body, t1, t2)) { - if (t1 == f) return result = t2, true; - if (t2 == f) return result = t1, true; - return false; - } - if (m_manager.is_ite(body, t1, t2, t3)) { - expr_ref body1(m_manager), body2(m_manager); - if (is_fun_def(f, t2, body1) && is_fun_def(f, t3, body2)) { - // f is not free in t1 - result = m_manager.mk_ite(t1, body1, body2); - return true; - } - } - return false; - } - - }; diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 2a555e6b5..1f57a7550 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1167,8 +1167,6 @@ namespace smt { void add_rec_funs_to_model(); - bool is_fun_def(expr* f, expr* q, expr_ref& body); - public: bool can_propagate() const; diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index 093d215b6..dfdb035c5 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -318,7 +318,7 @@ namespace smt { bool model_checker::check_rec_fun(quantifier* q) { TRACE("model_checker", tout << mk_pp(q, m) << "\n";); - SASSERT(q->get_num_patterns() == 1); + SASSERT(q->get_num_patterns() == 2); // first pattern is the function, second is the body. expr* fn = to_app(q->get_pattern(0))->get_arg(0); SASSERT(is_app(fn)); func_decl* f = to_app(fn)->get_decl(); From 723b507a887499517c69182947a2c611f7487edb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 24 Mar 2017 10:11:39 -0700 Subject: [PATCH 043/105] properly handle recursive function definitions #898 Signed-off-by: Nikolaj Bjorner --- src/sat/sat_simplifier.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 8b753fb67..007751220 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -149,7 +149,6 @@ namespace sat { } void simplifier::operator()(bool learned) { - std::cout << s.rlimit().count() << "\n"; if (s.inconsistent()) return; if (!m_subsumption && !m_elim_blocked_clauses && !m_resolution) From 3a9857940e21e6978ba7a75548b6cf26c4707386 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 25 Mar 2017 19:31:01 +0100 Subject: [PATCH 044/105] add missing axioms for str.at. Issue #953 Signed-off-by: Nikolaj Bjorner --- src/ast/bv_decl_plugin.h | 10 +++++----- src/ast/macros/macro_util.cpp | 36 ++++++++++++++--------------------- src/ast/macros/macro_util.h | 3 +-- src/smt/theory_seq.cpp | 15 +++++++++++++++ 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index ac0ff7f79..33cf094b9 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -406,11 +406,11 @@ public: app * mk_bv_not(expr * arg) { return m_manager.mk_app(get_fid(), OP_BNOT, arg); } app * mk_bv_xor(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BXOR, num, args); } app * mk_bv_neg(expr * arg) { return m_manager.mk_app(get_fid(), OP_BNEG, arg); } - app * mk_bv_urem(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_BUREM, arg1, arg2); } - app * mk_bv_srem(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_BSREM, arg1, arg2); } - app * mk_bv_add(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_BADD, arg1, arg2); } - app * mk_bv_sub(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_BSUB, arg1, arg2); } - app * mk_bv_mul(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_BMUL, arg1, arg2); } + app * mk_bv_urem(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUREM, arg1, arg2); } + app * mk_bv_srem(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSREM, arg1, arg2); } + app * mk_bv_add(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BADD, arg1, arg2); } + app * mk_bv_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSUB, arg1, arg2); } + app * mk_bv_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BMUL, arg1, arg2); } app * mk_zero_extend(unsigned n, expr* e) { parameter p(n); return m_manager.mk_app(get_fid(), OP_ZERO_EXT, 1, &p, 1, &e); diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp index 99732871c..fce6f1b28 100644 --- a/src/ast/macros/macro_util.cpp +++ b/src/ast/macros/macro_util.cpp @@ -19,22 +19,22 @@ Revision History: --*/ #include"macro_util.h" #include"occurs.h" +#include"ast_util.h" #include"arith_simplifier_plugin.h" -#include"basic_simplifier_plugin.h" #include"bv_simplifier_plugin.h" #include"var_subst.h" #include"ast_pp.h" #include"ast_ll_pp.h" -#include"ast_util.h" #include"for_each_expr.h" #include"well_sorted.h" +#include"bool_rewriter.h" macro_util::macro_util(ast_manager & m, simplifier & s): m_manager(m), + m_bv(m), m_simplifier(s), m_arith_simp(0), m_bv_simp(0), - m_basic_simp(0), m_forbidden_set(0), m_curr_clause(0) { } @@ -55,24 +55,17 @@ bv_simplifier_plugin * macro_util::get_bv_simp() const { return m_bv_simp; } -basic_simplifier_plugin * macro_util::get_basic_simp() const { - if (m_basic_simp == 0) { - const_cast(this)->m_basic_simp = static_cast(m_simplifier.get_plugin(m_manager.get_basic_family_id())); - } - SASSERT(m_basic_simp != 0); - return m_basic_simp; -} bool macro_util::is_bv(expr * n) const { - return get_bv_simp()->is_bv(n); + return m_bv.is_bv(n); } bool macro_util::is_bv_sort(sort * s) const { - return get_bv_simp()->is_bv_sort(s); + return m_bv.is_bv_sort(s); } bool macro_util::is_add(expr * n) const { - return get_arith_simp()->is_add(n) || get_bv_simp()->is_add(n); + return get_arith_simp()->is_add(n) || m_bv.is_bv_add(n); } bool macro_util::is_times_minus_one(expr * n, expr * & arg) const { @@ -80,11 +73,11 @@ bool macro_util::is_times_minus_one(expr * n, expr * & arg) const { } bool macro_util::is_le(expr * n) const { - return get_arith_simp()->is_le(n) || get_bv_simp()->is_le(n); + return get_arith_simp()->is_le(n) || m_bv.is_bv_ule(n) || m_bv.is_bv_sle(n); } bool macro_util::is_le_ge(expr * n) const { - return get_arith_simp()->is_le_ge(n) || get_bv_simp()->is_le_ge(n); + return get_arith_simp()->is_le_ge(n) || m_bv.is_bv_ule(n) || m_bv.is_bv_sle(n); } poly_simplifier_plugin * macro_util::get_poly_simp_for(sort * s) const { @@ -102,7 +95,7 @@ app * macro_util::mk_zero(sort * s) const { void macro_util::mk_sub(expr * t1, expr * t2, expr_ref & r) const { if (is_bv(t1)) { - get_bv_simp()->mk_sub(t1, t2, r); + r = m_bv.mk_bv_sub(t1, t2); } else { get_arith_simp()->mk_sub(t1, t2, r); @@ -111,7 +104,7 @@ void macro_util::mk_sub(expr * t1, expr * t2, expr_ref & r) const { void macro_util::mk_add(expr * t1, expr * t2, expr_ref & r) const { if (is_bv(t1)) { - get_bv_simp()->mk_add(t1, t2, r); + r = m_bv.mk_bv_add(t1, t2); } else { get_arith_simp()->mk_add(t1, t2, r); @@ -429,7 +422,7 @@ void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned & num_decl new_args.push_back(new_var); new_conds.push_back(new_cond); } - get_basic_simp()->mk_and(new_conds.size(), new_conds.c_ptr(), cond); + bool_rewriter(m_manager).mk_and(new_conds.size(), new_conds.c_ptr(), cond); head = m_manager.mk_app(qhead->get_decl(), new_args.size(), new_args.c_ptr()); num_decls = next_var_idx; } @@ -687,7 +680,7 @@ void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def, if (cond == 0) new_cond = extra_cond; else - get_basic_simp()->mk_and(cond, extra_cond, new_cond); + bool_rewriter(m_manager).mk_and(cond, extra_cond, new_cond); } else { hint_to_macro_head(m_manager, head, num_decls, new_head); @@ -719,20 +712,19 @@ void macro_util::get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_con if (m_curr_clause == 0) return; SASSERT(is_clause(m_manager, m_curr_clause)); - basic_simplifier_plugin * bs = get_basic_simp(); expr_ref_buffer neg_other_lits(m_manager); unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause); for (unsigned i = 0; i < num_lits; i++) { expr * l = get_clause_literal(m_manager, m_curr_clause, i); if (l != except_lit) { expr_ref neg_l(m_manager); - bs->mk_not(l, neg_l); + bool_rewriter(m_manager).mk_not(l, neg_l); neg_other_lits.push_back(neg_l); } } if (neg_other_lits.empty()) return; - get_basic_simp()->mk_and(neg_other_lits.size(), neg_other_lits.c_ptr(), extra_cond); + bool_rewriter(m_manager).mk_and(neg_other_lits.size(), neg_other_lits.c_ptr(), extra_cond); } void macro_util::collect_poly_args(expr * n, expr * exception, ptr_buffer & args) { diff --git a/src/ast/macros/macro_util.h b/src/ast/macros/macro_util.h index 033f6ecb4..8aa8e550e 100644 --- a/src/ast/macros/macro_util.h +++ b/src/ast/macros/macro_util.h @@ -62,10 +62,10 @@ public: private: ast_manager & m_manager; + bv_util m_bv; simplifier & m_simplifier; arith_simplifier_plugin * m_arith_simp; bv_simplifier_plugin * m_bv_simp; - basic_simplifier_plugin * m_basic_simp; obj_hashtable * m_forbidden_set; bool is_forbidden(func_decl * f) const { return m_forbidden_set != 0 && m_forbidden_set->contains(f); } @@ -99,7 +99,6 @@ public: arith_simplifier_plugin * get_arith_simp() const; bv_simplifier_plugin * get_bv_simp() const; - basic_simplifier_plugin * get_basic_simp() const; bool is_macro_head(expr * n, unsigned num_decls) const; bool is_left_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index daf5e3702..663d4cbe1 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -1981,6 +1981,7 @@ bool theory_seq::solve_nc(unsigned idx) { } if (c != n.contains()) { m_ncs.push_back(nc(c, deps)); + m_new_propagation = true; return true; } return false; @@ -2403,6 +2404,14 @@ void theory_seq::display(std::ostream & out) const { } } + if (!m_ncs.empty()) { + out << "Non contains:\n"; + for (unsigned i = 0; i < m_ncs.size(); ++i) { + out << "not " << mk_pp(m_ncs[i].contains(), m) << "\n"; + display_deps(out << " <- ", m_ncs[i].deps()); out << "\n"; + } + } + } void theory_seq::display_equations(std::ostream& out) const { @@ -3496,6 +3505,7 @@ void theory_seq::add_extract_suffix_axiom(expr* e, expr* s, expr* i) { let e = at(s, i) 0 <= i < len(s) -> s = xey & len(x) = i & len(e) = 1 + i < 0 \/ i >= len(s) -> e = empty */ void theory_seq::add_at_axiom(expr* e) { @@ -3509,13 +3519,18 @@ void theory_seq::add_at_axiom(expr* e) { expr_ref y = mk_skolem(m_post, s, mk_sub(mk_sub(len_s, i), one)); expr_ref xey = mk_concat(x, e, y); expr_ref len_x(m_util.str.mk_length(x), m); + expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); literal i_ge_0 = mk_literal(m_autil.mk_ge(i, zero)); literal i_ge_len_s = mk_literal(m_autil.mk_ge(mk_sub(i, m_util.str.mk_length(s)), zero)); + add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, xey)); add_axiom(~i_ge_0, i_ge_len_s, mk_eq(one, len_e, false)); add_axiom(~i_ge_0, i_ge_len_s, mk_eq(i, len_x, false)); + + add_axiom(i_ge_0, mk_eq(s, emp, false)); + add_axiom(~i_ge_len_s, mk_eq(s, emp, false)); } /** From 041520f72753f7c4283ccc9bc133df0da9f0d080 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 28 Mar 2017 18:17:22 +0100 Subject: [PATCH 045/105] SMT2 compliancy fix; NRA includes conversion of Int numerals --- src/ast/arith_decl_plugin.cpp | 59 ++++++++++++++++++++--------------- src/ast/arith_decl_plugin.h | 2 ++ 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index 546f037de..1c01496cf 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -35,7 +35,7 @@ struct arith_decl_plugin::algebraic_numbers_wrapper { ~algebraic_numbers_wrapper() { } - + unsigned mk_id(algebraic_numbers::anum const & val) { SASSERT(!m_amanager.is_rational(val)); unsigned new_id = m_id_gen.mk(); @@ -121,7 +121,7 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) { m_int_decl = m->mk_sort(symbol("Int"), sort_info(id, INT_SORT)); m->inc_ref(m_int_decl); sort * i = m_int_decl; - + sort * b = m->mk_bool_sort(); #define MK_PRED(FIELD, NAME, KIND, SORT) { \ @@ -140,7 +140,7 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) { MK_PRED(m_i_ge_decl, ">=", OP_GE, i); MK_PRED(m_i_lt_decl, "<", OP_LT, i); MK_PRED(m_i_gt_decl, ">", OP_GT, i); - + #define MK_AC_OP(FIELD, NAME, KIND, SORT) { \ func_decl_info info(id, KIND); \ info.set_associative(); \ @@ -205,7 +205,7 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) { MK_UNARY(m_asinh_decl, "asinh", OP_ASINH, r); MK_UNARY(m_acosh_decl, "acosh", OP_ACOSH, r); MK_UNARY(m_atanh_decl, "atanh", OP_ATANH, r); - + func_decl * pi_decl = m->mk_const_decl(symbol("pi"), r, func_decl_info(id, OP_PI)); m_pi = m->mk_const(pi_decl); m->inc_ref(m_pi); @@ -213,7 +213,7 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) { func_decl * e_decl = m->mk_const_decl(symbol("euler"), r, func_decl_info(id, OP_E)); m_e = m->mk_const(e_decl); m->inc_ref(m_e); - + func_decl * z_pw_z_int = m->mk_const_decl(symbol("0^0-int"), i, func_decl_info(id, OP_0_PW_0_INT)); m_0_pw_0_int = m->mk_const(z_pw_z_int); m->inc_ref(m_0_pw_0_int); @@ -221,7 +221,7 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) { func_decl * z_pw_z_real = m->mk_const_decl(symbol("0^0-real"), r, func_decl_info(id, OP_0_PW_0_REAL)); m_0_pw_0_real = m->mk_const(z_pw_z_real); m->inc_ref(m_0_pw_0_real); - + MK_OP(m_neg_root_decl, "neg-root", OP_NEG_ROOT, r); MK_UNARY(m_div_0_decl, "/0", OP_DIV_0, r); MK_UNARY(m_idiv_0_decl, "div0", OP_IDIV_0, i); @@ -285,7 +285,8 @@ arith_decl_plugin::arith_decl_plugin(): m_idiv_0_decl(0), m_mod_0_decl(0), m_u_asin_decl(0), - m_u_acos_decl(0) { + m_u_acos_decl(0), + m_convert_int_numerals_to_real(false) { } arith_decl_plugin::~arith_decl_plugin() { @@ -418,7 +419,7 @@ app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) { if (val.is_unsigned()) { unsigned u_val = val.get_unsigned(); if (u_val < MAX_SMALL_NUM_TO_CACHE) { - if (is_int) { + if (is_int && !m_convert_int_numerals_to_real) { app * r = m_small_ints.get(u_val, 0); if (r == 0) { parameter p[2] = { parameter(val), parameter(1) }; @@ -442,7 +443,7 @@ app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) { } parameter p[2] = { parameter(val), parameter(static_cast(is_int)) }; func_decl * decl; - if (is_int) + if (is_int && !m_convert_int_numerals_to_real) decl = m_manager->mk_const_decl(m_intv_sym, m_int_decl, func_decl_info(m_family_id, OP_NUM, 2, p)); else decl = m_manager->mk_const_decl(m_realv_sym, m_real_decl, func_decl_info(m_family_id, OP_NUM, 2, p)); @@ -479,14 +480,14 @@ static bool has_real_arg(ast_manager * m, unsigned num_args, expr * const * args } static bool is_const_op(decl_kind k) { - return - k == OP_PI || + return + k == OP_PI || k == OP_E || k == OP_0_PW_0_INT || k == OP_0_PW_0_REAL; } - -func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + +func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (k == OP_NUM) return mk_num_decl(num_parameters, parameters, arity); @@ -503,7 +504,7 @@ func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters } } -func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { if (k == OP_NUM) return mk_num_decl(num_parameters, parameters, num_args); @@ -521,9 +522,17 @@ func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters } void arith_decl_plugin::get_sort_names(svector& sort_names, symbol const & logic) { - // TODO: only define Int and Real in the right logics - sort_names.push_back(builtin_name("Int", INT_SORT)); - sort_names.push_back(builtin_name("Real", REAL_SORT)); + if (logic == "NRA" || + logic == "QF_NRA" || + logic == "QF_UFNRA") { + m_convert_int_numerals_to_real = true; + sort_names.push_back(builtin_name("Real", REAL_SORT)); + } + else { + // TODO: only define Int and Real in the right logics + sort_names.push_back(builtin_name("Int", INT_SORT)); + sort_names.push_back(builtin_name("Real", REAL_SORT)); + } } void arith_decl_plugin::get_op_names(svector& op_names, symbol const & logic) { @@ -563,16 +572,16 @@ void arith_decl_plugin::get_op_names(svector& op_names, symbol con } bool arith_decl_plugin::is_value(app * e) const { - return - is_app_of(e, m_family_id, OP_NUM) || + return + is_app_of(e, m_family_id, OP_NUM) || is_app_of(e, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM) || is_app_of(e, m_family_id, OP_PI) || is_app_of(e, m_family_id, OP_E); } bool arith_decl_plugin::is_unique_value(app * e) const { - return - is_app_of(e, m_family_id, OP_NUM) || + return + is_app_of(e, m_family_id, OP_NUM) || is_app_of(e, m_family_id, OP_PI) || is_app_of(e, m_family_id, OP_E); } @@ -671,7 +680,7 @@ expr_ref arith_util::mk_mul_simplify(expr_ref_vector const& args) { } expr_ref arith_util::mk_mul_simplify(unsigned sz, expr* const* args) { expr_ref result(m_manager); - + switch (sz) { case 0: result = mk_numeral(rational(1), true); @@ -681,7 +690,7 @@ expr_ref arith_util::mk_mul_simplify(unsigned sz, expr* const* args) { break; default: result = mk_mul(sz, args); - break; + break; } return result; } @@ -692,7 +701,7 @@ expr_ref arith_util::mk_add_simplify(expr_ref_vector const& args) { } expr_ref arith_util::mk_add_simplify(unsigned sz, expr* const* args) { expr_ref result(m_manager); - + switch (sz) { case 0: result = mk_numeral(rational(0), true); @@ -702,7 +711,7 @@ expr_ref arith_util::mk_add_simplify(unsigned sz, expr* const* args) { break; default: result = mk_add(sz, args); - break; + break; } return result; } diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 410c50852..668eebcc9 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -152,6 +152,8 @@ protected: ptr_vector m_small_ints; ptr_vector m_small_reals; + bool m_convert_int_numerals_to_real; + func_decl * mk_func_decl(decl_kind k, bool is_real); virtual void set_manager(ast_manager * m, family_id id); decl_kind fix_kind(decl_kind k, unsigned arity); From ef3d340c85e4dbe7bdfb74303bae3468cfe35d59 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 31 Mar 2017 12:04:46 +0100 Subject: [PATCH 046/105] Improved decl_collector for uninterpreted sorts in :print_benchmark output --- src/ast/decl_collector.cpp | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/ast/decl_collector.cpp b/src/ast/decl_collector.cpp index b663a9df3..773ebefc7 100644 --- a/src/ast/decl_collector.cpp +++ b/src/ast/decl_collector.cpp @@ -23,8 +23,21 @@ void decl_collector::visit_sort(sort * n) { family_id fid = n->get_family_id(); if (m().is_uninterp(n)) m_sorts.push_back(n); - if (fid == m_dt_fid) + if (fid == m_dt_fid) { m_sorts.push_back(n); + + unsigned num_cnstr = m_dt_util.get_datatype_num_constructors(n); + for (unsigned i = 0; i < num_cnstr; i++) { + func_decl * cnstr = m_dt_util.get_datatype_constructors(n)->get(i); + m_decls.push_back(cnstr); + ptr_vector const & cnstr_acc = *m_dt_util.get_constructor_accessors(cnstr); + unsigned num_cas = cnstr_acc.size(); + for (unsigned j = 0; j < num_cas; j++) { + func_decl * accsr = cnstr_acc.get(j); + m_decls.push_back(accsr); + } + } + } } bool decl_collector::is_bool(sort * s) { @@ -38,14 +51,15 @@ void decl_collector::visit_func(func_decl * n) { m_preds.push_back(n); else m_decls.push_back(n); - } + } } decl_collector::decl_collector(ast_manager & m, bool preds): m_manager(m), - m_sep_preds(preds) { + m_sep_preds(preds), + m_dt_util(m) { m_basic_fid = m_manager.get_basic_family_id(); - m_dt_fid = m_manager.mk_family_id("datatype"); + m_dt_fid = m_dt_util.get_family_id(); } void decl_collector::visit(ast* n) { @@ -55,7 +69,7 @@ void decl_collector::visit(ast* n) { n = todo.back(); todo.pop_back(); if (!m_visited.is_marked(n)) { - m_visited.mark(n, true); + m_visited.mark(n, true); switch(n->get_kind()) { case AST_APP: { app * a = to_app(n); @@ -64,7 +78,7 @@ void decl_collector::visit(ast* n) { } todo.push_back(a->get_decl()); break; - } + } case AST_QUANTIFIER: { quantifier * q = to_quantifier(n); unsigned num_decls = q->get_num_decls(); @@ -77,7 +91,7 @@ void decl_collector::visit(ast* n) { } break; } - case AST_SORT: + case AST_SORT: visit_sort(to_sort(n)); break; case AST_FUNC_DECL: { From 0fb31611135d0d3fc305414ff449e522d92f63ec Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 31 Mar 2017 12:10:51 +0100 Subject: [PATCH 047/105] Updated declarations in decl_collector --- src/ast/decl_collector.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ast/decl_collector.h b/src/ast/decl_collector.h index 678f3805d..0067d18eb 100644 --- a/src/ast/decl_collector.h +++ b/src/ast/decl_collector.h @@ -21,6 +21,7 @@ Revision History: #define SMT_DECL_COLLECTOR_H_ #include"ast.h" +#include"datatype_decl_plugin.h" class decl_collector { ast_manager & m_manager; @@ -28,9 +29,10 @@ class decl_collector { ptr_vector m_sorts; ptr_vector m_decls; ptr_vector m_preds; - ast_mark m_visited; - family_id m_basic_fid; - family_id m_dt_fid; + ast_mark m_visited; + family_id m_basic_fid; + family_id m_dt_fid; + datatype_util m_dt_util; void visit_sort(sort* n); bool is_bool(sort* s); From c99205fa7e9931cea4a267d789cbd8c83744c320 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2017 08:12:53 -0700 Subject: [PATCH 048/105] return box model based on index. Issue #955 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/basic_cmds.cpp | 35 +++++++++++++++++++++++++++------- src/cmd_context/cmd_context.h | 1 + src/opt/opt_context.cpp | 8 ++++++++ src/opt/opt_context.h | 1 + 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index d951f7710..f09d35158 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -100,13 +100,34 @@ public: ATOMIC_CMD(exit_cmd, "exit", "exit.", ctx.print_success(); throw stop_parser_exception();); -ATOMIC_CMD(get_model_cmd, "get-model", "retrieve model for the last check-sat command", { - if (!ctx.is_model_available() || ctx.get_check_sat_result() == 0) - throw cmd_exception("model is not available"); - model_ref m; - ctx.get_check_sat_result()->get_model(m); - ctx.display_model(m); -}); +class get_model_cmd : public cmd { + unsigned m_index; +public: + get_model_cmd(): cmd("get-model"), m_index(0) {} + virtual char const * get_usage() const { return "[]"; } + virtual char const * get_descr(cmd_context & ctx) const { + return "retrieve model for the last check-sat command.\nSupply optional index if retrieving a model corresponding to a box optimization objective"; + } + virtual unsigned get_arity() const { return VAR_ARITY; } + virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_UINT; } + virtual void set_next_arg(cmd_context & ctx, unsigned index) { m_index = index; } + virtual void execute(cmd_context & ctx) { + if (!ctx.is_model_available() || ctx.get_check_sat_result() == 0) + throw cmd_exception("model is not available"); + model_ref m; + if (m_index > 0 && ctx.get_opt()) { + ctx.get_opt()->get_box_model(m, m_index); + } + else { + ctx.get_check_sat_result()->get_model(m); + } + ctx.display_model(m); + } + virtual void reset(cmd_context& ctx) { + m_index = 0; + } +}; + ATOMIC_CMD(get_assignment_cmd, "get-assignment", "retrieve assignment", { if (!ctx.is_model_available() || ctx.get_check_sat_result() == 0) diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 8eee632dc..92943c71c 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -124,6 +124,7 @@ public: virtual bool is_pareto() = 0; virtual void set_logic(symbol const& s) = 0; virtual bool print_model() const = 0; + virtual void get_box_model(model_ref& mdl, unsigned index) = 0; virtual void updt_params(params_ref const& p) = 0; }; diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index d1b7a489e..af3c57baa 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -342,6 +342,14 @@ namespace opt { fix_model(mdl); } + void context::get_box_model(model_ref& mdl, unsigned index) { + if (index >= m_box_models.size()) { + throw default_exception("index into models is out of bounds"); + } + mdl = m_box_models[index]; + fix_model(mdl); + } + lbool context::execute_min_max(unsigned index, bool committed, bool scoped, bool is_max) { if (scoped) get_solver().push(); lbool result = m_optsmt.lex(index, is_max); diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index f51f75830..53bfc19c5 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -186,6 +186,7 @@ namespace opt { virtual bool print_model() const; virtual void set_model(model_ref& _m) { m_model = _m; } virtual void get_model(model_ref& _m); + virtual void get_box_model(model_ref& _m, unsigned index); virtual void fix_model(model_ref& _m); virtual void collect_statistics(statistics& stats) const; virtual proof* get_proof() { return 0; } From 8f798fef1a10706f9871f7baff12b564326d6fc7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2017 08:24:12 -0700 Subject: [PATCH 049/105] fix python interface for string extract to take symbolic indices per bug report from Kun Wei Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 16d7fbb5f..568ffe9a2 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -3701,12 +3701,8 @@ def Extract(high, low, a): high = StringVal(high) if is_seq(high): s = high - offset = _py2expr(low, high.ctx) - length = _py2expr(a, high.ctx) - - if __debug__: - _z3_assert(is_int(offset) and is_int(length), "Second and third arguments must be integers") - return SeqRef(Z3_mk_seq_extract(s.ctx_ref(), s.as_ast(), offset.as_ast(), length.as_ast()), s.ctx) + offset, length = _coerce_exprs(low, a, s.ctx) + return SeqRef(Z3_mk_seq_extract(s.ctx_ref(), s.as_ast(), offset.as_ast(), length.as_ast()), s.ctx) if __debug__: _z3_assert(low <= high, "First argument must be greater than or equal to second argument") _z3_assert(_is_int(high) and high >= 0 and _is_int(low) and low >= 0, "First and second arguments must be non negative integers") From 582880346e8c8ad814f59391d304a04bdc3b5cef Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2017 09:22:56 -0700 Subject: [PATCH 050/105] add index option to 'eval' command for box objectives. Issue #955 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 66 +++++++++++++++++++++++++++---- src/cmd_context/eval_cmd.cpp | 9 ++++- 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index a62eea6ea..3d7da43a7 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -12,6 +12,7 @@ Abstract: Author: Nikolaj Bjorner (nbjorner) 2015-12-5 + Murphy Berzish 2017-02-21 Notes: @@ -514,30 +515,56 @@ br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& resu bool constantPos = m_autil.is_numeral(b, pos); bool constantLen = m_autil.is_numeral(c, len); - // case 1: pos<0 or len<0 + // case 1: pos<0 or len<=0 // rewrite to "" - if ( (constantPos && pos.is_neg()) || (constantLen && len.is_neg()) ) { + if ( (constantPos && pos.is_neg()) || (constantLen && !len.is_pos()) ) { result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } // case 1.1: pos >= length(base) // rewrite to "" - if (constantBase && constantPos && pos.get_unsigned() >= s.length()) { + if (constantBase && constantPos && pos >= rational(s.length())) { result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } + constantPos &= pos.is_unsigned(); + constantLen &= len.is_unsigned(); + if (constantBase && constantPos && constantLen) { if (pos.get_unsigned() + len.get_unsigned() >= s.length()) { // case 2: pos+len goes past the end of the string unsigned _len = s.length() - pos.get_unsigned() + 1; result = m_util.str.mk_string(s.extract(pos.get_unsigned(), _len)); - return BR_DONE; } else { // case 3: pos+len still within string result = m_util.str.mk_string(s.extract(pos.get_unsigned(), len.get_unsigned())); - return BR_DONE; } + return BR_DONE; + } + + if (constantPos && constantLen) { + unsigned _pos = pos.get_unsigned(); + unsigned _len = len.get_unsigned(); + SASSERT(_len > 0); + expr_ref_vector as(m()), bs(m()); + m_util.str.get_concat(a, as); + for (unsigned i = 0; i < as.size() && _len > 0; ++i) { + if (m_util.str.is_unit(as[i].get())) { + if (_pos == 0) { + bs.push_back(as[i].get()); + --_len; + } + else { + --_pos; + } + } + else { + return BR_FAILED; + } + } + result = m_util.str.mk_concat(bs); + return BR_DONE; } return BR_FAILED; @@ -640,10 +667,33 @@ br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) { result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } - if (m_util.str.is_string(a, c) && r.is_unsigned() && r < rational(c.length())) { - result = m_util.str.mk_string(c.extract(r.get_unsigned(), 1)); + if (m_util.str.is_string(a, c)) { + if (r.is_unsigned() && r < rational(c.length())) { + result = m_util.str.mk_string(c.extract(r.get_unsigned(), 1)); + } + else { + result = m_util.str.mk_empty(m().get_sort(a)); + } return BR_DONE; - } + } + if (r.is_unsigned()) { + len = r.get_unsigned(); + expr_ref_vector as(m()); + m_util.str.get_concat(a, as); + for (unsigned i = 0; i < as.size(); ++i) { + if (m_util.str.is_unit(as[i].get())) { + if (len == 0) { + result = as[i].get(); + return BR_DONE; + } + --len; + } + else { + return BR_FAILED; + } + } + } + } return BR_FAILED; } diff --git a/src/cmd_context/eval_cmd.cpp b/src/cmd_context/eval_cmd.cpp index 94583001b..86078a13c 100644 --- a/src/cmd_context/eval_cmd.cpp +++ b/src/cmd_context/eval_cmd.cpp @@ -38,6 +38,7 @@ public: virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { model_evaluator::get_param_descrs(p); insert_timeout(p); + p.insert("model_index", CPK_UINT, "(default: 0) index of model from box optimization objective"); } virtual void prepare(cmd_context & ctx) { @@ -58,9 +59,15 @@ public: if (!ctx.is_model_available()) throw cmd_exception("model is not available"); model_ref md; + unsigned index = m_params.get_uint("model_index", 0); check_sat_result * last_result = ctx.get_check_sat_result(); SASSERT(last_result); - last_result->get_model(md); + if (index == 0 || !ctx.get_opt()) { + last_result->get_model(md); + } + else { + ctx.get_opt()->get_box_model(md, index); + } expr_ref r(ctx.m()); unsigned timeout = m_params.get_uint("timeout", UINT_MAX); unsigned rlimit = m_params.get_uint("rlimit", 0); From c4b26cd691ddfa303b36a6b5a37ac1948352e5be Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 Mar 2017 16:38:15 -0700 Subject: [PATCH 051/105] add bypass to allow recursive functions from API Signed-off-by: Nikolaj Bjorner --- src/api/api_quant.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/api/api_quant.cpp b/src/api/api_quant.cpp index bf64aa571..e87b9446f 100644 --- a/src/api/api_quant.cpp +++ b/src/api/api_quant.cpp @@ -69,14 +69,17 @@ extern "C" { } expr * const* ps = reinterpret_cast(patterns); expr * const* no_ps = reinterpret_cast(no_patterns); - pattern_validator v(mk_c(c)->m()); - for (unsigned i = 0; i < num_patterns; i++) { - if (!v(num_decls, ps[i], 0, 0)) { - SET_ERROR_CODE(Z3_INVALID_PATTERN); - return 0; + symbol qid = to_symbol(quantifier_id); + bool is_rec = mk_c(c)->m().rec_fun_qid() == qid; + if (!is_rec) { + pattern_validator v(mk_c(c)->m()); + for (unsigned i = 0; i < num_patterns; i++) { + if (!v(num_decls, ps[i], 0, 0)) { + SET_ERROR_CODE(Z3_INVALID_PATTERN); + return 0; + } } } - sort* const* ts = reinterpret_cast(sorts); svector names; for (unsigned i = 0; i < num_decls; ++i) { @@ -88,7 +91,7 @@ extern "C" { (0 != is_forall), names.size(), ts, names.c_ptr(), to_expr(body), weight, - to_symbol(quantifier_id), + qid, to_symbol(skolem_id), num_patterns, ps, num_no_patterns, no_ps From 7d35fcb17eb035ebd1b63ff3d62c7a4101fe7435 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 5 Apr 2017 19:42:02 +0100 Subject: [PATCH 052/105] Avoid null pointer warnings in justifications. --- src/smt/smt_justification.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/smt/smt_justification.cpp b/src/smt/smt_justification.cpp index c091f6973..7a9938cd0 100644 --- a/src/smt/smt_justification.cpp +++ b/src/smt/smt_justification.cpp @@ -246,13 +246,15 @@ namespace smt { simple_justification::simple_justification(region & r, unsigned num_lits, literal const * lits): m_num_literals(num_lits) { - m_literals = new (r) literal[num_lits]; - memcpy(m_literals, lits, sizeof(literal) * num_lits); + if (num_lits != 0) { + m_literals = new (r) literal[num_lits]; + memcpy(m_literals, lits, sizeof(literal) * num_lits); #ifdef Z3DEBUG - for (unsigned i = 0; i < num_lits; i++) { - SASSERT(lits[i] != null_literal); - } + for (unsigned i = 0; i < num_lits; i++) { + SASSERT(lits[i] != null_literal); + } #endif + } } void simple_justification::get_antecedents(conflict_resolution & cr) { From d3908857576b25f0ae781bdac41a2e6bdd42eb7c Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 6 Apr 2017 18:37:29 +0100 Subject: [PATCH 053/105] Added utility to compare quantifier instantiation profiles generated via smt.qi.profile=true --- contrib/qprofdiff/Makefile | 7 + contrib/qprofdiff/main.cpp | 238 ++++++++++++++++++++ contrib/qprofdiff/qprofdiff.vcxproj | 137 +++++++++++ contrib/qprofdiff/qprofdiff.vcxproj.filters | 22 ++ 4 files changed, 404 insertions(+) create mode 100644 contrib/qprofdiff/Makefile create mode 100644 contrib/qprofdiff/main.cpp create mode 100644 contrib/qprofdiff/qprofdiff.vcxproj create mode 100644 contrib/qprofdiff/qprofdiff.vcxproj.filters diff --git a/contrib/qprofdiff/Makefile b/contrib/qprofdiff/Makefile new file mode 100644 index 000000000..6b90bed51 --- /dev/null +++ b/contrib/qprofdiff/Makefile @@ -0,0 +1,7 @@ +qprofdiff: main.cpp + $(CXX) $(CXXFLAGS) main.cpp -o qprofdiff + +all: qprofdiff + +clean: + rm -f qprofdiff diff --git a/contrib/qprofdiff/main.cpp b/contrib/qprofdiff/main.cpp new file mode 100644 index 000000000..44c76b17d --- /dev/null +++ b/contrib/qprofdiff/main.cpp @@ -0,0 +1,238 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + main.cpp + +Abstract: + + Main file for qprofdiff. + +Author: + + Christoph M. Wintersteiger (cwinter) + +Revision History: +--*/ +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +set options; + +// Profile format: +// [quantifier_instances] qname : num_instances : max_generation : max_cost_s +const string prefix = "[quantifier_instances]"; +unsigned prefix_len = prefix.length(); +typedef struct { unsigned num_instances, max_generation, max_cost; } map_entry; + +string trim(string str) { + size_t linx = str.find_first_not_of(' '); + size_t rinx = str.find_last_not_of(' '); + return str.substr(linx, rinx-linx+1); +} + +int parse(string const & filename, map & data) { + ifstream fs(filename.c_str()); + + if (!fs.is_open()) { + cout << "Can't open file '" << filename << "'" << endl; + return ENOENT; + } + + string qid; + string tokens[4]; + unsigned cur_token = 0; + + while (!fs.eof()) { + string line; + getline(fs, line); + + if (line.substr(0, prefix_len) == prefix) { + line = trim(line.substr(prefix_len)); + size_t from = 0, ti = 0; + for (size_t inx = line.find(':', from); + inx != string::npos; + inx = line.find(':', from)) { + tokens[ti] = trim(line.substr(from, inx-from)); + from = inx+1; + ti++; + } + if (from != line.length() && ti < 4) + tokens[ti] = trim(line.substr(from)); + + qid = tokens[0]; + + if (data.find(qid) == data.end()) { + map_entry & entry = data[qid]; + entry.num_instances = entry.max_generation = entry.max_cost = 0; + } + + map_entry & entry = data[qid]; + entry.num_instances += atoi(tokens[1].c_str()); + entry.max_generation = std::max(entry.max_generation, (unsigned)atoi(tokens[2].c_str())); + entry.max_cost = max(entry.max_cost, (unsigned)atoi(tokens[3].c_str())); + } + } + + fs.close(); + + return 0; +} + +void display_data(map & data) { + for (map::iterator it = data.begin(); + it != data.end(); + it++) + cout << it->first << ": " << it->second.num_instances << + ", " << it->second.max_generation << + ", " << it->second.max_cost << endl; +} + + +typedef struct { int d_num_instances, d_max_generation, d_max_cost; } diff_entry; +typedef struct { string qid; diff_entry e; } diff_item; + +bool diff_item_lt_inst(diff_item const & l, diff_item const & r) { + return l.e.d_num_instances < r.e.d_num_instances; +} + +bool diff_item_lt_gen(diff_item const & l, diff_item const & r) { + return l.e.d_max_generation< r.e.d_max_generation; +} + +bool diff_item_lt_cost(diff_item const & l, diff_item const & r) { + return l.e.d_max_cost < r.e.d_max_cost; +} + +void display_indicator(int const & delta, bool suppress_unchanged) { + if (delta < 0) + cout << "+ "; + else if (delta > 0) + cout << "- "; + else if (delta == 0 && !suppress_unchanged) + cout << "= "; +} + +void diff(map & left, map & right) { + map diff_data; + + for (map::const_iterator lit = left.begin(); + lit != left.end(); + lit++) { + string const & qid = lit->first; + map_entry const & lentry = lit->second; + + map::const_iterator rit = right.find(qid); + if (rit != right.end()) { + map_entry const & rentry = rit->second; + + diff_entry & de = diff_data[qid]; + de.d_num_instances = lentry.num_instances - rentry.num_instances; + de.d_max_generation = lentry.max_generation - rentry.max_generation; + de.d_max_cost = lentry.max_cost - rentry.max_cost; + } + } + + vector flat_data; + for (map::const_iterator it = diff_data.begin(); + it != diff_data.end(); + it++) { + flat_data.push_back(diff_item()); + flat_data.back().qid = it->first; + flat_data.back().e = it->second; + } + + stable_sort(flat_data.begin(), flat_data.end(), + options.find("-si") != options.end() ? diff_item_lt_inst : + options.find("-sg") != options.end() ? diff_item_lt_gen : + options.find("-sc") != options.end() ? diff_item_lt_cost : + diff_item_lt_inst); + + bool suppress_unchanged = options.find("-n") != options.end(); + + for (vector::const_iterator it = flat_data.begin(); + it != flat_data.end(); + it++) { + diff_item const & d = *it; + string const & qid = d.qid; + diff_entry const & e = d.e; + + int const & delta = + (options.find("-si") != options.end()) ? e.d_num_instances : + (options.find("-sg") != options.end()) ? e.d_max_generation : + (options.find("-sc") != options.end()) ? e.d_max_cost : + e.d_num_instances; + + display_indicator(delta, suppress_unchanged); + + if (delta != 0 || !suppress_unchanged) + cout << qid << " (" << + (e.d_num_instances > 0 ? "" : "+") << -e.d_num_instances << " inst., " << + (e.d_max_generation > 0 ? "" : "+") << -e.d_max_generation << " max. gen., " << + (e.d_max_cost > 0 ? "" : "+") << -e.d_max_cost << " max. cost)" << + endl; + } +} + +void display_usage() { + cout << "Usage: qprofdiff [options] " << endl; + cout << "Options:" << endl; + cout << " -n Suppress unchanged items" << endl; + cout << " -si Sort by difference in number of instances" << endl; + cout << " -sg Sort by difference in max. generation" << endl; + cout << " -sc Sort by difference in max. cost" << endl; +} + +int main(int argc, char ** argv) { + char * filename1 = 0; + char * filename2 = 0; + + for (int i = 1; i < argc; i++) { + int len = string(argv[i]).length(); + if (len > 1 && argv[i][0] == '-') { + options.insert(string(argv[i])); + } + else if (filename1 == 0) + filename1 = argv[i]; + else if (filename2 == 0) + filename2 = argv[i]; + else { + cout << "Invalid argument: " << argv[i] << endl << endl; + display_usage(); + return EINVAL; + } + } + + if (filename1 == 0 || filename2 == 0) { + cout << "Two filenames required." << endl << endl; + display_usage(); + return EINVAL; + } + + + cout << "Comparing " << filename1 << " to " << filename2 << endl; + + map data1, data2; + + int r = parse(filename1, data1); + if (r != 0) return r; + r = parse(filename2, data2); + if (r != 0) return r; + + // display_data(data1); + // display_data(data2); + + diff(data1, data2); + + return 0; +} \ No newline at end of file diff --git a/contrib/qprofdiff/qprofdiff.vcxproj b/contrib/qprofdiff/qprofdiff.vcxproj new file mode 100644 index 000000000..b6584e126 --- /dev/null +++ b/contrib/qprofdiff/qprofdiff.vcxproj @@ -0,0 +1,137 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {96E7E3EF-4162-474D-BD32-C702632AAF2B} + qprofdiff + 8.1 + + + + Application + true + v141 + NotSet + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + $(IncludePath) + $(LibraryPath) + + + $(IncludePath) + $(LibraryPath) + + + + Level3 + Disabled + true + MultiThreadedDebugDLL + ..\..\src\util;%(AdditionalIncludeDirectories) + + + ProgramDatabase + + + $(LibraryPath);%(AdditionalLibraryDirectories) + + + + + Level3 + Disabled + true + ..\..\src\util;%(AdditionalIncludeDirectories) + + + + + Level3 + MaxSpeed + true + true + true + ..\..\src\util;%(AdditionalIncludeDirectories) + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + ..\..\src\util;%(AdditionalIncludeDirectories) + + + true + true + + + + + + + + + \ No newline at end of file diff --git a/contrib/qprofdiff/qprofdiff.vcxproj.filters b/contrib/qprofdiff/qprofdiff.vcxproj.filters new file mode 100644 index 000000000..0d8d9e457 --- /dev/null +++ b/contrib/qprofdiff/qprofdiff.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file From f3c990d356f477cecf65dfc795041e6f3e0ab126 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 7 Apr 2017 17:54:28 +0100 Subject: [PATCH 054/105] Fixes for qprofdiff --- contrib/qprofdiff/main.cpp | 111 +++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 34 deletions(-) diff --git a/contrib/qprofdiff/main.cpp b/contrib/qprofdiff/main.cpp index 44c76b17d..d6a403cf2 100644 --- a/contrib/qprofdiff/main.cpp +++ b/contrib/qprofdiff/main.cpp @@ -78,9 +78,9 @@ int parse(string const & filename, map & data) { } map_entry & entry = data[qid]; - entry.num_instances += atoi(tokens[1].c_str()); - entry.max_generation = std::max(entry.max_generation, (unsigned)atoi(tokens[2].c_str())); - entry.max_cost = max(entry.max_cost, (unsigned)atoi(tokens[3].c_str())); + entry.num_instances = atoi(tokens[1].c_str()); + entry.max_generation = (unsigned)atoi(tokens[2].c_str()); + entry.max_cost = (unsigned)atoi(tokens[3].c_str()); } } @@ -94,33 +94,56 @@ void display_data(map & data) { it != data.end(); it++) cout << it->first << ": " << it->second.num_instances << - ", " << it->second.max_generation << - ", " << it->second.max_cost << endl; + ", " << it->second.max_generation << + ", " << it->second.max_cost << endl; } -typedef struct { int d_num_instances, d_max_generation, d_max_cost; } diff_entry; +typedef struct { + int d_num_instances, d_max_generation, d_max_cost; + bool left_only, right_only; +} diff_entry; + typedef struct { string qid; diff_entry e; } diff_item; -bool diff_item_lt_inst(diff_item const & l, diff_item const & r) { - return l.e.d_num_instances < r.e.d_num_instances; +#define DIFF_LT(X) bool diff_item_lt_ ## X (diff_item const & l, diff_item const & r) { \ + return \ + l.e.left_only ? (r.e.left_only ? l.qid < r.qid : false) : \ + l.e.right_only ? (r.e.right_only ? l.qid < r.qid : true) : \ + r.e.right_only ? false : \ + r.e.left_only ? true : \ + l.e.d_ ## X < r.e.d_ ## X ; \ } -bool diff_item_lt_gen(diff_item const & l, diff_item const & r) { - return l.e.d_max_generation< r.e.d_max_generation; -} +DIFF_LT(num_instances) +DIFF_LT(max_generation) +DIFF_LT(max_cost) -bool diff_item_lt_cost(diff_item const & l, diff_item const & r) { - return l.e.d_max_cost < r.e.d_max_cost; -} +int indicate(diff_entry const & e, bool suppress_unchanged) { + if (e.left_only) { + cout << "< "; + return INT_MIN; + } + else if (e.right_only) { + cout << "> "; + return INT_MAX; + } + else { + int const & delta = + (options.find("-si") != options.end()) ? e.d_num_instances : + (options.find("-sg") != options.end()) ? e.d_max_generation : + (options.find("-sc") != options.end()) ? e.d_max_cost : + e.d_num_instances; -void display_indicator(int const & delta, bool suppress_unchanged) { - if (delta < 0) - cout << "+ "; - else if (delta > 0) - cout << "- "; - else if (delta == 0 && !suppress_unchanged) - cout << "= "; + if (delta < 0) + cout << "+ "; + else if (delta > 0) + cout << "- "; + else if (delta == 0 && !suppress_unchanged) + cout << "= "; + + return delta; + } } void diff(map & left, map & right) { @@ -135,12 +158,38 @@ void diff(map & left, map & right) { map::const_iterator rit = right.find(qid); if (rit != right.end()) { map_entry const & rentry = rit->second; - diff_entry & de = diff_data[qid]; + + de.left_only = de.right_only = false; de.d_num_instances = lentry.num_instances - rentry.num_instances; de.d_max_generation = lentry.max_generation - rentry.max_generation; de.d_max_cost = lentry.max_cost - rentry.max_cost; } + else { + diff_entry & de = diff_data[qid]; + de.left_only = true; + de.right_only = false; + de.d_num_instances = lentry.num_instances; + de.d_max_generation = lentry.max_generation; + de.d_max_cost = lentry.max_cost; + } + } + + for (map::const_iterator rit = right.begin(); + rit != right.end(); + rit++) { + string const & qid = rit->first; + map_entry const & rentry = rit->second; + + map::const_iterator lit = left.find(qid); + if (lit == left.end()) { + diff_entry & de = diff_data[qid]; + de.left_only = false; + de.right_only = true; + de.d_num_instances = -(int)rentry.num_instances; + de.d_max_generation = -(int)rentry.max_generation; + de.d_max_cost = -(int)rentry.max_cost; + } } vector flat_data; @@ -153,10 +202,10 @@ void diff(map & left, map & right) { } stable_sort(flat_data.begin(), flat_data.end(), - options.find("-si") != options.end() ? diff_item_lt_inst : - options.find("-sg") != options.end() ? diff_item_lt_gen : - options.find("-sc") != options.end() ? diff_item_lt_cost : - diff_item_lt_inst); + options.find("-si") != options.end() ? diff_item_lt_num_instances: + options.find("-sg") != options.end() ? diff_item_lt_max_generation : + options.find("-sc") != options.end() ? diff_item_lt_max_cost : + diff_item_lt_num_instances); bool suppress_unchanged = options.find("-n") != options.end(); @@ -167,15 +216,9 @@ void diff(map & left, map & right) { string const & qid = d.qid; diff_entry const & e = d.e; - int const & delta = - (options.find("-si") != options.end()) ? e.d_num_instances : - (options.find("-sg") != options.end()) ? e.d_max_generation : - (options.find("-sc") != options.end()) ? e.d_max_cost : - e.d_num_instances; + int delta = indicate(e, suppress_unchanged); - display_indicator(delta, suppress_unchanged); - - if (delta != 0 || !suppress_unchanged) + if (!(delta == 0 && suppress_unchanged)) cout << qid << " (" << (e.d_num_instances > 0 ? "" : "+") << -e.d_num_instances << " inst., " << (e.d_max_generation > 0 ? "" : "+") << -e.d_max_generation << " max. gen., " << From 23f4a0c332d1b88708c317a116d1912b029bc0b6 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 7 Apr 2017 17:57:49 +0100 Subject: [PATCH 055/105] Build fix for qprofdiff --- contrib/qprofdiff/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/qprofdiff/main.cpp b/contrib/qprofdiff/main.cpp index d6a403cf2..661e976ec 100644 --- a/contrib/qprofdiff/main.cpp +++ b/contrib/qprofdiff/main.cpp @@ -16,6 +16,7 @@ Author: Revision History: --*/ #include +#include #include #include @@ -278,4 +279,4 @@ int main(int argc, char ** argv) { diff(data1, data2); return 0; -} \ No newline at end of file +} From 9a757ffffe242d62c2f5802955b72f95963c4302 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 7 Apr 2017 18:09:35 +0100 Subject: [PATCH 056/105] Result ordering fix for qprofdiff --- contrib/qprofdiff/main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/contrib/qprofdiff/main.cpp b/contrib/qprofdiff/main.cpp index 661e976ec..58d21b77d 100644 --- a/contrib/qprofdiff/main.cpp +++ b/contrib/qprofdiff/main.cpp @@ -108,12 +108,14 @@ typedef struct { typedef struct { string qid; diff_entry e; } diff_item; #define DIFF_LT(X) bool diff_item_lt_ ## X (diff_item const & l, diff_item const & r) { \ + int l_lt_r = l.e.d_ ## X < r.e.d_ ## X; \ + int l_eq_r = l.e.d_ ## X == r.e.d_ ## X; \ return \ - l.e.left_only ? (r.e.left_only ? l.qid < r.qid : false) : \ - l.e.right_only ? (r.e.right_only ? l.qid < r.qid : true) : \ + l.e.left_only ? (r.e.left_only ? ((l_eq_r) ? l.qid < r.qid : l_lt_r) : false) : \ + l.e.right_only ? (r.e.right_only ? ((l_eq_r) ? l.qid < r.qid : l_lt_r) : true) : \ r.e.right_only ? false : \ r.e.left_only ? true : \ - l.e.d_ ## X < r.e.d_ ## X ; \ + l_lt_r; \ } DIFF_LT(num_instances) From 27a17588575d82774e2f812bbe2735084b43e855 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 7 Apr 2017 21:19:20 +0100 Subject: [PATCH 057/105] Added rewriter.ignore_patterns_on_ground_qbody option to disable simplification of quantifiers that have their universals appear only in patterns, but otherwise have a ground body. --- src/ast/normal_forms/defined_names.cpp | 36 +- src/ast/rewriter/der.cpp | 62 +- src/ast/rewriter/rewriter_params.pyg | 3 +- src/ast/rewriter/th_rewriter.cpp | 72 +-- src/ast/rewriter/var_subst.cpp | 14 +- src/ast/rewriter/var_subst.h | 21 +- src/ast/simplifier/distribute_forall.cpp | 22 +- src/ast/simplifier/elim_bounds.cpp | 14 +- src/ast/simplifier/simplifier.cpp | 94 +-- src/cmd_context/extra_cmds/dbg_cmds.cpp | 43 +- src/duality/duality_wrapper.cpp | 82 +-- src/muz/base/dl_rule.cpp | 82 +-- src/muz/pdr/pdr_context.cpp | 4 +- src/muz/tab/tab_context.cpp | 206 +++---- src/qe/qe_lite.cpp | 575 ++++++++++--------- src/qe/qe_lite.h | 10 +- src/tactic/bv/elim_small_bv_tactic.cpp | 9 +- src/tactic/core/distribute_forall_tactic.cpp | 32 +- src/tactic/ufbv/ufbv_rewriter.cpp | 190 +++--- 19 files changed, 795 insertions(+), 776 deletions(-) diff --git a/src/ast/normal_forms/defined_names.cpp b/src/ast/normal_forms/defined_names.cpp index c1d9b36a5..1ac2049ac 100644 --- a/src/ast/normal_forms/defined_names.cpp +++ b/src/ast/normal_forms/defined_names.cpp @@ -28,7 +28,7 @@ struct defined_names::impl { typedef obj_map expr2proof; ast_manager & m_manager; symbol m_z3name; - + /** \brief Mapping from expressions to their names. A name is an application. If the expression does not have free variables, then the name is just a constant. @@ -38,25 +38,25 @@ struct defined_names::impl { \brief Mapping from expressions to the apply-def proof. That is, for each expression e, m_expr2proof[e] is the proof e and m_expr2name[2] are observ. equivalent. - + This mapping is not used if proof production is disabled. */ expr2proof m_expr2proof; - + /** \brief Domain of m_expr2name. It is used to keep the expressions alive and for backtracking */ - expr_ref_vector m_exprs; + expr_ref_vector m_exprs; expr_ref_vector m_names; //!< Range of m_expr2name. It is used to keep the names alive. proof_ref_vector m_apply_proofs; //!< Range of m_expr2proof. It is used to keep the def-intro proofs alive. - - + + unsigned_vector m_lims; //!< Backtracking support. - + impl(ast_manager & m, char const * prefix); virtual ~impl(); - + app * gen_name(expr * e, sort_ref_buffer & var_sorts, buffer & var_names); void cache_new_name(expr * e, app * name); void cache_new_name_intro_proof(expr * e, proof * pr); @@ -106,7 +106,7 @@ app * defined_names::impl::gen_name(expr * e, sort_ref_buffer & var_sorts, buffe for (unsigned i = 0; i < num_vars; i++) { sort * s = uv.get(i); if (s) { - domain.push_back(s); + domain.push_back(s); new_args.push_back(m_manager.mk_var(i, s)); var_sorts.push_back(s); } @@ -162,7 +162,7 @@ void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer var_names; - + n = gen_name(e, var_sorts, var_names); cache_new_name(e, n); - + TRACE("mk_definition_bug", tout << "name: " << mk_ismt2_pp(n, m_manager) << "\n";); // variables are in reverse order in quantifiers std::reverse(var_sorts.c_ptr(), var_sorts.c_ptr() + var_sorts.size()); std::reverse(var_names.c_ptr(), var_names.c_ptr() + var_names.size()); - + mk_definition(e, n, var_sorts, var_names, new_def); - + TRACE("mk_definition_bug", tout << "new_def:\n" << mk_ismt2_pp(new_def, m_manager) << "\n";); - + if (m_manager.proofs_enabled()) { new_def_pr = m_manager.mk_def_intro(new_def); pr = m_manager.mk_apply_def(e, n, new_def_pr); @@ -311,11 +311,11 @@ void defined_names::reset() { m_pos_impl->reset(); } -unsigned defined_names::get_num_names() const { +unsigned defined_names::get_num_names() const { return m_impl->get_num_names() + m_pos_impl->get_num_names(); } -func_decl * defined_names::get_name_decl(unsigned i) const { +func_decl * defined_names::get_name_decl(unsigned i) const { SASSERT(i < get_num_names()); unsigned n1 = m_impl->get_num_names(); return i < n1 ? m_impl->get_name_decl(i) : m_pos_impl->get_name_decl(i - n1); diff --git a/src/ast/rewriter/der.cpp b/src/ast/rewriter/der.cpp index 83ed94ece..aef5d8ddd 100644 --- a/src/ast/rewriter/der.cpp +++ b/src/ast/rewriter/der.cpp @@ -36,7 +36,7 @@ static bool is_neg_var(ast_manager & m, expr * e, unsigned num_decls) { /** \brief Return true if \c e is of the form (not (= VAR t)) or (not (iff VAR t)) or (iff VAR t) or (iff (not VAR) t) or (VAR IDX) or (not (VAR IDX)). - The last case can be viewed + The last case can be viewed */ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { // (not (= VAR t)) and (not (iff VAR t)) cases @@ -49,7 +49,7 @@ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { return false; if (!is_var(lhs, num_decls)) std::swap(lhs, rhs); - SASSERT(is_var(lhs, num_decls)); + SASSERT(is_var(lhs, num_decls)); // Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles... // if (occurs(lhs, rhs)) { // return false; @@ -67,7 +67,7 @@ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { if (is_var(lhs, num_decls) || is_var(rhs, num_decls)) { if (!is_var(lhs, num_decls)) std::swap(lhs, rhs); - SASSERT(is_var(lhs, num_decls)); + SASSERT(is_var(lhs, num_decls)); // Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles... // if (occurs(lhs, rhs)) { // return false; @@ -83,11 +83,11 @@ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { if (!is_neg_var(m_manager, lhs, num_decls)) std::swap(lhs, rhs); SASSERT(is_neg_var(m_manager, lhs, num_decls)); - expr * lhs_var = to_app(lhs)->get_arg(0); + expr * lhs_var = to_app(lhs)->get_arg(0); // Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles... // if (occurs(lhs_var, rhs)) { // return false; - // } + // } v = to_var(lhs_var); t = rhs; TRACE("der", tout << mk_pp(e, m_manager) << "\n";); @@ -134,11 +134,11 @@ void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) { pr = m_manager.mk_transitivity(pr, curr_pr); } } while (q != r && is_quantifier(r)); - + // Eliminate variables that have become unused if (reduced && is_forall(r)) { quantifier * q = to_quantifier(r); - elim_unused_vars(m_manager, q, r); + elim_unused_vars(m_manager, q, params_ref(), r); if (m_manager.proofs_enabled()) { proof * p1 = m_manager.mk_elim_unused_vars(q, r); pr = m_manager.mk_transitivity(pr, p1); @@ -153,24 +153,24 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { r = q; return; } - + expr * e = q->get_expr(); unsigned num_decls = q->get_num_decls(); var * v = 0; - expr_ref t(m_manager); + expr_ref t(m_manager); if (m_manager.is_or(e)) { unsigned num_args = to_app(e)->get_num_args(); unsigned i = 0; unsigned diseq_count = 0; unsigned largest_vinx = 0; - + m_map.reset(); m_pos2var.reset(); m_inx2var.reset(); - + m_pos2var.reserve(num_args, -1); - + // Find all disequalities for (; i < num_args; i++) { if (is_var_diseq(to_app(e)->get_arg(i), num_decls, v, t)) { @@ -192,7 +192,7 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { get_elimination_order(); SASSERT(m_order.size() <= diseq_count); // some might be missing because of cycles - if (!m_order.empty()) { + if (!m_order.empty()) { create_substitution(largest_vinx + 1); apply_substitution(q, r); } @@ -202,22 +202,22 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { r = q; } } - // Remark: get_elimination_order/top-sort checks for cycles, but it is not invoked for unit clauses. + // Remark: get_elimination_order/top-sort checks for cycles, but it is not invoked for unit clauses. // So, we must perform a occurs check here. else if (is_var_diseq(e, num_decls, v, t) && !occurs(v, t)) { r = m_manager.mk_false(); } - else + else r = q; - + if (m_manager.proofs_enabled()) { pr = r == q ? 0 : m_manager.mk_der(q, r); - } + } } void der_sort_vars(ptr_vector & vars, ptr_vector & definitions, unsigned_vector & order) { order.reset(); - + // eliminate self loops, and definitions containing quantifiers. bool found = false; for (unsigned i = 0; i < definitions.size(); i++) { @@ -228,7 +228,7 @@ void der_sort_vars(ptr_vector & vars, ptr_vector & definitions, unsig else found = true; // found at least one candidate } - + if (!found) return; @@ -329,14 +329,14 @@ void der::get_elimination_order() { // der::top_sort ts(m_manager); der_sort_vars(m_inx2var, m_map, m_order); - TRACE("der", + TRACE("der", tout << "Elimination m_order:" << std::endl; for(unsigned i=0; iget_expr(); - unsigned num_args=to_app(e)->get_num_args(); - + unsigned num_args=to_app(e)->get_num_args(); + // get a new expression m_new_args.reset(); for(unsigned i = 0; i < num_args; i++) { int x = m_pos2var[i]; - if (x != -1 && m_map[x] != 0) + if (x != -1 && m_map[x] != 0) continue; // this is a disequality with definition (vanishes) - + m_new_args.push_back(to_app(e)->get_arg(i)); } unsigned sz = m_new_args.size(); expr_ref t(m_manager); t = (sz == 1) ? m_new_args[0] : m_manager.mk_or(sz, m_new_args.c_ptr()); - expr_ref new_e(m_manager); + expr_ref new_e(m_manager); m_subst(t, m_subst_map.size(), m_subst_map.c_ptr(), new_e); - + // don't forget to update the quantifier patterns expr_ref_buffer new_patterns(m_manager); expr_ref_buffer new_no_patterns(m_manager); @@ -392,7 +392,7 @@ void der::apply_substitution(quantifier * q, expr_ref & r) { new_no_patterns.push_back(new_nopat); } - r = m_manager.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), + r = m_manager.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), new_no_patterns.size(), new_no_patterns.c_ptr(), new_e); } @@ -404,9 +404,9 @@ struct der_rewriter_cfg : public default_rewriter_cfg { ast_manager & m() const { return m_der.m(); } - bool reduce_quantifier(quantifier * old_q, - expr * new_body, - expr * const * new_patterns, + 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) { diff --git a/src/ast/rewriter/rewriter_params.pyg b/src/ast/rewriter/rewriter_params.pyg index 5bd17f556..06500086a 100644 --- a/src/ast/rewriter/rewriter_params.pyg +++ b/src/ast/rewriter/rewriter_params.pyg @@ -8,5 +8,6 @@ def_module_params('rewriter', ("push_ite_bv", BOOL, False, "push if-then-else over bit-vector terms."), ("pull_cheap_ite", BOOL, False, "pull if-then-else terms when cheap."), ("bv_ineq_consistency_test_max", UINT, 0, "max size of conjunctions on which to perform consistency test based on inequalities on bitvectors."), - ("cache_all", BOOL, False, "cache all intermediate results."))) + ("cache_all", BOOL, False, "cache all intermediate results."), + ("ignore_patterns_on_ground_qbody", BOOL, True, "ignores patterns on quantifiers that don't mention their bound variables."))) diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 0c57ea609..b561e02fc 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -54,6 +54,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { bool m_cache_all; bool m_push_ite_arith; bool m_push_ite_bv; + bool m_ignore_patterns_on_ground_qbody; // substitution support expr_dependency_ref m_used_dependencies; // set of dependencies of used substitutions @@ -70,8 +71,9 @@ struct th_rewriter_cfg : public default_rewriter_cfg { m_cache_all = p.cache_all(); m_push_ite_arith = p.push_ite_arith(); m_push_ite_bv = p.push_ite_bv(); + m_ignore_patterns_on_ground_qbody = p.ignore_patterns_on_ground_qbody(); } - + void updt_params(params_ref const & p) { m_b_rw.updt_params(p); m_a_rw.updt_params(p); @@ -82,7 +84,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { updt_local_params(p); } - bool flat_assoc(func_decl * f) const { + bool flat_assoc(func_decl * f) const { if (!m_flat) return false; family_id fid = f->get_family_id(); if (fid == null_family_id) @@ -98,10 +100,10 @@ struct th_rewriter_cfg : public default_rewriter_cfg { } bool rewrite_patterns() const { return false; } - + bool cache_all_results() const { return m_cache_all; } - bool max_steps_exceeded(unsigned num_steps) const { + bool max_steps_exceeded(unsigned num_steps) const { cooperate("simplifier"); if (memory::get_allocation_size() > m_max_memory) throw rewriter_exception(Z3_MAX_MEMORY_MSG); @@ -179,13 +181,13 @@ struct th_rewriter_cfg : public default_rewriter_cfg { st = m_ar_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_seq_rw.get_fid()) st = m_seq_rw.mk_eq_core(args[0], args[1], result); - + if (st != BR_FAILED) return st; } if (k == OP_EQ || k == OP_IFF) { SASSERT(num == 2); - st = apply_tamagotchi(args[0], args[1], result); + st = apply_tamagotchi(args[0], args[1], result); if (st != BR_FAILED) return st; } @@ -239,13 +241,13 @@ struct th_rewriter_cfg : public default_rewriter_cfg { } else { if (SWAP) { - result = m().mk_ite(ite->get_arg(0), + result = m().mk_ite(ite->get_arg(0), m().mk_app(p, value, ite->get_arg(1)), m().mk_app(p, value, ite->get_arg(2))); return BR_REWRITE2; } else { - result = m().mk_ite(ite->get_arg(0), + result = m().mk_ite(ite->get_arg(0), m().mk_app(p, ite->get_arg(1), value), m().mk_app(p, ite->get_arg(2), value)); return BR_REWRITE2; @@ -257,7 +259,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { // ite-value-tree := (ite c ) // subtree := value // | (ite c ) - // + // bool is_ite_value_tree(expr * t) { if (!m().is_ite(t)) return false; @@ -281,7 +283,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { } return true; } - + br_status pull_ite(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { if (num == 2 && m().is_bool(f->get_range()) && !m().is_bool(args[0])) { if (m().is_ite(args[0])) { @@ -325,7 +327,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { if (!is_app(t)) return false; family_id fid = to_app(t)->get_family_id(); - return ((fid == m_a_rw.get_fid() && m_push_ite_arith) || + return ((fid == m_a_rw.get_fid() && m_push_ite_arith) || (fid == m_bv_rw.get_fid() && m_push_ite_bv)); } @@ -349,7 +351,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { } return false; } - + /** \brief Try to "unify" t1 and t2 Examples @@ -463,7 +465,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { } // terms matched... bool is_int = m_a_util.is_int(t1); - if (!new_t1) + if (!new_t1) new_t1 = m_a_util.mk_numeral(rational(0), is_int); if (!new_t2) new_t2 = m_a_util.mk_numeral(rational(0), is_int); @@ -476,7 +478,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { args.push_back(arg); } SASSERT(!args.empty()); - if (args.size() == 1) + if (args.size() == 1) c = args[0]; else c = m_a_util.mk_add(args.size(), args.c_ptr()); @@ -518,7 +520,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { // Apply transformations of the form // - // (ite c (+ k1 a) (+ k2 a)) --> (+ (ite c k1 k2) a) + // (ite c (+ k1 a) (+ k2 a)) --> (+ (ite c k1 k2) a) // (ite c (* k1 a) (* k2 a)) --> (* (ite c k1 k2) a) // // These transformations are useful for bit-vector problems, since @@ -536,7 +538,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { if (unify(t, e, f_prime, new_t, new_e, common, first)) { if (first) result = m().mk_app(f_prime, common, m().mk_ite(c, new_t, new_e)); - else + else result = m().mk_app(f_prime, m().mk_ite(c, new_t, new_e), common); return BR_DONE; } @@ -558,7 +560,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { result_pr = 0; br_status st = reduce_app_core(f, num, args, result); if (st != BR_DONE && st != BR_FAILED) { - CTRACE("th_rewriter_step", st != BR_FAILED, + CTRACE("th_rewriter_step", st != BR_FAILED, tout << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n"; tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";); @@ -576,7 +578,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { else st = pull_ite(result); } - CTRACE("th_rewriter_step", st != BR_FAILED, + CTRACE("th_rewriter_step", st != BR_FAILED, tout << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n"; tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";); @@ -593,28 +595,28 @@ struct th_rewriter_cfg : public default_rewriter_cfg { } - bool reduce_quantifier(quantifier * old_q, - expr * new_body, - expr * const * new_patterns, + 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) { quantifier_ref q1(m()); proof * p1 = 0; - if (is_quantifier(new_body) && + if (is_quantifier(new_body) && to_quantifier(new_body)->is_forall() == old_q->is_forall() && !old_q->has_patterns() && !to_quantifier(new_body)->has_patterns()) { - + quantifier * nested_q = to_quantifier(new_body); - + ptr_buffer sorts; - buffer names; + buffer names; sorts.append(old_q->get_num_decls(), old_q->get_decl_sorts()); names.append(old_q->get_num_decls(), old_q->get_decl_names()); sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts()); names.append(nested_q->get_num_decls(), nested_q->get_decl_names()); - + q1 = m().mk_quantifier(old_q->is_forall(), sorts.size(), sorts.c_ptr(), @@ -624,9 +626,9 @@ struct th_rewriter_cfg : public default_rewriter_cfg { old_q->get_qid(), old_q->get_skid(), 0, 0, 0, 0); - + SASSERT(is_well_sorted(m(), q1)); - + if (m().proofs_enabled()) { SASSERT(old_q->get_expr() == new_body); p1 = m().mk_pull_quant(old_q, q1); @@ -635,24 +637,24 @@ struct th_rewriter_cfg : public default_rewriter_cfg { else { ptr_buffer new_patterns_buf; ptr_buffer new_no_patterns_buf; - + new_patterns_buf.append(old_q->get_num_patterns(), new_patterns); new_no_patterns_buf.append(old_q->get_num_no_patterns(), new_no_patterns); remove_duplicates(new_patterns_buf); remove_duplicates(new_no_patterns_buf); - - q1 = m().update_quantifier(old_q, + + q1 = m().update_quantifier(old_q, new_patterns_buf.size(), new_patterns_buf.c_ptr(), new_no_patterns_buf.size(), new_no_patterns_buf.c_ptr(), new_body); TRACE("reduce_quantifier", tout << mk_ismt2_pp(old_q, m()) << "\n----->\n" << mk_ismt2_pp(q1, m()) << "\n";); SASSERT(is_well_sorted(m(), q1)); } - - elim_unused_vars(m(), q1, result); + + elim_unused_vars(m(), q1, params_ref(), result); TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << mk_ismt2_pp(result, m()) << "\n";); - + result_pr = 0; if (m().proofs_enabled()) { proof * p2 = 0; @@ -758,7 +760,7 @@ unsigned th_rewriter::get_num_steps() const { void th_rewriter::cleanup() { ast_manager & m = m_imp->m(); dealloc(m_imp); - m_imp = alloc(imp, m, m_params); + m_imp = alloc(imp, m, m_params); } void th_rewriter::reset() { diff --git a/src/ast/rewriter/var_subst.cpp b/src/ast/rewriter/var_subst.cpp index 37b335a9d..fd290c8fe 100644 --- a/src/ast/rewriter/var_subst.cpp +++ b/src/ast/rewriter/var_subst.cpp @@ -39,10 +39,16 @@ void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, exp tout << mk_ismt2_pp(result, m_reducer.m()) << "\n";); } +unused_vars_eliminator::unused_vars_eliminator(ast_manager & m, params_ref const & params) : + m(m), m_subst(m), m_params(params) +{ + m_ignore_patterns_on_ground_qbody = m_params.get_bool("ignore_patterns_on_ground_qbody", true); +} + 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. + if (m_ignore_patterns_on_ground_qbody && is_ground(q->get_expr())) { + // Ignore patterns if the body is a ground formula. result = q->get_expr(); return; } @@ -146,8 +152,8 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { SASSERT(is_well_sorted(m, result)); } -void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { - unused_vars_eliminator el(m); +void elim_unused_vars(ast_manager & m, quantifier * q, params_ref const & params, expr_ref & result) { + unused_vars_eliminator el(m, params); el(q, result); } diff --git a/src/ast/rewriter/var_subst.h b/src/ast/rewriter/var_subst.h index 9d04cebe3..21aa58399 100644 --- a/src/ast/rewriter/var_subst.h +++ b/src/ast/rewriter/var_subst.h @@ -21,6 +21,7 @@ Notes: #include"rewriter.h" #include"used_vars.h" +#include"params.h" /** \brief Alias for var_shifter class. @@ -31,7 +32,7 @@ typedef var_shifter shift_vars; \brief Variable substitution functor. It substitutes variables by expressions. The expressions may contain variables. */ -class var_subst { +class var_subst { beta_reducer m_reducer; bool m_std_order; public: @@ -39,7 +40,7 @@ public: bool std_order() const { return m_std_order; } /** - When std_order() == true, + When std_order() == true, I'm using the same standard used in quantifier instantiation. (VAR 0) is stored in the last position of the array. ... @@ -55,15 +56,17 @@ 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; + ast_manager & m; + var_subst m_subst; + used_vars m_used; + params_ref m_params; + bool m_ignore_patterns_on_ground_qbody; public: - unused_vars_eliminator(ast_manager& m): m(m), m_subst(m) {} + unused_vars_eliminator(ast_manager & m, params_ref const & params); void operator()(quantifier* q, expr_ref& r); }; -void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & r); +void elim_unused_vars(ast_manager & m, quantifier * q, params_ref const & params, expr_ref & r); /** \brief Instantiate quantifier q using the given exprs. @@ -86,7 +89,7 @@ class expr_free_vars { expr_sparse_mark m_mark; ptr_vector m_sorts; ptr_vector m_todo; -public: +public: void reset(); void operator()(expr* e); void accumulate(expr* e); @@ -96,7 +99,7 @@ public: bool contains(unsigned idx) const { return idx < m_sorts.size() && m_sorts[idx] != 0; } void set_default_sort(sort* s); void reverse() { m_sorts.reverse(); } - sort*const* c_ptr() const { return m_sorts.c_ptr(); } + sort*const* c_ptr() const { return m_sorts.c_ptr(); } }; #endif diff --git a/src/ast/simplifier/distribute_forall.cpp b/src/ast/simplifier/distribute_forall.cpp index bd2af5675..78e5d5ded 100644 --- a/src/ast/simplifier/distribute_forall.cpp +++ b/src/ast/simplifier/distribute_forall.cpp @@ -14,7 +14,7 @@ Author: Leonardo de Moura (leonardo) 2010-04-02. Revision History: - + Christoph Wintersteiger 2010-04-06: Added implementation. --*/ @@ -40,7 +40,7 @@ bool distribute_forall::visit_children(expr * n) { bool visited = true; unsigned j; switch(n->get_kind()) { - case AST_VAR: + case AST_VAR: break; case AST_APP: j = to_app(n)->get_num_args(); @@ -86,15 +86,15 @@ void distribute_forall::reduce1_app(app * a) { SASSERT(is_cached(a->get_arg(j))); expr * c = get_cached(a->get_arg(j)); SASSERT(c!=0); - if (c != a->get_arg(j)) + if (c != a->get_arg(j)) reduced = true; m_new_args[j] = c; - } + } if (reduced) { na = m_manager.mk_app(a->get_decl(), num_args, m_new_args.c_ptr()); } - + cache_result(a, na); } @@ -126,11 +126,11 @@ void distribute_forall::reduce1_quantifier(quantifier * q) { quantifier_ref tmp_q(m_manager); tmp_q = m_manager.update_quantifier(q, not_arg); expr_ref new_q(m_manager); - elim_unused_vars(m_manager, tmp_q, new_q); + elim_unused_vars(m_manager, tmp_q, params_ref(), new_q); new_args.push_back(new_q); } expr_ref result(m_manager); - // m_bsimp.mk_and actually constructs a (not (or ...)) formula, + // m_bsimp.mk_and actually constructs a (not (or ...)) formula, // it will also apply basic simplifications. m_bsimp.mk_and(new_args.size(), new_args.c_ptr(), result); cache_result(q, result); @@ -148,15 +148,15 @@ void distribute_forall::operator()(expr * f, expr_ref & result) { while (!m_todo.empty()) { expr * e = m_todo.back(); - if (visit_children(e)) { + if (visit_children(e)) { m_todo.pop_back(); reduce1(e); - } + } } result = get_cached(f); SASSERT(result!=0); - TRACE("distribute_forall", tout << mk_ll_pp(f, m_manager) << "======>\n" + TRACE("distribute_forall", tout << mk_ll_pp(f, m_manager) << "======>\n" << mk_ll_pp(result, m_manager);); } @@ -166,5 +166,5 @@ expr * distribute_forall::get_cached(expr * n) const { void distribute_forall::cache_result(expr * n, expr * r) { SASSERT(r != 0); - m_cache.insert(n, r); + m_cache.insert(n, r); } diff --git a/src/ast/simplifier/elim_bounds.cpp b/src/ast/simplifier/elim_bounds.cpp index a4e145e0a..7a40b8602 100644 --- a/src/ast/simplifier/elim_bounds.cpp +++ b/src/ast/simplifier/elim_bounds.cpp @@ -32,7 +32,7 @@ elim_bounds::elim_bounds(ast_manager & m): (<= x k) (<= (+ x (* -1 y)) k) - (<= (+ x (* -1 t)) k) + (<= (+ x (* -1 t)) k) (<= (+ t (* -1 x)) k) x and y are a bound variables, t is a ground term and k is a numeral @@ -65,14 +65,14 @@ bool elim_bounds::is_bound(expr * n, var * & lower, var * & upper) { if (neg) le = !le; - + if (is_var(n)) { upper = to_var(n); } else if (m_util.is_add(n) && to_app(n)->get_num_args() == 2) { expr * arg1 = to_app(n)->get_arg(0); expr * arg2 = to_app(n)->get_arg(1); - if (is_var(arg1)) + if (is_var(arg1)) upper = to_var(arg1); else if (!is_ground(arg1)) return false; @@ -95,7 +95,7 @@ bool elim_bounds::is_bound(expr * n, var * & lower, var * & upper) { if (!le) std::swap(upper, lower); - + return true; } @@ -188,7 +188,7 @@ void elim_bounds::operator()(quantifier * q, expr_ref & r) { } quantifier_ref new_q(m_manager); new_q = m_manager.update_quantifier(q, new_body); - elim_unused_vars(m_manager, new_q, r); + elim_unused_vars(m_manager, new_q, params_ref(), r); TRACE("elim_bounds", tout << mk_pp(q, m_manager) << "\n" << mk_pp(r, m_manager) << "\n";); } @@ -199,10 +199,10 @@ bool elim_bounds_star::visit_quantifier(quantifier * q) { visit(q->get_expr(), visited); return visited; } - + void elim_bounds_star::reduce1_quantifier(quantifier * q) { if (!q->is_forall() || q->get_num_patterns() != 0) { - cache_result(q, q, 0); + cache_result(q, q, 0); return; } quantifier_ref new_q(m); diff --git a/src/ast/simplifier/simplifier.cpp b/src/ast/simplifier/simplifier.cpp index 6f7e62fd4..498244919 100644 --- a/src/ast/simplifier/simplifier.cpp +++ b/src/ast/simplifier/simplifier.cpp @@ -33,8 +33,8 @@ simplifier::simplifier(ast_manager & m): m_ac_support(true) { } -void simplifier::register_plugin(plugin * p) { - m_plugins.register_plugin(p); +void simplifier::register_plugin(plugin * p) { + m_plugins.register_plugin(p); } simplifier::~simplifier() { @@ -46,13 +46,13 @@ void simplifier::enable_ac_support(bool flag) { ptr_vector::const_iterator it = m_plugins.begin(); ptr_vector::const_iterator end = m_plugins.end(); for (; it != end; ++it) { - if (*it != 0) + if (*it != 0) (*it)->enable_ac_support(flag); } } /** - \brief External interface for the simplifier. + \brief External interface for the simplifier. A client will invoke operator()(s, r, p) to simplify s. The result is stored in r. When proof generation is enabled, a proof for the equivalence (or equisatisfiability) @@ -69,14 +69,14 @@ void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) { proof * result_proof; switch (m.proof_mode()) { case PGM_DISABLED: // proof generation is disabled. - reduce_core(s); + reduce_core(s); // after executing reduce_core, the result of the simplification is in the cache get_cached(s, result, result_proof); r = result; p = m.mk_undef_proof(); break; case PGM_COARSE: // coarse proofs... in this case, we do not produce a step by step (fine grain) proof to show the equivalence (or equisatisfiability) of s an r. - m_subst_proofs.reset(); // m_subst_proofs is an auxiliary vector that is used to justify substitutions. See comment on method get_subst. + m_subst_proofs.reset(); // m_subst_proofs is an auxiliary vector that is used to justify substitutions. See comment on method get_subst. reduce_core(s); get_cached(s, result, result_proof); r = result; @@ -163,7 +163,7 @@ bool simplifier::visit_children(expr * n) { // The method ast_manager::mk_app is used to create the flat version of an AC operator. // In Z3 1.x, we used multi-ary operators. This creates problems for the superposition engine. // So, starting at Z3 2.x, only boolean operators can be multi-ary. - // Example: + // Example: // (and (and a b) (and c d)) --> (and a b c d) // (+ (+ a b) (+ c d)) --> (+ a (+ b (+ c d))) // Remark: The flattening is only applied if m_ac_support is true. @@ -178,7 +178,7 @@ bool simplifier::visit_children(expr * n) { } return visited; } - case AST_QUANTIFIER: + case AST_QUANTIFIER: return visit_quantifier(to_quantifier(n)); default: UNREACHABLE(); @@ -188,7 +188,7 @@ bool simplifier::visit_children(expr * n) { /** \brief Visit the children of n assuming it is an AC (associative-commutative) operator. - + For example, if n is of the form (+ (+ a b) (+ c d)), this method will return true if the nodes a, b, c and d have been already simplified. The nodes (+ a b) and (+ c d) are not really checked. @@ -216,7 +216,7 @@ bool simplifier::visit_ac(app * n) { expr * arg = n->get_arg(i); if (is_app_of(arg, decl)) todo.push_back(to_app(arg)); - else + else visit(arg, visited); } } @@ -319,7 +319,7 @@ void simplifier::reduce1_app_core(app * n) { proof * p; if (n == r) p = 0; - else if (r != s) + else if (r != s) // we use a "theory rewrite generic proof" to justify the step // s = (decl arg_0' ... arg_{n-1}') --> r p = m.mk_transitivity(p1, m.mk_rewrite(s, r)); @@ -368,7 +368,7 @@ void simplifier::reduce1_ac_app_core(app * n) { proof_ref p1(m); mk_ac_congruent_term(n, n_c, p1); TRACE("ac", tout << "expr:\n" << mk_pp(n, m) << "\ncongruent term:\n" << mk_pp(n_c, m) << "\n";); - expr_ref r(m); + expr_ref r(m); func_decl * decl = n->get_decl(); family_id fid = decl->get_family_id(); plugin * p = get_plugin(fid); @@ -415,7 +415,7 @@ void simplifier::reduce1_ac_app_core(app * n) { proof * p; if (n == r.get()) p = 0; - else if (r.get() != n_c.get()) + else if (r.get() != n_c.get()) p = m.mk_transitivity(p1, m.mk_rewrite(n_c, r)); else p = p1; @@ -434,7 +434,7 @@ void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * sprintf_s(buffer, ARRAYSIZE(buffer), "lemma_%d.smt", g_rewrite_lemma_id); #else sprintf(buffer, "rewrite_lemma_%d.smt", g_rewrite_lemma_id); -#endif +#endif ast_smt_pp pp(m); pp.set_benchmark_name("rewrite_lemma"); pp.set_status("unsat"); @@ -450,7 +450,7 @@ void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * /** \brief Return in \c result an expression \c e equivalent to (f args[0] ... args[num_args - 1]), and store in \c pr a proof for (= (f args[0] ... args[num_args - 1]) e) - + If e is identical to (f args[0] ... args[num_args - 1]), then pr is set to 0. */ void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result) { @@ -474,7 +474,7 @@ void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args //dump_rewrite_lemma(decl, num_args, args, result.get()); return; } - + result = m.mk_app(decl, num_args, args); } @@ -494,17 +494,17 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) { proof * arg_proof; get_cached(arg, new_arg, arg_proof); - CTRACE("simplifier_bug", (arg != new_arg) != (arg_proof != 0), + CTRACE("simplifier_bug", (arg != new_arg) != (arg_proof != 0), tout << mk_ll_pp(arg, m) << "\n---->\n" << mk_ll_pp(new_arg, m) << "\n"; tout << "#" << arg->get_id() << " #" << new_arg->get_id() << "\n"; tout << arg << " " << new_arg << "\n";); - - + + if (arg != new_arg) { has_new_args = true; proofs.push_back(arg_proof); SASSERT(arg_proof); - } + } else { SASSERT(arg_proof == 0); } @@ -526,10 +526,10 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) { /** \brief Store the new arguments of \c n in result. Store in p a proof for (= n (f result[0] ... result[num_args - 1])), where f is the function symbol of n. - + If there are no new arguments or fine grain proofs are disabled, then p is set to 0. - Return true there are new arguments. + Return true there are new arguments. */ bool simplifier::get_args(app * n, ptr_vector & result, proof_ref & p) { bool has_new_args = false; @@ -565,10 +565,10 @@ bool simplifier::get_args(app * n, ptr_vector & result, proof_ref & p) { void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) { SASSERT(m_ac_support); func_decl * f = n->get_decl(); - + m_ac_cache.reset(); m_ac_pr_cache.reset(); - + ptr_buffer todo; ptr_buffer new_args; ptr_buffer new_arg_prs; @@ -621,7 +621,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) { todo.pop_back(); if (!has_new_arg) { m_ac_cache.insert(curr, curr); - if (m.fine_grain_proofs()) + if (m.fine_grain_proofs()) m_ac_pr_cache.insert(curr, 0); } else { @@ -634,7 +634,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) { } } } - + SASSERT(m_ac_cache.contains(n)); app * new_n = 0; m_ac_cache.find(n, new_n); @@ -646,7 +646,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) { } } -#define White 0 +#define White 0 #define Grey 1 #define Black 2 @@ -688,7 +688,7 @@ void simplifier::ac_top_sort(app * n, ptr_buffer & result) { while (!todo.empty()) { expr * curr = todo.back(); int color; - obj_map::obj_map_entry * entry = colors.insert_if_not_there2(curr, White); + obj_map::obj_map_entry * entry = colors.insert_if_not_there2(curr, White); SASSERT(entry); color = entry->get_data().m_value; switch (color) { @@ -731,7 +731,7 @@ void simplifier::get_ac_args(app * n, ptr_vector & args, vector ac_top_sort(n, sorted_exprs); SASSERT(!sorted_exprs.empty()); SASSERT(sorted_exprs[sorted_exprs.size()-1] == n); - + TRACE("ac", tout << mk_ll_pp(n, m, true, false) << "#" << n->get_id() << "\nsorted expressions...\n"; for (unsigned i = 0; i < sorted_exprs.size(); i++) { tout << "#" << sorted_exprs[i]->get_id() << " "; @@ -747,7 +747,7 @@ void simplifier::get_ac_args(app * n, ptr_vector & args, vector expr * curr = sorted_exprs[j]; rational mult; m_ac_mults.find(curr, mult); - SASSERT(!mult.is_zero()); + SASSERT(!mult.is_zero()); if (is_app_of(curr, decl)) { unsigned num_args = to_app(curr)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { @@ -772,16 +772,16 @@ void simplifier::reduce1_quantifier(quantifier * q) { quantifier_ref q1(m); proof * p1 = 0; - - if (is_quantifier(new_body) && + + if (is_quantifier(new_body) && to_quantifier(new_body)->is_forall() == q->is_forall() && !to_quantifier(q)->has_patterns() && !to_quantifier(new_body)->has_patterns()) { - + quantifier * nested_q = to_quantifier(new_body); ptr_buffer sorts; - buffer names; + buffer names; sorts.append(q->get_num_decls(), q->get_decl_sorts()); names.append(q->get_num_decls(), q->get_decl_names()); sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts()); @@ -797,7 +797,7 @@ void simplifier::reduce1_quantifier(quantifier * q) { q->get_skid(), 0, 0, 0, 0); SASSERT(is_well_sorted(m, q1)); - + if (m.fine_grain_proofs()) { quantifier * q0 = m.update_quantifier(q, new_body); proof * p0 = q == q0 ? 0 : m.mk_quant_intro(q, q0, new_body_pr); @@ -817,13 +817,13 @@ void simplifier::reduce1_quantifier(quantifier * q) { get_cached(q->get_pattern(i), new_pattern, new_pattern_pr); if (m.is_pattern(new_pattern)) { new_patterns.push_back(new_pattern); - } + } } num = q->get_num_no_patterns(); for (unsigned i = 0; i < num; i++) { get_cached(q->get_no_pattern(i), new_pattern, new_pattern_pr); new_no_patterns.push_back(new_pattern); - } + } remove_duplicates(new_patterns); remove_duplicates(new_no_patterns); @@ -833,7 +833,7 @@ void simplifier::reduce1_quantifier(quantifier * q) { q->get_decl_sorts(), q->get_decl_names(), new_body, - q->get_weight(), + q->get_weight(), q->get_qid(), q->get_skid(), new_patterns.size(), @@ -850,10 +850,10 @@ void simplifier::reduce1_quantifier(quantifier * q) { p1 = q == q1 ? 0 : m.mk_quant_intro(q, q1, new_body_pr); } } - + expr_ref r(m); - elim_unused_vars(m, q1, r); - + elim_unused_vars(m, q1, params_ref(), r); + proof * pr = 0; if (m.fine_grain_proofs()) { proof * p2 = 0; @@ -871,7 +871,7 @@ void simplifier::reduce1_quantifier(quantifier * q) { void simplifier::borrow_plugins(simplifier const & s) { ptr_vector::const_iterator it = s.begin_plugins(); ptr_vector::const_iterator end = s.end_plugins(); - for (; it != end; ++it) + for (; it != end; ++it) register_plugin(*it); } @@ -882,7 +882,7 @@ void simplifier::enable_presimp() { enable_ac_support(false); ptr_vector::const_iterator it = begin_plugins(); ptr_vector::const_iterator end = end_plugins(); - for (; it != end; ++it) + for (; it != end; ++it) (*it)->enable_presimp(true); } @@ -905,7 +905,7 @@ bool subst_simplifier::get_subst(expr * n, expr_ref & r, proof_ref & p) { m_subst_map->get(n, _r, _p); r = _r; p = _p; - if (m.coarse_grain_proofs()) + if (m.coarse_grain_proofs()) m_subst_proofs.push_back(p); return true; } @@ -917,7 +917,7 @@ static void push_core(ast_manager & m, expr * e, proof * pr, expr_ref_vector & r TRACE("preprocessor", tout << mk_pp(e, m) << "\n"; if (pr) tout << mk_ll_pp(pr, m) << "\n\n";); - if (m.is_true(e)) + if (m.is_true(e)) return; result.push_back(e); if (m.proofs_enabled()) @@ -952,9 +952,9 @@ void push_assertion(ast_manager & m, expr * e, proof * pr, expr_ref_vector & res CTRACE("push_assertion", !(pr == 0 || m.is_undef_proof(pr) || m.get_fact(pr) == e), tout << mk_pp(e, m) << "\n" << mk_pp(m.get_fact(pr), m) << "\n";); SASSERT(pr == 0 || m.is_undef_proof(pr) || m.get_fact(pr) == e); - if (m.is_and(e)) + if (m.is_and(e)) push_and(m, to_app(e), pr, result, result_prs); - else if (m.is_not(e) && m.is_or(to_app(e)->get_arg(0))) + else if (m.is_not(e) && m.is_or(to_app(e)->get_arg(0))) push_not_or(m, to_app(to_app(e)->get_arg(0)), pr, result, result_prs); else push_core(m, e, pr, result, result_prs); diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 509b5ff2e..7ee1c0aeb 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -29,11 +29,12 @@ Notes: #include"bound_manager.h" #include"used_vars.h" #include"var_subst.h" +#include"gparams.h" #ifndef _EXTERNAL_RELEASE -BINARY_SYM_CMD(get_quantifier_body_cmd, - "dbg-get-qbody", +BINARY_SYM_CMD(get_quantifier_body_cmd, + "dbg-get-qbody", " ", "store the body of the quantifier in the global variable ", CPK_EXPR, @@ -43,8 +44,8 @@ BINARY_SYM_CMD(get_quantifier_body_cmd, store_expr_ref(ctx, m_sym, to_quantifier(arg)->get_expr()); }); -BINARY_SYM_CMD(set_cmd, - "dbg-set", +BINARY_SYM_CMD(set_cmd, + "dbg-set", " ", "store in the global variable ", CPK_EXPR, @@ -57,7 +58,7 @@ UNARY_CMD(pp_var_cmd, "dbg-pp-var", "", "pretty print a global variable expr * t = get_expr_ref(ctx, arg); SASSERT(t != 0); ctx.display(ctx.regular_stream(), t); - ctx.regular_stream() << std::endl; + ctx.regular_stream() << std::endl; }); BINARY_SYM_CMD(shift_vars_cmd, @@ -71,7 +72,7 @@ BINARY_SYM_CMD(shift_vars_cmd, var_shifter s(ctx.m()); s(t, arg, r); store_expr_ref(ctx, m_sym, r.get()); -}); +}); UNARY_CMD(pp_shared_cmd, "dbg-pp-shared", "", "display shared subterms of the given term", CPK_EXPR, expr *, { shared_occs s(ctx.m()); @@ -81,7 +82,7 @@ UNARY_CMD(pp_shared_cmd, "dbg-pp-shared", "", "display shared subterms of shared_occs::iterator end = s.end_shared(); for (; it != end; ++it) { expr * curr = *it; - ctx.regular_stream() << std::endl << " "; + ctx.regular_stream() << std::endl << " "; ctx.display(ctx.regular_stream(), curr, 2); } ctx.regular_stream() << ")" << std::endl; @@ -112,7 +113,7 @@ public: if (m_idx == 1) return CPK_SYMBOL_LIST; return CPK_SYMBOL; } - virtual void set_next_arg(cmd_context & ctx, symbol const & s) { + virtual void set_next_arg(cmd_context & ctx, symbol const & s) { if (m_idx == 0) { m_source = get_expr_ref(ctx, s); } @@ -146,24 +147,24 @@ UNARY_CMD(bool_rewriter_cmd, "dbg-bool-rewriter", "", "apply the Boolean r bool_rewriter_star r(ctx.m(), p); r(arg, t); ctx.display(ctx.regular_stream(), t); - ctx.regular_stream() << std::endl; + ctx.regular_stream() << std::endl; }); UNARY_CMD(bool_frewriter_cmd, "dbg-bool-flat-rewriter", "", "apply the Boolean (flattening) rewriter to the given term", CPK_EXPR, expr *, { expr_ref t(ctx.m()); - { + { params_ref p; p.set_bool("flat", true); bool_rewriter_star r(ctx.m(), p); r(arg, t); } ctx.display(ctx.regular_stream(), t); - ctx.regular_stream() << std::endl; + ctx.regular_stream() << std::endl; }); UNARY_CMD(elim_and_cmd, "dbg-elim-and", "", "apply the Boolean rewriter (eliminating AND operator and flattening) to the given term", CPK_EXPR, expr *, { expr_ref t(ctx.m()); - { + { params_ref p; p.set_bool("flat", true); p.set_bool("elim_and", true); @@ -171,7 +172,7 @@ UNARY_CMD(elim_and_cmd, "dbg-elim-and", "", "apply the Boolean rewriter (e r(arg, t); } ctx.display(ctx.regular_stream(), t); - ctx.regular_stream() << std::endl; + ctx.regular_stream() << std::endl; }); class lt_cmd : public cmd { @@ -192,7 +193,7 @@ public: } virtual void execute(cmd_context & ctx) { bool r = lt(m_t1, m_t2); - ctx.regular_stream() << (r ? "true" : "false") << std::endl; + ctx.regular_stream() << (r ? "true" : "false") << std::endl; } }; @@ -249,7 +250,7 @@ UNARY_CMD(set_next_id, "dbg-set-next-id", "", "set the next expression UNARY_CMD(used_vars_cmd, "dbg-used-vars", "", "test used_vars functor", CPK_EXPR, expr *, { used_vars proc; - if (is_quantifier(arg)) + if (is_quantifier(arg)) arg = to_quantifier(arg)->get_expr(); proc(arg); ctx.regular_stream() << "(vars"; @@ -258,7 +259,7 @@ UNARY_CMD(used_vars_cmd, "dbg-used-vars", "", "test used_vars functor", CP ctx.regular_stream() << "\n (" << std::left << std::setw(6) << i << " "; if (s != 0) ctx.display(ctx.regular_stream(), s, 10); - else + else ctx.regular_stream() << ""; ctx.regular_stream() << ")"; } @@ -271,7 +272,7 @@ UNARY_CMD(elim_unused_vars_cmd, "dbg-elim-unused-vars", "", "eliminate unu return; } expr_ref r(ctx.m()); - elim_unused_vars(ctx.m(), to_quantifier(arg), r); + elim_unused_vars(ctx.m(), to_quantifier(arg), gparams::get(), r); SASSERT(!is_quantifier(r) || !to_quantifier(r)->may_have_unused_vars()); ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; @@ -287,18 +288,18 @@ public: virtual char const * get_descr() const { return "instantiate the quantifier using the given expressions."; } virtual unsigned get_arity() const { return 2; } virtual void prepare(cmd_context & ctx) { m_q = 0; m_args.reset(); } - + virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_q == 0) return CPK_EXPR; else return CPK_EXPR_LIST; } - + virtual void set_next_arg(cmd_context & ctx, expr * s) { if (!is_quantifier(s)) throw cmd_exception("invalid command, quantifier expected."); m_q = to_quantifier(s); } - + virtual void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) { if (num != m_q->get_num_decls()) throw cmd_exception("invalid command, mismatch between the number of quantified variables and the number of arguments."); @@ -331,7 +332,7 @@ public: class instantiate_nested_cmd : public instantiate_cmd_core { public: instantiate_nested_cmd():instantiate_cmd_core("dbg-instantiate-nested") {} - + virtual char const * get_descr() const { return "instantiate the quantifier nested in the outermost quantifier, this command is used to test the instantiation procedure with quantifiers that contain free variables."; } virtual void set_next_arg(cmd_context & ctx, expr * s) { diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 7ee76d4d6..35033f739 100755 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -41,8 +41,8 @@ namespace Duality { params_ref p; p.set_bool("proof", true); // this is currently useless if(models) - p.set_bool("model", true); - p.set_bool("unsat_core", true); + p.set_bool("model", true); + p.set_bool("unsat_core", true); bool mbqi = c.get_config().get().get_bool("mbqi",true); p.set_bool("mbqi",mbqi); // just to test p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants @@ -57,7 +57,7 @@ namespace Duality { m_mode = m().proof_mode(); } - expr context::constant(const std::string &name, const sort &ty){ + expr context::constant(const std::string &name, const sort &ty){ symbol s = str_symbol(name.c_str()); return cook(m().mk_const(m().mk_const_decl(s, ty))); } @@ -111,7 +111,7 @@ namespace Duality { } expr context::mki(family_id fid, ::decl_kind dk, int n, ::expr **args){ - return cook(m().mk_app(fid, dk, 0, 0, n, (::expr **)args)); + return cook(m().mk_app(fid, dk, 0, 0, n, (::expr **)args)); } expr context::make(decl_kind op, const std::vector &args){ @@ -168,9 +168,9 @@ namespace Duality { expr_abstract(m(), 0, num_bound, VEC2PTR(bound_asts), to_expr(body.raw()), abs_body); expr_ref result(m()); result = m().mk_quantifier( - op == Forall, - names.size(), VEC2PTR(types), VEC2PTR(names), abs_body.get(), - 0, + op == Forall, + names.size(), VEC2PTR(types), VEC2PTR(names), abs_body.get(), + 0, ::symbol(), ::symbol(), 0, 0, @@ -194,9 +194,9 @@ namespace Duality { } expr_ref result(m()); result = m().mk_quantifier( - op == Forall, - names.size(), VEC2PTR(types), VEC2PTR(names), to_expr(body.raw()), - 0, + op == Forall, + names.size(), VEC2PTR(types), VEC2PTR(names), to_expr(body.raw()), + 0, ::symbol(), ::symbol(), 0, 0, @@ -273,7 +273,7 @@ namespace Duality { return OtherArray; } } - + return Other; } @@ -340,7 +340,7 @@ namespace Duality { params p; return simplify(p); } - + expr context::make_var(int idx, const sort &s){ ::sort * a = to_sort(s.raw()); return cook(m().mk_var(idx,a)); @@ -348,7 +348,7 @@ namespace Duality { expr expr::qe_lite() const { - ::qe_lite qe(m()); + ::qe_lite qe(m(), params_ref()); expr_ref result(to_expr(raw()),m()); proof_ref pf(m()); qe(result,pf); @@ -356,7 +356,7 @@ namespace Duality { } expr expr::qe_lite(const std::set &idxs, bool index_of_bound) const { - ::qe_lite qe(m()); + ::qe_lite qe(m(), params_ref()); expr_ref result(to_expr(raw()),m()); proof_ref pf(m()); uint_set uis; @@ -412,16 +412,16 @@ namespace Duality { std::vector < ::sort * > _domain(domain.size()); for(unsigned i = 0; i < domain.size(); i++) _domain[i] = to_sort(domain[i].raw()); - ::func_decl* d = m().mk_fresh_func_decl(prefix, - _domain.size(), + ::func_decl* d = m().mk_fresh_func_decl(prefix, + _domain.size(), VEC2PTR(_domain), to_sort(range.raw())); return func_decl(*this,d); } func_decl context::fresh_func_decl(char const * prefix, sort const & range){ - ::func_decl* d = m().mk_fresh_func_decl(prefix, - 0, + ::func_decl* d = m().mk_fresh_func_decl(prefix, + 0, 0, to_sort(range.raw())); return func_decl(*this,d); @@ -462,30 +462,30 @@ namespace Duality { incremental, _theory.size(), VEC2PTR(_theory)); - + if(lb == Z3_L_FALSE){ interpolants.resize(_interpolants.size()); for (unsigned i = 0; i < _interpolants.size(); ++i) { interpolants[i] = expr(ctx(),_interpolants[i]); } - } - + } + if (_model) { model = iz3wrapper::model(ctx(), _model); } - + if(_labels){ labels = _labels; } - + return lb; } #endif - + static int linearize_assumptions(int num, TermTree *assumptions, - std::vector > &linear_assumptions, + std::vector > &linear_assumptions, std::vector &parents){ for(unsigned i = 0; i < assumptions->getChildren().size(); i++) num = linearize_assumptions(num, assumptions->getChildren()[i], linear_assumptions, parents); @@ -501,7 +501,7 @@ namespace Duality { } static int unlinearize_interpolants(int num, - TermTree* assumptions, + TermTree* assumptions, const std::vector &interpolant, TermTree * &tree_interpolant) { @@ -522,7 +522,7 @@ namespace Duality { literals &labels, bool incremental ) - + { int size = assumptions->number(0); std::vector > linear_assumptions(size); @@ -540,36 +540,36 @@ namespace Duality { ptr_vector< ::ast> _theory(theory.size()); for(unsigned i = 0; i < theory.size(); i++) _theory[i] = theory[i]; - - + + if(!incremental){ push(); for(unsigned i = 0; i < linear_assumptions.size(); i++) for(unsigned j = 0; j < linear_assumptions[i].size(); j++) add(linear_assumptions[i][j]); } - + check_result res = unsat; if(!m_solver->get_proof()) res = check(); - + if(res == unsat){ interpolation_options_struct opts; if(weak_mode) - opts.set("weak","1"); - + opts.set("weak","1"); + ::ast *proof = m_solver->get_proof(); try { iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts); } // If there's an interpolation bug, throw a char * - // exception so duality can catch it and restart. + // exception so duality can catch it and restart. catch (const interpolation_failure &f) { throw f.msg(); } - + std::vector linearized_interpolants(_interpolants.size()); for(unsigned i = 0; i < _interpolants.size(); i++) linearized_interpolants[i] = expr(ctx(),_interpolants[i]); @@ -585,13 +585,13 @@ namespace Duality { model_ref _m; m_solver->get_model(_m); model = Duality::model(ctx(),_m.get()); - + #if 0 if(_labels){ labels = _labels; } #endif - + if(!incremental) pop(); @@ -603,7 +603,7 @@ namespace Duality { void interpolating_solver::SetWeakInterpolants(bool weak){ weak_mode = weak; } - + void interpolating_solver::SetPrintToFile(const std::string &filename){ print_filename = filename; @@ -618,14 +618,14 @@ namespace Duality { void interpolating_solver::RemoveInterpolationAxiom(const expr & t){ // theory.remove(t); } - + const char *interpolating_solver::profile(){ // return Z3_interpolation_profile(ctx()); return ""; } - + static void get_assumptions_rec(stl_ext::hash_set &memo, const proof &pf, std::vector &assumps){ if(memo.find(pf) != memo.end())return; memo.insert(pf); @@ -657,7 +657,7 @@ namespace Duality { model_smt2_pp(std::cout, m(), *m_model, 0); std::cout << std::endl; } - + void model::show_hash() const { std::ostringstream ss; model_smt2_pp(ss, m(), *m_model, 0); diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index 56cc6e154..ffb964f47 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -47,14 +47,14 @@ Revision History: namespace datalog { - rule_manager::rule_manager(context& ctx) + rule_manager::rule_manager(context& ctx) : m(ctx.get_manager()), m_ctx(ctx), m_body(m), m_head(m), m_args(m), m_hnf(m), - m_qe(m), + m_qe(m, params_ref()), m_rwr(m), m_ufproc(m) {} @@ -98,7 +98,7 @@ namespace datalog { var_idx_set& rule_manager::finalize_collect_vars() { unsigned sz = m_free_vars.size(); for (unsigned i = 0; i < sz; ++i) { - if (m_free_vars[i]) m_var_idx.insert(i); + if (m_free_vars[i]) m_var_idx.insert(i); } return m_var_idx; } @@ -139,7 +139,7 @@ namespace datalog { } - void rule_manager::mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { + 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); @@ -147,7 +147,7 @@ namespace datalog { if (fml1 != fml && pr) { pr = m.mk_asserted(fml1); } - remove_labels(fml1, pr); + remove_labels(fml1, pr); mk_rule_core(fml1, pr, rules, name); } @@ -162,7 +162,7 @@ namespace datalog { else { is_negated.push_back(false); } - } + } } void rule_manager::mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name) { @@ -170,7 +170,7 @@ namespace datalog { proof_ref_vector prs(m); m_hnf.reset(); m_hnf.set_name(name); - + m_hnf(fml, p, fmls, prs); for (unsigned i = 0; i < m_hnf.get_fresh_predicates().size(); ++i) { m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false); @@ -181,7 +181,7 @@ namespace datalog { } 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); @@ -208,13 +208,13 @@ namespace datalog { } 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()) { + if (m_ctx.fix_unbound_vars()) { fix_unbound_vars(r, true); } @@ -242,10 +242,10 @@ namespace datalog { for (unsigned i = 0; i < m_args.size(); ++i) { body.push_back(ensure_app(m_args[i].get())); } - } + } else { head = ensure_app(fml); - } + } return index; } @@ -262,12 +262,12 @@ namespace datalog { func_decl* rule_manager::mk_query(expr* query, rule_set& rules) { TRACE("dl", tout << mk_pp(query, m) << "\n";); - + ptr_vector vars; svector names; app_ref_vector body(m); expr_ref q(m); - + // Add implicit variables. // Remove existential prefix. bind_variables(query, false, q); @@ -278,7 +278,7 @@ namespace datalog { m_free_vars(q); vars.append(m_free_vars.size(), m_free_vars.c_ptr()); if (vars.contains(static_cast(0))) { - var_subst sub(m, false); + var_subst sub(m, false); expr_ref_vector args(m); // [s0, 0, s2, ..] // [0 -> 0, 1 -> x, 2 -> 1, ..] @@ -313,7 +313,7 @@ namespace datalog { } } - // we want outermost declared variable first to + // we want outermost declared variable first to // follow order of quantified variables so we reverse vars. while (vars.size() > names.size()) { names.push_back(symbol(names.size())); @@ -321,9 +321,9 @@ namespace datalog { vars.reverse(); names.reverse(); func_decl* qpred = m_ctx.mk_fresh_head_predicate(symbol("query"), symbol(), vars.size(), vars.c_ptr(), body_pred); - m_ctx.register_predicate(qpred, false); + m_ctx.register_predicate(qpred, false); rules.set_output_predicate(qpred); - + if (m_ctx.get_model_converter()) { filter_model_converter* mc = alloc(filter_model_converter, m); mc->insert(qpred); @@ -366,7 +366,7 @@ namespace datalog { for (unsigned i = 0; i < r.size(); ++i) { body.push_back(ensure_app(r[i].get())); } - } + } void rule_manager::hoist_compound(unsigned& num_bound, app_ref& fml, app_ref_vector& body) { @@ -440,7 +440,7 @@ namespace datalog { if (is_quantifier(e)) { q = to_quantifier(e); return q->is_forall(); - } + } return false; } @@ -454,7 +454,7 @@ namespace datalog { return app_ref(m.mk_eq(e, m.mk_true()), m); } } - + void rule_manager::check_app(expr* e) { if (!is_app(e)) { std::ostringstream out; @@ -481,7 +481,7 @@ namespace datalog { bool has_neg = false; for (unsigned i = 0; i < n; i++) { - bool is_neg = (is_negated != 0 && is_negated[i]); + bool is_neg = (is_negated != 0 && is_negated[i]); app * curr = tail[i]; if (is_neg && !m_ctx.is_predicate(curr)) { @@ -571,7 +571,7 @@ namespace datalog { case 1: fml = m.mk_implies(body[0].get(), fml); break; default: fml = m.mk_implies(m.mk_and(body.size(), body.c_ptr()), fml); break; } - + m_free_vars(fml); if (m_free_vars.empty()) { return; @@ -579,7 +579,7 @@ namespace datalog { svector names; used_symbols<> us; m_free_vars.set_default_sort(m.mk_bool_sort()); - + us(fml); m_free_vars.reverse(); for (unsigned j = 0, i = 0; i < m_free_vars.size(); ++j) { @@ -594,8 +594,8 @@ namespace datalog { ++i; } } - } - fml = m.mk_forall(m_free_vars.size(), m_free_vars.c_ptr(), names.c_ptr(), fml); + } + fml = m.mk_forall(m_free_vars.size(), m_free_vars.c_ptr(), names.c_ptr(), fml); } std::ostream& rule_manager::display_smt2(rule const& r, std::ostream & out) { @@ -749,7 +749,7 @@ namespace datalog { quant_tail = m.mk_exists(q_var_cnt, qsorts.c_ptr(), qnames.c_ptr(), unbound_tail_pre_quant); if (try_quantifier_elimination) { - TRACE("dl_rule_unbound_fix_pre_qe", + TRACE("dl_rule_unbound_fix_pre_qe", tout<<"rule: "; r->display(m_ctx, tout); tout<<"tail with unbound vars: "<display(m_ctx, tout); tout<<"tail with unbound vars: "<name(), false); - // keep old variable indices around so we can compose with substitutions. + // keep old variable indices around so we can compose with substitutions. // r->norm_vars(*this); } @@ -835,7 +835,7 @@ namespace datalog { void rule_manager::check_valid_head(expr * head) const { SASSERT(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); @@ -874,14 +874,14 @@ namespace datalog { m.get_allocator().deallocate(get_obj_size(n), this); } - void rule::set_proof(ast_manager& m, proof* p) { + void rule::set_proof(ast_manager& m, proof* p) { if (p) { - m.inc_ref(p); + m.inc_ref(p); } if (m_proof) { - m.dec_ref(m_proof); + m.dec_ref(m_proof); } - m_proof = p; + m_proof = p; } bool rule::is_in_tail(const func_decl * p, bool only_positive) const { @@ -896,7 +896,7 @@ namespace datalog { // - // non-predicates may appear only in the interpreted tail, it is therefore + // non-predicates may appear only in the interpreted tail, it is therefore // sufficient only to check the tail. // bool rule_manager::has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const { @@ -911,7 +911,7 @@ namespace datalog { // - // Quantifiers may appear only in the interpreted tail, it is therefore + // Quantifiers may appear only in the interpreted tail, it is therefore // sufficient only to check the interpreted tail. // void rule_manager::has_quantifiers(rule const& r, bool& existential, bool& universal) const { @@ -945,7 +945,7 @@ namespace datalog { unsigned sz = get_tail_size(); for (unsigned i = 0; i < sz; ++i) { used.process(get_tail(i)); - } + } } void rule::get_vars(ast_manager& m, ptr_vector& sorts) const { @@ -994,13 +994,13 @@ namespace datalog { app * old_tail = get_tail(i); expr_ref new_tail_e(m); vs(old_tail, subst_vals.size(), subst_vals.c_ptr(), new_tail_e); - bool sign = is_neg_tail(i); + bool sign = is_neg_tail(i); m.inc_ref(new_tail_e); m.dec_ref(old_tail); m_tail[i] = TAG(app *, to_app(new_tail_e), sign); } } - + void rule::display(context & ctx, std::ostream & out) const { ast_manager & m = ctx.get_manager(); //out << mk_pp(m_head, m); @@ -1068,7 +1068,7 @@ namespace datalog { } - + }; diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index 587488fc9..7484a77fa 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -1007,7 +1007,7 @@ namespace pdr { return m_cache[l]; } - void model_search::erase_children(model_node& n, bool backtrack) { + void model_search::erase_children(model_node& n, bool backtrack) { ptr_vector todo, nodes; todo.append(n.children()); remove_goal(n); @@ -2241,7 +2241,7 @@ namespace pdr { vars.append(aux_vars.size(), aux_vars.c_ptr()); scoped_ptr rep; - qe_lite qe(m); + qe_lite qe(m, m_params.p); expr_ref phi1 = m_pm.mk_and(Phi); qe(vars, phi1); TRACE("pdr", tout << "Eliminated\n" << mk_pp(phi1, m) << "\n";); diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index 0a6c4c294..35eb5d936 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -53,10 +53,10 @@ namespace tb { app* t = to_app(_t); if (m.is_value(s) && m.is_value(t)) { - IF_VERBOSE(2, verbose_stream() << "different:" << mk_pp(s, m) << " " << mk_pp(t, m) << "\n";); + IF_VERBOSE(2, verbose_stream() << "different:" << mk_pp(s, m) << " " << mk_pp(t, m) << "\n";); return l_false; } - + if (m_dt.is_constructor(s) && m_dt.is_constructor(t)) { if (s->get_decl() == t->get_decl()) { lbool state = l_true; @@ -75,7 +75,7 @@ namespace tb { return state; } else { - IF_VERBOSE(2, verbose_stream() << "different constructors:" << mk_pp(s, m) << " " << mk_pp(t, m) << "\n";); + IF_VERBOSE(2, verbose_stream() << "different constructors:" << mk_pp(s, m) << " " << mk_pp(t, m) << "\n";); return l_false; } } @@ -109,7 +109,7 @@ namespace tb { case l_false: return false; default: - conds.push_back(m.mk_eq(p, t)); + conds.push_back(m.mk_eq(p, t)); return true; } } @@ -117,7 +117,7 @@ namespace tb { public: matcher(ast_manager& m): m(m), m_dt(m) {} - + bool operator()(app* pat, app* term, substitution& s, expr_ref_vector& conds) { // top-most term to match is a predicate. The predicates should be the same. if (pat->get_decl() != term->get_decl() || @@ -149,7 +149,7 @@ namespace tb { } } return true; - } + } }; class clause { @@ -165,22 +165,22 @@ namespace tb { unsigned m_next_rule; // next rule to expand goal on unsigned m_ref; // reference count - public: - + public: + clause(ast_manager& m): m_head(m), m_predicates(m), m_constraint(m), m_seqno(0), - m_index(0), + m_index(0), m_num_vars(0), - m_predicate_index(0), + m_predicate_index(0), m_parent_rule(0), m_parent_index(0), m_next_rule(static_cast(-1)), m_ref(0) { } - + void set_seqno(unsigned seqno) { m_seqno = seqno; } unsigned get_seqno() const { return m_seqno; } unsigned get_next_rule() const { return m_next_rule; } @@ -198,10 +198,10 @@ namespace tb { 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; } - void set_parent(ref& parent) { + void set_parent(ref& parent) { m_parent_index = parent->get_index(); m_parent_rule = parent->get_next_rule(); - } + } expr_ref get_body() const { ast_manager& m = get_manager(); @@ -247,7 +247,7 @@ namespace tb { } if (!vars.empty()) { body = m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), body); - } + } return body; } @@ -273,18 +273,18 @@ namespace tb { reduce_equalities(); // IF_VERBOSE(1, display(verbose_stream());); } - + void inc_ref() { m_ref++; } - + void dec_ref() { --m_ref; if (m_ref == 0) { dealloc(this); } } - + void display(std::ostream& out) const { ast_manager& m = m_head.get_manager(); expr_ref_vector fmls(m); @@ -304,7 +304,7 @@ namespace tb { } out << mk_pp(fml, m) << "\n"; } - + private: ast_manager& get_manager() const { return m_head.get_manager(); } @@ -314,7 +314,7 @@ namespace tb { // - m_head - head predicate // - m_predicates - auxiliary predicates in body. // - m_constraint - side constraint - // + // void init_from_rule(datalog::rule_ref const& r) { ast_manager& m = get_manager(); expr_ref_vector fmls(m); @@ -328,7 +328,7 @@ namespace tb { m_predicates.reset(); for (unsigned i = 0; i < utsz; ++i) { m_predicates.push_back(r->get_tail(i)); - } + } bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), m_constraint); } @@ -348,13 +348,13 @@ namespace tb { if (get_subst(rw, subst, i, fmls)) { fmls[i] = m.mk_true(); } - } + } subst.apply(1, delta, expr_offset(m_head, 0), tmp); m_head = to_app(tmp); for (unsigned i = 0; i < m_predicates.size(); ++i) { subst.apply(1, delta, expr_offset(m_predicates[i].get(), 0), tmp); m_predicates[i] = to_app(tmp); - } + } bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), m_constraint); subst.apply(1, delta, expr_offset(m_constraint, 0), m_constraint); rw(m_constraint); @@ -404,7 +404,7 @@ namespace tb { throw non_constructor(); } } - void operator()(var* v) { } + void operator()(var* v) { } void operator()(quantifier* ) { throw non_constructor(); } @@ -421,7 +421,7 @@ namespace tb { return true; } - }; + }; // rules class rules { @@ -456,7 +456,7 @@ namespace tb { 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); + e->get_data().m_value.push_back(idx); } unsigned get_num_rules(func_decl* p) const { @@ -475,14 +475,14 @@ namespace tb { for (; it != end; ++it) { decls.push_back(it->m_key); } - } + } ref get_rule(func_decl* p, unsigned idx) const { map::obj_map_entry* e = m_index.find_core(p); SASSERT(p); unsigned rule_id = e->get_data().get_value()[idx]; return m_rules[rule_id]; - } + } private: void reset() { m_rules.reset(); @@ -509,7 +509,7 @@ namespace tb { bool_rewriter m_rw; smt_params m_fparams; smt::kernel m_solver; - + public: index(ast_manager& m): m(m), @@ -520,7 +520,7 @@ namespace tb { m_matcher(m), m_refs(m), m_subst(m), - m_qe(m), + m_qe(m, params_ref()), m_rw(m), m_solver(m, m_fparams) {} @@ -544,7 +544,7 @@ namespace tb { } private: - + void setup(clause const& g) { m_preds.reset(); m_refs.reset(); @@ -569,8 +569,8 @@ namespace tb { } vs(g.get_constraint(), vars.size(), vars.c_ptr(), fml); fmls.push_back(fml); - m_precond = m.mk_and(fmls.size(), fmls.c_ptr()); - IF_VERBOSE(2, + m_precond = m.mk_and(fmls.size(), fmls.c_ptr()); + IF_VERBOSE(2, verbose_stream() << "setup-match: "; for (unsigned i = 0; i < m_preds.size(); ++i) { verbose_stream() << mk_pp(m_preds[i].get(), m) << " "; @@ -587,18 +587,18 @@ namespace tb { return true; } } - return false; + return false; } // // check that each predicate in r is matched by some predicate in premise. // for now: skip multiple matches within the same rule (incomplete). // bool match_rule(unsigned rule_index) { - clause const& g = *m_index[rule_index]; + clause const& g = *m_index[rule_index]; m_sideconds.reset(); m_subst.reset(); m_subst.reserve(2, g.get_num_vars()); - + IF_VERBOSE(2, g.display(verbose_stream() << "try-match\n");); return match_head(g); @@ -628,9 +628,9 @@ namespace tb { } verbose_stream() << mk_pp(q, m) << " = " << mk_pp(p, m) << "\n"; ); - - if (q->get_decl() == p->get_decl() && + + if (q->get_decl() == p->get_decl() && m_matcher(q, p, m_subst, m_sideconds) && match_predicates(predicate_index + 1, g)) { return true; @@ -646,7 +646,7 @@ namespace tb { expr_ref q(m), postcond(m); expr_ref_vector fmls(m_sideconds); m_subst.reset_cache(); - + for (unsigned i = 0; !m.canceled() && i < fmls.size(); ++i) { m_subst.apply(2, deltas, expr_offset(fmls[i].get(), 0), q); fmls[i] = q; @@ -680,7 +680,7 @@ namespace tb { verbose_stream() << "check: " << mk_pp(postcond, m, 7 + g.get_num_predicates()) << "\n";); if (!is_ground(postcond)) { - IF_VERBOSE(1, verbose_stream() << "TBD: non-ground\n" + IF_VERBOSE(1, verbose_stream() << "TBD: non-ground\n" << mk_pp(postcond, m) << "\n"; m_clause->display(verbose_stream()); verbose_stream() << "\n=>\n"; @@ -743,7 +743,7 @@ namespace tb { double m_weight_multiply; unsigned m_update_frequency; unsigned m_next_update; - + public: selection(datalog::context& ctx): @@ -766,7 +766,7 @@ namespace tb { scores.reset(); basic_score_predicate(p, scores); insert_score(p->get_decl(), scores); - } + } normalize_scores(rs); } @@ -783,7 +783,7 @@ namespace tb { default: return weight_select(g); - } + } } void reset() { @@ -867,8 +867,8 @@ namespace tb { } } IF_VERBOSE(1, verbose_stream() << "select:" << result << "\n";); - - return result; + + return result; } unsigned basic_weight_select(clause const& g) { @@ -957,7 +957,7 @@ namespace tb { } } - + double score_predicate(app* p) { double score = 1; if (find_score(p, score)) { @@ -1031,7 +1031,7 @@ namespace tb { } else { m_score_map.insert(f, scores); - } + } } }; @@ -1044,15 +1044,15 @@ namespace tb { expr_ref_vector m_sub1; expr_ref_vector m_sub2; public: - unifier(ast_manager& m): - m(m), + unifier(ast_manager& m): + m(m), m_unifier(m), m_S1(m), m_S2(m, false), m_rename(m), - m_sub1(m), + m_sub1(m), m_sub2(m) {} - + bool operator()(ref& tgt, unsigned idx, ref& src, bool compute_subst, ref& result) { return unify(*tgt, idx, *src, compute_subst, result); } @@ -1066,12 +1066,12 @@ namespace tb { } } - bool unify(clause const& tgt, unsigned idx, clause const& src, bool compute_subst, ref& result) { - qe_lite qe(m); + bool unify(clause const& tgt, unsigned idx, clause const& src, bool compute_subst, ref& result) { + qe_lite qe(m, params_ref()); reset(); 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); + m_S1.reserve(2, var_cnt); if (!m_unifier(tgt.get_predicate(idx), src.get_head(), m_S1)) { return false; } @@ -1080,7 +1080,7 @@ namespace tb { app_ref head(m); result = alloc(clause, m); unsigned delta[2] = { 0, var_cnt }; - m_S1.apply(2, delta, expr_offset(tgt.get_head(), 0), tmp); + m_S1.apply(2, delta, expr_offset(tgt.get_head(), 0), tmp); head = to_app(tmp); for (unsigned i = 0; i < tgt.get_num_predicates(); ++i) { if (i != idx) { @@ -1096,7 +1096,7 @@ namespace tb { } m_S1.apply(2, delta, expr_offset(tgt.get_constraint(), 0), tmp); m_S1.apply(2, delta, expr_offset(src.get_constraint(), 1), tmp2); - constraint = m.mk_and(tmp, tmp2); + constraint = m.mk_and(tmp, tmp2); // perform trival quantifier-elimination: uint_set index_set; @@ -1114,7 +1114,7 @@ namespace tb { if (m.is_false(constraint)) { return false; } - + // initialize rule. result->init(head, predicates, constraint); ptr_vector vars; @@ -1147,10 +1147,10 @@ namespace tb { extract_subst(delta, src, 1); } // init result using head, predicates, constraint - return true; + return true; } - - + + private: void reset() { m_S1.reset(); @@ -1175,9 +1175,9 @@ namespace tb { else { insert_subst(offset, m.mk_true()); } - } + } } - + void insert_subst(unsigned offset, expr* e) { if (offset == 0) { m_sub1.push_back(e); @@ -1201,7 +1201,7 @@ namespace tb { // - // Given a clause + // Given a clause // P(s) :- P(t), Phi(x). // Compute the clauses: // acc: P(s) :- Delta(z,t), P(z), Phi(x). @@ -1237,7 +1237,7 @@ namespace tb { head = m.mk_app(delta, zszs.size(), zszs.c_ptr()); for (unsigned i = 0; i < zs.size(); ++i) { zszs[i+zs.size()] = q->get_arg(i); - } + } pred = m.mk_app(delta, zszs.size(), zszs.c_ptr()); preds.push_back(pred); for (unsigned i = 1; i < g.get_num_predicates(); ++i) { @@ -1247,28 +1247,28 @@ namespace tb { preds.push_back(m.mk_app(q->get_decl(), zs.size(), zs.c_ptr())); acc->init(p, preds, g.get_constraint()); - IF_VERBOSE(1, + IF_VERBOSE(1, delta1->display(verbose_stream() << "delta1:\n"); delta2->display(verbose_stream() << "delta2:\n"); acc->display(verbose_stream() << "acc:\n");); } - // + // // Given a sequence of clauses and inference rules // compute a super-predicate and auxiliary clauses. - // + // // P1(x) :- P2(y), R(z) // P2(y) :- P3(z), T(u) // P3(z) :- P1(x), U(v) // => // P1(x) :- P1(x), R(z), T(u), U(v) - // + // ref resolve_rules(unsigned num_clauses, clause*const* clauses, unsigned const* positions) { ref result = clauses[0]; ref tmp; unsigned offset = 0; - for (unsigned i = 0; i + 1 < num_clauses; ++i) { + for (unsigned i = 0; i + 1 < num_clauses; ++i) { clause const& cl = *clauses[i+1]; offset += positions[i]; VERIFY (m_unifier.unify(*result, offset, cl, false, tmp)); @@ -1276,7 +1276,7 @@ namespace tb { } return result; } - + private: @@ -1286,7 +1286,7 @@ namespace tb { unsigned num_vars = g.get_num_vars(); for (unsigned i = 0; i < p->get_num_args(); ++i) { result.push_back(m.mk_var(num_vars+i, m.get_sort(p->get_arg(i)))); - } + } return result; } }; @@ -1341,7 +1341,7 @@ namespace datalog { uint_set m_displayed_rules; public: imp(context& ctx): - m_ctx(ctx), + m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_index(m), @@ -1358,7 +1358,7 @@ namespace datalog { m_fparams.m_timeout = 1000; } - ~imp() {} + ~imp() {} lbool query(expr* query) { m_ctx.ensure_opened(); @@ -1378,7 +1378,7 @@ namespace datalog { IF_VERBOSE(1, display_clause(*get_clause(), verbose_stream() << "g" << get_clause()->get_seqno() << " ");); return run(); } - + void cleanup() { m_clauses.reset(); } @@ -1400,7 +1400,7 @@ namespace datalog { expr_ref get_answer() const { switch(m_status) { - case l_undef: + case l_undef: UNREACHABLE(); return expr_ref(m.mk_false(), m); case l_true: { @@ -1415,7 +1415,7 @@ namespace datalog { return expr_ref(m.mk_true(), m); } private: - + void select_predicate() { tb::clause & g = *get_clause(); unsigned num_predicates = g.get_num_predicates(); @@ -1430,17 +1430,17 @@ namespace datalog { IF_VERBOSE(2, verbose_stream() << mk_pp(g.get_predicate(pi), m) << "\n";); } } - + void apply_rule(ref& r) { ref clause = get_clause(); - ref next_clause; + ref next_clause; if (m_unifier(clause, clause->get_predicate_index(), r, false, next_clause) && !query_is_tautology(*next_clause)) { init_clause(next_clause); unsigned subsumer = 0; - IF_VERBOSE(1, + IF_VERBOSE(1, display_rule(*clause, verbose_stream()); - display_premise(*clause, + display_premise(*clause, verbose_stream() << "g" << next_clause->get_seqno() << " "); display_clause(*next_clause, verbose_stream()); ); @@ -1462,8 +1462,8 @@ namespace datalog { m_instruction = tb::SELECT_RULE; } } - - void select_rule() { + + void select_rule() { tb::clause& g = *get_clause(); g.inc_next_rule(); unsigned pi = g.get_predicate_index(); @@ -1481,7 +1481,7 @@ namespace datalog { void backtrack() { SASSERT(!m_clauses.empty()); - m_clauses.pop_back(); + m_clauses.pop_back(); if (m_clauses.empty()) { m_instruction = tb::SATISFIABLE; } @@ -1500,16 +1500,16 @@ namespace datalog { return l_undef; } switch(m_instruction) { - case tb::SELECT_PREDICATE: - select_predicate(); + case tb::SELECT_PREDICATE: + select_predicate(); break; - case tb::SELECT_RULE: - select_rule(); + case tb::SELECT_RULE: + select_rule(); break; case tb::BACKTRACK: backtrack(); break; - case tb::SATISFIABLE: + case tb::SATISFIABLE: m_status = l_false; return l_false; case tb::UNSATISFIABLE: @@ -1522,18 +1522,18 @@ namespace datalog { return l_undef; } } - } + } bool query_is_tautology(tb::clause const& g) { expr_ref fml = g.to_formula(); fml = m.mk_not(fml); m_solver.push(); m_solver.assert_expr(fml); - lbool is_sat = m_solver.check(); + lbool is_sat = m_solver.check(); m_solver.pop(1); TRACE("dl", tout << is_sat << ":\n" << mk_pp(fml, m) << "\n";); - + return l_false == is_sat; } @@ -1560,7 +1560,7 @@ namespace datalog { void display_premise(tb::clause& p, std::ostream& out) { func_decl* f = p.get_predicate(p.get_predicate_index())->get_decl(); - out << "{g" << p.get_seqno() << " " << f->get_name() << " pos: " + out << "{g" << p.get_seqno() << " " << f->get_name() << " pos: " << p.get_predicate_index() << " rule: " << p.get_next_rule() << "}\n"; } @@ -1576,21 +1576,21 @@ namespace datalog { ref replayed_clause; replace_proof_converter pc(m); - // clause is a empty clause. + // clause is a empty clause. // Pretend it is asserted. // It gets replaced by premises. - SASSERT(clause->get_num_predicates() == 0); + SASSERT(clause->get_num_predicates() == 0); expr_ref root = clause->to_formula(); vector substs; - while (0 != clause->get_index()) { - SASSERT(clause->get_parent_index() < clause->get_index()); + while (0 != clause->get_index()) { + SASSERT(clause->get_parent_index() < clause->get_index()); unsigned p_index = clause->get_parent_index(); unsigned p_rule = clause->get_parent_rule(); ref parent = m_clauses[p_index]; unsigned pi = parent->get_predicate_index(); func_decl* pred = parent->get_predicate(pi)->get_decl(); - ref rl = m_rules.get_rule(pred, p_rule); + ref rl = m_rules.get_rule(pred, p_rule); VERIFY(m_unifier(parent, parent->get_predicate_index(), rl, true, replayed_clause)); expr_ref_vector s1(m_unifier.get_rule_subst(true)); expr_ref_vector s2(m_unifier.get_rule_subst(false)); @@ -1614,36 +1614,36 @@ namespace datalog { } expr_ref body = clause.get_body(); var_subst vs(m, false); - vs(body, subst.size(), subst.c_ptr(), body); + vs(body, subst.size(), subst.c_ptr(), body); out << mk_pp(body, m) << "\n"; } - void resolve_rule(replace_proof_converter& pc, tb::clause const& r1, tb::clause const& r2, + 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 fml = res.to_formula(); vector substs; svector > positions; substs.push_back(s1); - substs.push_back(s2); + substs.push_back(s2); scoped_proof _sc(m); proof_ref pr(m); proof_ref_vector premises(m); 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)); + positions.push_back(std::make_pair(idx+1, 0)); 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)) { + m_imp(alloc(imp, ctx)) { } tab::~tab() { dealloc(m_imp); - } + } lbool tab::query(expr* query) { return m_imp->query(query); } diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index 2b73381a9..eccc2d0c7 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -48,8 +48,8 @@ class is_variable_test : public is_variable_proc { is_var_kind m_var_kind; public: is_variable_test(uint_set const& vars, bool index_of_bound) : - m_var_set(vars), - m_num_decls(0), + m_var_set(vars), + m_num_decls(0), m_var_kind(index_of_bound?BY_VAR_SET:BY_VAR_SET_COMPLEMENT) {} is_variable_test(unsigned num_decls) : @@ -83,7 +83,7 @@ namespace eq { is_variable_proc* m_is_variable; var_subst m_subst; expr_ref_vector m_new_exprs; - + ptr_vector m_map; int_vector m_pos2var; ptr_vector m_inx2var; @@ -91,10 +91,11 @@ namespace eq { expr_ref_vector m_subst_map; expr_ref_buffer m_new_args; th_rewriter m_rewriter; - + params_ref m_params; + void der_sort_vars(ptr_vector & vars, ptr_vector & definitions, unsigned_vector & order) { order.reset(); - + // eliminate self loops, and definitions containing quantifiers. bool found = false; for (unsigned i = 0; i < definitions.size(); i++) { @@ -105,18 +106,18 @@ namespace eq { else found = true; // found at least one candidate } - + if (!found) return; - + typedef std::pair frame; svector todo; - + expr_fast_mark1 visiting; expr_fast_mark2 done; - + unsigned vidx, num; - + for (unsigned i = 0; i < definitions.size(); i++) { if (definitions[i] == 0) continue; @@ -193,11 +194,11 @@ namespace eq { } } } - + bool is_variable(expr * e) const { return (*m_is_variable)(e); } - + bool is_neg_var(ast_manager & m, expr * e, var*& v) { expr* e1; if (m.is_not(e, e1) && is_variable(e1)) { @@ -208,13 +209,13 @@ namespace eq { return false; } } - - + + /** - \brief Return true if e can be viewed as a variable disequality. + \brief Return true if e can be viewed as a variable disequality. Store the variable id in v and the definition in t. For example: - + if e is (not (= (VAR 1) T)), then v assigned to 1, and t to T. if e is (iff (VAR 2) T), then v is assigned to 2, and t to (not T). (not T) is used because this formula is equivalent to (not (iff (VAR 2) (not T))), @@ -225,7 +226,7 @@ namespace eq { if (m.is_not(e, e1)) { return is_var_eq(e, vs, ts); } - else if (is_var_eq(e, vs, ts) && vs.size() == 1 && m.is_bool(vs[0])) { + else if (is_var_eq(e, vs, ts) && vs.size() == 1 && m.is_bool(vs[0])) { expr_ref tmp(m); bool_rewriter(m).mk_not(ts[0].get(), tmp); ts[0] = tmp; @@ -305,7 +306,7 @@ namespace eq { todo.pop_back(); if (a.is_add(e)) { for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { - todo.push_back(std::make_pair(sign, to_app(e)->get_arg(i))); + todo.push_back(std::make_pair(sign, to_app(e)->get_arg(i))); } } else if (is_invertible_mul(is_int, e, a_val)) { @@ -322,7 +323,7 @@ namespace eq { } return false; } - + bool arith_solve(expr * lhs, expr * rhs, expr * eq, ptr_vector& vs, expr_ref_vector& ts) { return solve_arith(lhs, rhs, vs, ts); } @@ -339,7 +340,7 @@ namespace eq { TRACE("qe_lite", tout << mk_pp(eq, m) << "\n";); return true; } - + bool same_vars(ptr_vector const& vs1, ptr_vector const& vs2) const { if (vs1.size() != vs2.size()) { @@ -356,12 +357,12 @@ namespace eq { /** \brief Return true if e can be viewed as a variable equality. */ - + 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 + + // (= 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)) { @@ -384,7 +385,7 @@ namespace eq { } return false; } - + // (ite cond (= VAR t) (= VAR t2)) case expr* cond, *e2, *e3; if (m.is_ite(e, cond, e2, e3)) { @@ -400,7 +401,7 @@ namespace eq { } return false; } - + // VAR = true case if (is_variable(e)) { ts.push_back(m.mk_true()); @@ -408,7 +409,7 @@ namespace eq { TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); return true; } - + // VAR = false case if (is_neg_var(m, e, v)) { ts.push_back(m.mk_false()); @@ -416,56 +417,56 @@ namespace eq { TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); return true; } - + return false; } - - + + bool is_var_def(bool check_eq, expr* e, ptr_vector& vs, expr_ref_vector& ts) { if (check_eq) { return is_var_eq(e, vs, ts); } else { return is_var_diseq(e, vs, ts); - } + } } - + void get_elimination_order() { m_order.reset(); - + TRACE("top_sort", tout << "DEFINITIONS: " << std::endl; for(unsigned i = 0; i < m_map.size(); i++) if(m_map[i]) tout << "VAR " << i << " = " << mk_pp(m_map[i], m) << std::endl; ); - + der_sort_vars(m_inx2var, m_map, m_order); - - TRACE("qe_lite", + + TRACE("qe_lite", tout << "Elimination m_order:" << std::endl; for(unsigned i=0; iget_expr(); if ((q->is_forall() && m.is_or(e)) || @@ -474,15 +475,15 @@ namespace eq { args = to_app(e)->get_args(); } } - + void apply_substitution(quantifier * q, expr_ref & r) { - + expr * e = q->get_expr(); unsigned num_args = 1; expr* const* args = &e; flatten_args(q, num_args, args); bool_rewriter rw(m); - + // get a new expression m_new_args.reset(); for(unsigned i = 0; i < num_args; i++) { @@ -495,7 +496,7 @@ namespace eq { r = q; return; } - + expr_ref t(m); if (q->is_forall()) { rw.mk_or(m_new_args.size(), m_new_args.c_ptr(), t); @@ -503,9 +504,9 @@ namespace eq { else { rw.mk_and(m_new_args.size(), m_new_args.c_ptr(), t); } - expr_ref new_e(m); + expr_ref new_e(m); m_subst(t, m_subst_map.size(), m_subst_map.c_ptr(), new_e); - + // don't forget to update the quantifier patterns expr_ref_buffer new_patterns(m); expr_ref_buffer new_no_patterns(m); @@ -514,17 +515,17 @@ namespace eq { m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_pat); new_patterns.push_back(new_pat); } - + for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { expr_ref new_nopat(m); m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_nopat); new_no_patterns.push_back(new_nopat); } - - r = m.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), + + r = m.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), new_no_patterns.size(), new_no_patterns.c_ptr(), new_e); } - + void reduce_quantifier1(quantifier * q, expr_ref & r, proof_ref & pr) { expr * e = q->get_expr(); is_variable_test is_v(q->get_num_decls()); @@ -532,17 +533,17 @@ namespace eq { unsigned num_args = 1; expr* const* args = &e; flatten_args(q, num_args, args); - + unsigned def_count = 0; unsigned largest_vinx = 0; - + find_definitions(num_args, args, q->is_exists(), def_count, largest_vinx); - + if (def_count > 0) { get_elimination_order(); SASSERT(m_order.size() <= def_count); // some might be missing because of cycles - - if (!m_order.empty()) { + + if (!m_order.empty()) { create_substitution(largest_vinx + 1); apply_substitution(q, r); } @@ -554,31 +555,32 @@ namespace eq { TRACE("der_bug", tout << "Did not find any diseq\n" << mk_pp(q, m) << "\n";); r = q; } - + if (m.proofs_enabled()) { pr = r == q ? 0 : m.mk_der(q, r); - } - } - + } + } + void elim_unused_vars(expr_ref& r, proof_ref &pr) { if (is_quantifier(r)) { quantifier * q = to_quantifier(r); - ::elim_unused_vars(m, q, r); + + ::elim_unused_vars(m, q, m_params, r); if (m.proofs_enabled()) { proof * p1 = m.mk_elim_unused_vars(q, r); pr = m.mk_transitivity(pr, p1); } } } - + void find_definitions(unsigned num_args, expr* const* args, bool is_exists, unsigned& def_count, unsigned& largest_vinx) { def_count = 0; largest_vinx = 0; m_map.reset(); m_pos2var.reset(); - m_inx2var.reset(); + m_inx2var.reset(); m_pos2var.reserve(num_args, -1); - + // Find all definitions for (unsigned i = 0; i < num_args; i++) { checkpoint(); @@ -591,12 +593,12 @@ namespace eq { unsigned idx = v->get_idx(); if (m_map.get(idx, 0) == 0) { m_map.reserve(idx + 1, 0); - m_inx2var.reserve(idx + 1, 0); + m_inx2var.reserve(idx + 1, 0); m_map[idx] = t; m_inx2var[idx] = v; m_pos2var[i] = idx; def_count++; - largest_vinx = std::max(idx, largest_vinx); + largest_vinx = std::max(idx, largest_vinx); m_new_exprs.push_back(t); } } @@ -646,10 +648,10 @@ namespace eq { tmp = m.mk_and(conjs.size(), conjs.c_ptr()); tout << "after flatten\n" << mk_pp(tmp, m) << "\n";); } - + void flatten_constructor(app* c, app* r, expr_ref_vector& conjs) { SASSERT(dt.is_constructor(c)); - + func_decl* d = c->get_decl(); if (dt.is_constructor(r->get_decl())) { @@ -661,7 +663,7 @@ namespace eq { } else { conjs.push_back(m.mk_false()); - } + } } else { func_decl* rec = dt.get_constructor_recognizer(d); @@ -683,7 +685,7 @@ namespace eq { bool remove_unconstrained(expr_ref_vector& conjs) { bool reduced = false, change = true; - expr* r, *l, *ne; + expr* r, *l, *ne; while (change) { change = false; for (unsigned i = 0; i < conjs.size(); ++i) { @@ -704,21 +706,21 @@ namespace eq { } return reduced; } - + bool reduce_var_set(expr_ref_vector& conjs) { unsigned def_count = 0; unsigned largest_vinx = 0; bool reduced = false; flatten_definitions(conjs); - + find_definitions(conjs.size(), conjs.c_ptr(), true, def_count, largest_vinx); - + if (def_count > 0) { get_elimination_order(); SASSERT(m_order.size() <= def_count); // some might be missing because of cycles - - if (!m_order.empty()) { + + if (!m_order.empty()) { expr_ref r(m), new_r(m); r = m.mk_and(conjs.size(), conjs.c_ptr()); create_substitution(largest_vinx + 1); @@ -739,35 +741,36 @@ namespace eq { void checkpoint() { cooperate("der"); - if (m.canceled()) + if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } public: - der(ast_manager & m): - m(m), + der(ast_manager & m, params_ref const & p): + m(m), a(m), dt(m), - m_is_variable(0), - m_subst(m), - m_new_exprs(m), - m_subst_map(m), - m_new_args(m), - m_rewriter(m) {} - + m_is_variable(0), + m_subst(m), + m_new_exprs(m), + m_subst_map(m), + m_new_args(m), + m_rewriter(m), + m_params(p) {} + void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} - + void operator()(quantifier * q, expr_ref & r, proof_ref & pr) { - TRACE("qe_lite", tout << mk_pp(q, m) << "\n";); + TRACE("qe_lite", tout << mk_pp(q, m) << "\n";); pr = 0; r = q; - reduce_quantifier(q, r, pr); + reduce_quantifier(q, r, pr); if (r != q) { elim_unused_vars(r, pr); } } - - void reduce_quantifier(quantifier * q, expr_ref & r, proof_ref & pr) { + + void reduce_quantifier(quantifier * q, expr_ref & r, proof_ref & pr) { r = q; // Keep applying reduce_quantifier1 until r doesn't change anymore do { @@ -779,15 +782,15 @@ namespace eq { pr = m.mk_transitivity(pr, curr_pr); } } while (q != r && is_quantifier(r)); - + m_new_exprs.reset(); } - + void operator()(expr_ref_vector& r) { while (reduce_var_set(r)) ; m_new_exprs.reset(); } - + ast_manager& get_manager() const { return m; } @@ -804,7 +807,7 @@ namespace ar { is_variable_proc* m_is_variable; ptr_vector m_todo; expr_mark m_visited; - + bool is_variable(expr * e) const { return (*m_is_variable)(e); } @@ -827,7 +830,7 @@ namespace ar { 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. 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)] @@ -873,7 +876,7 @@ namespace ar { bool solve_select(expr_ref_vector& conjs, unsigned i, expr* e) { expr* e1, *e2; - return + return m.is_eq(e, e1, e2) && (solve_select(conjs, i, e1, e2) || solve_select(conjs, i, e2, e1)); @@ -887,8 +890,8 @@ namespace ar { bool solve_neq_select(expr_ref_vector& conjs, unsigned i, expr* e) { expr* e1, *a1, *a2; if (m.is_not(e, e1) && m.is_eq(e1, a1, a2)) { - if (a.is_select(a1) && - a.is_select(a2) && + if (a.is_select(a1) && + a.is_select(a2) && to_app(a1)->get_num_args() == to_app(a2)->get_num_args()) { expr* e1 = to_app(a1)->get_arg(0); expr* e2 = to_app(a2)->get_arg(0); @@ -937,7 +940,7 @@ namespace ar { void operator()(expr* e) {} void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} - + }; }; // namespace ar @@ -976,27 +979,27 @@ namespace fm { for (; it != end; ++it) it->~rational(); } - + unsigned hash() const { return hash_u(m_id); } }; - + typedef ptr_vector constraints; - + class constraint_set { - unsigned_vector m_id2pos; + unsigned_vector m_id2pos; constraints m_set; public: typedef constraints::const_iterator iterator; - - bool contains(constraint const & c) const { - if (c.m_id >= m_id2pos.size()) - return false; - return m_id2pos[c.m_id] != UINT_MAX; + + bool contains(constraint const & c) const { + if (c.m_id >= m_id2pos.size()) + return false; + return m_id2pos[c.m_id] != UINT_MAX; } - + bool empty() const { return m_set.empty(); } unsigned size() const { return m_set.size(); } - + void insert(constraint & c) { unsigned id = c.m_id; m_id2pos.reserve(id+1, UINT_MAX); @@ -1006,7 +1009,7 @@ namespace fm { m_id2pos[id] = pos; m_set.push_back(&c); } - + void erase(constraint & c) { unsigned id = c.m_id; if (id >= m_id2pos.size()) @@ -1018,27 +1021,27 @@ namespace fm { unsigned last_pos = m_set.size() - 1; if (pos != last_pos) { constraint * last_c = m_set[last_pos]; - m_set[pos] = last_c; + m_set[pos] = last_c; m_id2pos[last_c->m_id] = pos; } m_set.pop_back(); } - + constraint & erase() { SASSERT(!empty()); - constraint & c = *m_set.back(); + constraint & c = *m_set.back(); m_id2pos[c.m_id] = UINT_MAX; m_set.pop_back(); return c; } - + void reset() { m_id2pos.reset(); m_set.reset(); } void finalize() { m_id2pos.finalize(); m_set.finalize(); } - + iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } }; - + class fm { ast_manager & m; is_variable_proc* m_is_variable; @@ -1068,24 +1071,24 @@ namespace fm { bool m_inconsistent; expr_dependency_ref m_inconsistent_core; constraint_set m_sub_todo; - + // --------------------------- // // OCC clause recognizer // // --------------------------- - + bool is_literal(expr * t) const { expr * atom; return is_uninterp_const(t) || (m.is_not(t, atom) && is_uninterp_const(atom)); } - + bool is_constraint(expr * t) const { return !is_literal(t); } - + bool is_var(expr * t, expr * & x) const { - + if ((*m_is_variable)(t)) { x = t; return true; @@ -1096,24 +1099,24 @@ namespace fm { } return false; } - + bool is_var(expr * t) const { expr * x; return is_var(t, x); } - + bool is_linear_mon_core(expr * t, expr * & x) const { expr * c; if (m_util.is_mul(t, c, x) && m_util.is_numeral(c) && is_var(x, x)) return true; return is_var(t, x); } - + bool is_linear_mon(expr * t) const { expr * x; return is_linear_mon_core(t, x); } - + bool is_linear_pol(expr * t) const { unsigned num_mons; expr * const * mons; @@ -1125,7 +1128,7 @@ namespace fm { num_mons = 1; mons = &t; } - + expr_fast_mark2 visited; bool all_forbidden = true; for (unsigned i = 0; i < num_mons; i++) { @@ -1141,7 +1144,7 @@ namespace fm { } return !all_forbidden; } - + bool is_linear_ineq(expr * t) const { bool result = false; m.is_not(t, t); @@ -1153,7 +1156,7 @@ namespace fm { return result; } - + bool is_occ(expr * t) { if (m_fm_occ && m.is_or(t)) { unsigned num = to_app(t)->get_num_args(); @@ -1176,7 +1179,7 @@ namespace fm { } return is_linear_ineq(t); } - + // --------------------------- // // Memory mng @@ -1195,12 +1198,12 @@ namespace fm { for (unsigned i = 0; i < sz; i++) del_constraint(cs[i]); } - + void reset_constraints() { del_constraints(m_constraints.size(), m_constraints.c_ptr()); m_constraints.reset(); } - + constraint * mk_constraint(unsigned num_lits, literal * lits, unsigned num_vars, var * xs, rational * as, rational & c, bool strict, expr_dependency * dep) { unsigned sz = constraint::get_obj_size(num_lits, num_vars); @@ -1236,15 +1239,15 @@ namespace fm { m.inc_ref(dep); return cnstr; } - + // --------------------------- // // Util // // --------------------------- - + unsigned num_vars() const { return m_is_int.size(); } - + // multiply as and c, by the lcm of their denominators void mk_int(unsigned num, rational * as, rational & c) { rational l = denominator(c); @@ -1259,7 +1262,7 @@ namespace fm { SASSERT(as[i].is_int()); } } - + void normalize_coeffs(constraint & c) { if (c.m_num_vars == 0) return; @@ -1281,7 +1284,7 @@ namespace fm { for (unsigned i = 0; i < c.m_num_vars; i++) c.m_as[i] /= g; } - + void display(std::ostream & out, constraint const & c) const { for (unsigned i = 0; i < c.m_num_lits; i++) { literal l = c.m_lits[i]; @@ -1308,10 +1311,10 @@ namespace fm { out << c.m_c; out << ")"; } - + /** \brief Return true if c1 subsumes c2 - + c1 subsumes c2 If 1) All literals of c1 are literals of c2 2) polynomial of c1 == polynomial of c2 @@ -1329,13 +1332,13 @@ namespace fm { return false; if (!c1.m_strict && c2.m_strict && c1.m_c == c2.m_c) return false; - + m_counter += c1.m_num_lits + c2.m_num_lits; - + for (unsigned i = 0; i < c1.m_num_vars; i++) { m_var2pos[c1.m_xs[i]] = i; } - + bool failed = false; for (unsigned i = 0; i < c2.m_num_vars; i++) { unsigned pos1 = m_var2pos[c2.m_xs[i]]; @@ -1344,21 +1347,21 @@ namespace fm { break; } } - + for (unsigned i = 0; i < c1.m_num_vars; i++) { m_var2pos[c1.m_xs[i]] = UINT_MAX; } - + if (failed) return false; - + for (unsigned i = 0; i < c2.m_num_lits; i++) { literal l = c2.m_lits[i]; bvar b = lit2bvar(l); SASSERT(m_bvar2sign[b] == 0); m_bvar2sign[b] = sign(l) ? -1 : 1; } - + for (unsigned i = 0; i < c1.m_num_lits; i++) { literal l = c1.m_lits[i]; bvar b = lit2bvar(l); @@ -1368,19 +1371,19 @@ namespace fm { break; } } - + for (unsigned i = 0; i < c2.m_num_lits; i++) { literal l = c2.m_lits[i]; bvar b = lit2bvar(l); m_bvar2sign[b] = 0; } - + if (failed) return false; - + return true; } - + void backward_subsumption(constraint const & c) { if (c.m_num_vars == 0) return; @@ -1422,7 +1425,7 @@ namespace fm { } cs.set_end(it2); } - + void subsume() { while (!m_sub_todo.empty()) { constraint & c = m_sub_todo.erase(); @@ -1433,13 +1436,13 @@ namespace fm { } public: - + // --------------------------- // // Initialization // // --------------------------- - + fm(ast_manager & _m): m(_m), m_is_variable(0), @@ -1453,11 +1456,11 @@ namespace fm { m_counter = 0; m_inconsistent = false; } - + ~fm() { reset_constraints(); } - + void updt_params() { m_fm_real_only = false; m_fm_limit = 5000000; @@ -1466,9 +1469,9 @@ namespace fm { m_fm_extra = 0; m_fm_occ = true; } - + private: - + struct forbidden_proc { fm & m_owner; forbidden_proc(fm & o):m_owner(o) {} @@ -1480,7 +1483,7 @@ namespace fm { void operator()(app * n) { } void operator()(quantifier * n) {} }; - + void init_forbidden_set(expr_ref_vector const & g) { m_forbidden_set.reset(); expr_fast_mark1 visited; @@ -1494,7 +1497,7 @@ namespace fm { quick_for_each_expr(proc, visited, f); } } - + void init(expr_ref_vector const & g) { m_sub_todo.reset(); m_id_gen.reset(); @@ -1517,24 +1520,24 @@ namespace fm { m_inconsistent_core = 0; init_forbidden_set(g); } - + // --------------------------- // // Internal data-structures // // --------------------------- - + static bool sign(literal l) { return l < 0; } static bvar lit2bvar(literal l) { return l < 0 ? -l : l; } - - bool is_int(var x) const { + + bool is_int(var x) const { return m_is_int[x] != 0; } - + bool is_forbidden(var x) const { return m_forbidden[x] != 0; } - + bool all_int(constraint const & c) const { for (unsigned i = 0; i < c.m_num_vars; i++) { if (!is_int(c.m_xs[i])) @@ -1542,7 +1545,7 @@ namespace fm { } return true; } - + app * to_expr(constraint const & c) { expr * ineq; if (c.m_num_vars == 0) { @@ -1577,20 +1580,20 @@ namespace fm { ineq = m_util.mk_le(lhs, rhs); } } - + if (c.m_num_lits == 0) { if (ineq) return to_app(ineq); else return m.mk_false(); } - + ptr_buffer lits; for (unsigned i = 0; i < c.m_num_lits; i++) { literal l = c.m_lits[i]; if (sign(l)) lits.push_back(m.mk_not(m_bvar2expr.get(lit2bvar(l)))); - else + else lits.push_back(m_bvar2expr.get(lit2bvar(l))); } if (ineq) @@ -1600,7 +1603,7 @@ namespace fm { else return m.mk_or(lits.size(), lits.c_ptr()); } - + var mk_var(expr * t) { SASSERT(::is_var(t)); SASSERT(m_util.is_int(t) || m_util.is_real(t)); @@ -1617,12 +1620,12 @@ namespace fm { SASSERT(m_var2expr.size() == m_is_int.size()); SASSERT(m_lowers.size() == m_is_int.size()); SASSERT(m_uppers.size() == m_is_int.size()); - SASSERT(m_forbidden.size() == m_is_int.size()); + SASSERT(m_forbidden.size() == m_is_int.size()); SASSERT(m_var2pos.size() == m_is_int.size()); TRACE("qe_lite", tout << mk_pp(t,m) << " |-> " << x << " forbidden: " << forbidden << "\n";); return x; } - + bvar mk_bvar(expr * t) { SASSERT(is_uninterp_const(t)); SASSERT(m.is_bool(t)); @@ -1634,7 +1637,7 @@ namespace fm { SASSERT(p > 0); return p; } - + var to_var(expr * t) { var x; if (!m_expr2var.find(t, x)) @@ -1644,22 +1647,22 @@ namespace fm { TRACE("qe_lite", tout << mk_ismt2_pp(t, m) << " --> " << x << "\n";); return x; } - + bvar to_bvar(expr * t) { bvar p; if (m_expr2bvar.find(t, p)) return p; return mk_bvar(t); } - + literal to_literal(expr * t) { if (m.is_not(t, t)) - return -to_bvar(t); + return -to_bvar(t); else return to_bvar(t); } - - + + void add_constraint(expr * f, expr_dependency * dep) { TRACE("qe_lite", tout << mk_pp(f, m) << "\n";); SASSERT(!m.is_or(f) || m_fm_occ); @@ -1711,7 +1714,7 @@ namespace fm { num_mons = 1; mons = &lhs; } - + bool all_int = true; for (unsigned j = 0; j < num_mons; j++) { expr * monomial = mons[j]; @@ -1740,9 +1743,9 @@ namespace fm { } } } - + TRACE("qe_lite", tout << "before mk_constraint: "; for (unsigned i = 0; i < xs.size(); i++) tout << " " << xs[i]; tout << "\n";); - + constraint * new_c = mk_constraint(lits.size(), lits.c_ptr(), xs.size(), @@ -1751,15 +1754,15 @@ namespace fm { c, strict, dep); - + TRACE("qe_lite", tout << "add_constraint: "; display(tout, *new_c); tout << "\n";); VERIFY(register_constraint(new_c)); } - + bool is_false(constraint const & c) const { return c.m_num_lits == 0 && c.m_num_vars == 0 && (c.m_c.is_neg() || (c.m_strict && c.m_c.is_zero())); } - + bool register_constraint(constraint * c) { normalize_coeffs(*c); if (is_false(*c)) { @@ -1768,20 +1771,20 @@ namespace fm { TRACE("qe_lite", tout << "is false "; display(tout, *c); tout << "\n";); return false; } - + bool r = false; - + for (unsigned i = 0; i < c->m_num_vars; i++) { var x = c->m_xs[i]; if (!is_forbidden(x)) { r = true; - if (c->m_as[i].is_neg()) + if (c->m_as[i].is_neg()) m_lowers[x].push_back(c); else m_uppers[x].push_back(c); } } - + if (r) { m_sub_todo.insert(*c); m_constraints.push_back(c); @@ -1794,7 +1797,7 @@ namespace fm { return false; } } - + void init_use_list(expr_ref_vector const & g) { unsigned sz = g.size(); for (unsigned i = 0; !m_inconsistent && i < sz; i++) { @@ -1812,13 +1815,13 @@ namespace fm { return UINT_MAX; return static_cast(r); } - + typedef std::pair x_cost; - + struct x_cost_lt { char_vector const m_is_int; x_cost_lt(char_vector & is_int):m_is_int(is_int) {} - bool operator()(x_cost const & p1, x_cost const & p2) const { + bool operator()(x_cost const & p1, x_cost const & p2) const { // Integer variables with cost 0 can be eliminated even if they depend on real variables. // Cost 0 == no lower or no upper bound. if (p1.second == 0) { @@ -1828,7 +1831,7 @@ namespace fm { if (p2.second == 0) return false; bool int1 = m_is_int[p1.first] != 0; bool int2 = m_is_int[p2.first] != 0; - return (!int1 && int2) || (int1 == int2 && p1.second < p2.second); + return (!int1 && int2) || (int1 == int2 && p1.second < p2.second); } }; @@ -1842,7 +1845,7 @@ namespace fm { } // x_cost_lt is not a total order on variables std::stable_sort(x_cost_vector.begin(), x_cost_vector.end(), x_cost_lt(m_is_int)); - TRACE("qe_lite", + TRACE("qe_lite", svector::iterator it2 = x_cost_vector.begin(); svector::iterator end2 = x_cost_vector.end(); for (; it2 != end2; ++it2) { @@ -1855,7 +1858,7 @@ namespace fm { xs.push_back(it2->first); } } - + void cleanup_constraints(constraints & cs) { unsigned j = 0; unsigned sz = cs.size(); @@ -1868,7 +1871,7 @@ namespace fm { } cs.shrink(j); } - + // Set all_int = true if all variables in c are int. // Set unit_coeff = true if the coefficient of x in c is 1 or -1. // If all_int = false, then unit_coeff may not be set. @@ -1900,8 +1903,8 @@ namespace fm { unit_coeff = false; } } - - // An integer variable x may be eliminated, if + + // An integer variable x may be eliminated, if // 1- All variables in the contraints it occur are integer. // 2- The coefficient of x in all lower bounds (or all upper bounds) is unit. bool can_eliminate(var x) const { @@ -1915,7 +1918,7 @@ namespace fm { analyze(m_uppers[x], x, all_int, u_unit); return all_int && (l_unit || u_unit); } - + void copy_constraints(constraints const & s, clauses & t) { constraints::const_iterator it = s.begin(); constraints::const_iterator end = s.end(); @@ -1924,23 +1927,23 @@ namespace fm { t.push_back(c); } } - + clauses tmp_clauses; void save_constraints(var x) { } - + void mark_constraints_dead(constraints const & cs) { constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); for (; it != end; ++it) (*it)->m_dead = true; } - + void mark_constraints_dead(var x) { save_constraints(x); mark_constraints_dead(m_lowers[x]); mark_constraints_dead(m_uppers[x]); } - + void get_coeff(constraint const & c, var x, rational & a) { for (unsigned i = 0; i < c.m_num_vars; i++) { if (c.m_xs[i] == x) { @@ -1950,11 +1953,11 @@ namespace fm { } UNREACHABLE(); } - + var_vector new_xs; vector new_as; svector new_lits; - + constraint * resolve(constraint const & l, constraint const & u, var x) { m_counter += l.m_num_vars + u.m_num_vars + l.m_num_lits + u.m_num_lits; rational a, b; @@ -1963,14 +1966,14 @@ namespace fm { SASSERT(a.is_neg()); SASSERT(b.is_pos()); a.neg(); - + SASSERT(!is_int(x) || a.is_one() || b.is_one()); - + new_xs.reset(); new_as.reset(); rational new_c = l.m_c*b + u.m_c*a; bool new_strict = l.m_strict || u.m_strict; - + for (unsigned i = 0; i < l.m_num_vars; i++) { var xi = l.m_xs[i]; if (xi == x) @@ -1983,7 +1986,7 @@ namespace fm { SASSERT(new_xs[m_var2pos[xi]] == xi); SASSERT(new_xs.size() == new_as.size()); } - + for (unsigned i = 0; i < u.m_num_vars; i++) { var xi = u.m_xs[i]; if (xi == x) @@ -1997,7 +2000,7 @@ namespace fm { new_as[pos] += u.m_as[i] * a; } } - + // remove zeros and check whether all variables are int bool all_int = true; unsigned sz = new_xs.size(); @@ -2015,17 +2018,17 @@ namespace fm { } new_xs.shrink(j); new_as.shrink(j); - + if (all_int && new_strict) { new_strict = false; new_c --; } - + // reset m_var2pos for (unsigned i = 0; i < l.m_num_vars; i++) { m_var2pos[l.m_xs[i]] = UINT_MAX; } - + if (new_xs.empty() && (new_c.is_pos() || (!new_strict && new_c.is_zero()))) { // literal is true TRACE("qe_lite", tout << "resolution " << x << " consequent literal is always true: \n"; @@ -2034,7 +2037,7 @@ namespace fm { display(tout, u); tout << "\n";); return 0; // no constraint needs to be created. } - + new_lits.reset(); for (unsigned i = 0; i < l.m_num_lits; i++) { literal lit = l.m_lits[i]; @@ -2042,7 +2045,7 @@ namespace fm { m_bvar2sign[p] = sign(lit) ? -1 : 1; new_lits.push_back(lit); } - + bool tautology = false; for (unsigned i = 0; i < u.m_num_lits && !tautology; i++) { literal lit = u.m_lits[i]; @@ -2063,14 +2066,14 @@ namespace fm { UNREACHABLE(); } } - + // reset m_bvar2sign for (unsigned i = 0; i < l.m_num_lits; i++) { literal lit = l.m_lits[i]; bvar p = lit2bvar(lit); m_bvar2sign[p] = 0; } - + if (tautology) { TRACE("qe_lite", tout << "resolution " << x << " tautology: \n"; display(tout, l); @@ -2080,7 +2083,7 @@ namespace fm { } expr_dependency * new_dep = m.mk_join(l.m_dep, u.m_dep); - + if (new_lits.empty() && new_xs.empty() && (new_c.is_neg() || (new_strict && new_c.is_zero()))) { TRACE("qe_lite", tout << "resolution " << x << " inconsistent: \n"; display(tout, l); @@ -2090,7 +2093,7 @@ namespace fm { m_inconsistent_core = new_dep; return 0; } - + constraint * new_cnstr = mk_constraint(new_lits.size(), new_lits.c_ptr(), new_xs.size(), @@ -2105,45 +2108,45 @@ namespace fm { tout << "\n"; display(tout, u); tout << "\n---->\n"; - display(tout, *new_cnstr); + display(tout, *new_cnstr); tout << "\n"; tout << "new_dep: " << new_dep << "\n";); - + return new_cnstr; } - + ptr_vector new_constraints; - + bool try_eliminate(var x) { constraints & l = m_lowers[x]; constraints & u = m_uppers[x]; cleanup_constraints(l); cleanup_constraints(u); - + if (l.empty() || u.empty()) { // easy case mark_constraints_dead(x); TRACE("qe_lite", tout << "variable was eliminated (trivial case)\n";); return true; } - + unsigned num_lowers = l.size(); unsigned num_uppers = u.size(); - + if (num_lowers > m_fm_cutoff1 && num_uppers > m_fm_cutoff1) return false; - + if (num_lowers * num_uppers > m_fm_cutoff2) return false; - + if (!can_eliminate(x)) return false; - + m_counter += num_lowers * num_uppers; - + TRACE("qe_lite", tout << "eliminating " << mk_ismt2_pp(m_var2expr.get(x), m) << "\nlowers:\n"; display_constraints(tout, l); tout << "uppers:\n"; display_constraints(tout, u);); - + unsigned num_old_cnstrs = num_uppers + num_lowers; unsigned limit = num_old_cnstrs + m_fm_extra; unsigned num_new_cnstrs = 0; @@ -2164,13 +2167,13 @@ namespace fm { } } } - + mark_constraints_dead(x); - + unsigned sz = new_constraints.size(); - + m_counter += sz; - + for (unsigned i = 0; i < sz; i++) { constraint * c = new_constraints[i]; backward_subsumption(*c); @@ -2179,7 +2182,7 @@ namespace fm { TRACE("qe_lite", tout << "variables was eliminated old: " << num_old_cnstrs << " new_constraints: " << sz << "\n";); return true; } - + void copy_remaining(vector & v2cs) { vector::iterator it = v2cs.begin(); vector::iterator end = v2cs.end(); @@ -2199,13 +2202,13 @@ namespace fm { } v2cs.finalize(); } - + // Copy remaining clauses to m_new_fmls void copy_remaining() { copy_remaining(m_uppers); copy_remaining(m_lowers); } - + void checkpoint() { cooperate("fm"); if (m.canceled()) @@ -2224,12 +2227,12 @@ namespace fm { } else { TRACE("qe_lite", display(tout);); - + subsume(); var_vector candidates; - sort_candidates(candidates); - unsigned eliminated = 0; - + sort_candidates(candidates); + unsigned eliminated = 0; + unsigned num = candidates.size(); for (unsigned i = 0; i < num; i++) { checkpoint(); @@ -2251,8 +2254,8 @@ namespace fm { reset_constraints(); fmls.reset(); fmls.append(m_new_fmls); - } - + } + void display_constraints(std::ostream & out, constraints const & cs) const { constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); @@ -2262,7 +2265,7 @@ namespace fm { out << "\n"; } } - + void display(std::ostream & out) const { unsigned num = num_vars(); for (var x = 0; x < num; x++) { @@ -2284,10 +2287,10 @@ public: ast_manager& m; public: elim_cfg(impl& i): m_imp(i), m(i.m) {} - - bool reduce_quantifier(quantifier * q, - expr * new_body, - expr * const * new_patterns, + + bool reduce_quantifier(quantifier * q, + expr * new_body, + expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { @@ -2299,13 +2302,13 @@ public: for (unsigned i = 0; i < q->get_num_decls(); ++i) { indices.insert(i); } - m_imp(indices, true, result); + m_imp(indices, true, result); if (is_forall(q)) { result = push_not(result); } result = m.update_quantifier( - q, - q->get_num_patterns(), new_patterns, + q, + q->get_num_patterns(), new_patterns, q->get_num_no_patterns(), new_no_patterns, result); m_imp.m_rewriter(result); return true; @@ -2315,7 +2318,7 @@ public: class elim_star : public rewriter_tpl { elim_cfg m_cfg; public: - elim_star(impl& i): + elim_star(impl& i): rewriter_tpl(i.m, false, m_cfg), m_cfg(i) {} @@ -2346,21 +2349,21 @@ private: } public: - impl(ast_manager& m): - m(m), - m_der(m), - m_fm(m), - m_array_der(m), - m_elim_star(*this), + impl(ast_manager & m, params_ref const & p): + m(m), + m_der(m, p), + m_fm(m), + m_array_der(m), + m_elim_star(*this), m_rewriter(m) {} - + void operator()(app_ref_vector& vars, expr_ref& fml) { if (vars.empty()) { return; } expr_ref tmp(fml); quantifier_ref q(m); - proof_ref pr(m); + proof_ref pr(m); symbol qe_lite("QE"); expr_abstract(m, 0, vars.size(), (expr*const*)vars.c_ptr(), fml, tmp); ptr_vector sorts; @@ -2386,12 +2389,12 @@ public: ++j; } } - vars.resize(j); - } + vars.resize(j); + } else { fml = tmp; } - } + } void operator()(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); @@ -2438,8 +2441,8 @@ public: }; -qe_lite::qe_lite(ast_manager& m) { - m_impl = alloc(impl, m); +qe_lite::qe_lite(ast_manager & m, params_ref const & p) { + m_impl = alloc(impl, m, p); } qe_lite::~qe_lite() { @@ -2464,14 +2467,14 @@ void qe_lite::operator()(uint_set const& index_set, bool index_of_bound, expr_re } class qe_lite_tactic : public tactic { - + struct imp { ast_manager& m; qe_lite m_qe; - imp(ast_manager& m, params_ref const& p): + imp(ast_manager& m, params_ref const & p): m(m), - m_qe(m) + m_qe(m, p) {} void checkpoint() { @@ -2479,7 +2482,7 @@ class qe_lite_tactic : public tactic { throw tactic_exception(m.limit().get_cancel_msg()); cooperate("qe-lite"); } - + void debug_diff(expr* a, expr* b) { ptr_vector as, bs; as.push_back(a); @@ -2515,9 +2518,9 @@ class qe_lite_tactic : public tactic { } } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); @@ -2540,7 +2543,7 @@ class qe_lite_tactic : public tactic { if (produce_proofs) { 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); + new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } else { new_pr = g->pr(i); @@ -2548,7 +2551,7 @@ class qe_lite_tactic : public tactic { } if (f != new_f) { TRACE("qe", tout << mk_pp(f, m) << "\n" << new_f << "\n";); - g->update(i, new_f, new_pr, g->dep(i)); + g->update(i, new_f, new_pr, g->dep(i)); } } g->inc_depth(); @@ -2558,7 +2561,7 @@ class qe_lite_tactic : public tactic { } }; - + params_ref m_params; imp * m_imp; @@ -2567,7 +2570,7 @@ public: m_params(p) { m_imp = alloc(imp, m, p); } - + virtual ~qe_lite_tactic() { dealloc(m_imp); } @@ -2581,20 +2584,20 @@ public: // m_imp->updt_params(p); } - + virtual void collect_param_descrs(param_descrs & r) { // m_imp->collect_param_descrs(r); } - - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, + + virtual void operator()(goal_ref const & in, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } - + virtual void collect_statistics(statistics & st) const { // m_imp->collect_statistics(st); } @@ -2603,13 +2606,13 @@ public: // m_imp->reset_statistics(); } - + virtual void cleanup() { ast_manager & m = m_imp->m; dealloc(m_imp); m_imp = alloc(imp, m, m_params); } - + }; tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p) { diff --git a/src/qe/qe_lite.h b/src/qe/qe_lite.h index 48874f5cf..cff547f36 100644 --- a/src/qe/qe_lite.h +++ b/src/qe/qe_lite.h @@ -7,7 +7,7 @@ Module Name: Abstract: - Light weight partial quantifier-elimination procedures + Light weight partial quantifier-elimination procedures Author: @@ -31,14 +31,14 @@ class qe_lite { class impl; impl * m_impl; public: - qe_lite(ast_manager& m); + qe_lite(ast_manager & m, params_ref const & p); ~qe_lite(); /** \brief - Apply light-weight quantifier elimination - on constants provided as vector of variables. + Apply light-weight quantifier elimination + on constants provided as vector of variables. Return the updated formula and updated set of variables that were not eliminated. */ @@ -66,4 +66,4 @@ tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p = params_ref()) ADD_TACTIC("qe-light", "apply light-weight quantifier elimination.", "mk_qe_lite_tactic(m, p)") */ -#endif +#endif diff --git a/src/tactic/bv/elim_small_bv_tactic.cpp b/src/tactic/bv/elim_small_bv_tactic.cpp index e395433f5..8cfc27950 100644 --- a/src/tactic/bv/elim_small_bv_tactic.cpp +++ b/src/tactic/bv/elim_small_bv_tactic.cpp @@ -34,6 +34,7 @@ class elim_small_bv_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; + params_ref m_params; bv_util m_util; simplifier m_simp; ref m_mc; @@ -47,6 +48,7 @@ class elim_small_bv_tactic : public tactic { rw_cfg(ast_manager & _m, params_ref const & p) : m(_m), + m_params(p), m_util(_m), m_simp(_m), m_bindings(_m), @@ -119,7 +121,7 @@ class elim_small_bv_tactic : public tactic { return res; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { TRACE("elim_small_bv_app", expr_ref tmp(m.mk_app(f, num, args), m); tout << "reduce " << tmp << std::endl; ); return BR_FAILED; } @@ -178,7 +180,7 @@ class elim_small_bv_tactic : public tactic { quantifier_ref new_q(m); new_q = m.update_quantifier(q, body); - unused_vars_eliminator el(m); + unused_vars_eliminator el(m, m_params); el(new_q, result); TRACE("elim_small_bv", tout << "elimination result: " << mk_ismt2_pp(result, m) << std::endl; ); @@ -203,6 +205,7 @@ class elim_small_bv_tactic : public tactic { } void updt_params(params_ref const & p) { + m_params = p; m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); m_max_bits = p.get_uint("max_bits", 4); @@ -305,7 +308,7 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); - std::swap(d, m_imp); + std::swap(d, m_imp); dealloc(d); } diff --git a/src/tactic/core/distribute_forall_tactic.cpp b/src/tactic/core/distribute_forall_tactic.cpp index 769f415f2..5d525a836 100644 --- a/src/tactic/core/distribute_forall_tactic.cpp +++ b/src/tactic/core/distribute_forall_tactic.cpp @@ -24,9 +24,9 @@ class distribute_forall_tactic : public tactic { ast_manager & m; rw_cfg(ast_manager & _m):m(_m) {} - bool reduce_quantifier(quantifier * old_q, - expr * new_body, - expr * const * new_patterns, + 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) { @@ -34,7 +34,7 @@ class distribute_forall_tactic : public tactic { if (!old_q->is_forall()) { return false; } - + if (m.is_not(new_body) && m.is_or(to_app(new_body)->get_arg(0))) { // (forall X (not (or F1 ... Fn))) // --> @@ -50,13 +50,13 @@ class distribute_forall_tactic : public tactic { quantifier_ref tmp_q(m); tmp_q = m.update_quantifier(old_q, not_arg); expr_ref new_q(m); - elim_unused_vars(m, tmp_q, new_q); + elim_unused_vars(m, tmp_q, params_ref(), new_q); new_args.push_back(new_q); } result = m.mk_and(new_args.size(), new_args.c_ptr()); return true; } - + if (m.is_and(new_body)) { // (forall X (and F1 ... Fn)) // --> @@ -70,20 +70,20 @@ class distribute_forall_tactic : public tactic { quantifier_ref tmp_q(m); tmp_q = m.update_quantifier(old_q, arg); expr_ref new_q(m); - elim_unused_vars(m, tmp_q, new_q); + elim_unused_vars(m, tmp_q, params_ref(), new_q); new_args.push_back(new_q); } result = m.mk_and(new_args.size(), new_args.c_ptr()); return true; } - + return false; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; - + rw(ast_manager & m, bool proofs_enabled): rewriter_tpl(m, proofs_enabled, m_cfg), m_cfg(m) { @@ -99,19 +99,19 @@ public: return alloc(distribute_forall_tactic); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, + virtual void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); ast_manager & m = g->m(); bool produce_proofs = g->proofs_enabled(); rw r(m, produce_proofs); - m_rw = &r; + m_rw = &r; mc = 0; pc = 0; core = 0; result.reset(); tactic_report report("distribute-forall", *g); - + expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); @@ -126,12 +126,12 @@ public: } g->update(idx, new_curr, new_pr, g->dep(idx)); } - + g->inc_depth(); result.push_back(g.get()); TRACE("distribute-forall", g->display(tout);); SASSERT(g->is_well_sorted()); - m_rw = 0; + m_rw = 0; } virtual void cleanup() {} diff --git a/src/tactic/ufbv/ufbv_rewriter.cpp b/src/tactic/ufbv/ufbv_rewriter.cpp index 40fdf5e3e..f5f18d234 100644 --- a/src/tactic/ufbv/ufbv_rewriter.cpp +++ b/src/tactic/ufbv/ufbv_rewriter.cpp @@ -49,7 +49,7 @@ ufbv_rewriter::~ufbv_rewriter() { bool ufbv_rewriter::is_demodulator(expr * e, expr_ref & large, expr_ref & small) const { if (e->get_kind() == AST_QUANTIFIER) { quantifier * q = to_quantifier(e); - if (q->is_forall()) { + if (q->is_forall()) { expr * qe = q->get_expr(); if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) { app * eq = to_app(q->get_expr()); @@ -61,7 +61,7 @@ bool ufbv_rewriter::is_demodulator(expr * e, expr_ref & large, expr_ref & small) << mk_pp(lhs, m_manager) << "\n" << mk_pp(rhs, m_manager) << "\n" << "subset: " << subset << ", smaller: " << smaller << "\n";); - // We only track uninterpreted functions, everything else is likely too expensive. + // We only track uninterpreted functions, everything else is likely too expensive. if ((subset == +1 || subset == +2) && smaller == +1) { if (is_uninterp(rhs)) { large = rhs; @@ -78,7 +78,7 @@ bool ufbv_rewriter::is_demodulator(expr * e, expr_ref & large, expr_ref & small) } #endif } - + if ((subset == -1 || subset == +2) && smaller == -1) { if (is_uninterp(lhs)) { large = lhs; @@ -113,13 +113,13 @@ bool ufbv_rewriter::is_demodulator(expr * e, expr_ref & large, expr_ref & small) return false; } -class var_set_proc { +class var_set_proc { uint_set & m_set; public: var_set_proc(uint_set &s):m_set(s) {} void operator()(var * n) { m_set.insert(n->get_idx()); } void operator()(quantifier * n) {} - void operator()(app * n) {} + void operator()(app * n) {} }; int ufbv_rewriter::is_subset(expr * e1, expr * e2) const { @@ -132,10 +132,10 @@ int ufbv_rewriter::is_subset(expr * e1, expr * e2) const { for_each_expr(proc1, e1); var_set_proc proc2(ev2); for_each_expr(proc2, e2); - - return (ev1==ev2 ) ? +2 : // We return +2 if the sets are equal. - (ev1.subset_of(ev2)) ? +1 : - (ev2.subset_of(ev1)) ? -1 : + + return (ev1==ev2 ) ? +2 : // We return +2 if the sets are equal. + (ev1.subset_of(ev2)) ? +1 : + (ev2.subset_of(ev1)) ? -1 : 0 ; } @@ -154,8 +154,8 @@ int ufbv_rewriter::is_smaller(expr * e1, expr * e2) const { else if (is_uninterp(e1) && !is_uninterp(e2)) return -1; - // two uninterpreted functions are ordered first by the number of - // arguments, then by their id. + // two uninterpreted functions are ordered first by the number of + // arguments, then by their id. if (is_uninterp(e1) && is_uninterp(e2)) { if (to_app(e1)->get_num_args() < to_app(e2)->get_num_args()) return +1; @@ -163,10 +163,10 @@ int ufbv_rewriter::is_smaller(expr * e1, expr * e2) const { return -1; else { unsigned a = to_app(e1)->get_decl()->get_id(); - unsigned b = to_app(e2)->get_decl()->get_id(); - if (a < b) + unsigned b = to_app(e2)->get_decl()->get_id(); + if (a < b) return +1; - else if (a > b) + else if (a > b) return -1; } } @@ -185,8 +185,8 @@ int ufbv_rewriter::is_smaller(expr * e1, expr * e2) const { default: UNREACHABLE(); } - return (sz1 == sz2) ? 0 : - (sz1 < sz2) ? +1 : + return (sz1 == sz2) ? 0 : + (sz1 < sz2) ? +1 : -1 ; } @@ -194,9 +194,9 @@ class max_var_id_proc { unsigned m_max_var_id; public: max_var_id_proc(void):m_max_var_id(0) {} - void operator()(var * n) { - if(n->get_idx() > m_max_var_id) - m_max_var_id = n->get_idx(); + void operator()(var * n) { + if(n->get_idx() > m_max_var_id) + m_max_var_id = n->get_idx(); } void operator()(quantifier * n) {} void operator()(app * n) {} @@ -206,7 +206,7 @@ public: unsigned ufbv_rewriter::max_var_id(expr * e) { max_var_id_proc proc; - for_each_expr(proc, e); + for_each_expr(proc, e); return proc.get_max(); } @@ -219,14 +219,14 @@ void ufbv_rewriter::insert_fwd_idx(expr * large, expr * small, quantifier * demo func_decl * fd = to_app(large)->get_decl(); fwd_idx_map::iterator it = m_fwd_idx.find_iterator(fd); - if (it == m_fwd_idx.end()) { - quantifier_set * qs = alloc(quantifier_set, 1); + if (it == m_fwd_idx.end()) { + quantifier_set * qs = alloc(quantifier_set, 1); m_fwd_idx.insert(fd, qs); it = m_fwd_idx.find_iterator(fd); } SASSERT(it->m_value); - it->m_value->insert(demodulator); + it->m_value->insert(demodulator); m_manager.inc_ref(demodulator); m_manager.inc_ref(large); @@ -238,13 +238,13 @@ void ufbv_rewriter::remove_fwd_idx(func_decl * f, quantifier * demodulator) { TRACE("demodulator_fwd", tout << "REMOVE: " << std::hex << (size_t)demodulator << std::endl; ); fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f); - if (it != m_fwd_idx.end()) { + if (it != m_fwd_idx.end()) { demodulator2lhs_rhs::iterator fit = m_demodulator2lhs_rhs.find_iterator(demodulator); m_manager.dec_ref(fit->m_value.first); m_manager.dec_ref(fit->m_value.second); m_manager.dec_ref(demodulator); m_demodulator2lhs_rhs.erase(demodulator); - it->m_value->erase(demodulator); + it->m_value->erase(demodulator); } else { SASSERT(m_demodulator2lhs_rhs.contains(demodulator)); } @@ -281,13 +281,13 @@ void ufbv_rewriter::show_fwd_idx(std::ostream & out) { } } -bool ufbv_rewriter::rewrite1(func_decl * f, ptr_vector & m_new_args, expr_ref & np) { +bool ufbv_rewriter::rewrite1(func_decl * f, ptr_vector & m_new_args, expr_ref & np) { fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f); if (it != m_fwd_idx.end()) { TRACE("demodulator_bug", tout << "trying to rewrite: " << f->get_name() << " args:\n"; for (unsigned i = 0; i < m_new_args.size(); i++) { tout << mk_pp(m_new_args[i], m_manager) << "\n"; }); quantifier_set::iterator dit = it->m_value->begin(); - quantifier_set::iterator dend = it->m_value->end(); + quantifier_set::iterator dend = it->m_value->end(); for ( ; dit != dend ; dit++ ) { quantifier * d = *dit; @@ -302,7 +302,7 @@ bool ufbv_rewriter::rewrite1(func_decl * f, ptr_vector & m_new_args, expr_ TRACE("demodulator_bug", tout << "Matching with demodulator: " << mk_pp(d, m_manager) << std::endl; ); SASSERT(large->get_decl() == f); - + if (m_match_subst(large, l_s.second, m_new_args.c_ptr(), np)) { TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(l_s.second, m_manager) << "\n===>\n" << mk_pp(np, m_manager) << "\n";); return true; @@ -331,22 +331,22 @@ void ufbv_rewriter::rewrite_cache(expr * e, expr * new_e, bool done) { } expr * ufbv_rewriter::rewrite(expr * n) { - if (m_fwd_idx.empty()) + if (m_fwd_idx.empty()) return n; TRACE("demodulator", tout << "rewrite: " << mk_pp(n, m_manager) << std::endl; ); app * a; - + SASSERT(m_rewrite_todo.empty()); m_rewrite_cache.reset(); - + m_rewrite_todo.push_back(n); while (!m_rewrite_todo.empty()) { TRACE("demodulator_stack", tout << "STACK: " << std::endl; - for ( unsigned i = 0; iget_decl(); m_new_args.reset(); @@ -389,12 +389,12 @@ expr * ufbv_rewriter::rewrite(expr * n) { // No pop. } else { if(all_untouched) { - rewrite_cache(e, actual, true); - } + rewrite_cache(e, actual, true); + } else { expr_ref na(m_manager); if (f->get_family_id() != m_manager.get_basic_family_id()) - na = m_manager.mk_app(f, m_new_args.size(), m_new_args.c_ptr()); + na = m_manager.mk_app(f, m_new_args.size(), m_new_args.c_ptr()); else m_bsimp.reduce(f, m_new_args.size(), m_new_args.c_ptr(), na); TRACE("demodulator_bug", tout << "e:\n" << mk_pp(e, m_manager) << "\nnew_args: \n"; @@ -405,9 +405,9 @@ expr * ufbv_rewriter::rewrite(expr * n) { } m_rewrite_todo.pop_back(); } - } + } break; - case AST_QUANTIFIER: { + case AST_QUANTIFIER: { expr * body = to_quantifier(actual)->get_expr(); if (m_rewrite_cache.contains(body)) { const expr_bool_pair ebp = m_rewrite_cache.get(body); @@ -417,13 +417,13 @@ expr * ufbv_rewriter::rewrite(expr * n) { q = m_manager.update_quantifier(to_quantifier(actual), new_body); m_new_exprs.push_back(q); expr_ref new_q(m_manager); - elim_unused_vars(m_manager, q, new_q); + elim_unused_vars(m_manager, q, params_ref(), new_q); m_new_exprs.push_back(new_q); - rewrite_cache(e, new_q, true); + rewrite_cache(e, new_q, true); m_rewrite_todo.pop_back(); } else { m_rewrite_todo.push_back(body); - } + } break; } default: @@ -437,7 +437,7 @@ expr * ufbv_rewriter::rewrite(expr * n) { expr * r = ebp.first; TRACE("demodulator", tout << "rewrite result: " << mk_pp(r, m_manager) << std::endl; ); - + return r; } @@ -448,7 +448,7 @@ public: 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) { + void operator()(app * n) { // We track only uninterpreted and constant functions. if (n->get_num_args()==0) return; SASSERT(m_expr && m_expr != (expr*) 0x00000003); @@ -464,7 +464,7 @@ public: m_back_idx.insert(d, e); } } - } + } }; class ufbv_rewriter::remove_back_idx_proc { @@ -473,15 +473,15 @@ class ufbv_rewriter::remove_back_idx_proc { public: 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) { + void operator()(quantifier * n) {} + void operator()(app * n) { // We track only uninterpreted and constant functions. if (n->get_num_args()==0) return; func_decl * d=n->get_decl(); - if (d->get_family_id() == null_family_id) { + if (d->get_family_id() == null_family_id) { back_idx_map::iterator it = m_back_idx.find_iterator(d); if (it != m_back_idx.end()) { - SASSERT(it->m_value); + SASSERT(it->m_value); it->m_value->remove(m_expr); } } @@ -489,12 +489,12 @@ public: }; void ufbv_rewriter::reschedule_processed(func_decl * f) { - //use m_back_idx to find all formulas p in m_processed that contains f { + //use m_back_idx to find all formulas p in m_processed that contains f { back_idx_map::iterator it = m_back_idx.find_iterator(f); if (it != m_back_idx.end()) { SASSERT(it->m_value); expr_set temp; - + expr_set::iterator sit = it->m_value->begin(); expr_set::iterator send = it->m_value->end(); for ( ; sit != send ; sit++ ) { @@ -502,7 +502,7 @@ void ufbv_rewriter::reschedule_processed(func_decl * f) { if (m_processed.contains(p)) temp.insert(p); } - + sit = temp.begin(); send = temp.end(); for ( ; sit != send; sit++) { @@ -511,7 +511,7 @@ void ufbv_rewriter::reschedule_processed(func_decl * f) { m_processed.remove(p); 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 + // insert p into m_todo m_todo.push_back(p); } } @@ -529,40 +529,40 @@ bool ufbv_rewriter::can_rewrite(expr * n, expr * lhs) { while (!stack.empty()) { curr = stack.back(); - + if (visited.is_marked(curr)) { stack.pop_back(); continue; } switch(curr->get_kind()) { - case AST_VAR: + case AST_VAR: visited.mark(curr, true); stack.pop_back(); break; case AST_APP: - if (for_each_expr_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) { + if (for_each_expr_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) { if (m_match_subst(lhs, curr)) return true; visited.mark(curr, true); stack.pop_back(); } break; - + case AST_QUANTIFIER: - if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_patterns(), + if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_patterns(), to_quantifier(curr)->get_patterns())) { break; } - if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_no_patterns(), + if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_no_patterns(), to_quantifier(curr)->get_no_patterns())) { break; } if (!visited.is_marked(to_quantifier(curr)->get_expr())) { stack.push_back(to_quantifier(curr)->get_expr()); break; - } + } stack.pop_back(); break; @@ -597,7 +597,7 @@ void ufbv_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { expr * occ = *esit; if (!is_quantifier(occ)) - continue; + continue; // Use the fwd idx to find out whether this is a demodulator. demodulator2lhs_rhs::iterator d2lr_it = m_demodulator2lhs_rhs.find_iterator(to_quantifier(occ)); @@ -605,22 +605,22 @@ void ufbv_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { l = d2lr_it->m_value.first; quantifier_ref d(m_manager); func_decl_ref df(m_manager); - d = to_quantifier(occ); + d = to_quantifier(occ); df = to_app(l)->get_decl(); // Now we know there is an occurrence of f in d - // if n' can rewrite d { + // if n' can rewrite d { if (can_rewrite(d, lhs)) { TRACE("demodulator", tout << "Rescheduling: " << std::endl << mk_pp(d, m_manager) << std::endl; ); // remove d from m_fwd_idx remove_fwd_idx(df, d); - // remove d from m_back_idx + // remove d from m_back_idx // just remember it here, because otherwise it and/or esit might become invalid? - // to_remove.insert(d); + // to_remove.insert(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); + m_todo.push_back(d); } } } @@ -629,10 +629,10 @@ void ufbv_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { //for (ptr_vector::iterator it = to_remove.begin(); it != to_remove.end(); it++) { // expr * d = *it; // remove_back_idx_proc proc(m_manager, m_back_idx, d); - // for_each_expr(proc, d); + // for_each_expr(proc, d); //} } - + void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { if (m_manager.proofs_enabled()) { // Let us not waste time with proof production @@ -655,7 +655,7 @@ void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * m_match_subst.reserve(max_vid); - while (!m_todo.empty()) { + while (!m_todo.empty()) { // let n be the next formula in m_todo. expr_ref cur(m_manager); cur = m_todo.back(); @@ -670,21 +670,21 @@ void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * expr_ref large(m_manager), small(m_manager); if (!is_demodulator(np, large, small)) { // 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) + 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_back_idx, np); for_each_expr(proc, np); - } else { + } else { // np is a demodulator that allows us to replace 'large' with 'small'. TRACE("demodulator", tout << "Found demodulator: " << std::endl; - tout << mk_pp(large.get(), m_manager) << std::endl << " ---> " << + tout << mk_pp(large.get(), m_manager) << std::endl << " ---> " << std::endl << mk_pp(small.get(), m_manager) << std::endl; ); TRACE("demodulator_s", tout << "Found demodulator: " << std::endl; - tout << to_app(large)->get_decl()->get_name() << + tout << to_app(large)->get_decl()->get_name() << "[" << to_app(large)->get_depth() << "]" << " ---> "; if (is_app(small)) - tout << to_app(small)->get_decl()->get_name() << + tout << to_app(small)->get_decl()->get_name() << "[" << to_app(small)->get_depth() << "]" << std::endl; else tout << mk_pp(small.get(), m_manager) << std::endl; ); @@ -695,14 +695,14 @@ void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * reschedule_processed(f); reschedule_demodulators(f, large); - + // insert n' into m_fwd_idx insert_fwd_idx(large, small, to_quantifier(np)); // update m_back_idx add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); - } + } } // the result is the contents of m_processed + all demodulators in m_fwd_idx. @@ -743,10 +743,10 @@ ufbv_rewriter::match_subst::match_subst(ast_manager & m): */ struct match_args_aux_proc { substitution & m_subst; - struct no_match {}; - + struct no_match {}; + match_args_aux_proc(substitution & s):m_subst(s) {} - + void operator()(var * n) { expr_offset r; if (m_subst.find(n, 0, r)) { @@ -766,7 +766,7 @@ struct match_args_aux_proc { bool ufbv_rewriter::match_subst::match_args(app * lhs, expr * const * args) { m_cache.reset(); m_todo.reset(); - + // fill todo-list, and perform quick success/failure tests m_all_args_eq = true; unsigned num_args = lhs->get_num_args(); @@ -777,21 +777,21 @@ bool ufbv_rewriter::match_subst::match_args(app * lhs, expr * const * args) { m_all_args_eq = false; if (is_app(t_arg) && is_app(i_arg) && to_app(t_arg)->get_decl() != to_app(i_arg)->get_decl()) { // quick failure... - return false; + return false; } m_todo.push_back(expr_pair(t_arg, i_arg)); } - - if (m_all_args_eq) { + + if (m_all_args_eq) { // quick success worked... return true; } m_subst.reset(); - + while (!m_todo.empty()) { expr_pair const & p = m_todo.back(); - + if (is_var(p.first)) { expr_offset r; if (m_subst.find(to_var(p.first), 0, r)) { @@ -814,7 +814,7 @@ bool ufbv_rewriter::match_subst::match_args(app * lhs, expr * const * args) { SASSERT(is_app(p.first) && is_app(p.second)); - if (to_app(p.first)->is_ground() && !to_app(p.second)->is_ground()) + if (to_app(p.first)->is_ground() && !to_app(p.second)->is_ground()) return false; if (p.first == p.second && to_app(p.first)->is_ground()) { @@ -827,7 +827,7 @@ bool ufbv_rewriter::match_subst::match_args(app * lhs, expr * const * args) { m_todo.pop_back(); continue; } - + if (p.first == p.second) { // p.first and p.second is not ground... @@ -855,10 +855,10 @@ bool ufbv_rewriter::match_subst::match_args(app * lhs, expr * const * args) { app * n1 = to_app(p.first); app * n2 = to_app(p.second); - + if (n1->get_decl() != n2->get_decl()) return false; - + unsigned num_args1 = n1->get_num_args(); if (num_args1 != n2->get_num_args()) return false; @@ -867,7 +867,7 @@ bool ufbv_rewriter::match_subst::match_args(app * lhs, expr * const * args) { if (num_args1 == 0) continue; - + m_cache.insert(p); unsigned j = num_args1; while (j > 0) { @@ -886,7 +886,7 @@ bool ufbv_rewriter::match_subst::operator()(app * lhs, expr * rhs, expr * const new_rhs = rhs; return true; } - unsigned deltas[2] = { 0, 0 }; + unsigned deltas[2] = { 0, 0 }; m_subst.apply(2, deltas, expr_offset(rhs, 0), new_rhs); return true; } From ec29a03c8ff033fc8a38e98d439411012538b185 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 7 Apr 2017 21:22:38 -0700 Subject: [PATCH 058/105] add facility to dispense with cancellation (not activated at this point). Address #961 by expanding recurisve function definitions that are not tautologies if the current model does not validate Signed-off-by: Nikolaj Bjorner --- src/sat/sat_simplifier.cpp | 3 +- src/sat/sat_solver.cpp | 1 + src/sat/sat_solver.h | 12 +++++++ src/smt/smt_context_inv.cpp | 3 ++ src/smt/smt_model_checker.cpp | 62 ++++++++++++++++++++--------------- src/smt/smt_model_checker.h | 3 +- src/smt/smt_quantifier.cpp | 4 +++ src/smt/smt_quantifier.h | 2 ++ 8 files changed, 62 insertions(+), 28 deletions(-) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 007751220..923f5ae49 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -154,6 +154,8 @@ namespace sat { if (!m_subsumption && !m_elim_blocked_clauses && !m_resolution) return; + // solver::scoped_disable_checkpoint _scoped_disable_checkpoint(s); + initialize(); CASSERT("sat_solver", s.check_invariant()); @@ -167,7 +169,6 @@ namespace sat { CASSERT("sat_solver", s.check_invariant()); m_need_cleanup = false; m_use_list.init(s.num_vars()); - init_visited(); m_learned_in_use_lists = false; if (learned) { register_clauses(s.m_learned); diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 9c858a29a..08c70fba5 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -33,6 +33,7 @@ namespace sat { solver::solver(params_ref const & p, reslimit& l, extension * ext): m_rlimit(l), + m_checkpoint_enabled(true), m_config(p), m_ext(ext), m_par(0), diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 6c91565aa..42291609d 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -72,6 +72,7 @@ namespace sat { struct abort_solver {}; protected: reslimit& m_rlimit; + bool m_checkpoint_enabled; config m_config; stats m_stats; extension * m_ext; @@ -214,6 +215,16 @@ namespace sat { } } }; + class scoped_disable_checkpoint { + solver& s; + public: + scoped_disable_checkpoint(solver& s): s(s) { + s.m_checkpoint_enabled = false; + } + ~scoped_disable_checkpoint() { + s.m_checkpoint_enabled = true; + } + }; unsigned select_watch_lit(clause const & cls, unsigned starting_at) const; unsigned select_learned_watch_lit(clause const & cls) const; bool simplify_clause(unsigned & num_lits, literal * lits) const; @@ -257,6 +268,7 @@ namespace sat { lbool status(clause const & c) const; clause_offset get_offset(clause const & c) const { return m_cls_allocator.get_offset(&c); } void checkpoint() { + if (!m_checkpoint_enabled) return; if (!m_rlimit.inc()) { m_mc.reset(); m_model_is_current = false; diff --git a/src/smt/smt_context_inv.cpp b/src/smt/smt_context_inv.cpp index 7d009a037..f63e07b57 100644 --- a/src/smt/smt_context_inv.cpp +++ b/src/smt/smt_context_inv.cpp @@ -433,6 +433,9 @@ namespace smt { if (!is_ground(n)) { continue; } + if (is_quantifier(n) && m.is_rec_fun_def(to_quantifier(n))) { + continue; + } switch (get_assignment(*it)) { case l_undef: break; diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index dfdb035c5..279ea20cf 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -316,7 +316,7 @@ namespace smt { return false; } - bool model_checker::check_rec_fun(quantifier* q) { + bool model_checker::check_rec_fun(quantifier* q, bool strict_rec_fun) { TRACE("model_checker", tout << mk_pp(q, m) << "\n";); SASSERT(q->get_num_patterns() == 2); // first pattern is the function, second is the body. expr* fn = to_app(q->get_pattern(0))->get_arg(0); @@ -340,7 +340,7 @@ namespace smt { } sub(q->get_expr(), num_decls, args.c_ptr(), tmp); m_curr_model->eval(tmp, result, true); - if (m.is_false(result)) { + if (strict_rec_fun ? !m.is_true(result) : m.is_false(result)) { add_instance(q, args, 0); return false; } @@ -365,10 +365,10 @@ namespace smt { bool model_checker::check(proto_model * md, obj_map const & root2value) { SASSERT(md != 0); + m_root2value = &root2value; - ptr_vector::const_iterator it = m_qm->begin_quantifiers(); - ptr_vector::const_iterator end = m_qm->end_quantifiers(); - if (it == end) + + if (m_qm->num_quantifiers() == 0) return true; if (m_iteration_idx >= m_params.m_mbqi_max_iterations) { @@ -393,6 +393,36 @@ namespace smt { bool found_relevant = false; unsigned num_failures = 0; + check_quantifiers(false, found_relevant, num_failures); + + + if (found_relevant) + m_iteration_idx++; + + TRACE("model_checker", tout << "model after check:\n"; model_pp(tout, *md);); + TRACE("model_checker", tout << "model checker result: " << (num_failures == 0) << "\n";); + m_max_cexs += m_params.m_mbqi_max_cexs; + + if (num_failures == 0 && !m_context->validate_model()) { + num_failures = 1; + // this time force expanding recursive function definitions + // that are not forced true in the current model. + check_quantifiers(true, found_relevant, num_failures); + } + if (num_failures == 0) + m_curr_model->cleanup(); + if (m_params.m_mbqi_trace) { + if (num_failures == 0) + verbose_stream() << "(smt.mbqi :succeeded true)\n"; + else + verbose_stream() << "(smt.mbqi :num-failures " << num_failures << ")\n"; + } + return num_failures == 0; + } + + void model_checker::check_quantifiers(bool strict_rec_fun, bool& found_relevant, unsigned& num_failures) { + ptr_vector::const_iterator it = m_qm->begin_quantifiers(); + ptr_vector::const_iterator end = m_qm->end_quantifiers(); for (; it != end; ++it) { quantifier * q = *it; if(!m_qm->mbqi_enabled(q)) continue; @@ -406,7 +436,7 @@ namespace smt { } found_relevant = true; if (m.is_rec_fun_def(q)) { - if (!check_rec_fun(q)) { + if (!check_rec_fun(q, strict_rec_fun)) { TRACE("model_checker", tout << "checking recursive function failed\n";); num_failures++; } @@ -420,26 +450,6 @@ namespace smt { } } } - - if (found_relevant) - m_iteration_idx++; - - TRACE("model_checker", tout << "model after check:\n"; model_pp(tout, *md);); - TRACE("model_checker", tout << "model checker result: " << (num_failures == 0) << "\n";); - m_max_cexs += m_params.m_mbqi_max_cexs; - - if (num_failures == 0 && !m_context->validate_model()) { - num_failures = 1; - } - if (num_failures == 0) - m_curr_model->cleanup(); - if (m_params.m_mbqi_trace) { - if (num_failures == 0) - verbose_stream() << "(smt.mbqi :succeeded true)\n"; - else - verbose_stream() << "(smt.mbqi :num-failures " << num_failures << ")\n"; - } - return num_failures == 0; } void model_checker::init_search_eh() { diff --git a/src/smt/smt_model_checker.h b/src/smt/smt_model_checker.h index b94ddb6bb..1b7713d59 100644 --- a/src/smt/smt_model_checker.h +++ b/src/smt/smt_model_checker.h @@ -59,7 +59,8 @@ namespace smt { void assert_neg_q_m(quantifier * q, expr_ref_vector & sks); bool add_blocking_clause(model * cex, expr_ref_vector & sks); bool check(quantifier * q); - bool check_rec_fun(quantifier* q); + bool check_rec_fun(quantifier* q, bool strict_rec_fun); + void check_quantifiers(bool strict_rec_fun, bool& found_relevant, unsigned& num_failures); struct instance { quantifier * m_q; diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index bad788f5d..10e2df988 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -397,6 +397,10 @@ namespace smt { return m_imp->m_quantifiers.end(); } + unsigned quantifier_manager::num_quantifiers() const { + return m_imp->m_quantifiers.size(); + } + // The default plugin uses E-matching, MBQI and quick-checker class default_qm_plugin : public quantifier_manager_plugin { quantifier_manager * m_qm; diff --git a/src/smt/smt_quantifier.h b/src/smt/smt_quantifier.h index bc249ed1a..6dcf20583 100644 --- a/src/smt/smt_quantifier.h +++ b/src/smt/smt_quantifier.h @@ -91,6 +91,8 @@ namespace smt { ptr_vector::const_iterator begin_quantifiers() const; ptr_vector::const_iterator end_quantifiers() const; + unsigned num_quantifiers() const; + }; class quantifier_manager_plugin { From 95cf1447eace96a71c7582c9c39f4d3a82cdc118 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 10 Apr 2017 13:18:45 +0100 Subject: [PATCH 059/105] Added maintainers.txt for qprofdiff --- contrib/qprofdiff/maintainers.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 contrib/qprofdiff/maintainers.txt diff --git a/contrib/qprofdiff/maintainers.txt b/contrib/qprofdiff/maintainers.txt new file mode 100644 index 000000000..01167f6d3 --- /dev/null +++ b/contrib/qprofdiff/maintainers.txt @@ -0,0 +1,3 @@ +# Maintainers + +- Christoph M. Wintersteiger (@wintersteiger, cwinter@microsoft.com) From b67c1c550127b8bd05d490eec876083fded6afe3 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 10 Apr 2017 16:28:41 +0100 Subject: [PATCH 060/105] Fixed valgrind warning. Fixes #972 --- src/ast/rewriter/arith_rewriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 81385c2af..2b2087e3b 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -194,7 +194,7 @@ bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & } } expr* t1, *t2; - bool is_int; + bool is_int = false; if (m_util.is_mod(arg2)) { std::swap(arg1, arg2); switch (kind) { From 67513a2cf574bbac681b6c7ae7f05677d390bc0e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Apr 2017 07:40:09 +0800 Subject: [PATCH 061/105] fix detection of bounds under conjunctions. Issue #971 Signed-off-by: Nikolaj Bjorner --- .../portfolio/bounded_int2bv_solver.cpp | 74 +++++++++++++------ src/tactic/portfolio/enum2bv_solver.cpp | 8 ++ src/tactic/portfolio/pb2bv_solver.cpp | 19 ++++- 3 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/tactic/portfolio/bounded_int2bv_solver.cpp b/src/tactic/portfolio/bounded_int2bv_solver.cpp index 53c20b253..83693abba 100644 --- a/src/tactic/portfolio/bounded_int2bv_solver.cpp +++ b/src/tactic/portfolio/bounded_int2bv_solver.cpp @@ -34,19 +34,19 @@ Notes: class bounded_int2bv_solver : public solver_na2as { ast_manager& m; params_ref m_params; - bv_util m_bv; - arith_util m_arith; - expr_ref_vector m_assertions; + mutable bv_util m_bv; + mutable arith_util m_arith; + mutable expr_ref_vector m_assertions; ref m_solver; - ptr_vector m_bounds; - func_decl_ref_vector m_bv_fns; - func_decl_ref_vector m_int_fns; + mutable ptr_vector m_bounds; + mutable func_decl_ref_vector m_bv_fns; + mutable func_decl_ref_vector m_int_fns; unsigned_vector m_bv_fns_lim; - obj_map m_int2bv; - obj_map m_bv2int; - obj_map m_bv2offset; - bv2int_rewriter_ctx m_rewriter_ctx; - bv2int_rewriter_star m_rewriter; + mutable obj_map m_int2bv; + mutable obj_map m_bv2int; + mutable obj_map m_bv2offset; + mutable bv2int_rewriter_ctx m_rewriter_ctx; + mutable bv2int_rewriter_star m_rewriter; public: @@ -78,7 +78,19 @@ public: } virtual void assert_expr(expr * t) { + unsigned i = m_assertions.size(); m_assertions.push_back(t); + while (i < m_assertions.size()) { + t = m_assertions[i].get(); + if (m.is_and(t)) { + m_assertions.append(to_app(t)->get_num_args(), to_app(t)->get_args()); + m_assertions[i] = m_assertions.back(); + m_assertions.pop_back(); + } + else { + ++i; + } + } } virtual void push_core() { @@ -184,7 +196,7 @@ private: } filter_model_converter filter(m); for (unsigned i = 0; i < m_bv_fns.size(); ++i) { - filter.insert(m_bv_fns[i]); + filter.insert(m_bv_fns[i].get()); } filter(mdl, 0); } @@ -205,13 +217,13 @@ private: ext(mdl, 0); } - void accumulate_sub(expr_safe_replace& sub) { + void accumulate_sub(expr_safe_replace& sub) const { for (unsigned i = 0; i < m_bounds.size(); ++i) { accumulate_sub(sub, *m_bounds[i]); } } - void accumulate_sub(expr_safe_replace& sub, bound_manager& bm) { + void accumulate_sub(expr_safe_replace& sub, bound_manager& bm) const { bound_manager::iterator it = bm.begin(), end = bm.end(); for (; it != end; ++it) { expr* e = *it; @@ -252,19 +264,20 @@ private: sub.insert(e, t); } else { - IF_VERBOSE(1, - verbose_stream() << "unprocessed entry: " << mk_pp(e, m) << "\n"; - if (bm.has_lower(e, lo, s1)) { - verbose_stream() << "lower: " << lo << " " << s1 << "\n"; - } - if (bm.has_upper(e, hi, s2)) { - verbose_stream() << "upper: " << hi << " " << s2 << "\n"; - }); + TRACE("pb", + tout << "unprocessed entry: " << mk_pp(e, m) << "\n"; + if (bm.has_lower(e, lo, s1)) { + tout << "lower: " << lo << " " << s1 << "\n"; + } + if (bm.has_upper(e, hi, s2)) { + tout << "upper: " << hi << " " << s2 << "\n"; + }); } } } - unsigned get_num_bits(rational const& k) { + + unsigned get_num_bits(rational const& k) const { SASSERT(!k.is_neg()); SASSERT(k.is_int()); rational two(2); @@ -277,11 +290,13 @@ private: return num_bits; } - void flush_assertions() { + void flush_assertions() const { + if (m_assertions.empty()) return; bound_manager& bm = *m_bounds.back(); for (unsigned i = 0; i < m_assertions.size(); ++i) { bm(m_assertions[i].get()); } + TRACE("int2bv", bm.display(tout);); expr_safe_replace sub(m); accumulate_sub(sub); proof_ref proof(m); @@ -304,6 +319,17 @@ private: m_assertions.reset(); m_rewriter.reset(); } + + virtual unsigned get_num_assertions() const { + flush_assertions(); + return m_solver->get_num_assertions(); + } + + virtual expr * get_assertion(unsigned idx) const { + flush_assertions(); + return m_solver->get_assertion(idx); + } + }; solver * mk_bounded_int2bv_solver(ast_manager & m, params_ref const & p, solver* s) { diff --git a/src/tactic/portfolio/enum2bv_solver.cpp b/src/tactic/portfolio/enum2bv_solver.cpp index f3288d8d6..9afd97de5 100644 --- a/src/tactic/portfolio/enum2bv_solver.cpp +++ b/src/tactic/portfolio/enum2bv_solver.cpp @@ -163,6 +163,14 @@ public: ext(mdl, 0); } + virtual unsigned get_num_assertions() const { + return m_solver->get_num_assertions(); + } + + virtual expr * get_assertion(unsigned idx) const { + return m_solver->get_assertion(idx); + } + }; solver * mk_enum2bv_solver(ast_manager & m, params_ref const & p, solver* s) { diff --git a/src/tactic/portfolio/pb2bv_solver.cpp b/src/tactic/portfolio/pb2bv_solver.cpp index bfd533e8a..090ea9f76 100644 --- a/src/tactic/portfolio/pb2bv_solver.cpp +++ b/src/tactic/portfolio/pb2bv_solver.cpp @@ -27,9 +27,9 @@ Notes: class pb2bv_solver : public solver_na2as { ast_manager& m; params_ref m_params; - expr_ref_vector m_assertions; - ref m_solver; - pb2bv_rewriter m_rewriter; + mutable expr_ref_vector m_assertions; + mutable ref m_solver; + mutable pb2bv_rewriter m_rewriter; public: @@ -107,8 +107,19 @@ public: filter(mdl, 0); } + virtual unsigned get_num_assertions() const { + flush_assertions(); + return m_solver->get_num_assertions(); + } + + virtual expr * get_assertion(unsigned idx) const { + flush_assertions(); + return m_solver->get_assertion(idx); + } + + private: - void flush_assertions() { + void flush_assertions() const { proof_ref proof(m); expr_ref fml(m); expr_ref_vector fmls(m); From 4140afa4cb276377f15aeb10dc945b5af450a3c7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Apr 2017 10:49:42 +0800 Subject: [PATCH 062/105] add regular expression membership for range of int.to.str functions. Issue #957 Signed-off-by: Nikolaj Bjorner --- src/ast/seq_decl_plugin.h | 1 + src/smt/theory_seq.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index a7e534bbb..2882e905d 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -304,6 +304,7 @@ public: app* mk_to_re(expr* s) { return m.mk_app(m_fid, OP_SEQ_TO_RE, 1, &s); } app* mk_in_re(expr* s, expr* r) { return m.mk_app(m_fid, OP_SEQ_IN_RE, s, r); } + app* mk_range(expr* s1, expr* s2) { return m.mk_app(m_fid, OP_RE_RANGE, s1, s2); } app* mk_concat(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_CONCAT, r1, r2); } app* mk_union(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_UNION, r1, r2); } app* mk_inter(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_INTERSECT, r1, r2); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 663d4cbe1..672d3c440 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2345,6 +2345,17 @@ bool theory_seq::add_itos_axiom(expr* e) { return false; } add_axiom(mk_eq(e2, n, false)); + +#if 1 + expr_ref num_re(m), opt_re(m); + num_re = m_util.re.mk_range(m_util.str.mk_string(symbol("0")), m_util.str.mk_string(symbol("9"))); + num_re = m_util.re.mk_plus(num_re); + opt_re = m_util.re.mk_opt(m_util.re.mk_to_re(m_util.str.mk_string(symbol("-")))); + num_re = m_util.re.mk_concat(opt_re, num_re); + app_ref in_re(m_util.re.mk_in_re(e, num_re), m); + internalize_term(in_re); + propagate_in_re(in_re, true); +#endif m_trail_stack.push(push_replay(alloc(replay_axiom, m, e))); return true; } From 7bb5e72e073d47b123ddc15eb49c997577214e38 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Apr 2017 09:09:30 +0700 Subject: [PATCH 063/105] add missing string/re operations #977 and adding Pseudo-Boolean operations to Java API Signed-off-by: Nikolaj Bjorner --- src/api/java/Context.java | 139 ++++++++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 37 deletions(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index d50968a32..549694de0 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -1890,43 +1890,43 @@ public class Context implements AutoCloseable { /** * Create the empty sequence. */ - public SeqExpr MkEmptySeq(Sort s) + public SeqExpr mkEmptySeq(Sort s) { checkContextMatch(s); - return new SeqExpr(this, Native.mkSeqEmpty(nCtx(), s.getNativeObject())); + return (SeqExpr) Expr.create(this, Native.mkSeqEmpty(nCtx(), s.getNativeObject())); } /** * Create the singleton sequence. */ - public SeqExpr MkUnit(Expr elem) + public SeqExpr mkUnit(Expr elem) { checkContextMatch(elem); - return new SeqExpr(this, Native.mkSeqUnit(nCtx(), elem.getNativeObject())); + return (SeqExpr) Expr.create(this, Native.mkSeqUnit(nCtx(), elem.getNativeObject())); } /** * Create a string constant. */ - public SeqExpr MkString(String s) + public SeqExpr mkString(String s) { - return new SeqExpr(this, Native.mkString(nCtx(), s)); + return (SeqExpr) Expr.create(this, Native.mkString(nCtx(), s)); } /** * Concatentate sequences. */ - public SeqExpr MkConcat(SeqExpr... t) + public SeqExpr mkConcat(SeqExpr... t) { checkContextMatch(t); - return new SeqExpr(this, Native.mkSeqConcat(nCtx(), t.length, AST.arrayToNative(t))); + return (SeqExpr) Expr.create(this, Native.mkSeqConcat(nCtx(), t.length, AST.arrayToNative(t))); } /** * Retrieve the length of a given sequence. */ - public IntExpr MkLength(SeqExpr s) + public IntExpr mkLength(SeqExpr s) { checkContextMatch(s); return (IntExpr) Expr.create(this, Native.mkSeqLength(nCtx(), s.getNativeObject())); @@ -1935,130 +1935,195 @@ public class Context implements AutoCloseable { /** * Check for sequence prefix. */ - public BoolExpr MkPrefixOf(SeqExpr s1, SeqExpr s2) + public BoolExpr mkPrefixOf(SeqExpr s1, SeqExpr s2) { checkContextMatch(s1, s2); - return new BoolExpr(this, Native.mkSeqPrefix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); + return (BoolExpr) Expr.create(this, Native.mkSeqPrefix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); } /** * Check for sequence suffix. */ - public BoolExpr MkSuffixOf(SeqExpr s1, SeqExpr s2) + public BoolExpr mkSuffixOf(SeqExpr s1, SeqExpr s2) { checkContextMatch(s1, s2); - return new BoolExpr(this, Native.mkSeqSuffix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); + return (BoolExpr)Expr.create(this, Native.mkSeqSuffix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); } /** * Check for sequence containment of s2 in s1. */ - public BoolExpr MkContains(SeqExpr s1, SeqExpr s2) + public BoolExpr mkContains(SeqExpr s1, SeqExpr s2) { checkContextMatch(s1, s2); - return new BoolExpr(this, Native.mkSeqContains(nCtx(), s1.getNativeObject(), s2.getNativeObject())); + return (BoolExpr) Expr.create(this, Native.mkSeqContains(nCtx(), s1.getNativeObject(), s2.getNativeObject())); } /** * Retrieve sequence of length one at index. */ - public SeqExpr MkAt(SeqExpr s, IntExpr index) + public SeqExpr mkAt(SeqExpr s, IntExpr index) { checkContextMatch(s, index); - return new SeqExpr(this, Native.mkSeqAt(nCtx(), s.getNativeObject(), index.getNativeObject())); + return (SeqExpr) Expr.create(this, Native.mkSeqAt(nCtx(), s.getNativeObject(), index.getNativeObject())); } /** * Extract subsequence. */ - public SeqExpr MkExtract(SeqExpr s, IntExpr offset, IntExpr length) + public SeqExpr mkExtract(SeqExpr s, IntExpr offset, IntExpr length) { checkContextMatch(s, offset, length); - return new SeqExpr(this, Native.mkSeqExtract(nCtx(), s.getNativeObject(), offset.getNativeObject(), length.getNativeObject())); + return (SeqExpr) Expr.create(this, Native.mkSeqExtract(nCtx(), s.getNativeObject(), offset.getNativeObject(), length.getNativeObject())); } /** * Extract index of sub-string starting at offset. */ - public IntExpr MkIndexOf(SeqExpr s, SeqExpr substr, ArithExpr offset) + public IntExpr mkIndexOf(SeqExpr s, SeqExpr substr, ArithExpr offset) { checkContextMatch(s, substr, offset); - return new IntExpr(this, Native.mkSeqIndex(nCtx(), s.getNativeObject(), substr.getNativeObject(), offset.getNativeObject())); + return (IntExpr)Expr.create(this, Native.mkSeqIndex(nCtx(), s.getNativeObject(), substr.getNativeObject(), offset.getNativeObject())); } /** * Replace the first occurrence of src by dst in s. */ - public SeqExpr MkReplace(SeqExpr s, SeqExpr src, SeqExpr dst) + public SeqExpr mkReplace(SeqExpr s, SeqExpr src, SeqExpr dst) { checkContextMatch(s, src, dst); - return new SeqExpr(this, Native.mkSeqReplace(nCtx(), s.getNativeObject(), src.getNativeObject(), dst.getNativeObject())); + return (SeqExpr) Expr.create(this, Native.mkSeqReplace(nCtx(), s.getNativeObject(), src.getNativeObject(), dst.getNativeObject())); } /** * Convert a regular expression that accepts sequence s. */ - public ReExpr MkToRe(SeqExpr s) + public ReExpr mkToRe(SeqExpr s) { checkContextMatch(s); - return new ReExpr(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); + return (ReExpr) Expr.create(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); } /** * Check for regular expression membership. */ - public BoolExpr MkInRe(SeqExpr s, ReExpr re) + public BoolExpr mkInRe(SeqExpr s, ReExpr re) { checkContextMatch(s, re); - return new BoolExpr(this, Native.mkSeqInRe(nCtx(), s.getNativeObject(), re.getNativeObject())); + return (BoolExpr) Expr.create(this, Native.mkSeqInRe(nCtx(), s.getNativeObject(), re.getNativeObject())); } /** * Take the Kleene star of a regular expression. */ - public ReExpr MkStar(ReExpr re) + public ReExpr mkStar(ReExpr re) { checkContextMatch(re); - return new ReExpr(this, Native.mkReStar(nCtx(), re.getNativeObject())); + return (ReExpr) Expr.create(this, Native.mkReStar(nCtx(), re.getNativeObject())); } + + /** + * Take the bounded Kleene star of a regular expression. + */ + public ReExpr mkLoop(ReExpr re, uint lo, uint hi = 0) + { + return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, hi)); + } + /** * Take the Kleene plus of a regular expression. */ - public ReExpr MPlus(ReExpr re) + public ReExpr mkPlus(ReExpr re) { checkContextMatch(re); - return new ReExpr(this, Native.mkRePlus(nCtx(), re.getNativeObject())); + return (ReExpr) Expr.create(this, Native.mkRePlus(nCtx(), re.getNativeObject())); } /** * Create the optional regular expression. */ - public ReExpr MOption(ReExpr re) + public ReExpr mkOption(ReExpr re) { checkContextMatch(re); - return new ReExpr(this, Native.mkReOption(nCtx(), re.getNativeObject())); + return (ReExpr) Expr.create(this, Native.mkReOption(nCtx(), re.getNativeObject())); } /** * Create the concatenation of regular languages. */ - public ReExpr MkConcat(ReExpr... t) + public ReExpr mkConcat(ReExpr... t) { checkContextMatch(t); - return new ReExpr(this, Native.mkReConcat(nCtx(), t.length, AST.arrayToNative(t))); + return (ReExpr) Expr.create(this, Native.mkReConcat(nCtx(), t.length, AST.arrayToNative(t))); } /** * Create the union of regular languages. */ - public ReExpr MkUnion(ReExpr... t) + public ReExpr mkUnion(ReExpr... t) { checkContextMatch(t); - return new ReExpr(this, Native.mkReUnion(nCtx(), t.length, AST.arrayToNative(t))); + return (ReExpr) Expr.create(this, Native.mkReUnion(nCtx(), t.length, AST.arrayToNative(t))); } + + /** + * Create a range expression. + */ + public ReExpr MkRange(SeqExpr lo, SeqExpr hi) + { + checkContextMatch(lo, hi); + return (ReExpr) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); + } + + + /** + * Create an at-most-k constraint. + */ + public BoolExpr mkAtMost(BoolExpr[] args, loong k) + { + checkContextMatch(args); + return (BoolExpr) Expr.create(this, Native.mkAtmost(nCtx(), args.length, AST.arrayToNative(args), k)); + } + + /** + * Create an at-least-k constraint. + */ + public BoolExpr mkAtLeast(BoolExpr[] args, long k) + { + checkContextMatch(args); + return (BoolExpr) Expr.create(this, Native.mkAtleast(nCtx(), args.length, AST.arrayToNative(args), k)); + } + + /** + * Create a pseudo-Boolean less-or-equal constraint. + */ + public BoolExpr mkPBLe(int[] coeffs, BoolExpr[] args, int k) + { + checkContextMatch(args); + return (BoolExpr) Expr.create(this, Native.mkPble(nCtx(), args.length, AST.arrayToNative(args), coeffs, k)); + } + + /** + * Create a pseudo-Boolean greater-or-equal constraint. + */ + public BoolExpr mkPBGe(int[] coeffs, BoolExpr[] args, int k) + { + checkContextMatch(args); + return (BoolExpr) Expr.create(this, Native.mkPbge(nCtx(), args.length, AST.arrayToNative(args), coeffs, k)); + } + + /** + * Create a pseudo-Boolean equal constraint. + */ + public BoolExpr mkPBEq(int[] coeffs, BoolExpr[] args, int k) + { + checkContextMatch(args); + return (BoolExpr) Expr.create(this, Native.mkPbeq(nCtx(), args.length, AST.arrayToNative(args), coeffs, k)); + } + /** * Create a Term of a given sort. From 48638c6f1e08bd6bf9223a783b6cdd70a79c314e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Apr 2017 09:34:13 +0700 Subject: [PATCH 064/105] fix for #975, add mask to ensure character encoding is unique within range of bits used for encoding Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 3 +++ src/ast/seq_decl_plugin.cpp | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 3d7da43a7..b4dbfa6df 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -1435,6 +1435,7 @@ bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_ zstring s; bool lchange = false; SASSERT(lhs.empty()); + TRACE("seq", tout << ls << "\n"; tout << rs << "\n";); // solve from back while (true) { while (!rs.empty() && m_util.str.is_empty(rs.back())) { @@ -1552,9 +1553,11 @@ bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_ head2 < rs.size() && m_util.str.is_string(ls[head1].get(), s1) && m_util.str.is_string(rs[head2].get(), s2)) { + TRACE("seq", tout << s1 << " - " << s2 << " " << s1.length() << " " << s2.length() << "\n";); unsigned l = std::min(s1.length(), s2.length()); for (unsigned i = 0; i < l; ++i) { if (s1[i] != s2[i]) { + TRACE("seq", tout << "different at position " << i << " " << s1[i] << " " << s2[i] << "\n";); return false; } } diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 9af4687d2..f282043e6 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -126,13 +126,14 @@ static bool is_escape_char(char const *& s, unsigned& result) { zstring::zstring(encoding enc): m_encoding(enc) {} zstring::zstring(char const* s, encoding enc): m_encoding(enc) { + unsigned mask = 0xFF; // TBD for UTF while (*s) { unsigned ch; if (is_escape_char(s, ch)) { - m_buffer.push_back(ch); + m_buffer.push_back(ch & mask); } else { - m_buffer.push_back(*s); + m_buffer.push_back(*s & mask); ++s; } } From e4b9080165dce794561f26d4008fd9c9a0eb9bde Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Apr 2017 15:04:13 +0800 Subject: [PATCH 065/105] include timeout/rlimit parameters in optmize Signed-off-by: Nikolaj Bjorner --- src/api/api_opt.cpp | 2 +- src/api/java/Context.java | 12 ++++++++++-- src/opt/opt_params.pyg | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index a2299aec3..0d1b4ef8d 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -128,7 +128,7 @@ extern "C" { lbool r = l_undef; cancel_eh eh(mk_c(c)->m().limit()); unsigned timeout = to_optimize_ptr(o)->get_params().get_uint("timeout", mk_c(c)->get_timeout()); - unsigned rlimit = mk_c(c)->get_rlimit(); + unsigned rlimit = to_optimize_ptr(o)->get_params().get_uint("rlimit", mk_c(c)->get_rlimit()); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 549694de0..9fe4e0c38 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2024,13 +2024,21 @@ public class Context implements AutoCloseable { } /** - * Take the bounded Kleene star of a regular expression. + * Take the lower and upper-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(ReExpr re, uint lo, uint hi = 0) + public ReExpr mkLoop(ReExpr re, uint lo, uint hi) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, hi)); } + /** + * Take the lower-bounded Kleene star of a regular expression. + */ + public ReExpr mkLoop(ReExpr re, uint lo) + { + return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, 0)); + } + /** * Take the Kleene plus of a regular expression. diff --git a/src/opt/opt_params.pyg b/src/opt/opt_params.pyg index a7c9e0011..51f58396c 100644 --- a/src/opt/opt_params.pyg +++ b/src/opt/opt_params.pyg @@ -5,6 +5,8 @@ def_module_params('opt', ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'core_maxsat', 'wmax', 'maxres', 'pd-maxres'"), ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', or 'box'"), ('dump_benchmarks', BOOL, False, 'dump benchmarks for profiling'), + ('timeout', UINT, UINT_MAX, 'timeout (in milliseconds) (UINT_MAX and 0 mean no timeout)'), + ('rlimit', UINT, 0, 'resource limit (0 means no limit)'), ('print_model', BOOL, False, 'display model for satisfiable constraints'), ('enable_sls', BOOL, False, 'enable SLS tuning during weighted maxsast'), ('enable_sat', BOOL, True, 'enable the new SAT core for propositional constraints'), From 87c3b5ee5140b758e3dff863b39b0e9128250b02 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Apr 2017 15:29:02 +0800 Subject: [PATCH 066/105] replace uint by long Signed-off-by: Nikolaj Bjorner --- src/api/java/Context.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 9fe4e0c38..4a443f171 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2026,7 +2026,7 @@ public class Context implements AutoCloseable { /** * Take the lower and upper-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(ReExpr re, uint lo, uint hi) + public ReExpr mkLoop(ReExpr re, long lo, long hi) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, hi)); } @@ -2034,7 +2034,7 @@ public class Context implements AutoCloseable { /** * Take the lower-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(ReExpr re, uint lo) + public ReExpr mkLoop(ReExpr re, long lo) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, 0)); } From ab6abe901fcf96c0509eb17251cd6b8fd2d9d6bd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Apr 2017 15:57:30 +0800 Subject: [PATCH 067/105] replace long by int Signed-off-by: Nikolaj Bjorner --- src/api/java/Context.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 4a443f171..da7c692b8 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2026,7 +2026,7 @@ public class Context implements AutoCloseable { /** * Take the lower and upper-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(ReExpr re, long lo, long hi) + public ReExpr mkLoop(ReExpr re, int lo, int hi) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, hi)); } @@ -2034,7 +2034,7 @@ public class Context implements AutoCloseable { /** * Take the lower-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(ReExpr re, long lo) + public ReExpr mkLoop(ReExpr re, int lo) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, 0)); } @@ -2090,7 +2090,7 @@ public class Context implements AutoCloseable { /** * Create an at-most-k constraint. */ - public BoolExpr mkAtMost(BoolExpr[] args, loong k) + public BoolExpr mkAtMost(BoolExpr[] args, long k) { checkContextMatch(args); return (BoolExpr) Expr.create(this, Native.mkAtmost(nCtx(), args.length, AST.arrayToNative(args), k)); From 9933c360507ed795eeaff816e99d0d14417956f8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Apr 2017 17:06:03 +0800 Subject: [PATCH 068/105] replace long by int Signed-off-by: Nikolaj Bjorner --- src/api/java/Context.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index da7c692b8..790ffdca7 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2090,7 +2090,7 @@ public class Context implements AutoCloseable { /** * Create an at-most-k constraint. */ - public BoolExpr mkAtMost(BoolExpr[] args, long k) + public BoolExpr mkAtMost(BoolExpr[] args, int k) { checkContextMatch(args); return (BoolExpr) Expr.create(this, Native.mkAtmost(nCtx(), args.length, AST.arrayToNative(args), k)); @@ -2099,7 +2099,7 @@ public class Context implements AutoCloseable { /** * Create an at-least-k constraint. */ - public BoolExpr mkAtLeast(BoolExpr[] args, long k) + public BoolExpr mkAtLeast(BoolExpr[] args, int k) { checkContextMatch(args); return (BoolExpr) Expr.create(this, Native.mkAtleast(nCtx(), args.length, AST.arrayToNative(args), k)); From 8b5627e361e2e19ff26dac18d920e01906590ad4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 16 Apr 2017 12:13:30 +0900 Subject: [PATCH 069/105] additional API per #977 Signed-off-by: Nikolaj Bjorner --- src/api/java/Context.java | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 790ffdca7..db7a08711 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2057,7 +2057,17 @@ public class Context implements AutoCloseable { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReOption(nCtx(), re.getNativeObject())); } + + /** + * Create the complement regular expression. + */ + public ReExpr mkComplement(ReExpr re) + { + checkContextMatch(re); + return (ReExpr) Expr.create(this, Native.mkReComplement(nCtx(), re.getNativeObject())); + } + /** * Create the concatenation of regular languages. */ @@ -2075,7 +2085,15 @@ public class Context implements AutoCloseable { checkContextMatch(t); return (ReExpr) Expr.create(this, Native.mkReUnion(nCtx(), t.length, AST.arrayToNative(t))); } - + + /** + * Create the intersection of regular languages. + */ + public ReExpr mkIntersect(ReExpr... t) + { + checkContextMatch(t); + return (ReExpr) Expr.create(this, Native.mkReIntersect(nCtx(), t.length, AST.arrayToNative(t))); + } /** * Create a range expression. From 66e61b8a31a04a858af570e0665fcdd61fc0c2d2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 17 Apr 2017 03:03:59 -0700 Subject: [PATCH 070/105] issues #963 #912 --- src/ast/rewriter/seq_rewriter.cpp | 31 ++++++++++++++++++++++- src/smt/theory_seq.cpp | 41 +++++++++++++++++++++++++------ src/smt/theory_seq.h | 1 + 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index b4dbfa6df..3737f4651 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -60,7 +60,12 @@ expr_ref sym_expr::accept(expr* e) { } std::ostream& sym_expr::display(std::ostream& out) const { - return out << m_t; + switch (m_ty) { + case t_char: return out << m_t; + case t_range: return out << m_t << ":" << m_s; + case t_pred: return out << m_t; + } + return out << "expression type not recognized"; } struct display_expr1 { @@ -237,6 +242,7 @@ eautomaton* re2automaton::re2aut(expr* e) { unsigned nb = s1.num_bits(); expr_ref _start(bv.mk_numeral(start, nb), m); expr_ref _stop(bv.mk_numeral(stop, nb), m); + TRACE("seq", tout << "Range: " << start << " " << stop << "\n";); a = alloc(eautomaton, sm, sym_expr::mk_range(_start, _stop)); return a.detach(); } @@ -646,6 +652,29 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { result = m().mk_true(); return BR_DONE; } + bool all_units = true; + for (unsigned i = 0; i < bs.size(); ++i) { + all_units = m_util.str.is_unit(bs[i].get()); + } + for (unsigned i = 0; i < as.size(); ++i) { + all_units = m_util.str.is_unit(as[i].get()); + } + if (all_units) { + if (as.size() < bs.size()) { + result = m().mk_false(); + return BR_DONE; + } + expr_ref_vector ors(m()); + for (unsigned i = 0; i < as.size() - bs.size() + 1; ++i) { + expr_ref_vector ands(m()); + for (unsigned j = 0; j < bs.size(); ++j) { + ands.push_back(m().mk_eq(as[i + j].get(), bs[j].get())); + } + ors.push_back(::mk_and(ands)); + } + result = ::mk_or(ors); + return BR_REWRITE_FULL; + } return BR_FAILED; } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 672d3c440..9dd082de0 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -255,6 +255,11 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>solve_eqs\n";); return FC_CONTINUE; } + if (check_contains()) { + ++m_stats.m_propagate_contains; + TRACE("seq", tout << ">>propagate_contains\n";); + return FC_CONTINUE; + } if (solve_nqs(0)) { ++m_stats.m_solve_nqs; TRACE("seq", tout << ">>solve_nqs\n";); @@ -290,11 +295,6 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>propagate_automata\n";); return FC_CONTINUE; } - if (check_contains()) { - ++m_stats.m_propagate_contains; - TRACE("seq", tout << ">>propagate_contains\n";); - return FC_CONTINUE; - } if (is_solved()) { TRACE("seq", tout << ">>is_solved\n";); return FC_DONE; @@ -1159,7 +1159,7 @@ bool theory_seq::check_extensionality() { } /* - \brief check negated contains constriants. + \brief check negated contains constraints. */ bool theory_seq::check_contains() { context & ctx = get_context(); @@ -1199,6 +1199,11 @@ bool theory_seq::is_solved() { IF_VERBOSE(10, display_disequation(verbose_stream() << "(seq.giveup ", m_nqs[0]); verbose_stream() << " is unsolved)\n";); return false; } + if (!m_ncs.empty()) { + TRACE("seq", display_nc(tout << "(seq.giveup ", m_ncs[0]); tout << " is unsolved)\n";); + IF_VERBOSE(10, display_nc(verbose_stream() << "(seq.giveup ", m_ncs[0]); verbose_stream() << " is unsolved)\n";); + return false; + } return true; } @@ -1984,6 +1989,22 @@ bool theory_seq::solve_nc(unsigned idx) { m_new_propagation = true; return true; } + + expr* e1, *e2; + if (m.is_eq(c, e1, e2)) { + literal eq = mk_eq(e1, e2, false); + propagate_lit(deps, 0, 0, ~eq); + return true; + } + + if (m.is_or(c)) { + for (unsigned i = 0; i < to_app(c)->get_num_args(); ++i) { + expr_ref ci(to_app(c)->get_arg(i), m); + m_ncs.push_back(nc(ci, deps)); + } + m_new_propagation = true; + return true; + } return false; } @@ -2418,13 +2439,17 @@ void theory_seq::display(std::ostream & out) const { if (!m_ncs.empty()) { out << "Non contains:\n"; for (unsigned i = 0; i < m_ncs.size(); ++i) { - out << "not " << mk_pp(m_ncs[i].contains(), m) << "\n"; - display_deps(out << " <- ", m_ncs[i].deps()); out << "\n"; + display_nc(out, m_ncs[i]); } } } +void theory_seq::display_nc(std::ostream& out, nc const& nc) const { + out << "not " << mk_pp(nc.contains(), m) << "\n"; + display_deps(out << " <- ", nc.deps()); out << "\n"; +} + void theory_seq::display_equations(std::ostream& out) const { for (unsigned i = 0; i < m_eqs.size(); ++i) { display_equation(out, m_eqs[i]); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index aa7ddec1b..2b8fb2fd7 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -570,6 +570,7 @@ namespace smt { void display_disequation(std::ostream& out, ne const& e) const; void display_deps(std::ostream& out, dependency* deps) const; void display_deps(std::ostream& out, literal_vector const& lits, enode_pair_vector const& eqs) const; + void display_nc(std::ostream& out, nc const& nc) const; public: theory_seq(ast_manager& m); virtual ~theory_seq(); From 71da36f85c221765dcc8f8cf5b3ecd356a50ee72 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 18 Apr 2017 15:13:11 +0100 Subject: [PATCH 071/105] Added core.extend_nonlocal_patterns parameter to improve unsat cores. --- src/smt/params/smt_params_helper.pyg | 3 +- src/smt/smt_solver.cpp | 62 ++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index a3f163ed4..6ac4aab04 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -64,5 +64,6 @@ def_module_params(module_name='smt', ('core.validate', BOOL, False, 'validate unsat core produced by SMT context'), ('core.minimize', BOOL, False, 'minimize unsat core produced by SMT context'), ('core.extend_patterns', BOOL, False, 'extend unsat core with literals that trigger (potential) quantifier instances'), - ('core.extend_patterns.max_distance', UINT, UINT_MAX, 'limits the distance of a pattern-extended unsat core') + ('core.extend_patterns.max_distance', UINT, UINT_MAX, 'limits the distance of a pattern-extended unsat core'), + ('core.extend_nonlocal_patterns', BOOL, False, 'extend unsat cores with literals that have quantifiers with patterns that contain symbols which are not in the quantifier\'s body') )) diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 59a5ddf8f..cd912b72e 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -38,6 +38,7 @@ namespace smt { bool m_minimizing_core; bool m_core_extend_patterns; unsigned m_core_extend_patterns_max_distance; + bool m_core_extend_nonlocal_patterns; obj_map m_name2assertion; public: @@ -48,13 +49,15 @@ namespace smt { m_context(m, m_smt_params), m_minimizing_core(false), m_core_extend_patterns(false), - m_core_extend_patterns_max_distance(UINT_MAX) { + m_core_extend_patterns_max_distance(UINT_MAX), + m_core_extend_nonlocal_patterns(false) { m_logic = l; if (m_logic != symbol::null) m_context.set_logic(m_logic); smt_params_helper smth(p); m_core_extend_patterns = smth.core_extend_patterns(); m_core_extend_patterns_max_distance = smth.core_extend_patterns_max_distance(); + m_core_extend_nonlocal_patterns = smth.core_extend_nonlocal_patterns(); } virtual solver * translate(ast_manager & m, params_ref const & p) { @@ -81,6 +84,8 @@ namespace smt { m_context.updt_params(p); smt_params_helper smth(p); m_core_extend_patterns = smth.core_extend_patterns(); + m_core_extend_patterns_max_distance = smth.core_extend_patterns_max_distance(); + m_core_extend_nonlocal_patterns = smth.core_extend_nonlocal_patterns(); } virtual void collect_param_descrs(param_descrs & r) { @@ -172,6 +177,8 @@ namespace smt { if (m_core_extend_patterns) add_pattern_literals_to_core(r); + if (m_core_extend_nonlocal_patterns) + add_nonlocal_pattern_literals_to_core(r); } virtual void get_model(model_ref & m) { @@ -250,7 +257,7 @@ namespace smt { } }; - void collect_pattern_func_decls(expr_ref & e, func_decl_set & fds) { + void collect_pattern_fds(expr_ref & e, func_decl_set & fds) { collect_pattern_fds_proc p(get_manager(), fds); expr_mark visited; for_each_expr(p, visited, e); @@ -295,7 +302,7 @@ namespace smt { expr_ref name(core[i], m); SASSERT(m_name2assertion.contains(name)); expr_ref assrtn(m_name2assertion.find(name), m); - collect_pattern_func_decls(assrtn, pattern_fds); + collect_pattern_fds(assrtn, pattern_fds); } if (!pattern_fds.empty()) { @@ -317,6 +324,55 @@ namespace smt { break; } } + + struct collect_body_fds_proc { + ast_manager & m; + func_decl_set & m_fds; + collect_body_fds_proc(ast_manager & m, func_decl_set & fds) : + m(m), m_fds(fds) { + } + void operator()(var * n) {} + void operator()(app * n) {} + void operator()(quantifier * n) { + collect_fds_proc p(m, m_fds); + expr_fast_mark1 visited; + quick_for_each_expr(p, visited, n->get_expr()); + } + }; + + void collect_body_func_decls(expr_ref & e, func_decl_set & fds) { + ast_manager & m = get_manager(); + collect_body_fds_proc p(m, fds); + expr_mark visited; + for_each_expr(p, visited, e); + } + + void add_nonlocal_pattern_literals_to_core(ptr_vector & core) { + ast_manager & m = get_manager(); + + obj_map::iterator it = m_name2assertion.begin(); + obj_map::iterator end = m_name2assertion.end(); + for (unsigned i = 0; it != end; it++, i++) { + expr_ref name(it->m_key, m); + expr_ref assrtn(it->m_value, m); + + if (!core.contains(name)) { + func_decl_set pattern_fds, body_fds; + collect_pattern_fds(assrtn, pattern_fds); + collect_body_func_decls(assrtn, body_fds); + + func_decl_set::iterator pit = pattern_fds.begin(); + func_decl_set::iterator pend= pattern_fds.end(); + for (; pit != pend; pit++) { + func_decl * fd = *pit; + if (!body_fds.contains(fd)) { + core.insert(name); + break; + } + } + } + } + } }; }; From a02a7f44430475e0ec2f09ac64b94598a0c6448e Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 19 Apr 2017 13:04:04 +0100 Subject: [PATCH 072/105] Whitespace --- src/smt/old_interval.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/smt/old_interval.cpp b/src/smt/old_interval.cpp index 616b74ed6..da03bc03e 100644 --- a/src/smt/old_interval.cpp +++ b/src/smt/old_interval.cpp @@ -81,7 +81,7 @@ ext_numeral & ext_numeral::operator*=(ext_numeral const & other) { m_value.reset(); return *this; } - + if (is_infinite() || other.is_infinite()) { if (sign() == other.sign()) m_kind = PLUS_INFINITY; @@ -203,7 +203,7 @@ interval::interval(v_dependency_manager & m, rational const & val, v_dependency m_lower_dep(l_dep), m_upper_dep(u_dep) { } - + /** \brief Create intervals (-oo, val], (-oo, val), [val, oo), (val, oo) */ @@ -271,8 +271,8 @@ interval & interval::operator-=(interval const & other) { return operator+=(tmp); } -v_dependency * interval::join(v_dependency * d1, v_dependency * d2, v_dependency * d3, v_dependency * d4) { - return m_manager.mk_join(m_manager.mk_join(d1, d2), m_manager.mk_join(d3,d4)); +v_dependency * interval::join(v_dependency * d1, v_dependency * d2, v_dependency * d3, v_dependency * d4) { + return m_manager.mk_join(m_manager.mk_join(d1, d2), m_manager.mk_join(d3,d4)); } /** @@ -318,7 +318,7 @@ interval & interval::operator*=(interval const & other) { v_dependency * d_d = other.m_upper_dep; TRACE("interval_bug", tout << "operator*= " << *this << " " << other << "\n";); - + if (is_N()) { if (other.is_N()) { // x <= b <= 0, y <= d <= 0 --> b*d <= x*y @@ -452,7 +452,7 @@ interval & interval::operator*=(interval const & other) { m_upper_dep = m_upper.is_infinite() ? 0 : join(b_d, d_d, a_d); } else { - // 0 <= a <= x, 0 <= c <= y --> a*c <= x*y + // 0 <= a <= x, 0 <= c <= y --> a*c <= x*y // x <= b, y <= d --> x*y <= b*d (uses the fact that x is pos (a is not negative) or y is pos (c is not negative)) TRACE("interval_bug", tout << "(P, P)\n";); SASSERT(other.is_P()); @@ -467,7 +467,7 @@ interval & interval::operator*=(interval const & other) { } } TRACE("interval_bug", tout << "operator*= result: " << *this << "\n";); - CTRACE("interval", !(!(contains_zero1 || contains_zero2) || contains_zero()), + CTRACE("interval", !(!(contains_zero1 || contains_zero2) || contains_zero()), tout << "contains_zero1: " << contains_zero1 << ", contains_zero2: " << contains_zero2 << ", contains_zero(): " << contains_zero() << "\n";); SASSERT(!(contains_zero1 || contains_zero2) || contains_zero()); return *this; @@ -482,7 +482,7 @@ bool interval::contains_zero() const { tout << "m_upper.is_zero: " << m_upper.is_zero() << "\n"; tout << "m_upper_open: " << m_upper_open << "\n"; tout << "result: " << ((m_lower.is_neg() || (m_lower.is_zero() && !m_lower_open)) && (m_upper.is_pos() || (m_upper.is_zero() && !m_upper_open))) << "\n";); - return + return (m_lower.is_neg() || (m_lower.is_zero() && !m_lower_open)) && (m_upper.is_pos() || (m_upper.is_zero() && !m_upper_open)); } @@ -510,7 +510,7 @@ interval & interval::inv() { ext_numeral new_upper; if (m_lower.is_zero()) { SASSERT(m_lower_open); - ext_numeral plus_infinity(true); + ext_numeral plus_infinity(true); new_upper = plus_infinity; } else { @@ -595,7 +595,7 @@ void interval::expt(unsigned n) { else if (m_upper.is_neg()) { // [l, u]^n = [u^n, l^n] if u < 0 // a <= x <= b < 0 --> x^n <= a^n (use lower and upper bound -- need the fact that x is negative) - // x <= b < 0 --> b^n <= x^n + // x <= b < 0 --> b^n <= x^n std::swap(m_lower, m_upper); std::swap(m_lower_open, m_upper_open); std::swap(m_lower_dep, m_upper_dep); @@ -614,7 +614,7 @@ void interval::expt(unsigned n) { m_upper = m_lower; m_upper_open = m_lower_open; } - m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep); + m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep); m_lower = ext_numeral(0); m_lower_open = false; m_lower_dep = 0; From 0a0b17540f307e041a791145c2c94ab57a7b6907 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 19 Apr 2017 13:07:04 +0100 Subject: [PATCH 073/105] Added rlimit.inc() for expensive interval exponentiation in the non-linear arithmetic theory. --- src/smt/theory_arith_nl.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index 52a117cd5..c8729ea36 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -339,8 +339,13 @@ namespace smt { tout << mk_pp(var, get_manager()) << "\n"; tout << "power " << power << ": " << expt(i, power) << "\n"; display_interval(tout << "target before: ", target); tout << "\n";); + i.expt(power); target *= i; + + get_manager().limit().inc((target.is_lower_open() || target.minus_infinity()) ? 1 : target.get_lower_value().bitsize()); + get_manager().limit().inc((target.is_upper_open() || target.plus_infinity()) ? 1 : target.get_upper_value().bitsize()); + TRACE("non_linear", display_interval(tout << "target after: ", target); tout << "\n";); } From 4b0f7bc222184c1ae649e23b88cfc4698d1c6da0 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Thu, 20 Apr 2017 17:22:05 +0100 Subject: [PATCH 074/105] Fix typo noted in #979. `g++` is the default compiler rather than the `gcc` binary. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4c6dd012..b9d0d6fe1 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ make sudo make install ``` -Note by default ``gcc`` is used as the C++ compiler if it is available. If you +Note by default ``g++`` is used as the C++ compiler if it is available. If you would prefer to use Clang change the ``mk_make.py`` line to ```bash From 2badef9d0bdacbe6a7645aad0ed7f5896c5d47f4 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Thu, 20 Apr 2017 17:25:00 +0100 Subject: [PATCH 075/105] Be more explicit about using Clang as the compiler as noted in #979. Referring to the ``mk_make.py`` line might lead someone to think they need to modify the ``mk_make.py`` file rather than change the command line invocation. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9d0d6fe1..f92a5389a 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ sudo make install ``` Note by default ``g++`` is used as the C++ compiler if it is available. If you -would prefer to use Clang change the ``mk_make.py`` line to +would prefer to use Clang change the ``mk_make.py`` invocation to: ```bash CXX=clang++ CC=clang python scripts/mk_make.py From a1bb1f2a13557856ea8112b5e856fc8731a040af Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 22 Apr 2017 13:15:00 -0400 Subject: [PATCH 076/105] pre-init assumptions and unsat core validation for smt theories --- src/smt/smt_context.cpp | 50 +++++++++++++++++++++++++++++++++++++---- src/smt/smt_context.h | 6 +++-- src/smt/smt_theory.h | 16 +++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index f1b043556..225a0d58d 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3072,11 +3072,11 @@ namespace smt { m_assumptions.reset(); } - void context::mk_unsat_core() { + lbool context::mk_unsat_core() { SASSERT(inconsistent()); if (!tracking_assumptions()) { SASSERT(m_assumptions.empty()); - return; + return l_false; } uint_set already_found_assumptions; literal_vector::const_iterator it = m_conflict_resolution->begin_unsat_core(); @@ -3101,7 +3101,17 @@ namespace smt { for (unsigned i = 0; i < sz; i++) { tout << mk_pp(m_unsat_core.get(i), m_manager) << "\n"; }); - validate_unsat_core(); + validate_unsat_core(); + // theory validation of unsat core + ptr_vector::iterator th_it = m_theory_set.begin(); + ptr_vector::iterator th_end = m_theory_set.end(); + for (; th_it != th_end; ++th_it) { + lbool theory_result = (*th_it)->validate_unsat_core(m_unsat_core); + if (theory_result == l_undef) { + return l_undef; + } + } + return l_false; } /** @@ -3144,6 +3154,14 @@ namespace smt { SASSERT(m_scope_lvl == 0); SASSERT(!m_setup.already_configured()); setup_context(m_fparams.m_auto_config); + + expr_ref_vector theory_assumptions(m_manager); + get_theory_assumptions(theory_assumptions); + if (!theory_assumptions.empty()) { + TRACE("search", tout << "Adding theory assumptions to context" << std::endl;); + return check(theory_assumptions.size(), theory_assumptions.c_ptr(), reset_cancel, true); + } + internalize_assertions(); lbool r = l_undef; if (m_asserted_formulas.inconsistent()) { @@ -3205,7 +3223,15 @@ namespace smt { (*it)->setup(); } - lbool context::check(unsigned num_assumptions, expr * const * assumptions, bool reset_cancel) { + void context::get_theory_assumptions(expr_ref_vector & theory_assumptions) { + ptr_vector::iterator it = m_theory_set.begin(); + ptr_vector::iterator end = m_theory_set.end(); + for (; it != end; ++it) { + (*it)->add_theory_assumptions(theory_assumptions); + } + } + + lbool context::check(unsigned ext_num_assumptions, expr * const * ext_assumptions, bool reset_cancel, bool already_did_theory_assumptions) { m_stats.m_num_checks++; TRACE("check_bug", tout << "STARTING check(num_assumptions, assumptions)\n"; tout << "inconsistent: " << inconsistent() << ", m_unsat_core.empty(): " << m_unsat_core.empty() << "\n"; @@ -3216,6 +3242,22 @@ namespace smt { m_unsat_core.reset(); if (!check_preamble(reset_cancel)) return l_undef; + + expr_ref_vector all_assumptions(m_manager); + for (unsigned i = 0; i < ext_num_assumptions; ++i) { + all_assumptions.push_back(ext_assumptions[i]); + } + if (!already_did_theory_assumptions) { + ptr_vector::iterator it = m_theory_set.begin(); + ptr_vector::iterator end = m_theory_set.end(); + for (; it != end; ++it) { + (*it)->add_theory_assumptions(all_assumptions); + } + } + + unsigned num_assumptions = all_assumptions.size(); + expr * const * assumptions = all_assumptions.c_ptr(); + if (!validate_assumptions(num_assumptions, assumptions)) return l_undef; TRACE("check_bug", tout << "inconsistent: " << inconsistent() << ", m_unsat_core.empty(): " << m_unsat_core.empty() << "\n";); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 1f57a7550..4f0c14f5a 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1059,7 +1059,9 @@ namespace smt { void reset_assumptions(); - void mk_unsat_core(); + void get_theory_assumptions(expr_ref_vector & theory_assumptions); + + lbool mk_unsat_core(); void validate_unsat_core(); @@ -1441,7 +1443,7 @@ namespace smt { void pop(unsigned num_scopes); - lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0, bool reset_cancel = true); + lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0, bool reset_cancel = true, bool already_did_theory_assumptions = false); lbool get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed); diff --git a/src/smt/smt_theory.h b/src/smt/smt_theory.h index cee36535f..2745a6efd 100644 --- a/src/smt/smt_theory.h +++ b/src/smt/smt_theory.h @@ -177,6 +177,22 @@ namespace smt { virtual void restart_eh() { } + /** + \brief This method is called by smt_context before the search starts + to get any extra assumptions the theory wants to use. + (See theory_str for an example) + */ + virtual void add_theory_assumptions(expr_ref_vector & assumptions) { + } + + /** + \brief This method is called from smt_context when an unsat core is generated. + The theory may change the answer to UNKNOWN by returning l_undef from this method. + */ + virtual lbool validate_unsat_core(expr_ref_vector & unsat_core) { + return l_false; + } + /** \brief This method is invoked before the search starts. */ From 367cc4b77f4ae283790556b848be31390a8e22c6 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 22 Apr 2017 13:36:09 -0400 Subject: [PATCH 077/105] check result of unsat core validation --- src/smt/smt_context.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 225a0d58d..32fb492ad 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3281,13 +3281,21 @@ namespace smt { TRACE("after_internalization", display(tout);); if (inconsistent()) { VERIFY(!resolve_conflict()); // build the proof - mk_unsat_core(); - r = l_false; + lbool result = mk_unsat_core(); + if (result == l_undef) { + r = l_undef; + } else { + r = l_false; + } } else { r = search(); - if (r == l_false) - mk_unsat_core(); + if (r == l_false) { + lbool result = mk_unsat_core(); + if (result == l_undef) { + r = l_undef; + } + } } } } From 5068d2083dc0609801f572a0e3d14df753d36a03 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 22 Apr 2017 11:36:03 -0700 Subject: [PATCH 078/105] tidy Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 15 ++++----------- src/smt/smt_context.h | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 32fb492ad..6a3c036ca 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3156,7 +3156,7 @@ namespace smt { setup_context(m_fparams.m_auto_config); expr_ref_vector theory_assumptions(m_manager); - get_theory_assumptions(theory_assumptions); + add_theory_assumptions(theory_assumptions); if (!theory_assumptions.empty()) { TRACE("search", tout << "Adding theory assumptions to context" << std::endl;); return check(theory_assumptions.size(), theory_assumptions.c_ptr(), reset_cancel, true); @@ -3223,7 +3223,7 @@ namespace smt { (*it)->setup(); } - void context::get_theory_assumptions(expr_ref_vector & theory_assumptions) { + void context::add_theory_assumptions(expr_ref_vector & theory_assumptions) { ptr_vector::iterator it = m_theory_set.begin(); ptr_vector::iterator end = m_theory_set.end(); for (; it != end; ++it) { @@ -3243,16 +3243,9 @@ namespace smt { if (!check_preamble(reset_cancel)) return l_undef; - expr_ref_vector all_assumptions(m_manager); - for (unsigned i = 0; i < ext_num_assumptions; ++i) { - all_assumptions.push_back(ext_assumptions[i]); - } + expr_ref_vector all_assumptions(m_manager, ext_num_assumptions, ext_assumptions); if (!already_did_theory_assumptions) { - ptr_vector::iterator it = m_theory_set.begin(); - ptr_vector::iterator end = m_theory_set.end(); - for (; it != end; ++it) { - (*it)->add_theory_assumptions(all_assumptions); - } + add_theory_assumptions(all_assumptions); } unsigned num_assumptions = all_assumptions.size(); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 4f0c14f5a..abdba86d1 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1059,7 +1059,7 @@ namespace smt { void reset_assumptions(); - void get_theory_assumptions(expr_ref_vector & theory_assumptions); + void add_theory_assumptions(expr_ref_vector & theory_assumptions); lbool mk_unsat_core(); From ce67c8277c62b10cbb295266c0c6cc939dc86910 Mon Sep 17 00:00:00 2001 From: Bruce Collie Date: Mon, 24 Apr 2017 12:59:44 +0000 Subject: [PATCH 079/105] Return check result in fixedpoint object This is a small change to fix a missing return statement. --- src/api/c++/z3++.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index b6157f3ff..9d9982523 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -2244,7 +2244,7 @@ namespace z3 { void from_file(char const* s) { Z3_fixedpoint_from_file(ctx(), m_fp, s); check_error(); } void add_rule(expr& rule, symbol const& name) { Z3_fixedpoint_add_rule(ctx(), m_fp, rule, name); check_error(); } void add_fact(func_decl& f, unsigned * args) { Z3_fixedpoint_add_fact(ctx(), m_fp, f, f.arity(), args); check_error(); } - check_result query(expr& q) { Z3_lbool r = Z3_fixedpoint_query(ctx(), m_fp, q); check_error(); to_check_result(r); } + check_result query(expr& q) { Z3_lbool r = Z3_fixedpoint_query(ctx(), m_fp, q); check_error(); return to_check_result(r); } check_result query(func_decl_vector& relations) { array rs(relations); Z3_lbool r = Z3_fixedpoint_query_relations(ctx(), m_fp, rs.size(), rs.ptr()); From 81ba729aab8d2181244de354f0bdb1ea8415691a Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 24 Apr 2017 15:25:45 +0100 Subject: [PATCH 080/105] [Doxygen] Fix script `--help` functionality. --- doc/mk_api_doc.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index edabcbd1b..bb5a19e5f 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -13,11 +13,13 @@ ML_ENABLED=False BUILD_DIR='../build' def display_help(exit_code): + assert isinstance(exit_code, int) print("mk_api_doc.py: Z3 documentation generator\n") print("\nOptions:") print(" -h, --help display this message.") print(" -b , --build= subdirectory where Z3 is built (default: ../build).") print(" --ml include ML/OCaml API documentation.") + sys.exit(exit_code) def parse_options(): global ML_ENABLED, BUILD_DIR @@ -34,8 +36,7 @@ def parse_options(): if opt in ('-b', '--build'): BUILD_DIR = mk_util.norm_path(arg) elif opt in ('h', '--help'): - display_help() - exit(1) + display_help(0) elif opt in ('--ml'): ML_ENABLED=True else: @@ -128,7 +129,7 @@ try: print("Generated ML/OCaml documentation.") print("Documentation was successfully generated at subdirectory './api/html'.") -except: +except Exception: exctype, value = sys.exc_info()[:2] print("ERROR: failed to generate documentation: %s" % value) exit(1) From ca678c3675a7aa1bd5b396b15660b7ab1163c193 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 24 Apr 2017 15:45:57 +0100 Subject: [PATCH 081/105] [Doxygen] Fix bug where `def_Type` directives in `z3.h` would appear in generated doxygen documentation. --- doc/mk_api_doc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index bb5a19e5f..e86205a7a 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -50,15 +50,16 @@ def mk_dir(d): if not os.path.exists(d): os.makedirs(d) -# Eliminate def_API and extra_API directives from file 'inf'. +# Eliminate def_API, extra_API, and def_Type directives from file 'inf'. # The result is stored in 'outf'. def cleanup_API(inf, outf): pat1 = re.compile(".*def_API.*") pat2 = re.compile(".*extra_API.*") + pat3 = re.compile(r".*def_Type\(.*") _inf = open(inf, 'r') _outf = open(outf, 'w') for line in _inf: - if not pat1.match(line) and not pat2.match(line): + if not pat1.match(line) and not pat2.match(line) and not pat3.match(line): _outf.write(line) try: From 34acaa8f564824f1ff2fddd5e7d27fc4b75a0f33 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 24 Apr 2017 13:34:10 -0700 Subject: [PATCH 082/105] update license for space/quotes per #982 Signed-off-by: Nikolaj Bjorner --- LICENSE.txt | 7 +++++-- src/interp/iz3proof_itp.cpp | 7 ++++++- src/sat/sat_solver.cpp | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 91c8070d0..cc90bed74 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -2,6 +2,9 @@ Z3 Copyright (c) Microsoft Corporation All rights reserved. MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 11cb2f6a2..26ef7386c 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -541,6 +541,7 @@ class iz3proof_itp_impl : public iz3proof_itp { placeholder_arg |= is_placeholder(args[i]); } try { + TRACE("duality", print_expr(tout, e); tout << "\n";); opr f = op(e); if(f == Equal && args[0] == args[1]) res = mk_true(); else if(f == And) res = my_and(args); @@ -853,6 +854,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){ if(pl == arg(pf,1)){ + TRACE("duality", print_expr(tout, pl); print_expr(tout << "\n", neg_equality); print_expr(tout << "\n", pf); tout << "\n";); ast cond = mk_true(); ast equa = sep_cond(arg(pf,0),cond); if(is_equivrel_chain(equa)){ @@ -1870,10 +1872,13 @@ class iz3proof_itp_impl : public iz3proof_itp { ast chain_ineqs(opr comp_op, LitType t, const ast &chain, const ast &lhs, const ast &rhs){ if(is_true(chain)){ - if(lhs != rhs) + if (lhs != rhs) { + TRACE("duality", print_expr(tout, lhs); tout << " "; print_expr(tout, rhs); tout << "\n";); throw bad_ineq_inference(); + } return make(Leq,make_int(rational(0)),make_int(rational(0))); } + TRACE("duality", print_expr(tout, chain); print_expr(tout << "\n", lhs); tout << " "; print_expr(tout, rhs); tout << "\n";); ast last = chain_last(chain); ast rest = chain_rest(chain); ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 9c858a29a..e382d1d00 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -3324,7 +3324,7 @@ namespace sat { if (is_sat == l_true) { delete_unfixed(unfixed_lits, unfixed_vars); } - extract_fixed_consequences(num_units, assumptions, unfixed_vars, conseq); + extract_fixed_consequences(num_units, assumptions, unfixed_vars, conseq); update_unfixed_literals(unfixed_lits, unfixed_vars); IF_VERBOSE(1, verbose_stream() << "(sat.get-consequences" << " iterations: " << num_iterations From 2cdb45605dcc3f17d0101f9669fb6ad8a4923e83 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 24 Apr 2017 16:24:15 +0100 Subject: [PATCH 083/105] [Doxygen] Switch to using `argparse` to parse command line arguments in `mk_api_doc.py`. Given that we need to add a bunch of new command line options it makes sense to use a less clumsy and concise API. --- doc/mk_api_doc.py | 50 ++++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index e86205a7a..d27351015 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -1,5 +1,9 @@ # Copyright (c) Microsoft Corporation 2015 +""" +Z3 API documentation generator script +""" +import argparse import os import shutil import re @@ -12,39 +16,23 @@ import shutil ML_ENABLED=False BUILD_DIR='../build' -def display_help(exit_code): - assert isinstance(exit_code, int) - print("mk_api_doc.py: Z3 documentation generator\n") - print("\nOptions:") - print(" -h, --help display this message.") - print(" -b , --build= subdirectory where Z3 is built (default: ../build).") - print(" --ml include ML/OCaml API documentation.") - sys.exit(exit_code) - def parse_options(): global ML_ENABLED, BUILD_DIR - - try: - options, remainder = getopt.gnu_getopt(sys.argv[1:], - 'b:h', - ['build=', 'help', 'ml']) - except: - print("ERROR: Invalid command line option") - display_help(1) - - for opt, arg in options: - if opt in ('-b', '--build'): - BUILD_DIR = mk_util.norm_path(arg) - elif opt in ('h', '--help'): - display_help(0) - elif opt in ('--ml'): - ML_ENABLED=True - else: - print("ERROR: Invalid command line option: %s" % opt) - display_help(1) - - - + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('-b', + '--build', + default=BUILD_DIR, + help='Directory where Z3 is built (default: %(default)s)', + ) + parser.add_argument('--ml', + action='store_true', + default=False, + help='Include ML/OCaml API documentation' + ) + pargs = parser.parse_args() + ML_ENABLED = pargs.ml + BUILD_DIR = pargs.build + return def mk_dir(d): if not os.path.exists(d): From 8a1df3df6293b5f8ac9be6d42d8ba6f23d708864 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 24 Apr 2017 21:52:59 +0100 Subject: [PATCH 084/105] [Doxygen] Add `--doxygen-executable` command line option to `mk_api_doc.py`. This allows a custom path to Doxygen to be specified. --- doc/mk_api_doc.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index d27351015..62130d73c 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -15,9 +15,10 @@ import shutil ML_ENABLED=False BUILD_DIR='../build' +DOXYGEN_EXE='doxygen' def parse_options(): - global ML_ENABLED, BUILD_DIR + global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-b', '--build', @@ -29,9 +30,15 @@ def parse_options(): default=False, help='Include ML/OCaml API documentation' ) + parser.add_argument('--doxygen-executable', + dest='doxygen_executable', + default=DOXYGEN_EXE, + help='Doxygen executable to use (default: %(default)s)', + ) pargs = parser.parse_args() ML_ENABLED = pargs.ml BUILD_DIR = pargs.build + DOXYGEN_EXE = pargs.doxygen_executable return def mk_dir(d): @@ -81,7 +88,7 @@ try: print("Removed annotations from z3_api.h.") try: - if subprocess.call(['doxygen', 'z3api.dox']) != 0: + if subprocess.call([DOXYGEN_EXE, 'z3api.dox']) != 0: print("ERROR: doxygen returned nonzero return code") exit(1) except: From 48b62d34b7563c8267e1c8f480d254e85154a88e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 24 Apr 2017 18:08:52 -0700 Subject: [PATCH 085/105] make sure consequence generation works with interpreted atoms/terms Signed-off-by: Nikolaj Bjorner --- src/smt/smt_consequences.cpp | 83 +++++++++++++++++++++++++++--------- src/smt/smt_context.h | 6 +-- 2 files changed, 66 insertions(+), 23 deletions(-) diff --git a/src/smt/smt_consequences.cpp b/src/smt/smt_consequences.cpp index 9558f6a3b..88f4308cf 100644 --- a/src/smt/smt_consequences.cpp +++ b/src/smt/smt_consequences.cpp @@ -44,13 +44,14 @@ namespace smt { // - e is an equality between a variable and value that is to be fixed. // - e is a data-type recognizer of a variable that is to be fixed. // - void context::extract_fixed_consequences(literal lit, obj_map& vars, index_set const& assumptions, expr_ref_vector& conseq) { + void context::extract_fixed_consequences(literal lit, obj_map& vars, obj_map const& var2orig, index_set const& assumptions, expr_ref_vector& conseq) { ast_manager& m = m_manager; datatype_util dt(m); expr* e1, *e2; expr_ref fml(m); if (lit == true_literal) return; expr* e = bool_var2expr(lit.var()); + TRACE("context", display(tout << mk_pp(e, m) << "\n");); index_set s; if (assumptions.contains(lit.var())) { s.insert(lit.var()); @@ -67,17 +68,23 @@ namespace smt { bool found = false; if (vars.contains(e)) { found = true; - fml = lit.sign() ? m.mk_not(e) : e; vars.erase(e); + e = var2orig.find(e); + fml = lit.sign() ? m.mk_not(e) : e; } else if (!lit.sign() && m.is_eq(e, e1, e2)) { - if (vars.contains(e2)) { - std::swap(e1, e2); - } - if (vars.contains(e1) && m.is_value(e2)) { + if (vars.contains(e2) && m.is_value(e1)) { + found = true; + vars.erase(e2); + e2 = var2orig.find(e2); + std::swap(e1, e2); + fml = m.mk_eq(e1, e2); + } + else if (vars.contains(e1) && m.is_value(e2)) { found = true; - fml = e; vars.erase(e1); + e1 = var2orig.find(e1); + fml = m.mk_eq(e1, e2); } } else if (!lit.sign() && is_app(e) && dt.is_recognizer(to_app(e)->get_decl())) { @@ -94,6 +101,7 @@ namespace smt { } void context::justify(literal lit, index_set& s) { + ast_manager& m = m_manager; b_justification js = get_justification(lit.var()); switch (js.get_kind()) { case b_justification::CLAUSE: { @@ -119,6 +127,9 @@ namespace smt { literal_vector literals; m_conflict_resolution->justification2literals(js.get_justification(), literals); for (unsigned j = 0; j < literals.size(); ++j) { + if (!m_antecedents.contains(literals[j].var())) { + TRACE("context", tout << literals[j] << " " << mk_pp(bool_var2expr(literals[j].var()), m) << "\n";); + } s |= m_antecedents.find(literals[j].var()); } break; @@ -126,13 +137,13 @@ namespace smt { } } - void context::extract_fixed_consequences(unsigned& start, obj_map& vars, index_set const& assumptions, expr_ref_vector& conseq) { + void context::extract_fixed_consequences(unsigned& start, obj_map& vars, obj_map const& var2orig, index_set const& assumptions, expr_ref_vector& conseq) { pop_to_search_lvl(); SASSERT(!inconsistent()); literal_vector const& lits = assigned_literals(); unsigned sz = lits.size(); for (unsigned i = start; i < sz; ++i) { - extract_fixed_consequences(lits[i], vars, assumptions, conseq); + extract_fixed_consequences(lits[i], vars, var2orig, assumptions, conseq); } start = sz; SASSERT(!inconsistent()); @@ -202,7 +213,7 @@ namespace smt { // Add a clause to short-circuit the congruence justifications for // next rounds. // - unsigned context::extract_fixed_eqs(obj_map& var2val, expr_ref_vector& conseq) { + unsigned context::extract_fixed_eqs(obj_map& var2val, obj_map const& var2orig, expr_ref_vector& conseq) { TRACE("context", tout << "extract fixed consequences\n";); ast_manager& m = m_manager; ptr_vector to_delete; @@ -220,7 +231,7 @@ namespace smt { s |= m_antecedents.find(literals[i].var()); } - fml = m.mk_eq(k, v); + fml = m.mk_eq(var2orig.find(k), v); fml = m.mk_implies(antecedent2fml(s), fml); conseq.push_back(fml); to_delete.push_back(k); @@ -242,9 +253,13 @@ namespace smt { literal context::mk_diseq(expr* e, expr* val) { ast_manager& m = m_manager; - if (m.is_bool(e)) { + if (m.is_bool(e) && b_internalized(e)) { return literal(get_bool_var(e), m.is_true(val)); } + else if (m.is_bool(e)) { + internalize_formula(e, false); + return literal(get_bool_var(e), !m.is_true(val)); + } else { expr_ref eq(mk_eq_atom(e, val), m); internalize_formula(eq, false); @@ -253,15 +268,39 @@ namespace smt { } lbool context::get_consequences(expr_ref_vector const& assumptions, - expr_ref_vector const& vars, + expr_ref_vector const& vars1, expr_ref_vector& conseq, expr_ref_vector& unfixed) { m_antecedents.reset(); + m_antecedents.insert(true_literal.var(), index_set()); pop_to_base_lvl(); + ast_manager& m = m_manager; + expr_ref_vector vars(m); + obj_map var2orig; + bool pushed = false; + for (unsigned i = 0; i < vars1.size(); ++i) { + expr* v = vars1[i]; + if (is_uninterp_const(v)) { + vars.push_back(v); + var2orig.insert(v, v); + } + else { + if (!pushed) { + pushed = true; + push(); + } + expr_ref c(m.mk_fresh_const("v", m.get_sort(v)), m); + expr_ref eq(m.mk_eq(c, v), m); + assert_expr(eq); + vars.push_back(c); + var2orig.insert(c, v); + } + } lbool is_sat = check(assumptions.size(), assumptions.c_ptr()); if (is_sat != l_true) { TRACE("context", tout << is_sat << "\n";); + if (pushed) pop(1); return is_sat; } @@ -272,23 +311,22 @@ namespace smt { } model_ref mdl; get_model(mdl); - ast_manager& m = m_manager; expr_ref_vector trail(m); model_evaluator eval(*mdl.get()); expr_ref val(m); TRACE("context", model_pp(tout, *mdl);); for (unsigned i = 0; i < vars.size(); ++i) { - eval(vars[i], val); + eval(vars[i].get(), val); if (m.is_value(val)) { trail.push_back(val); - var2val.insert(vars[i], val); + var2val.insert(vars[i].get(), val); } else { - unfixed.push_back(vars[i]); + unfixed.push_back(vars[i].get()); } } unsigned num_units = 0; - extract_fixed_consequences(num_units, var2val, _assumptions, conseq); + extract_fixed_consequences(num_units, var2val, var2orig, _assumptions, conseq); app_ref eq(m); TRACE("context", tout << "vars: " << vars.size() << "\n"; @@ -303,6 +341,7 @@ namespace smt { unsigned num_vars = 0; for (; it != end && num_vars < chunk_size; ++it) { if (get_cancel_flag()) { + if (pushed) pop(1); return l_undef; } expr* e = it->m_key; @@ -332,6 +371,7 @@ namespace smt { while (true) { is_sat = bounded_search(); if (is_sat != l_true && m_last_search_failure != OK) { + if (pushed) pop(1); return is_sat; } if (is_sat == l_undef) { @@ -349,8 +389,8 @@ namespace smt { if (is_sat == l_true) { delete_unfixed(var2val, unfixed); } - extract_fixed_consequences(num_units, var2val, _assumptions, conseq); - num_fixed_eqs += extract_fixed_eqs(var2val, conseq); + extract_fixed_consequences(num_units, var2val, var2orig, _assumptions, conseq); + num_fixed_eqs += extract_fixed_eqs(var2val, var2orig, conseq); IF_VERBOSE(1, display_consequence_progress(verbose_stream(), num_iterations, var2val.size(), conseq.size(), unfixed.size(), num_fixed_eqs);); TRACE("context", display_consequence_progress(tout, num_iterations, var2val.size(), conseq.size(), @@ -359,6 +399,9 @@ namespace smt { end_search(); DEBUG_CODE(validate_consequences(assumptions, vars, conseq, unfixed);); + if (pushed) { + pop(1); + } return l_true; } diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 1f57a7550..8fd958fb0 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1377,14 +1377,14 @@ namespace smt { typedef hashtable index_set; //typedef uint_set index_set; u_map m_antecedents; - void extract_fixed_consequences(literal lit, obj_map& var2val, index_set const& assumptions, expr_ref_vector& conseq); - void extract_fixed_consequences(unsigned& idx, obj_map& var2val, index_set const& assumptions, expr_ref_vector& conseq); + void extract_fixed_consequences(literal lit, obj_map& var2val, obj_map const& var2orig, index_set const& assumptions, expr_ref_vector& conseq); + void extract_fixed_consequences(unsigned& idx, obj_map& var2val, obj_map const& var2orig, index_set const& assumptions, expr_ref_vector& conseq); void display_consequence_progress(std::ostream& out, unsigned it, unsigned nv, unsigned fixed, unsigned unfixed, unsigned eq); unsigned delete_unfixed(obj_map& var2val, expr_ref_vector& unfixed); - unsigned extract_fixed_eqs(obj_map& var2val, expr_ref_vector& conseq); + unsigned extract_fixed_eqs(obj_map& var2val, obj_map const& var2orig, expr_ref_vector& conseq); expr_ref antecedent2fml(index_set const& ante); From bd8b0186d6e76fb589b3f16c51d5b9afebbb0e22 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Apr 2017 10:30:10 -0700 Subject: [PATCH 086/105] make SMT consequence finding work with compound terms and formulas Signed-off-by: Nikolaj Bjorner --- src/smt/smt_consequences.cpp | 100 +++++++++++++++++++++-------------- src/smt/smt_context.h | 11 ++-- 2 files changed, 67 insertions(+), 44 deletions(-) diff --git a/src/smt/smt_consequences.cpp b/src/smt/smt_consequences.cpp index 88f4308cf..65272207e 100644 --- a/src/smt/smt_consequences.cpp +++ b/src/smt/smt_consequences.cpp @@ -30,6 +30,7 @@ namespace smt { index_set::iterator it = vars.begin(), end = vars.end(); for (; it != end; ++it) { expr* e = bool_var2expr(*it); + e = m_assumption2orig.find(e); premises.push_back(get_assignment(*it) != l_false ? e : m_manager.mk_not(e)); } return mk_and(premises); @@ -44,7 +45,7 @@ namespace smt { // - e is an equality between a variable and value that is to be fixed. // - e is a data-type recognizer of a variable that is to be fixed. // - void context::extract_fixed_consequences(literal lit, obj_map& vars, obj_map const& var2orig, index_set const& assumptions, expr_ref_vector& conseq) { + void context::extract_fixed_consequences(literal lit, index_set const& assumptions, expr_ref_vector& conseq) { ast_manager& m = m_manager; datatype_util dt(m); expr* e1, *e2; @@ -66,32 +67,32 @@ namespace smt { } tout << "\n";); bool found = false; - if (vars.contains(e)) { + if (m_var2val.contains(e)) { found = true; - vars.erase(e); - e = var2orig.find(e); + m_var2val.erase(e); + e = m_var2orig.find(e); fml = lit.sign() ? m.mk_not(e) : e; } else if (!lit.sign() && m.is_eq(e, e1, e2)) { - if (vars.contains(e2) && m.is_value(e1)) { + if (m_var2val.contains(e2) && m.is_value(e1)) { found = true; - vars.erase(e2); - e2 = var2orig.find(e2); + m_var2val.erase(e2); + e2 = m_var2orig.find(e2); std::swap(e1, e2); fml = m.mk_eq(e1, e2); } - else if (vars.contains(e1) && m.is_value(e2)) { + else if (m_var2val.contains(e1) && m.is_value(e2)) { found = true; - vars.erase(e1); - e1 = var2orig.find(e1); + m_var2val.erase(e1); + e1 = m_var2orig.find(e1); fml = m.mk_eq(e1, e2); } } else if (!lit.sign() && is_app(e) && dt.is_recognizer(to_app(e)->get_decl())) { - if (vars.contains(to_app(e)->get_arg(0))) { + if (m_var2val.contains(to_app(e)->get_arg(0))) { found = true; fml = m.mk_eq(to_app(e)->get_arg(0), m.mk_const(dt.get_recognizer_constructor(to_app(e)->get_decl()))); - vars.erase(to_app(e)->get_arg(0)); + m_var2val.erase(to_app(e)->get_arg(0)); } } if (found) { @@ -137,13 +138,13 @@ namespace smt { } } - void context::extract_fixed_consequences(unsigned& start, obj_map& vars, obj_map const& var2orig, index_set const& assumptions, expr_ref_vector& conseq) { + void context::extract_fixed_consequences(unsigned& start, index_set const& assumptions, expr_ref_vector& conseq) { pop_to_search_lvl(); SASSERT(!inconsistent()); literal_vector const& lits = assigned_literals(); unsigned sz = lits.size(); for (unsigned i = start; i < sz; ++i) { - extract_fixed_consequences(lits[i], vars, var2orig, assumptions, conseq); + extract_fixed_consequences(lits[i], assumptions, conseq); } start = sz; SASSERT(!inconsistent()); @@ -161,10 +162,10 @@ namespace smt { // rules out as many non-fixed variables as possible. // - unsigned context::delete_unfixed(obj_map& var2val, expr_ref_vector& unfixed) { + unsigned context::delete_unfixed(expr_ref_vector& unfixed) { ast_manager& m = m_manager; ptr_vector to_delete; - obj_map::iterator it = var2val.begin(), end = var2val.end(); + obj_map::iterator it = m_var2val.begin(), end = m_var2val.end(); for (; it != end; ++it) { expr* k = it->m_key; expr* v = it->m_value; @@ -200,7 +201,7 @@ namespace smt { } } for (unsigned i = 0; i < to_delete.size(); ++i) { - var2val.remove(to_delete[i]); + m_var2val.remove(to_delete[i]); unfixed.push_back(to_delete[i]); } return to_delete.size(); @@ -213,12 +214,12 @@ namespace smt { // Add a clause to short-circuit the congruence justifications for // next rounds. // - unsigned context::extract_fixed_eqs(obj_map& var2val, obj_map const& var2orig, expr_ref_vector& conseq) { + unsigned context::extract_fixed_eqs(expr_ref_vector& conseq) { TRACE("context", tout << "extract fixed consequences\n";); ast_manager& m = m_manager; ptr_vector to_delete; expr_ref fml(m), eq(m); - obj_map::iterator it = var2val.begin(), end = var2val.end(); + obj_map::iterator it = m_var2val.begin(), end = m_var2val.end(); for (; it != end; ++it) { expr* k = it->m_key; expr* v = it->m_value; @@ -231,7 +232,7 @@ namespace smt { s |= m_antecedents.find(literals[i].var()); } - fml = m.mk_eq(var2orig.find(k), v); + fml = m.mk_eq(m_var2orig.find(k), v); fml = m.mk_implies(antecedent2fml(s), fml); conseq.push_back(fml); to_delete.push_back(k); @@ -246,7 +247,7 @@ namespace smt { } } for (unsigned i = 0; i < to_delete.size(); ++i) { - var2val.remove(to_delete[i]); + m_var2val.remove(to_delete[i]); } return to_delete.size(); } @@ -267,8 +268,8 @@ namespace smt { } } - lbool context::get_consequences(expr_ref_vector const& assumptions, - expr_ref_vector const& vars1, + lbool context::get_consequences(expr_ref_vector const& assumptions0, + expr_ref_vector const& vars0, expr_ref_vector& conseq, expr_ref_vector& unfixed) { @@ -276,14 +277,16 @@ namespace smt { m_antecedents.insert(true_literal.var(), index_set()); pop_to_base_lvl(); ast_manager& m = m_manager; - expr_ref_vector vars(m); - obj_map var2orig; + expr_ref_vector vars(m), assumptions(m); + m_var2val.reset(); + m_var2orig.reset(); + m_assumption2orig.reset(); bool pushed = false; - for (unsigned i = 0; i < vars1.size(); ++i) { - expr* v = vars1[i]; + for (unsigned i = 0; i < vars0.size(); ++i) { + expr* v = vars0[i]; if (is_uninterp_const(v)) { vars.push_back(v); - var2orig.insert(v, v); + m_var2orig.insert(v, v); } else { if (!pushed) { @@ -294,7 +297,25 @@ namespace smt { expr_ref eq(m.mk_eq(c, v), m); assert_expr(eq); vars.push_back(c); - var2orig.insert(c, v); + m_var2orig.insert(c, v); + } + } + for (unsigned i = 0; i < assumptions0.size(); ++i) { + expr* a = assumptions0[i]; + if (is_uninterp_const(a)) { + assumptions.push_back(a); + m_assumption2orig.insert(a, a); + } + else { + if (!pushed) { + pushed = true; + push(); + } + expr_ref c(m.mk_fresh_const("a", m.get_sort(a)), m); + expr_ref eq(m.mk_eq(c, a), m); + assert_expr(eq); + assumptions.push_back(c); + m_assumption2orig.insert(c, a); } } lbool is_sat = check(assumptions.size(), assumptions.c_ptr()); @@ -304,10 +325,9 @@ namespace smt { return is_sat; } - obj_map var2val; index_set _assumptions; for (unsigned i = 0; i < assumptions.size(); ++i) { - _assumptions.insert(get_literal(assumptions[i]).var()); + _assumptions.insert(get_literal(assumptions[i].get()).var()); } model_ref mdl; get_model(mdl); @@ -319,14 +339,14 @@ namespace smt { eval(vars[i].get(), val); if (m.is_value(val)) { trail.push_back(val); - var2val.insert(vars[i].get(), val); + m_var2val.insert(vars[i].get(), val); } else { unfixed.push_back(vars[i].get()); } } unsigned num_units = 0; - extract_fixed_consequences(num_units, var2val, var2orig, _assumptions, conseq); + extract_fixed_consequences(num_units, _assumptions, conseq); app_ref eq(m); TRACE("context", tout << "vars: " << vars.size() << "\n"; @@ -336,8 +356,8 @@ namespace smt { unsigned num_fixed_eqs = 0; unsigned chunk_size = 100; - while (!var2val.empty()) { - obj_map::iterator it = var2val.begin(), end = var2val.end(); + while (!m_var2val.empty()) { + obj_map::iterator it = m_var2val.begin(), end = m_var2val.end(); unsigned num_vars = 0; for (; it != end && num_vars < chunk_size; ++it) { if (get_cancel_flag()) { @@ -387,13 +407,13 @@ namespace smt { m_not_l = null_literal; } if (is_sat == l_true) { - delete_unfixed(var2val, unfixed); + delete_unfixed(unfixed); } - extract_fixed_consequences(num_units, var2val, var2orig, _assumptions, conseq); - num_fixed_eqs += extract_fixed_eqs(var2val, var2orig, conseq); - IF_VERBOSE(1, display_consequence_progress(verbose_stream(), num_iterations, var2val.size(), conseq.size(), + extract_fixed_consequences(num_units, _assumptions, conseq); + num_fixed_eqs += extract_fixed_eqs(conseq); + IF_VERBOSE(1, display_consequence_progress(verbose_stream(), num_iterations, m_var2val.size(), conseq.size(), unfixed.size(), num_fixed_eqs);); - TRACE("context", display_consequence_progress(tout, num_iterations, var2val.size(), conseq.size(), + TRACE("context", display_consequence_progress(tout, num_iterations, m_var2val.size(), conseq.size(), unfixed.size(), num_fixed_eqs);); } diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 8fd958fb0..4980d32e5 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1377,14 +1377,17 @@ namespace smt { typedef hashtable index_set; //typedef uint_set index_set; u_map m_antecedents; - void extract_fixed_consequences(literal lit, obj_map& var2val, obj_map const& var2orig, index_set const& assumptions, expr_ref_vector& conseq); - void extract_fixed_consequences(unsigned& idx, obj_map& var2val, obj_map const& var2orig, index_set const& assumptions, expr_ref_vector& conseq); + obj_map m_var2orig; + obj_map m_assumption2orig; + obj_map m_var2val; + void extract_fixed_consequences(literal lit, index_set const& assumptions, expr_ref_vector& conseq); + void extract_fixed_consequences(unsigned& idx, index_set const& assumptions, expr_ref_vector& conseq); void display_consequence_progress(std::ostream& out, unsigned it, unsigned nv, unsigned fixed, unsigned unfixed, unsigned eq); - unsigned delete_unfixed(obj_map& var2val, expr_ref_vector& unfixed); + unsigned delete_unfixed(expr_ref_vector& unfixed); - unsigned extract_fixed_eqs(obj_map& var2val, obj_map const& var2orig, expr_ref_vector& conseq); + unsigned extract_fixed_eqs(expr_ref_vector& conseq); expr_ref antecedent2fml(index_set const& ante); From 5f7ae920c6758f1b359fc1827b8492b750243e4b Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 24 Apr 2017 22:25:51 +0100 Subject: [PATCH 087/105] [Doxygen] Teach `mk_api_doc.py` a new command line option (`--temp-dir`) which allows the location of the temporary directory to be controlled. While I'm here also write `website.dox` into the temporary directory where it belongs instead of in the source tree and simplify the logic that deletes the temporary directory and its contents. --- doc/mk_api_doc.py | 61 ++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index 62130d73c..a8ee1e220 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -16,9 +16,10 @@ import shutil ML_ENABLED=False BUILD_DIR='../build' DOXYGEN_EXE='doxygen' +TEMP_DIR=os.path.join(os.getcwd(), 'tmp') def parse_options(): - global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE + global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE, TEMP_DIR parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-b', '--build', @@ -35,10 +36,17 @@ def parse_options(): default=DOXYGEN_EXE, help='Doxygen executable to use (default: %(default)s)', ) + parser.add_argument('--temp-dir', + dest='temp_dir', + default=TEMP_DIR, + help='Path to directory to use as temporary directory. ' + '(default: %(default)s)', + ) pargs = parser.parse_args() ML_ENABLED = pargs.ml BUILD_DIR = pargs.build DOXYGEN_EXE = pargs.doxygen_executable + TEMP_DIR = pargs.temp_dir return def mk_dir(d): @@ -60,8 +68,14 @@ def cleanup_API(inf, outf): try: parse_options() + print("Creating temporary directory \"{}\"".format(TEMP_DIR)) + mk_dir(TEMP_DIR) + # Short-hand for path to temporary file + def temp_path(path): + return os.path.join(TEMP_DIR, path) + fi = open('website.dox', 'r') - fo = open('website-adj.dox', 'w') + fo = open(temp_path('website.dox'), 'w') for line in fi: if (line != '[ML]\n'): @@ -71,20 +85,18 @@ try: fi.close() fo.close() + mk_dir('api/html') - mk_dir('tmp') - shutil.copyfile('website-adj.dox', 'tmp/website.dox') - os.remove('website-adj.dox') - shutil.copyfile('../src/api/python/z3/z3.py', 'tmp/z3py.py') - cleanup_API('../src/api/z3_api.h', 'tmp/z3_api.h') - cleanup_API('../src/api/z3_ast_containers.h', 'tmp/z3_ast_containers.h') - cleanup_API('../src/api/z3_algebraic.h', 'tmp/z3_algebraic.h') - cleanup_API('../src/api/z3_polynomial.h', 'tmp/z3_polynomial.h') - cleanup_API('../src/api/z3_rcf.h', 'tmp/z3_rcf.h') - cleanup_API('../src/api/z3_fixedpoint.h', 'tmp/z3_fixedpoint.h') - cleanup_API('../src/api/z3_optimization.h', 'tmp/z3_optimization.h') - cleanup_API('../src/api/z3_interp.h', 'tmp/z3_interp.h') - cleanup_API('../src/api/z3_fpa.h', 'tmp/z3_fpa.h') + shutil.copyfile('../src/api/python/z3/z3.py', temp_path('z3py.py')) + cleanup_API('../src/api/z3_api.h', temp_path('z3_api.h')) + cleanup_API('../src/api/z3_ast_containers.h', temp_path('z3_ast_containers.h')) + cleanup_API('../src/api/z3_algebraic.h', temp_path('z3_algebraic.h')) + cleanup_API('../src/api/z3_polynomial.h', temp_path('z3_polynomial.h')) + cleanup_API('../src/api/z3_rcf.h', temp_path('z3_rcf.h')) + cleanup_API('../src/api/z3_fixedpoint.h', temp_path('z3_fixedpoint.h')) + cleanup_API('../src/api/z3_optimization.h', temp_path('z3_optimization.h')) + cleanup_API('../src/api/z3_interp.h', temp_path('z3_interp.h')) + cleanup_API('../src/api/z3_fpa.h', temp_path('z3_fpa.h')) print("Removed annotations from z3_api.h.") try: @@ -95,23 +107,8 @@ try: print("ERROR: failed to execute 'doxygen', make sure doxygen (http://www.doxygen.org) is available in your system.") exit(1) print("Generated C and .NET API documentation.") - os.remove('tmp/z3_api.h') - os.remove('tmp/z3_ast_containers.h') - os.remove('tmp/z3_algebraic.h') - os.remove('tmp/z3_polynomial.h') - os.remove('tmp/z3_rcf.h') - os.remove('tmp/z3_fixedpoint.h') - os.remove('tmp/z3_optimization.h') - os.remove('tmp/z3_interp.h') - os.remove('tmp/z3_fpa.h') - print("Removed temporary file header files.") - - os.remove('tmp/website.dox') - print("Removed temporary file website.dox") - os.remove('tmp/z3py.py') - print("Removed temporary file z3py.py") - os.removedirs('tmp') - print("Removed temporary directory tmp.") + shutil.rmtree(os.path.realpath(TEMP_DIR)) + print("Removed temporary directory \"{}\"".format(TEMP_DIR)) sys.path.append('../src/api/python/z3') pydoc.writedoc('z3') shutil.move('z3.html', 'api/html/z3.html') From b4f8b001cecb9a341ddacb4e590b1dcaa01889fd Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 24 Apr 2017 22:55:41 +0100 Subject: [PATCH 088/105] [Doxygen] Teach `mk_api_doc.py` a new command line option (`--output-dir`) to control where output files are emitted. This is implemented by making `z3api.dox` a template file (renamed `z3api.cfg.in`) and populating the template at build time with the required settings. --- doc/mk_api_doc.py | 64 +++++++++++++++++++++++++++++---- doc/{z3api.dox => z3api.cfg.in} | 2 +- 2 files changed, 58 insertions(+), 8 deletions(-) rename doc/{z3api.dox => z3api.cfg.in} (99%) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index a8ee1e220..b4a58f392 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -17,9 +17,10 @@ ML_ENABLED=False BUILD_DIR='../build' DOXYGEN_EXE='doxygen' TEMP_DIR=os.path.join(os.getcwd(), 'tmp') +OUTPUT_DIRECTORY=os.path.join(os.getcwd(), 'api') def parse_options(): - global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE, TEMP_DIR + global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE, TEMP_DIR, OUTPUT_DIRECTORY parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-b', '--build', @@ -42,11 +43,17 @@ def parse_options(): help='Path to directory to use as temporary directory. ' '(default: %(default)s)', ) + parser.add_argument('--output-dir', + dest='output_dir', + default=OUTPUT_DIRECTORY, + help='Path to output directory (default: %(default)s)', + ) pargs = parser.parse_args() ML_ENABLED = pargs.ml BUILD_DIR = pargs.build DOXYGEN_EXE = pargs.doxygen_executable TEMP_DIR = pargs.temp_dir + OUTPUT_DIRECTORY = pargs.output_dir return def mk_dir(d): @@ -65,6 +72,41 @@ def cleanup_API(inf, outf): if not pat1.match(line) and not pat2.match(line) and not pat3.match(line): _outf.write(line) +def configure_file(template_file_path, output_file_path, substitutions): + """ + Read a template file ``template_file_path``, perform substitutions + found in the ``substitutions`` dictionary and write the result to + the output file ``output_file_path``. + + The template file should contain zero or more template strings of the + form ``@NAME@``. + + The substitutions dictionary maps old strings (without the ``@`` + symbols) to their replacements. + """ + assert isinstance(template_file_path, str) + assert isinstance(output_file_path, str) + assert isinstance(substitutions, dict) + assert len(template_file_path) > 0 + assert len(output_file_path) > 0 + print("Generating {} from {}".format(output_file_path, template_file_path)) + + if not os.path.exists(template_file_path): + raise Exception('Could not find template file "{}"'.format(template_file_path)) + + # Read whole template file into string + template_string = None + with open(template_file_path, 'r') as f: + template_string = f.read() + + # Do replacements + for (old_string, replacement) in substitutions.items(): + template_string = template_string.replace('@{}@'.format(old_string), replacement) + + # Write the string to the file + with open(output_file_path, 'w') as f: + f.write(template_string) + try: parse_options() @@ -74,6 +116,13 @@ try: def temp_path(path): return os.path.join(TEMP_DIR, path) + # Create configuration file from template + doxygen_config_substitutions = { + 'OUTPUT_DIRECTORY': OUTPUT_DIRECTORY, + } + doxygen_config_file = temp_path('z3api.cfg') + configure_file('z3api.cfg.in', doxygen_config_file, doxygen_config_substitutions) + fi = open('website.dox', 'r') fo = open(temp_path('website.dox'), 'w') @@ -86,7 +135,7 @@ try: fo.close() - mk_dir('api/html') + mk_dir(os.path.join(OUTPUT_DIRECTORY, 'html')) shutil.copyfile('../src/api/python/z3/z3.py', temp_path('z3py.py')) cleanup_API('../src/api/z3_api.h', temp_path('z3_api.h')) cleanup_API('../src/api/z3_ast_containers.h', temp_path('z3_ast_containers.h')) @@ -100,7 +149,7 @@ try: print("Removed annotations from z3_api.h.") try: - if subprocess.call([DOXYGEN_EXE, 'z3api.dox']) != 0: + if subprocess.call([DOXYGEN_EXE, doxygen_config_file]) != 0: print("ERROR: doxygen returned nonzero return code") exit(1) except: @@ -111,17 +160,18 @@ try: print("Removed temporary directory \"{}\"".format(TEMP_DIR)) sys.path.append('../src/api/python/z3') pydoc.writedoc('z3') - shutil.move('z3.html', 'api/html/z3.html') + shutil.move('z3.html', os.path.join(OUTPUT_DIRECTORY, 'html', 'z3.html')) print("Generated Python documentation.") if ML_ENABLED: - mk_dir('api/html/ml') - if subprocess.call(['ocamldoc', '-html', '-d', 'api\html\ml', '-sort', '-hide', 'Z3', '-I', '%s/api/ml' % BUILD_DIR, '../src/api/ml/z3enums.mli', '../src/api/ml/z3.mli']) != 0: + ml_output_dir = os.path.join(OUTPUT_DIRECTORY, 'html', 'ml') + mk_dir(ml_output_dir) + if subprocess.call(['ocamldoc', '-html', '-d', ml_output_dir, '-sort', '-hide', 'Z3', '-I', '%s/api/ml' % BUILD_DIR, '../src/api/ml/z3enums.mli', '../src/api/ml/z3.mli']) != 0: print("ERROR: ocamldoc failed.") exit(1) print("Generated ML/OCaml documentation.") - print("Documentation was successfully generated at subdirectory './api/html'.") + print("Documentation was successfully generated at subdirectory '{}'.".format(OUTPUT_DIRECTORY)) except Exception: exctype, value = sys.exc_info()[:2] print("ERROR: failed to generate documentation: %s" % value) diff --git a/doc/z3api.dox b/doc/z3api.cfg.in similarity index 99% rename from doc/z3api.dox rename to doc/z3api.cfg.in index c96a7be73..7e6c81e4f 100644 --- a/doc/z3api.dox +++ b/doc/z3api.cfg.in @@ -52,7 +52,7 @@ PROJECT_LOGO = # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. -OUTPUT_DIRECTORY = api +OUTPUT_DIRECTORY = @OUTPUT_DIRECTORY@ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output From 5a66f053848c05947a48e2aec953610291b39e28 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 24 Apr 2017 23:38:40 +0100 Subject: [PATCH 089/105] [Doxygen] Teach `mk_api_doc.py` to use `@` style substitutions to control whether OCaml documentation link is emitted. --- doc/mk_api_doc.py | 17 ++++++----------- doc/{website.dox => website.dox.in} | 3 +-- 2 files changed, 7 insertions(+), 13 deletions(-) rename doc/{website.dox => website.dox.in} (92%) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index b4a58f392..0a7efacfa 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -123,17 +123,12 @@ try: doxygen_config_file = temp_path('z3api.cfg') configure_file('z3api.cfg.in', doxygen_config_file, doxygen_config_substitutions) - fi = open('website.dox', 'r') - fo = open(temp_path('website.dox'), 'w') - - for line in fi: - if (line != '[ML]\n'): - fo.write(line) - elif (ML_ENABLED): - fo.write(' - ML/OCaml API\n') - fi.close() - fo.close() - + website_dox_substitutions = {} + if ML_ENABLED: + website_dox_substitutions['OCAML_API'] = '\n - ML/OCaml API\n' + else: + website_dox_substitutions['OCAML_API'] = '' + configure_file('website.dox.in', temp_path('website.dox'), website_dox_substitutions) mk_dir(os.path.join(OUTPUT_DIRECTORY, 'html')) shutil.copyfile('../src/api/python/z3/z3.py', temp_path('z3py.py')) diff --git a/doc/website.dox b/doc/website.dox.in similarity index 92% rename from doc/website.dox rename to doc/website.dox.in index 799949752..b00874c97 100644 --- a/doc/website.dox +++ b/doc/website.dox.in @@ -14,7 +14,6 @@ - \ref cppapi - .NET API - Java API - - Python API (also available in pydoc format) -[ML] + - Python API (also available in pydoc format)@OCAML_API@ - Try Z3 online at RiSE4Fun. */ From c78bf66df34ad6ea9aa104ec13f37b01de30769e Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 24 Apr 2017 23:49:44 +0100 Subject: [PATCH 090/105] [Doxygen] Fix bug in `mk_api_doc.py` where the generated doxygen configuration would not point at the correct path to the temporary directory. --- doc/mk_api_doc.py | 1 + doc/z3api.cfg.in | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index 0a7efacfa..cc59ffa69 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -119,6 +119,7 @@ try: # Create configuration file from template doxygen_config_substitutions = { 'OUTPUT_DIRECTORY': OUTPUT_DIRECTORY, + 'TEMP_DIR': TEMP_DIR } doxygen_config_file = temp_path('z3api.cfg') configure_file('z3api.cfg.in', doxygen_config_file, doxygen_config_substitutions) diff --git a/doc/z3api.cfg.in b/doc/z3api.cfg.in index 7e6c81e4f..bcc7113cc 100644 --- a/doc/z3api.cfg.in +++ b/doc/z3api.cfg.in @@ -684,7 +684,7 @@ WARN_LOGFILE = INPUT = ../src/api/dotnet \ ../src/api/java \ ../src/api/c++ \ - ./tmp + @TEMP_DIR@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is From 33af478ce24b33393b23952833064638fead3e8a Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 24 Apr 2017 23:55:51 +0100 Subject: [PATCH 091/105] [Doxygen] Fix some indentation in doxygen configuration file template. --- doc/z3api.cfg.in | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/z3api.cfg.in b/doc/z3api.cfg.in index bcc7113cc..cb07045b3 100644 --- a/doc/z3api.cfg.in +++ b/doc/z3api.cfg.in @@ -703,15 +703,15 @@ INPUT_ENCODING = UTF-8 # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = website.dox \ - z3_api.h \ - z3_algebraic.h \ - z3_polynomial.h \ - z3_rcf.h \ - z3_interp.h \ - z3_fpa.h \ + z3_api.h \ + z3_algebraic.h \ + z3_polynomial.h \ + z3_rcf.h \ + z3_interp.h \ + z3_fpa.h \ z3++.h \ z3py.py \ - *.cs \ + *.cs \ *.java # The RECURSIVE tag can be used to turn specify whether or not subdirectories From eb1c985a944b59a1cbbb55912a6fcfb030c5670e Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 00:24:46 +0100 Subject: [PATCH 092/105] [Doxygen] Fixed malformed code blocks in `z3_api.h`. These malformed `\code` blocks caused broken documentation to be generated. --- src/api/z3_api.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 272c94dda..557667b03 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3091,8 +3091,8 @@ extern "C" { \brief Create a numeral of a given sort. \param c logical context. - \param numeral A string representing the numeral value in decimal notation. The string may be of the form \code{[num]*[.[num]*][E[+|-][num]+]}. - If the given sort is a real, then the numeral can be a rational, that is, a string of the form \ccode{[num]* / [num]*}. + \param numeral A string representing the numeral value in decimal notation. The string may be of the form `[num]*[.[num]*][E[+|-][num]+]`. + If the given sort is a real, then the numeral can be a rational, that is, a string of the form `[num]* / [num]*` . \param ty The sort of the numeral. In the current implementation, the given sort can be an int, real, finite-domain, or bit-vectors of arbitrary size. \sa Z3_mk_int From 7242a77a3f377308dbfdd4b62a4dca7d80cb05d2 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 00:31:05 +0100 Subject: [PATCH 093/105] [Doxygen] Fix typo found with Doxygen warning ``` warning: Found unknown command `\s' ``` --- src/api/z3_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 557667b03..04f84fa2a 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3306,7 +3306,7 @@ extern "C" { Z3_ast Z3_API Z3_mk_seq_replace(Z3_context c, Z3_ast s, Z3_ast src, Z3_ast dst); /** - \brief Retrieve from \s the unit sequence positioned at position \c index. + \brief Retrieve from \c s the unit sequence positioned at position \c index. def_API('Z3_mk_seq_at' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) */ From fe702d7782db990cb90ff2bea390b100fdd65872 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 00:36:53 +0100 Subject: [PATCH 094/105] [Doxygen] Fix warning about non-existent functions. `Z3_push` and `Z3_pop` should be `Z3_solver_push` and `Z3_solver_pop` respectively. --- src/api/z3_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 04f84fa2a..45065f856 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1500,7 +1500,7 @@ extern "C" { All main interaction with Z3 happens in the context of a \c Z3_context. In contrast to #Z3_mk_context_rc, the life time of Z3_ast objects - are determined by the scope level of #Z3_push and #Z3_pop. + are determined by the scope level of #Z3_solver_push and #Z3_solver_pop. In other words, a Z3_ast object remains valid until there is a call to Z3_pop that takes the current scope below the level where the object was created. From e309174ec968d378f36415611dee1a09be52ccdd Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 11:32:26 +0100 Subject: [PATCH 095/105] [Doxygen] Add `--z3py-package-path` command line option to `mk_api_doc.py` so that the location of the z3py package can be specified. This is needed by the CMake build system because the complete Z3py package is not emitted in the source tree. Also fix a bug in the path added to the module/package search path. The directory containing the `z3` package needs to be added not the `z3` package directory itself. --- doc/mk_api_doc.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index cc59ffa69..3a1f8b3f8 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -18,9 +18,10 @@ BUILD_DIR='../build' DOXYGEN_EXE='doxygen' TEMP_DIR=os.path.join(os.getcwd(), 'tmp') OUTPUT_DIRECTORY=os.path.join(os.getcwd(), 'api') +Z3PY_PACKAGE_PATH='../src/api/python/z3' def parse_options(): - global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE, TEMP_DIR, OUTPUT_DIRECTORY + global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE, TEMP_DIR, OUTPUT_DIRECTORY, Z3PY_PACKAGE_PATH parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-b', '--build', @@ -48,12 +49,22 @@ def parse_options(): default=OUTPUT_DIRECTORY, help='Path to output directory (default: %(default)s)', ) + parser.add_argument('--z3py-package-path', + dest='z3py_package_path', + default=Z3PY_PACKAGE_PATH, + help='Path to directory containing Z3py package (default: %(default)s)', + ) pargs = parser.parse_args() ML_ENABLED = pargs.ml BUILD_DIR = pargs.build DOXYGEN_EXE = pargs.doxygen_executable TEMP_DIR = pargs.temp_dir OUTPUT_DIRECTORY = pargs.output_dir + Z3PY_PACKAGE_PATH = pargs.z3py_package_path + if not os.path.exists(Z3PY_PACKAGE_PATH): + raise Exception('"{}" does not exist'.format(Z3PY_PACKAGE_PATH)) + if not os.path.basename(Z3PY_PACKAGE_PATH) == 'z3': + raise Exception('"{}" does not end with "z3"'.format(Z3PY_PACKAGE_PATH)) return def mk_dir(d): @@ -154,7 +165,7 @@ try: print("Generated C and .NET API documentation.") shutil.rmtree(os.path.realpath(TEMP_DIR)) print("Removed temporary directory \"{}\"".format(TEMP_DIR)) - sys.path.append('../src/api/python/z3') + sys.path.append(os.path.dirname(Z3PY_PACKAGE_PATH)) pydoc.writedoc('z3') shutil.move('z3.html', os.path.join(OUTPUT_DIRECTORY, 'html', 'z3.html')) print("Generated Python documentation.") From cb6baa8bcb5f987cf6577307e1f7f0809386d981 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 11:47:50 +0100 Subject: [PATCH 096/105] [Doxygen] Put the path to the directory containing the Z3py package at the beginning of the search path so it is picked up first. This is to try to avoid picking an installed copy of Z3py. --- doc/mk_api_doc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index 3a1f8b3f8..10b2fcf55 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -165,7 +165,9 @@ try: print("Generated C and .NET API documentation.") shutil.rmtree(os.path.realpath(TEMP_DIR)) print("Removed temporary directory \"{}\"".format(TEMP_DIR)) - sys.path.append(os.path.dirname(Z3PY_PACKAGE_PATH)) + # Put z3py at the beginning of the search path to try to avoid picking up + # an installed copy of Z3py. + sys.path.insert(0, os.path.dirname(Z3PY_PACKAGE_PATH)) pydoc.writedoc('z3') shutil.move('z3.html', os.path.join(OUTPUT_DIRECTORY, 'html', 'z3.html')) print("Generated Python documentation.") From fa8f6f20a5fa59f3e4e180cbfbb0c575d7b4e1ed Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 13:07:30 +0100 Subject: [PATCH 097/105] [Doxygen] Teach `mk_api_doc.py` to prevent ".NET", "Z3py" and "Java" bindings from appearing in the generated documentation. This can be enabled with `--no-dotnet`, `--no-z3py`, and `--no-java` respectively. This fine-grained control is being added for the CMake build system which will need this control. --- doc/mk_api_doc.py | 102 ++++++++++++++++++++++++++++++++++++++++----- doc/website.dox.in | 7 +--- doc/z3api.cfg.in | 9 ++-- 3 files changed, 97 insertions(+), 21 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index 10b2fcf55..b61036d44 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -19,9 +19,13 @@ DOXYGEN_EXE='doxygen' TEMP_DIR=os.path.join(os.getcwd(), 'tmp') OUTPUT_DIRECTORY=os.path.join(os.getcwd(), 'api') Z3PY_PACKAGE_PATH='../src/api/python/z3' +Z3PY_ENABLED=True +DOTNET_ENABLED=True +JAVA_ENABLED=True def parse_options(): - global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE, TEMP_DIR, OUTPUT_DIRECTORY, Z3PY_PACKAGE_PATH + global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE, TEMP_DIR, OUTPUT_DIRECTORY + global Z3PY_PACKAGE_PATH, Z3PY_ENABLED, DOTNET_ENABLED, JAVA_ENABLED parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-b', '--build', @@ -54,6 +58,28 @@ def parse_options(): default=Z3PY_PACKAGE_PATH, help='Path to directory containing Z3py package (default: %(default)s)', ) + # FIXME: I would prefer not to have negative options (i.e. `--z3py` + # instead of `--no-z3py`) but historically these bindings have been on by + # default so we have options to disable generating documentation for these + # bindings rather than enable them. + parser.add_argument('--no-z3py', + dest='no_z3py', + action='store_true', + default=False, + help='Do not generate documentation for Python bindings', + ) + parser.add_argument('--no-dotnet', + dest='no_dotnet', + action='store_true', + default=False, + help='Do not generate documentation for .NET bindings', + ) + parser.add_argument('--no-java', + dest='no_java', + action='store_true', + default=False, + help='Do not generate documentation for Java bindings', + ) pargs = parser.parse_args() ML_ENABLED = pargs.ml BUILD_DIR = pargs.build @@ -65,6 +91,9 @@ def parse_options(): raise Exception('"{}" does not exist'.format(Z3PY_PACKAGE_PATH)) if not os.path.basename(Z3PY_PACKAGE_PATH) == 'z3': raise Exception('"{}" does not end with "z3"'.format(Z3PY_PACKAGE_PATH)) + Z3PY_ENABLED = not pargs.no_z3py + DOTNET_ENABLED = not pargs.no_dotnet + JAVA_ENABLED = not pargs.no_java return def mk_dir(d): @@ -132,18 +161,70 @@ try: 'OUTPUT_DIRECTORY': OUTPUT_DIRECTORY, 'TEMP_DIR': TEMP_DIR } + + if Z3PY_ENABLED: + print("Z3Py documentation enabled") + doxygen_config_substitutions['PYTHON_API_FILES'] = 'z3.py' + else: + print("Z3Py documentation disabled") + doxygen_config_substitutions['PYTHON_API_FILES'] = '' + if DOTNET_ENABLED: + print(".NET documentation enabled") + doxygen_config_substitutions['DOTNET_API_FILES'] = '*.cs' + doxygen_config_substitutions['DOTNET_API_SEARCH_PATHS'] = '../src/api/dotnet' + else: + print(".NET documentation disabled") + doxygen_config_substitutions['DOTNET_API_FILES'] = '' + doxygen_config_substitutions['DOTNET_API_SEARCH_PATHS'] = '' + if JAVA_ENABLED: + print("Java documentation enabled") + doxygen_config_substitutions['JAVA_API_FILES'] = '*.java' + doxygen_config_substitutions['JAVA_API_SEARCH_PATHS'] = '../src/api/java' + else: + print("Java documentation disabled") + doxygen_config_substitutions['JAVA_API_FILES'] = '' + doxygen_config_substitutions['JAVA_API_SEARCH_PATHS'] = '' + doxygen_config_file = temp_path('z3api.cfg') configure_file('z3api.cfg.in', doxygen_config_file, doxygen_config_substitutions) website_dox_substitutions = {} + bullet_point_prefix='\n - ' + if Z3PY_ENABLED: + website_dox_substitutions['PYTHON_API'] = ( + '{prefix}Python API ' + '(also available in pydoc format)' + ).format( + prefix=bullet_point_prefix) + else: + website_dox_substitutions['PYTHON_API'] = '' + if DOTNET_ENABLED: + website_dox_substitutions['DOTNET_API'] = ( + '{prefix}' + '' + '.NET API').format( + prefix=bullet_point_prefix) + else: + website_dox_substitutions['DOTNET_API'] = '' + if JAVA_ENABLED: + website_dox_substitutions['JAVA_API'] = ( + '{prefix}' + 'Java API').format( + prefix=bullet_point_prefix) + else: + website_dox_substitutions['JAVA_API'] = '' if ML_ENABLED: - website_dox_substitutions['OCAML_API'] = '\n - ML/OCaml API\n' + website_dox_substitutions['OCAML_API'] = ( + 'ML/OCaml API' + ).format( + prefix=bullet_point_prefix) else: website_dox_substitutions['OCAML_API'] = '' configure_file('website.dox.in', temp_path('website.dox'), website_dox_substitutions) mk_dir(os.path.join(OUTPUT_DIRECTORY, 'html')) - shutil.copyfile('../src/api/python/z3/z3.py', temp_path('z3py.py')) + if Z3PY_ENABLED: + shutil.copyfile('../src/api/python/z3/z3.py', temp_path('z3py.py')) cleanup_API('../src/api/z3_api.h', temp_path('z3_api.h')) cleanup_API('../src/api/z3_ast_containers.h', temp_path('z3_ast_containers.h')) cleanup_API('../src/api/z3_algebraic.h', temp_path('z3_algebraic.h')) @@ -162,15 +243,16 @@ try: except: print("ERROR: failed to execute 'doxygen', make sure doxygen (http://www.doxygen.org) is available in your system.") exit(1) - print("Generated C and .NET API documentation.") + print("Generated Doxygen based documentation") shutil.rmtree(os.path.realpath(TEMP_DIR)) print("Removed temporary directory \"{}\"".format(TEMP_DIR)) - # Put z3py at the beginning of the search path to try to avoid picking up - # an installed copy of Z3py. - sys.path.insert(0, os.path.dirname(Z3PY_PACKAGE_PATH)) - pydoc.writedoc('z3') - shutil.move('z3.html', os.path.join(OUTPUT_DIRECTORY, 'html', 'z3.html')) - print("Generated Python documentation.") + if Z3PY_ENABLED: + # Put z3py at the beginning of the search path to try to avoid picking up + # an installed copy of Z3py. + sys.path.insert(0, os.path.dirname(Z3PY_PACKAGE_PATH)) + pydoc.writedoc('z3') + shutil.move('z3.html', os.path.join(OUTPUT_DIRECTORY, 'html', 'z3.html')) + print("Generated pydoc Z3Py documentation.") if ML_ENABLED: ml_output_dir = os.path.join(OUTPUT_DIRECTORY, 'html', 'ml') diff --git a/doc/website.dox.in b/doc/website.dox.in index b00874c97..17a8552d1 100644 --- a/doc/website.dox.in +++ b/doc/website.dox.in @@ -10,10 +10,7 @@ This website hosts the automatically generated documentation for the Z3 APIs. - - \ref capi - - \ref cppapi - - .NET API - - Java API - - Python API (also available in pydoc format)@OCAML_API@ + - \ref capi + - \ref cppapi @DOTNET_API@ @JAVA_API@ @PYTHON_API@ @OCAML_API@ - Try Z3 online at RiSE4Fun. */ diff --git a/doc/z3api.cfg.in b/doc/z3api.cfg.in index cb07045b3..408e981d2 100644 --- a/doc/z3api.cfg.in +++ b/doc/z3api.cfg.in @@ -681,10 +681,9 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../src/api/dotnet \ - ../src/api/java \ +INPUT = @TEMP_DIR@ \ ../src/api/c++ \ - @TEMP_DIR@ + @DOTNET_API_SEARCH_PATHS@ @JAVA_API_SEARCH_PATHS@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -710,9 +709,7 @@ FILE_PATTERNS = website.dox \ z3_interp.h \ z3_fpa.h \ z3++.h \ - z3py.py \ - *.cs \ - *.java + @PYTHON_API_FILES@ @DOTNET_API_FILES@ @JAVA_API_FILES@ # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. From 09d7ebf1adcc3a4dfea3125f126f7a27a059797e Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 13:17:21 +0100 Subject: [PATCH 098/105] [Doxygen] Fix bug where temporary directory and output directory paths were not handled properly if paths contained spaces. --- doc/z3api.cfg.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/z3api.cfg.in b/doc/z3api.cfg.in index 408e981d2..7ccad3fbb 100644 --- a/doc/z3api.cfg.in +++ b/doc/z3api.cfg.in @@ -52,7 +52,7 @@ PROJECT_LOGO = # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. -OUTPUT_DIRECTORY = @OUTPUT_DIRECTORY@ +OUTPUT_DIRECTORY = "@OUTPUT_DIRECTORY@" # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output @@ -681,7 +681,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = @TEMP_DIR@ \ +INPUT = "@TEMP_DIR@" \ ../src/api/c++ \ @DOTNET_API_SEARCH_PATHS@ @JAVA_API_SEARCH_PATHS@ From e4bec1572aa404298abbacf7693e3f37e7453d04 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 13:36:48 +0100 Subject: [PATCH 099/105] [Doxygen] Teach `mk_api_doc.py` to allow multiple search paths for the ".NET" and "Java" bindings. The CMake build system needs this because the generated files exist in a different directory to the source files. Multiple paths can be specified using the `--dot-search-paths` and `--java-search-paths` options. --- doc/mk_api_doc.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index b61036d44..e331c60b9 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -22,10 +22,13 @@ Z3PY_PACKAGE_PATH='../src/api/python/z3' Z3PY_ENABLED=True DOTNET_ENABLED=True JAVA_ENABLED=True +DOTNET_API_SEARCH_PATHS=['../src/api/dotnet'] +JAVA_API_SEARCH_PATHS=['../src/api/java'] def parse_options(): global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE, TEMP_DIR, OUTPUT_DIRECTORY global Z3PY_PACKAGE_PATH, Z3PY_ENABLED, DOTNET_ENABLED, JAVA_ENABLED + global DOTNET_API_SEARCH_PATHS, JAVA_API_SEARCH_PATHS parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-b', '--build', @@ -80,6 +83,18 @@ def parse_options(): default=False, help='Do not generate documentation for Java bindings', ) + parser.add_argument('--dotnet-search-paths', + dest='dotnet_search_paths', + nargs='+', + default=DOTNET_API_SEARCH_PATHS, + help='Specify one or more path to look for .NET files (default: %(default)s).', + ) + parser.add_argument('--java-search-paths', + dest='java_search_paths', + nargs='+', + default=JAVA_API_SEARCH_PATHS, + help='Specify one or more paths to look for Java files (default: %(default)s).', + ) pargs = parser.parse_args() ML_ENABLED = pargs.ml BUILD_DIR = pargs.build @@ -94,6 +109,8 @@ def parse_options(): Z3PY_ENABLED = not pargs.no_z3py DOTNET_ENABLED = not pargs.no_dotnet JAVA_ENABLED = not pargs.no_java + DOTNET_API_SEARCH_PATHS = pargs.dotnet_search_paths + JAVA_API_SEARCH_PATHS = pargs.java_search_paths return def mk_dir(d): @@ -171,7 +188,11 @@ try: if DOTNET_ENABLED: print(".NET documentation enabled") doxygen_config_substitutions['DOTNET_API_FILES'] = '*.cs' - doxygen_config_substitutions['DOTNET_API_SEARCH_PATHS'] = '../src/api/dotnet' + dotnet_api_search_path_str = "" + for p in DOTNET_API_SEARCH_PATHS: + # Quote path so that paths with spaces are handled correctly + dotnet_api_search_path_str += "\"{}\" ".format(p) + doxygen_config_substitutions['DOTNET_API_SEARCH_PATHS'] = dotnet_api_search_path_str else: print(".NET documentation disabled") doxygen_config_substitutions['DOTNET_API_FILES'] = '' @@ -179,7 +200,11 @@ try: if JAVA_ENABLED: print("Java documentation enabled") doxygen_config_substitutions['JAVA_API_FILES'] = '*.java' - doxygen_config_substitutions['JAVA_API_SEARCH_PATHS'] = '../src/api/java' + java_api_search_path_str = "" + for p in JAVA_API_SEARCH_PATHS: + # Quote path so that paths with spaces are handled correctly + java_api_search_path_str += "\"{}\" ".format(p) + doxygen_config_substitutions['JAVA_API_SEARCH_PATHS'] = java_api_search_path_str else: print("Java documentation disabled") doxygen_config_substitutions['JAVA_API_FILES'] = '' From 121fd06cc2f3dec7fd2412815b9340e8dbfb90c0 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 14:36:02 +0100 Subject: [PATCH 100/105] [Doxygen] Fix `mk_api_doc.py` so it is not required that the current working directory be the `doc` directory in the source tree. --- doc/mk_api_doc.py | 39 +++++++++++++++++++++++++-------------- doc/z3api.cfg.in | 2 +- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index e331c60b9..6007b77c4 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -24,6 +24,7 @@ DOTNET_ENABLED=True JAVA_ENABLED=True DOTNET_API_SEARCH_PATHS=['../src/api/dotnet'] JAVA_API_SEARCH_PATHS=['../src/api/java'] +SCRIPT_DIR=os.path.abspath(os.path.dirname(__file__)) def parse_options(): global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE, TEMP_DIR, OUTPUT_DIRECTORY @@ -172,11 +173,15 @@ try: # Short-hand for path to temporary file def temp_path(path): return os.path.join(TEMP_DIR, path) + # Short-hand for path to file in `doc` directory + def doc_path(path): + return os.path.join(SCRIPT_DIR, path) # Create configuration file from template doxygen_config_substitutions = { 'OUTPUT_DIRECTORY': OUTPUT_DIRECTORY, - 'TEMP_DIR': TEMP_DIR + 'TEMP_DIR': TEMP_DIR, + 'CXX_API_SEARCH_PATHS': doc_path('../src/api/c++'), } if Z3PY_ENABLED: @@ -211,7 +216,10 @@ try: doxygen_config_substitutions['JAVA_API_SEARCH_PATHS'] = '' doxygen_config_file = temp_path('z3api.cfg') - configure_file('z3api.cfg.in', doxygen_config_file, doxygen_config_substitutions) + configure_file( + doc_path('z3api.cfg.in'), + doxygen_config_file, + doxygen_config_substitutions) website_dox_substitutions = {} bullet_point_prefix='\n - ' @@ -245,20 +253,23 @@ try: prefix=bullet_point_prefix) else: website_dox_substitutions['OCAML_API'] = '' - configure_file('website.dox.in', temp_path('website.dox'), website_dox_substitutions) + configure_file( + doc_path('website.dox.in'), + temp_path('website.dox'), + website_dox_substitutions) mk_dir(os.path.join(OUTPUT_DIRECTORY, 'html')) if Z3PY_ENABLED: - shutil.copyfile('../src/api/python/z3/z3.py', temp_path('z3py.py')) - cleanup_API('../src/api/z3_api.h', temp_path('z3_api.h')) - cleanup_API('../src/api/z3_ast_containers.h', temp_path('z3_ast_containers.h')) - cleanup_API('../src/api/z3_algebraic.h', temp_path('z3_algebraic.h')) - cleanup_API('../src/api/z3_polynomial.h', temp_path('z3_polynomial.h')) - cleanup_API('../src/api/z3_rcf.h', temp_path('z3_rcf.h')) - cleanup_API('../src/api/z3_fixedpoint.h', temp_path('z3_fixedpoint.h')) - cleanup_API('../src/api/z3_optimization.h', temp_path('z3_optimization.h')) - cleanup_API('../src/api/z3_interp.h', temp_path('z3_interp.h')) - cleanup_API('../src/api/z3_fpa.h', temp_path('z3_fpa.h')) + shutil.copyfile(doc_path('../src/api/python/z3/z3.py'), temp_path('z3py.py')) + cleanup_API(doc_path('../src/api/z3_api.h'), temp_path('z3_api.h')) + cleanup_API(doc_path('../src/api/z3_ast_containers.h'), temp_path('z3_ast_containers.h')) + cleanup_API(doc_path('../src/api/z3_algebraic.h'), temp_path('z3_algebraic.h')) + cleanup_API(doc_path('../src/api/z3_polynomial.h'), temp_path('z3_polynomial.h')) + cleanup_API(doc_path('../src/api/z3_rcf.h'), temp_path('z3_rcf.h')) + cleanup_API(doc_path('../src/api/z3_fixedpoint.h'), temp_path('z3_fixedpoint.h')) + cleanup_API(doc_path('../src/api/z3_optimization.h'), temp_path('z3_optimization.h')) + cleanup_API(doc_path('../src/api/z3_interp.h'), temp_path('z3_interp.h')) + cleanup_API(doc_path('../src/api/z3_fpa.h'), temp_path('z3_fpa.h')) print("Removed annotations from z3_api.h.") try: @@ -282,7 +293,7 @@ try: if ML_ENABLED: ml_output_dir = os.path.join(OUTPUT_DIRECTORY, 'html', 'ml') mk_dir(ml_output_dir) - if subprocess.call(['ocamldoc', '-html', '-d', ml_output_dir, '-sort', '-hide', 'Z3', '-I', '%s/api/ml' % BUILD_DIR, '../src/api/ml/z3enums.mli', '../src/api/ml/z3.mli']) != 0: + if subprocess.call(['ocamldoc', '-html', '-d', ml_output_dir, '-sort', '-hide', 'Z3', '-I', '%s/api/ml' % BUILD_DIR, doc_path('../src/api/ml/z3enums.mli'), doc_path('../src/api/ml/z3.mli')]) != 0: print("ERROR: ocamldoc failed.") exit(1) print("Generated ML/OCaml documentation.") diff --git a/doc/z3api.cfg.in b/doc/z3api.cfg.in index 7ccad3fbb..9e946aa7f 100644 --- a/doc/z3api.cfg.in +++ b/doc/z3api.cfg.in @@ -682,7 +682,7 @@ WARN_LOGFILE = # with spaces. INPUT = "@TEMP_DIR@" \ - ../src/api/c++ \ + "@CXX_API_SEARCH_PATHS@" \ @DOTNET_API_SEARCH_PATHS@ @JAVA_API_SEARCH_PATHS@ # This tag can be used to specify the character encoding of the source files From a6a6a9c29f96c63eec7d5efef6860553d366be21 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 15:43:03 +0100 Subject: [PATCH 101/105] [Doxygen] Fix link to ".NET" documentation it should point to the "Microsoft.Z3" namespace, not the "Microsoft.Z3.Context" class. This mirrors the link provided for the Java API. --- doc/mk_api_doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index 6007b77c4..014a152b4 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -234,7 +234,7 @@ try: if DOTNET_ENABLED: website_dox_substitutions['DOTNET_API'] = ( '{prefix}' - '' + '' '.NET API').format( prefix=bullet_point_prefix) else: From d4b7b489d072daaf268ee15f3f1517c4388cf8b3 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 25 Apr 2017 14:16:14 +0100 Subject: [PATCH 102/105] [CMake] Teach CMake to build the documentation for the API bindings and install them. The target for building the documentation is `api_docs`. This is off by default but can be enabled with the `BUILD_DOCUMENTATION` option. The C and C++ API documentation is always built but the Python, ".NET", and Java documentation are only built if they are enabled in the build system. The rationale for this is that it would be confusing to install documentation for API bindings that are not installed. By default `ALWAYS_BUILD_DOCS` is on which will slow down builds significantly but will ensure that when the `install` target is invoked the documentation is up-to-date. Unfortunately I couldn't find a better way to do this. `ALWAYS_BUILD_DOCS` can be disabled to get faster builds and still have the `api_docs` target available. --- CMakeLists.txt | 16 +++++- README-CMake.md | 7 +++ contrib/cmake/doc/CMakeLists.txt | 93 ++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 contrib/cmake/doc/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index cf46e7012..47a081e75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,10 +43,13 @@ message(STATUS "Z3 version ${Z3_VERSION}") # Set various useful variables depending on CMake version ################################################################################ if (("${CMAKE_VERSION}" VERSION_EQUAL "3.2") OR ("${CMAKE_VERSION}" VERSION_GREATER "3.2")) - # In CMake >= 3.2 add_custom_command() supports a ``USES_TERMINAL`` argument + # In CMake >= 3.2 add_custom_command() and add_custom_target() + # supports a ``USES_TERMINAL`` argument set(ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG "USES_TERMINAL") + set(ADD_CUSTOM_TARGET_USES_TERMINAL_ARG "USES_TERMINAL") else() set(ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG "") + set(ADD_CUSTOM_TARGET_USES_TERMINAL_ARG "") endif() ################################################################################ @@ -528,3 +531,14 @@ option(ENABLE_EXAMPLE_TARGETS "Build Z3 api examples" ON) if (ENABLE_EXAMPLE_TARGETS) add_subdirectory(examples) endif() + +################################################################################ +# Documentation +################################################################################ +option(BUILD_DOCUMENTATION "Build API documentation" OFF) +if (BUILD_DOCUMENTATION) + message(STATUS "Building documentation enabled") + add_subdirectory(doc) +else() + message(STATUS "Building documentation disabled") +endif() diff --git a/README-CMake.md b/README-CMake.md index 6a78b5d4c..4bbbd36a8 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -268,6 +268,7 @@ The following useful options can be passed to CMake whilst configuring. * ``CMAKE_INSTALL_PKGCONFIGDIR`` - STRING. The path to install pkgconfig files. * ``CMAKE_INSTALL_PYTHON_PKG_DIR`` - STRING. The path to install the z3 python bindings. This can be relative (to ``CMAKE_INSTALL_PREFIX``) or absolute. * ``CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR`` - STRING. The path to install CMake package files (e.g. ``/usr/lib/cmake/z3``). +* ``CMAKE_INSTALL_API_BINDINGS_DOC`` - STRING. The path to install documentation for API bindings. * ``ENABLE_TRACING_FOR_NON_DEBUG`` - BOOL. If set to ``TRUE`` enable tracing in non-debug builds, if set to ``FALSE`` disable tracing in non-debug builds. Note in debug builds tracing is always enabled. * ``BUILD_LIBZ3_SHARED`` - BOOL. If set to ``TRUE`` build libz3 as a shared library otherwise build as a static library. * ``ENABLE_EXAMPLE_TARGETS`` - BOOL. If set to ``TRUE`` add the build targets for building the API examples. @@ -286,6 +287,11 @@ The following useful options can be passed to CMake whilst configuring. * ``Z3_JAVA_JNI_LIB_INSTALLDIRR`` - STRING. The path to directory to install the Z3 Java JNI bridge library. This path should be relative to ``CMAKE_INSTALL_PREFIX``. * ``INCLUDE_GIT_DESCRIBE`` - BOOL. If set to ``TRUE`` and the source tree of Z3 is a git repository then the output of ``git describe`` will be included in the build. * ``INCLUDE_GIT_HASH`` - BOOL. If set to ``TRUE`` and the source tree of Z3 is a git repository then the git hash will be included in the build. +* ``BUILD_DOCUMENTATION`` - BOOL. If set to ``TRUE`` then documentation for the API bindings can be built by invoking the ``api_docs`` target. +* ``INSTALL_API_BINDINGS_DOCUMENTATION`` - BOOL. If set to ``TRUE`` and ``BUILD_DOCUMENTATION` is ``TRUE`` then documentation for API bindings will be installed + when running the ``install`` target. +* ``ALWAYS_BUILD_DOCS`` - BOOL. If set to ``TRUE`` and ``BUILD_DOCUMENTATION`` is ``TRUE`` then documentation for API bindings will always be built. + Disabling this is useful for faster incremental builds. The documentation can be manually built by invoking the ``api_docs`` target. On the command line these can be passed to ``cmake`` using the ``-D`` option. In ``ccmake`` and ``cmake-gui`` these can be set in the user interface. @@ -417,6 +423,7 @@ There are a few special targets: * ``clean`` all the built targets in the current directory and below * ``edit_cache`` will invoke one of the CMake tools (depending on which is available) to let you change configuration options. * ``rebuild_cache`` will reinvoke ``cmake`` for the project. +* ``api_docs`` will build the documentation for the API bindings. ### Setting build type specific flags diff --git a/contrib/cmake/doc/CMakeLists.txt b/contrib/cmake/doc/CMakeLists.txt new file mode 100644 index 000000000..86e208ab1 --- /dev/null +++ b/contrib/cmake/doc/CMakeLists.txt @@ -0,0 +1,93 @@ +find_package(Doxygen REQUIRED) +message(STATUS "DOXYGEN_EXECUTABLE: \"${DOXYGEN_EXECUTABLE}\"") +message(STATUS "DOXYGEN_VERSION: \"${DOXYGEN_VERSION}\"") + +set(DOC_DEST_DIR "${CMAKE_CURRENT_BINARY_DIR}/api") +set(DOC_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/temp") +set(MK_API_DOC_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/mk_api_doc.py") + +set(PYTHON_API_OPTIONS "") +set(DOTNET_API_OPTIONS "") +set(JAVA_API_OPTIONS "") +SET(DOC_EXTRA_DEPENDS "") + +if (BUILD_PYTHON_BINDINGS) + # FIXME: Don't hard code this path + list(APPEND PYTHON_API_OPTIONS "--z3py-package-path" "${CMAKE_BINARY_DIR}/python/z3") + list(APPEND DOC_EXTRA_DEPENDS "build_z3_python_bindings") +else() + list(APPEND PYTHON_API_OPTIONS "--no-z3py") +endif() + +if (BUILD_DOTNET_BINDINGS) + # FIXME: Don't hard code these paths + list(APPEND DOTNET_API_OPTIONS "--dotnet-search-paths" + "${CMAKE_SOURCE_DIR}/src/api/dotnet" + "${CMAKE_BINARY_DIR}/src/api/dotnet" + ) + list(APPEND DOC_EXTRA_DEPENDS "build_z3_dotnet_bindings") +else() + list(APPEND DOTNET_API_OPTIONS "--no-dotnet") +endif() + +if (BUILD_JAVA_BINDINGS) + # FIXME: Don't hard code these paths + list(APPEND JAVA_API_OPTIONS "--java-search-paths" + "${CMAKE_SOURCE_DIR}/src/api/java" + "${CMAKE_BINARY_DIR}/src/api/java" + ) + list(APPEND DOC_EXTRA_DEPENDS "build_z3_java_bindings") +else() + list(APPEND JAVA_API_OPTIONS "--no-java") +endif() + +option(ALWAYS_BUILD_DOCS "Always build documentation for API bindings" ON) +if (ALWAYS_BUILD_DOCS) + set(ALWAYS_BUILD_DOCS_ARG "ALL") +else() + set(ALWAYS_BUILD_DOCS_ARG "") + # FIXME: This sucks but there doesn't seem to be a way to make the top level + # install target depend on the `api_docs` target. + message(WARNING "Building documentation for API bindings is not part of the" + " all target. This may result in stale files being installed when running" + " the install target. You should run the api_docs target before running" + " the install target. Alternatively Set ALWAYS_BUILD_DOCS to ON to" + " automatically build documentation when running the install target." + ) +endif() + +add_custom_target(api_docs ${ALWAYS_BUILD_DOCS_ARG} + COMMAND + "${PYTHON_EXECUTABLE}" "${MK_API_DOC_SCRIPT}" + --doxygen-executable "${DOXYGEN_EXECUTABLE}" + --output-dir "${DOC_DEST_DIR}" + --temp-dir "${DOC_TEMP_DIR}" + ${PYTHON_API_OPTIONS} + ${DOTNET_API_OPTIONS} + ${JAVA_API_OPTIONS} + DEPENDS + ${DOC_EXTRA_DEPENDS} + BYPRODUCTS "${DOC_DEST_DIR}" + COMMENT "Generating documentation" + ${ADD_CUSTOM_TARGET_USES_TERMINAL_ARG} +) + +# Remove generated documentation when running `clean` target. +set_property(DIRECTORY APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES + "${DOC_DEST_DIR}" +) + +option(INSTALL_API_BINDINGS_DOCUMENTATION "Install documentation for API bindings" ON) +set(CMAKE_INSTALL_API_BINDINGS_DOC + "${CMAKE_INSTALL_DOCDIR}" + CACHE + PATH + "Path to install documentation for API bindings" +) +if (INSTALL_API_BINDINGS_DOCUMENTATION) + install( + DIRECTORY "${DOC_DEST_DIR}" + DESTINATION "${CMAKE_INSTALL_API_BINDINGS_DOC}" + ) +endif() From 8032217fd1f190c06ff8e4f243cf5b7b566bc3ca Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 26 Apr 2017 13:53:37 -0700 Subject: [PATCH 103/105] tuning and fixing consequence finding, adding dimacs evaluation Signed-off-by: Nikolaj Bjorner --- contrib/cmake/src/test/CMakeLists.txt | 1 + src/sat/sat_solver.cpp | 18 ++++- src/sat/sat_solver.h | 1 + src/test/cnf_backbones.cpp | 106 ++++++++++++++++++++++++++ src/test/ddnf.cpp | 1 - src/test/main.cpp | 1 + 6 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/test/cnf_backbones.cpp diff --git a/contrib/cmake/src/test/CMakeLists.txt b/contrib/cmake/src/test/CMakeLists.txt index 6ea07e84c..46781b2cc 100644 --- a/contrib/cmake/src/test/CMakeLists.txt +++ b/contrib/cmake/src/test/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(test-z3 bv_simplifier_plugin.cpp chashtable.cpp check_assumptions.cpp + cnf_backbones.cpp datalog_parser.cpp ddnf.cpp diff_logic.cpp diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 9031ad6b6..739a591e7 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -3284,13 +3284,17 @@ namespace sat { checkpoint(); literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); unsigned num_resolves = 0; + unsigned num_fixed = 0; + unsigned num_assigned = 0; lbool is_sat = l_true; for (; it != end; ++it) { - literal lit = *it; + literal lit = *it; if (value(lit) != l_undef) { + ++num_fixed; continue; } push(); + ++num_assigned; assign(~lit, justification()); propagate(false); while (inconsistent()) { @@ -3307,8 +3311,18 @@ namespace sat { break; } } + if (scope_lvl() == 1) { + it = unfixed_lits.begin(); + for (; it != end; ++it) { + literal lit = *it; + if (value(lit) == l_true) { + VERIFY(extract_fixed_consequences(lit, assumptions, unfixed_vars, conseq)); + } + } + } if (is_sat == l_true) { if (scope_lvl() == 1 && num_resolves > 0) { + IF_VERBOSE(1, verbose_stream() << "(sat.get-consequences backjump)\n";); is_sat = l_undef; } else { @@ -3331,6 +3345,8 @@ namespace sat { << " iterations: " << num_iterations << " variables: " << unfixed_lits.size() << " fixed: " << conseq.size() + << " status: " << is_sat + << " pre-assigned: " << num_fixed << " unfixed: " << lits.size() - conseq.size() - unfixed_lits.size() << ")\n";); diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 42291609d..c7f472a60 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -247,6 +247,7 @@ namespace sat { unsigned num_clauses() const; unsigned num_restarts() const { return m_restarts; } bool is_external(bool_var v) const { return m_external[v] != 0; } + void set_external(bool_var v) { m_external[v] = true; } bool was_eliminated(bool_var v) const { return m_eliminated[v] != 0; } unsigned scope_lvl() const { return m_scope_lvl; } lbool value(literal l) const { return static_cast(m_assignment[l.index()]); } diff --git a/src/test/cnf_backbones.cpp b/src/test/cnf_backbones.cpp new file mode 100644 index 000000000..c562cfe58 --- /dev/null +++ b/src/test/cnf_backbones.cpp @@ -0,0 +1,106 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +--*/ +#include +#include +#include +#include"timeout.h" +#include"rlimit.h" +#include"dimacs.h" +#include"sat_solver.h" +#include"gparams.h" + +static sat::solver * g_solver = 0; +static clock_t g_start_time; + +static void display_statistics() { + clock_t end_time = clock(); + if (g_solver) { + std::cout.flush(); + std::cerr.flush(); + + statistics st; + g_solver->collect_statistics(st); + st.update("total time", ((static_cast(end_time) - static_cast(g_start_time)) / CLOCKS_PER_SEC)); + st.display_smt2(std::cout); + } +} + +static void on_timeout() { + display_statistics(); + exit(0); +} + +static void STD_CALL on_ctrl_c(int) { + signal (SIGINT, SIG_DFL); + display_statistics(); + raise(SIGINT); +} + +static void display_model(sat::solver const & s) { + sat::model const & m = s.get_model(); + for (unsigned i = 1; i < m.size(); i++) { + switch (m[i]) { + case l_false: std::cout << "-" << i << " "; break; + case l_undef: break; + case l_true: std::cout << i << " "; break; + } + } + std::cout << "\n"; +} + + +static void cnf_backbones(char const* file_name) { + g_start_time = clock(); + register_on_timeout_proc(on_timeout); + signal(SIGINT, on_ctrl_c); + params_ref p = gparams::get_module("sat"); + p.set_bool("produce_models", true); + reslimit limit; + sat::solver solver(p, limit, 0); + g_solver = &solver; + + if (file_name) { + std::ifstream in(file_name); + if (in.bad() || in.fail()) { + std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; + exit(ERR_OPEN_FILE); + } + parse_dimacs(in, solver); + } + else { + parse_dimacs(std::cin, solver); + } + IF_VERBOSE(20, solver.display_status(verbose_stream());); + + vector conseq; + sat::bool_var_vector vars; + sat::literal_vector assumptions; + for (unsigned i = 1; i < solver.num_vars(); ++i) { + vars.push_back(i); + solver.set_external(i); + } + lbool r = solver.get_consequences(assumptions, vars, conseq); + + switch (r) { + case l_true: + std::cout << "sat\n"; + std::cout << vars.size() << " " << conseq.size() << "\n"; + break; + case l_undef: + std::cout << "unknown\n"; + break; + case l_false: + std::cout << "unsat\n"; + break; + } + display_statistics(); +} + +void tst_cnf_backbones(char ** argv, int argc, int& i) { + if (i + 1 < argc) { + cnf_backbones(argv[i + 1]); + ++i; + } +} diff --git a/src/test/ddnf.cpp b/src/test/ddnf.cpp index d2ca92557..31503842d 100644 --- a/src/test/ddnf.cpp +++ b/src/test/ddnf.cpp @@ -1,4 +1,3 @@ - /*++ Copyright (c) 2015 Microsoft Corporation diff --git a/src/test/main.cpp b/src/test/main.cpp index 9239d0119..4a54797ac 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -229,6 +229,7 @@ int main(int argc, char ** argv) { TST(model_evaluator); TST(get_consequences); TST(pb2bv); + TST_ARGV(cnf_backbones); //TST_ARGV(hs); } From a048d74bae705b38c9155cf9758b606704f00942 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 26 Apr 2017 14:05:33 -0700 Subject: [PATCH 104/105] adding interval designator to output of non-optimal objectives, fix python doc test Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 2 +- src/opt/opt_context.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 568ffe9a2..b4cdf834b 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -6194,7 +6194,7 @@ class Solver(Z3PPObject): >>> s.consequences([a],[b,c,d]) (sat, [Implies(a, b), Implies(a, c)]) >>> s.consequences([Not(c),d],[a,b,c,d]) - (sat, [Implies(Not(c), Not(c)), Implies(d, d), Implies(Not(c), Not(b)), Implies(Not(c), Not(a))]) + (sat, [Implies(d, d), Implies(Not(c), Not(c)), Implies(Not(c), Not(b)), Implies(Not(c), Not(a))]) """ if isinstance(assumptions, list): _asms = AstVector(None, self.ctx) diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index af3c57baa..1310727aa 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -1235,7 +1235,7 @@ namespace opt { out << " ("; display_objective(out, obj); if (get_lower_as_num(i) != get_upper_as_num(i)) { - out << " (" << get_lower(i) << " " << get_upper(i) << ")"; + out << " (interval " << get_lower(i) << " " << get_upper(i) << ")"; } else { out << " " << get_lower(i); From d3b30968fa3eba7b50ff27d538cea48d9777659f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 26 Apr 2017 16:55:56 -0700 Subject: [PATCH 105/105] added chunk based backbone utility Signed-off-by: Nikolaj Bjorner --- src/test/cnf_backbones.cpp | 142 +++++++++++++++++++++++++++++++++---- 1 file changed, 127 insertions(+), 15 deletions(-) diff --git a/src/test/cnf_backbones.cpp b/src/test/cnf_backbones.cpp index c562cfe58..a4eaa4222 100644 --- a/src/test/cnf_backbones.cpp +++ b/src/test/cnf_backbones.cpp @@ -50,8 +50,123 @@ static void display_model(sat::solver const & s) { std::cout << "\n"; } +static void display_status(lbool r) { + switch (r) { + case l_true: + std::cout << "sat\n"; + break; + case l_undef: + std::cout << "unknown\n"; + break; + case l_false: + std::cout << "unsat\n"; + break; + } +} -static void cnf_backbones(char const* file_name) { +static void prune_unfixed(sat::literal_vector& lambda, sat::model const& m) { + for (unsigned i = 0; i < lambda.size(); ++i) { + if ((m[lambda[i].var()] == l_false) != lambda[i].sign()) { + lambda[i] = lambda.back(); + lambda.pop_back(); + --i; + } + } +} + +// Algorithm 7: Corebased Algorithm with Chunking + +static void back_remove(sat::literal_vector& lits, sat::literal l) { + for (unsigned i = lits.size(); i > 0; ) { + --i; + if (lits[i] == l) { + lits[i] = lits.back(); + lits.pop_back(); + return; + } + } + std::cout << "UNREACHABLE\n"; +} + +static void brute_force_consequences(sat::solver& s, sat::literal_vector const& gamma, sat::literal_vector& backbones) { + for (unsigned i = 0; i < gamma.size(); ++i) { + sat::literal nlit = ~gamma[i]; + lbool r = s.check(1, &nlit); + if (r == l_false) { + backbones.push_back(gamma[i]); + } + } +} + +static lbool core_chunking(sat::solver& s, sat::bool_var_vector& vars, vector& conseq, unsigned K) { + lbool r = s.check(); + display_status(r); + if (r != l_true) { + return r; + } + sat::model const & m = s.get_model(); + sat::literal_vector lambda, backbones; + for (unsigned i = 1; i < m.size(); i++) { + lambda.push_back(sat::literal(i, m[i] == l_false)); + } + while (!lambda.empty()) { + IF_VERBOSE(1, verbose_stream() << "(sat-backbone-core " << lambda.size() << " " << backbones.size() << ")\n";); + unsigned k = std::min(K, lambda.size()); + sat::literal_vector gamma, omegaN; + for (unsigned i = 0; i < k; ++i) { + sat::literal l = lambda[lambda.size() - i - 1]; + gamma.push_back(l); + omegaN.push_back(~l); + } + while (true) { + r = s.check(omegaN.size(), omegaN.c_ptr()); + if (r == l_true) { + IF_VERBOSE(1, verbose_stream() << "(sat) " << omegaN << "\n";); + prune_unfixed(lambda, s.get_model()); + break; + } + sat::literal_vector const& core = s.get_core(); + sat::literal_vector occurs; + IF_VERBOSE(1, verbose_stream() << "(core " << core.size() << ")\n";); + for (unsigned i = 0; i < omegaN.size(); ++i) { + if (core.contains(omegaN[i])) { + occurs.push_back(omegaN[i]); + } + } + if (occurs.size() == 1) { + sat::literal lit = occurs.back(); + sat::literal nlit = ~lit; + backbones.push_back(~lit); + back_remove(lambda, ~lit); + back_remove(gamma, ~lit); + s.mk_clause(1, &nlit); + } + for (unsigned i = 0; i < omegaN.size(); ++i) { + if (occurs.contains(omegaN[i])) { + omegaN[i] = omegaN.back(); + omegaN.pop_back(); + --i; + } + } + if (omegaN.empty() && occurs.size() > 1) { + brute_force_consequences(s, gamma, backbones); + for (unsigned i = 0; i < gamma.size(); ++i) { + back_remove(lambda, gamma[i]); + } + break; + } + } + } + for (unsigned i = 0; i < backbones.size(); ++i) { + sat::literal_vector cons; + cons.push_back(backbones[i]); + conseq.push_back(cons); + } + return l_true; +} + + +static void cnf_backbones(bool use_chunk, char const* file_name) { g_start_time = clock(); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); @@ -81,26 +196,23 @@ static void cnf_backbones(char const* file_name) { vars.push_back(i); solver.set_external(i); } - lbool r = solver.get_consequences(assumptions, vars, conseq); - - switch (r) { - case l_true: - std::cout << "sat\n"; - std::cout << vars.size() << " " << conseq.size() << "\n"; - break; - case l_undef: - std::cout << "unknown\n"; - break; - case l_false: - std::cout << "unsat\n"; - break; + lbool r; + if (use_chunk) { + r = core_chunking(solver, vars, conseq, 100); } + else { + r = solver.get_consequences(assumptions, vars, conseq); + } + std::cout << vars.size() << " " << conseq.size() << "\n"; + display_status(r); display_statistics(); } void tst_cnf_backbones(char ** argv, int argc, int& i) { if (i + 1 < argc) { - cnf_backbones(argv[i + 1]); + bool use_chunk = (i + 2 < argc && argv[i + 2] == std::string("chunk")); + cnf_backbones(use_chunk, argv[i + 1]); ++i; + if (use_chunk) ++i; } }